pax_global_header00006660000000000000000000000064152251116110014505gustar00rootroot0000000000000052 comment=909affc32a13d14ae3a7ccf44d325ae63ce11baf apparmor-5.0.2/000077500000000000000000000000001522511161100133325ustar00rootroot00000000000000apparmor-5.0.2/.gitignore000066400000000000000000000262411522511161100153270ustar00rootroot00000000000000apparmor-* cscope.* binutils/aa-enabled binutils/aa-enabled.1 binutils/aa-exec binutils/aa-exec.1 binutils/aa-features-abi binutils/aa-features-abi.1 binutils/aa-load binutils/aa-load.8 binutils/aa-status binutils/aa-status.8 binutils/cJSON.o binutils/po/*.mo changehat/mod_apparmor/.libs changehat/mod_apparmor/mod_apparmor.8 changehat/mod_apparmor/mod_apparmor.8.html changehat/mod_apparmor/mod_apparmor.la changehat/mod_apparmor/mod_apparmor.lo changehat/mod_apparmor/mod_apparmor.slo changehat/mod_apparmor/mod_apparmor.so changehat/mod_apparmor/pod2htmd.tmp changehat/pam_apparmor/get_options.o changehat/pam_apparmor/pam_apparmor.o changehat/pam_apparmor/pam_apparmor.so init/aa-teardown.8 init/aa-teardown.8.html init/pod2htmd.tmp parser/po/*.mo parser/af_names.h parser/cap_names.h parser/generated_cap_names.h parser/generated_af_names.h parser/errnos.h parser/tst_lib parser/tst_misc parser/tst_regex parser/tst_symtab parser/tst_variable parser/tst/simple_tests/generated_*/* parser/parser_lex.c parser/parser_version.h parser/parser_yacc.c parser/parser_yacc.h parser/pod2htm*.tmp parser/libapparmor_re/*.o parser/libapparmor_re/libapparmor_re.a parser/*.o parser/*.7 parser/*.5 parser/*.8 parser/*.7.html parser/*.5.html parser/*.8.html parser/apparmor_parser parser/libapparmor_re/parse.cc parser/libapparmor_re/regexp.cc parser/techdoc.aux parser/techdoc.log parser/techdoc.pdf parser/techdoc.toc profiles/apparmor.d/local/* !profiles/apparmor.d/local/README libraries/libapparmor/*~ libraries/libapparmor/Makefile libraries/libapparmor/Makefile.in libraries/libapparmor/aclocal.m4 libraries/libapparmor/ar-lib libraries/libapparmor/audit.log libraries/libapparmor/autom4te.cache libraries/libapparmor/compile libraries/libapparmor/config.guess libraries/libapparmor/config.log libraries/libapparmor/config.status libraries/libapparmor/config.sub libraries/libapparmor/configure libraries/libapparmor/depcomp libraries/libapparmor/install-sh libraries/libapparmor/libtool libraries/libapparmor/ltmain.sh libraries/libapparmor/missing libraries/libapparmor/test-driver libraries/libapparmor/ylwrap libraries/libapparmor/doc/Makefile libraries/libapparmor/doc/Makefile.in libraries/libapparmor/doc/*.2 libraries/libapparmor/doc/aa_*.3 libraries/libapparmor/include/Makefile libraries/libapparmor/include/Makefile.in libraries/libapparmor/include/sys/Makefile libraries/libapparmor/include/sys/Makefile.in libraries/libapparmor/src/.deps libraries/libapparmor/src/.libs libraries/libapparmor/src/Makefile libraries/libapparmor/src/Makefile.in libraries/libapparmor/src/PMurHash.lo libraries/libapparmor/src/PMurHash.o libraries/libapparmor/src/af_protos.h libraries/libapparmor/src/change_hat.lo libraries/libapparmor/src/features.lo libraries/libapparmor/src/features.o libraries/libapparmor/src/grammar.lo libraries/libapparmor/src/grammar.o libraries/libapparmor/src/kernel.lo libraries/libapparmor/src/kernel.o libraries/libapparmor/src/kernel_interface.lo libraries/libapparmor/src/kernel_interface.o libraries/libapparmor/src/libaalogparse.lo libraries/libapparmor/src/libaalogparse.o libraries/libapparmor/src/libimmunix_warning.lo libraries/libapparmor/src/policy_cache.lo libraries/libapparmor/src/policy_cache.o libraries/libapparmor/src/private.lo libraries/libapparmor/src/private.o libraries/libapparmor/src/scanner.lo libraries/libapparmor/src/scanner.o libraries/libapparmor/src/libapparmor.pc libraries/libapparmor/src/libapparmor.la libraries/libapparmor/src/libimmunix.la libraries/libapparmor/src/grammar.c libraries/libapparmor/src/grammar.h libraries/libapparmor/src/scanner.c libraries/libapparmor/src/scanner.h libraries/libapparmor/src/test-suite.log libraries/libapparmor/src/tst_aalogmisc libraries/libapparmor/src/tst_aalogmisc.log libraries/libapparmor/src/tst_aalogmisc.o libraries/libapparmor/src/tst_aalogmisc.trs libraries/libapparmor/src/tst_aalogparse_cpp libraries/libapparmor/src/tst_aalogparse_cpp.log libraries/libapparmor/src/tst_aalogparse_cpp.o libraries/libapparmor/src/tst_aalogparse_cpp.trs libraries/libapparmor/src/tst_aalogparse_reentrancy libraries/libapparmor/src/tst_aalogparse_reentrancy.log libraries/libapparmor/src/tst_aalogparse_reentrancy.o libraries/libapparmor/src/tst_aalogparse_reentrancy.trs libraries/libapparmor/src/tst_aalogparse_oldname libraries/libapparmor/src/tst_aalogparse_oldname.log libraries/libapparmor/src/tst_aalogparse_oldname.o libraries/libapparmor/src/tst_aalogparse_oldname.trs libraries/libapparmor/src/tst_features libraries/libapparmor/src/tst_features.log libraries/libapparmor/src/tst_features.o libraries/libapparmor/src/tst_features.trs libraries/libapparmor/src/tst_kernel libraries/libapparmor/src/tst_kernel.log libraries/libapparmor/src/tst_kernel.o libraries/libapparmor/src/tst_kernel.trs libraries/libapparmor/swig/Makefile libraries/libapparmor/swig/Makefile.in libraries/libapparmor/swig/perl/LibAppArmor.bs libraries/libapparmor/swig/perl/LibAppArmor.pm libraries/libapparmor/swig/perl/Makefile libraries/libapparmor/swig/perl/Makefile.PL libraries/libapparmor/swig/perl/Makefile.in libraries/libapparmor/swig/perl/Makefile.perl libraries/libapparmor/swig/perl/Makefile.perle libraries/libapparmor/swig/perl/MYMETA.json libraries/libapparmor/swig/perl/MYMETA.yml libraries/libapparmor/swig/perl/blib libraries/libapparmor/swig/perl/libapparmor_wrap.c libraries/libapparmor/swig/perl/libapparmor_wrap.o libraries/libapparmor/swig/perl/pm_to_blib libraries/libapparmor/swig/python/LibAppArmor.py libraries/libapparmor/swig/python/LibAppArmor.egg-info/ libraries/libapparmor/swig/python/build/ libraries/libapparmor/swig/python/libapparmor_wrap.c libraries/libapparmor/swig/python/Makefile libraries/libapparmor/swig/python/Makefile.in libraries/libapparmor/swig/python/setup.py libraries/libapparmor/swig/python/test/Makefile libraries/libapparmor/swig/python/test/Makefile.in libraries/libapparmor/swig/python/test/test-suite.log libraries/libapparmor/swig/python/test/test_python.py libraries/libapparmor/swig/python/test/test_python.py.log libraries/libapparmor/swig/python/test/test_python.py.trs libraries/libapparmor/swig/ruby/LibAppArmor.so libraries/libapparmor/swig/ruby/LibAppArmor_wrap.c libraries/libapparmor/swig/ruby/LibAppArmor_wrap.o libraries/libapparmor/swig/ruby/Makefile libraries/libapparmor/swig/ruby/Makefile.in libraries/libapparmor/swig/ruby/Makefile.bak libraries/libapparmor/swig/ruby/Makefile.ruby libraries/libapparmor/swig/ruby/mkmf.log libraries/libapparmor/testsuite/.deps libraries/libapparmor/testsuite/.libs libraries/libapparmor/testsuite/Makefile libraries/libapparmor/testsuite/Makefile.in libraries/libapparmor/testsuite/libaalogparse.log libraries/libapparmor/testsuite/libaalogparse.sum libraries/libapparmor/testsuite/site.exp libraries/libapparmor/testsuite/test_multi.multi libraries/libapparmor/testsuite/config/Makefile libraries/libapparmor/testsuite/config/Makefile.in libraries/libapparmor/testsuite/lib/Makefile libraries/libapparmor/testsuite/lib/Makefile.in libraries/libapparmor/testsuite/libaalogparse.test/Makefile libraries/libapparmor/testsuite/libaalogparse.test/Makefile.in libraries/libapparmor/testsuite/test_multi/out libraries/libapparmor/testsuite/test_multi_multi-test_multi.o utils/*.8 utils/*.8.html utils/*.5 utils/*.5.html utils/*.tmp utils/po/*.mo utils/apparmor/*.pyc utils/apparmor/rule/*.pyc utils/apparmor.egg-info/ utils/build/ utils/htmlcov/ utils/test/common_test.pyc utils/test/.coverage utils/test/coverage-report.txt utils/test/htmlcov/ utils/test/pod2htmd.tmp utils/vim/apparmor.vim utils/vim/apparmor.vim.5 utils/vim/apparmor.vim.5.html utils/vim/pod2htmd.tmp tests/regression/apparmor/*.o tests/regression/apparmor/aa_policy_cache tests/regression/apparmor/access tests/regression/apparmor/at_secure tests/regression/apparmor/attach_disconnected tests/regression/apparmor/changehat tests/regression/apparmor/changehat_fail tests/regression/apparmor/changehat_fork tests/regression/apparmor/changehat_misc tests/regression/apparmor/changehat_misc2 tests/regression/apparmor/changehat_pthread tests/regression/apparmor/changehat_twice tests/regression/apparmor/changehat_wrapper tests/regression/apparmor/changeprofile tests/regression/apparmor/chdir tests/regression/apparmor/chgrp tests/regression/apparmor/chmod tests/regression/apparmor/chown tests/regression/apparmor/clone tests/regression/apparmor/complain tests/regression/apparmor/dbus_eavesdrop tests/regression/apparmor/dbus_message tests/regression/apparmor/dbus_service tests/regression/apparmor/dbus_unrequested_reply tests/regression/apparmor/deleted tests/regression/apparmor/disconnected_mount_complain tests/regression/apparmor/env_check tests/regression/apparmor/environ tests/regression/apparmor/exec tests/regression/apparmor/exec_qual tests/regression/apparmor/exec_qual2 tests/regression/apparmor/fchdir tests/regression/apparmor/fchgrp tests/regression/apparmor/fchmod tests/regression/apparmor/fchown tests/regression/apparmor/fd_inheritance tests/regression/apparmor/fd_inheritor tests/regression/apparmor/fork tests/regression/apparmor/getcon_verify tests/regression/apparmor/introspect tests/regression/apparmor/io_uring tests/regression/apparmor/link tests/regression/apparmor/link_subset tests/regression/apparmor/linkat_tmpfile tests/regression/apparmor/mkdir tests/regression/apparmor/mmap tests/regression/apparmor/mount tests/regression/apparmor/move_mount tests/regression/apparmor/named_pipe tests/regression/apparmor/net_inet_rcv tests/regression/apparmor/net_inet_snd tests/regression/apparmor/net_raw tests/regression/apparmor/open tests/regression/apparmor/openat tests/regression/apparmor/pipe tests/regression/apparmor/pivot_root tests/regression/apparmor/posix_mq_rcv tests/regression/apparmor/posix_mq_snd tests/regression/apparmor/ptrace tests/regression/apparmor/ptrace_helper tests/regression/apparmor/pwrite tests/regression/apparmor/query_label tests/regression/apparmor/readdir tests/regression/apparmor/rename tests/regression/apparmor/rw tests/regression/apparmor/socketpair tests/regression/apparmor/swap tests/regression/apparmor/symlink tests/regression/apparmor/syscall_chroot tests/regression/apparmor/syscall_ioperm tests/regression/apparmor/syscall_iopl tests/regression/apparmor/syscall_mknod tests/regression/apparmor/syscall_mlockall tests/regression/apparmor/syscall_ptrace tests/regression/apparmor/syscall_reboot tests/regression/apparmor/syscall_setdomainname tests/regression/apparmor/syscall_sethostname tests/regression/apparmor/syscall_setpriority tests/regression/apparmor/syscall_setscheduler tests/regression/apparmor/syscall_sysctl tests/regression/apparmor/sysctl_proc tests/regression/apparmor/sysv_mq_rcv tests/regression/apparmor/sysv_mq_snd tests/regression/apparmor/tcp tests/regression/apparmor/transition tests/regression/apparmor/unix_fd_client tests/regression/apparmor/unix_fd_server tests/regression/apparmor/unix_socket tests/regression/apparmor/unix_socket_client tests/regression/apparmor/unlink tests/regression/apparmor/userns tests/regression/apparmor/userns_setns tests/regression/apparmor/uservars.inc tests/regression/apparmor/xattrs tests/regression/apparmor/xattrs_profile tests/regression/apparmor/coredump **/__pycache__/ *.orig # Patterns related to spread integration tests *.img *.iso *.lock *.log *.qcow2 *.run .spread-reuse.yaml .spread-reuse.*.yaml spread-artifacts/ spread-logs/ apparmor-5.0.2/.gitlab-ci.yml000066400000000000000000000311051522511161100157660ustar00rootroot00000000000000spec: inputs: build-test-images: default: false type: boolean description: Explicitly build virtual machine images used by integration tests. --- image: ubuntu:latest # XXX - add a deploy stage to publish man pages, docs, and coverage # reports workflow: rules: - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == 'push' when: never - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - if: $CI_COMMIT_TAG - if: $CI_COMMIT_BRANCH stages: - build - test - spread .ubuntu-common: interruptible: true before_script: # Install build-dependencies by loading the package list from the ubuntu/debian cloud-init profile. - printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" install_deps "Installing dependencies..." - apt-get update -qq - apt-get install --yes yq make lsb-release libzstd-dev - | printf 'include .image-garden.mk\n$(info $(UBUNTU_CLOUD_INIT_USER_DATA_TEMPLATE))\n.PHONY: nothing\nnothing:\n' \ | make -f - nothing \ | yq '.packages | .[]' \ | xargs apt-get install --yes --no-install-recommends - printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" install_deps after_script: # Inspect the kernel and lsb-release. - lsb_release -a - uname -a build-all: stage: build extends: - .ubuntu-common script: # Run the spread prepare section to build everything. - yq -r '.prepare' shellcheck.xml" artifacts: when: always reports: junit: shellcheck.xml # Disabled due to aa-logprof dependency on /sbin/apparmor_parser existing # - make -C profiles check-profiles # test-pam_apparmor: # - stage: test # - script: # - cd changehat/pam_apparmor && make check include: - template: SAST.gitlab-ci.yml - template: Secret-Detection.gitlab-ci.yml variables: SAST_EXCLUDED_ANALYZERS: "eslint,flawfinder,semgrep,spotbugs" SAST_BANDIT_EXCLUDED_PATHS: "*/tst/*, */test/*" coverity: stage: .post interruptible: true extends: - .ubuntu-common script: - printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" install_extra_deps "Installing additional dependencies..." - apt-get install --no-install-recommends -y curl git texlive-latex-recommended - printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" install_extra_deps - curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN - tar xfz /tmp/cov-analysis-linux64.tgz - COV_VERSION=$(ls -dt cov-analysis-linux64-* | head -1) - PATH=$PATH:$(pwd)/$COV_VERSION/bin - make coverity - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL --form file=@$(ls apparmor-*-cov-int.tar.gz) --form version="$(git describe --tags)" --form description="$(git describe --tags) / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID" artifacts: paths: - "apparmor-*.tar.gz" rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PROJECT_PATH == "apparmor/apparmor" .image-garden-x86_64: stage: spread interruptible: true image: registry.gitlab.com/zygoon/image-garden:v0.4.2 tags: - linux - x86_64 - kvm variables: ARCH: x86_64 GARDEN_DL_DIR: dl CACHE_POLICY: pull-push CACHE_COMPRESSION_LEVEL: fastest before_script: # Restore the mtime of the .image-garden.mk file. This helps make determine # if there's actually something to do correctly. Git does not preserve the # mtime of files during checkout. - git restore-mtime .image-garden.mk # Prepare the image in dry-run mode. This helps in debugging cache misses # when files are not cached correctly by the runner, causing the build section # below to always do hevy-duty work. - printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" prepare_image_dry_run "Prepare image (dry run)" - image-garden make --dry-run --debug "$GARDEN_SYSTEM.$ARCH.run" "$GARDEN_SYSTEM.$ARCH.qcow2" "$GARDEN_SYSTEM.seed.iso" "$GARDEN_SYSTEM.user-data" "$GARDEN_SYSTEM.meta-data" - printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" prepare_image_dry_run script: # Prepare the image, for real. - printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" prepare_image "Prepare image" # If there's nothing to do then remove all the files that we would normally # cache so that GitLab skips the cache upload step. This saves significant # time required to re-compress and upload unchanged content. # The idea for how to do is is documented at # https://olex.biz/2025/04/gitlab-ci-prevent-cache-reupload-without-changes/ # The GitLab issue requesting a proper feature is # https://gitlab.com/gitlab-org/gitlab/-/issues/226068 - | if image-garden make --question "$GARDEN_SYSTEM.$ARCH.run" "$GARDEN_SYSTEM.$ARCH.qcow2" "$GARDEN_SYSTEM.seed.iso" "$GARDEN_SYSTEM.user-data" "$GARDEN_SYSTEM.meta-data"; then rm -f "$GARDEN_SYSTEM".* efi-code.*.img efi-vars.*.img rm -rf "$GARDEN_DL_DIR" else image-garden make "$GARDEN_SYSTEM.$ARCH.run" "$GARDEN_SYSTEM.$ARCH.qcow2" "$GARDEN_SYSTEM.seed.iso" "$GARDEN_SYSTEM.user-data" "$GARDEN_SYSTEM.meta-data" fi - printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" prepare_image cache: # Cache the base image (pre-customization). - key: image-garden-base-${GARDEN_SYSTEM}.${ARCH} policy: $CACHE_POLICY when: always paths: - $GARDEN_DL_DIR # Those are never mutated so they are safe to share. - efi-code.*.img - efi-vars.*.img # Cache the customized system. This cache depends on .image-garden.mk file # so that any customization updates are immediately acted upon. - key: prefix: image-garden-custom-${GARDEN_SYSTEM}.${ARCH}- files: - .image-garden.mk policy: $CACHE_POLICY when: always paths: - $GARDEN_SYSTEM.* - $GARDEN_SYSTEM.seed.iso - $GARDEN_SYSTEM.meta-data - $GARDEN_SYSTEM.user-data # This job builds and caches the image that the job below looks at. image-ubuntu-cloud-24.04-x86_64: extends: .image-garden-x86_64 variables: GARDEN_SYSTEM: ubuntu-cloud-24.04 needs: [] dependencies: [] rules: - if: $CI_COMMIT_TAG - if: $CI_PIPELINE_SOURCE == "merge_request_event" changes: paths: - .image-garden.mk - .gitlab-ci.yml compare_to: "refs/heads/master" - if: $CI_COMMIT_BRANCH && "$[[ inputs.build-test-images ]]" == "true" .spread-x86_64: extends: .image-garden-x86_64 variables: # GitLab project identifier of zygoon/spread-dist can be seen on # https://gitlab.com/zygoon/spread-dist, under the three-dot menu on # top-right. SPREAD_GITLAB_PROJECT_ID: "65375371" # Git revision of spread to install. # This must have been built via spread-dist. # TODO: switch to upstream 1.0 release when available. SPREAD_REV: 413817eda7bec07a3885e0717c178b965f8924e1 # Run all the tasks for a given system. SPREAD_ARGS: "garden:$GARDEN_SYSTEM:" SPREAD_GOARCH: amd64 before_script: # Restore the mtime of the .image-garden.mk file. This helps make determine # if there's actually something to do correctly. Git does not preserve the # mtime of files during checkout. - git restore-mtime .image-garden.mk # Prepare the image in dry-run mode. This helps in debugging cache misses # when files are not cached correctly by the runner, causing the build section # below to always do hevy-duty work. - printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" prepare_image_dry_run "Prepare image (dry run)" - image-garden make --dry-run --debug "$GARDEN_SYSTEM.$ARCH.run" "$GARDEN_SYSTEM.$ARCH.qcow2" "$GARDEN_SYSTEM.seed.iso" "$GARDEN_SYSTEM.user-data" "$GARDEN_SYSTEM.meta-data" - stat .image-garden.mk "$GARDEN_SYSTEM".* || true - printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" prepare_image_dry_run # Install the selected revision of spread. - printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" install_spread "Installing spread..." # Install pre-built spread from https://gitlab.com/zygoon/spread-dist generic package repository. - | curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --location --output spread "${CI_API_V4_URL}/projects/${SPREAD_GITLAB_PROJECT_ID}/packages/generic/spread/${SPREAD_REV}/spread.${SPREAD_GOARCH}" - chmod +x spread - printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" install_spread script: - printf '\e[0K%s:%s:%s\r\e[0K%s\n' section_start "$(date +%s)" run_spread "Running spread for $GARDEN_SYSTEM..." # TODO: transform to inject ^...$ to properly select jobs to run. - mkdir -p spread-logs spread-artifacts - ./spread -list $SPREAD_ARGS | sort | split --number=l/"${CI_NODE_INDEX:-1}"/"${CI_NODE_TOTAL:-1}" | xargs --verbose ./spread -v -artifacts ./spread-artifacts -v | tee spread-logs/"$GARDEN_SYSTEM".log - printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" run_spread artifacts: paths: - spread-logs - spread-artifacts when: always spread-ubuntu-cloud-24.04-x86_64: extends: .spread-x86_64 variables: GARDEN_SYSTEM: ubuntu-cloud-24.04 SPREAD_ARGS: garden:$GARDEN_SYSTEM:tests/regression/ garden:$GARDEN_SYSTEM:tests/profiles/ garden:$GARDEN_SYSTEM:tests/snapd/ CACHE_POLICY: pull dependencies: [] needs: - job: image-ubuntu-cloud-24.04-x86_64 optional: true parallel: 4 apparmor-5.0.2/.image-garden.mk000066400000000000000000000053521522511161100162660ustar00rootroot00000000000000# This file is read by image-garden when spread is allocating test machines. # All the package installation happens through cloud-init profiles defined # below. # NOTE: Should the kernel be out of date, just increment this value. Make will # re-create the image whenever the .image-garden.mk file is more recent than # the image itself. In reality all you need is touch(1), but this is more apt. unused=1 # This is the cloud-init user-data profile for all Debian systems. Note that it # is an extension of the default profile necessary for operation of # image-garden. define DEBIAN_CLOUD_INIT_USER_DATA_TEMPLATE $(CLOUD_INIT_USER_DATA_TEMPLATE) packages: - apache2-dev - attr - autoconf - autoconf-archive - automake - bison - build-essential - dejagnu - dosfstools - flake8 - flex - fuse-overlayfs - gdb - gettext - libdbus-1-dev - libpam0g-dev - libtool - liburing-dev - libzstd-dev - pkg-config - proftpd-core - python3-all-dev - python3-gi - python3-notify2 - python3-psutil - python3-setuptools - python3-tk - python3-ttkthemes - swig - tinyproxy # Update all the packages. This allows us to be on the up-to-date kernel # version that we cannot otherwise easily select with cloud init alone. Note # that we do not need to reboot the system as image garden shuts down the image # after first boot. On subsequent boot we will be running the latest kernel. package_upgrade: true package_update: true endef # Ubuntu shares cloud-init profile with Debian. UBUNTU_CLOUD_INIT_USER_DATA_TEMPLATE=$(DEBIAN_CLOUD_INIT_USER_DATA_TEMPLATE) # This is the cloud-init user-data profile for openSUSE Tumbleweed. define OPENSUSE_tumbleweed_CLOUD_INIT_USER_DATA_TEMPLATE $(CLOUD_INIT_USER_DATA_TEMPLATE) - sed -i -e 's/security=selinux/security=apparmor/g' /etc/default/grub - update-bootloader packages: - apache2-devel - attr - autoconf - autoconf-archive - automake - bison - dbus-1-devel - dejagnu - dosfstools - flex - fuse-overlayfs - gcc - gcc-c++ - gdb - gettext - gobject-introspection - libtool - liburing2-devel - libzstd-devel - make - pam-devel - pkg-config - python3-devel - python3-flake8 - python3-notify2 - python3-psutil - python3-setuptools - python3-setuptools - python3-tk - python311 - python311-devel - swig - which # See above for rationale. package_upgrade: true package_update: true endef define FEDORA_CLOUD_INIT_USER_DATA_TEMPLATE $(CLOUD_INIT_USER_DATA_TEMPLATE) packages: - attr - autoconf - autoconf-archive - automake - bison - dbus-devel - dejagnu - dosfstools - flex - gdb - gettext - httpd-devel - libstdc++-static - libtool - liburing-devel - libzstd-devel - pam-devel - perl - pkg-config - python3-devel - python3-flake8 - python3-gobject-base - python3-notify2 - python3-tkinter - swig # See above for rationale. package_upgrade: true package_update: true endef apparmor-5.0.2/.shellcheckrc000066400000000000000000000002371522511161100157670ustar00rootroot00000000000000# Don't follow source'd scripts disable=SC1090 disable=SC1091 # dash supports 'local' disable=SC2039 disable=SC3043 # dash supports 'echo -n' disable=SC3037 apparmor-5.0.2/LICENSE000066400000000000000000000432701522511161100143450ustar00rootroot00000000000000Unless otherwise noted in separate subdirectories, this license applies to the entire contents of this source tree: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. apparmor-5.0.2/Makefile000066400000000000000000000052441522511161100147770ustar00rootroot00000000000000# # .PHONY: all all: @echo "*** See README for information how to build AppArmor ***" exit 1 COMMONDIR=common include ${COMMONDIR}/Make.rules DIRS=libraries/libapparmor \ binutils \ parser \ utils \ init \ changehat/mod_apparmor \ changehat/pam_apparmor \ profiles \ tests # with conversion to git, we don't export from the remote REPO_URL?=git@gitlab.com:apparmor/apparmor.git REPO_BRANCH?=master COVERITY_DIR=cov-int RELEASE_DIR=apparmor-${VERSION} __SETUP_DIR?=. # We create a separate version for tags because git can't handle tags # with embedded ~s in them. No spaces around '-' or they'll get # embedded in ${VERSION} # apparmor version tag format 'vX.Y.ZZ' # apparmor branch name format 'apparmor-X.Y' TAG_VERSION="v$(subst ~,-,${VERSION})" # Add exclusion entries arguments for tar here, of the form: # --exclude dir_to_exclude --exclude other_dir TAR_EXCLUSIONS= .PHONY: tarball tarball: clean REPO_VERSION=`$(value REPO_VERSION_CMD)` && \ $(MAKE) export_dir __EXPORT_DIR=${RELEASE_DIR} __REPO_VERSION=$${REPO_VERSION} && \ $(MAKE) setup __SETUP_DIR=${RELEASE_DIR} && \ tar ${TAR_EXCLUSIONS} -cvzf ${RELEASE_DIR}.tar.gz ${RELEASE_DIR} .PHONY: snapshot snapshot: clean $(eval REPO_VERSION:=$(shell $(value REPO_VERSION_CMD))) $(eval SNAPSHOT_NAME=apparmor-$(VERSION)~$(shell echo $(REPO_VERSION) | cut -d '-' -f 2-)) $(MAKE) export_dir __EXPORT_DIR=${SNAPSHOT_NAME} __REPO_VERSION=${REPO_VERSION} && \ $(MAKE) setup __SETUP_DIR=${SNAPSHOT_NAME} && \ tar ${TAR_EXCLUSIONS} -cvzf ${SNAPSHOT_NAME}.tar.gz ${SNAPSHOT_NAME} .PHONY: coverity coverity: snapshot cd $(SNAPSHOT_NAME)/libraries/libapparmor && ./configure --with-python cov-build --dir $(COVERITY_DIR) -- sh -c \ "$(foreach dir, $(filter-out utils profiles tests, $(DIRS)), \ $(MAKE) -j $$(nproc) -C $(SNAPSHOT_NAME)/$(dir);) " tar -cvzf $(SNAPSHOT_NAME)-$(COVERITY_DIR).tar.gz $(COVERITY_DIR) .PHONY: export_dir export_dir: mkdir $(__EXPORT_DIR) /usr/bin/git archive --prefix=$(__EXPORT_DIR)/ --format tar $(__REPO_VERSION) | tar xv echo "$(REPO_URL) $(REPO_BRANCH) $(__REPO_VERSION)" > $(__EXPORT_DIR)/common/.stamp_rev .PHONY: clean clean: -rm -rf ${RELEASE_DIR} ./apparmor-${VERSION}~* ${COVERITY_DIR} for dir in $(DIRS); do \ $(MAKE) -C $$dir clean; \ done .PHONY: setup setup: cd $(__SETUP_DIR)/libraries/libapparmor && ./autogen.sh # parser has an extra doc to build $(MAKE) -C $(__SETUP_DIR)/parser extra_docs # libraries/libapparmor needs configure to have run before # building docs $(foreach dir, $(filter-out libraries/libapparmor tests, $(DIRS)), \ $(MAKE) -C $(__SETUP_DIR)/$(dir) docs;) .PHONY: tag tag: git tag -m 'AppArmor $(VERSION)' -s $(TAG_VERSION) apparmor-5.0.2/README.md000066400000000000000000000374151522511161100146230ustar00rootroot00000000000000# AppArmor [![Build status](https://gitlab.com/apparmor/apparmor/badges/master/build.svg)](https://gitlab.com/apparmor/apparmor/commits/master) [![Overall test coverage](https://gitlab.com/apparmor/apparmor/badges/master/coverage.svg)](https://gitlab.com/apparmor/apparmor/pipelines) [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/1699/badge)](https://bestpractices.coreinfrastructure.org/projects/1699) ------------ Introduction ------------ AppArmor protects systems from insecure or untrusted processes by running them in restricted confinement, while still allowing processes to share files, exercise privilege and communicate with other processes. AppArmor is a Mandatory Access Control (MAC) mechanism which uses the Linux Security Module (LSM) framework. The confinement's restrictions are mandatory and are not bound to identity, group membership, or object ownership. The protections provided are in addition to the kernel's regular access control mechanisms (including DAC) and can be used to restrict the superuser. The AppArmor kernel module and accompanying user-space tools are available under the GPL license (the exception is the libapparmor library, available under the LGPL license, which allows change_hat(2) and change_profile(2) to be used by non-GPL binaries). For more information, you can read the techdoc.pdf (available after building the parser) and by visiting the https://apparmor.net/ web site. ---------------- Getting in Touch ---------------- Please send all complaints, feature requests, rants about the software, and questions to the [AppArmor mailing list](https://lists.ubuntu.com/mailman/listinfo/apparmor). Bug reports can be filed against the AppArmor project on [GitLab](https://gitlab.com/apparmor/apparmor/-/issues) or reported to the mailing list directly for those who wish not to register for an account on GitLab. See the [wiki page](https://gitlab.com/apparmor/apparmor/wikis/home#reporting-bugs) for more information. Security issues can be filed in GitLab by opening up a new [issue](https://gitlab.com/apparmor/apparmor/-/issues) and selecting the tick box ```This issue is confidential and should only be visible to team members with at least Reporter access.``` or directed to `security@apparmor.net`. Additional details can be found in the [wiki](https://gitlab.com/apparmor/apparmor/wikis/home#reporting-security-vulnerabilities). -------------- Privacy Policy -------------- The AppArmor security project respects users privacy and data and does not collect data from or on its users beyond what is required for a given component to function. The AppArmor kernel security module will log violations to the audit subsystem, and those will be logged/forwarded/recorded on the user's system(s) according to how the administrator has logging configured. Again this is not forwarded to or collected by the AppArmor project. The AppArmor userspace tools do not collect information on the system user beyond the logs and information needed to interact with the user. This is not forwarded to, nor collected by the AppArmor project. Users may submit information as part of an email, bug report or merge request, etc. and that will be recorded as part of the mailing list, bug/issue tracker, or code repository but only as part of a user initiated action. The AppArmor project does not collect information from contributors beyond their interactions with the AppArmor project, code, and community. However contributors are subject to the terms and conditions and privacy policy of the individual platforms (currently GitLab) should they choose to contribute through those platforms. And those platforms may collect data on the user that the AppArmor project does not. Currently GitLab requires a user account to submit patches or report bugs and issues. If a contributor does not wish to create an account for these platforms the mailing list is available. Membership in the list is not required. Content from non-list members will be sent to moderation, to ensure that it is on topic, so there may be a delay in choosing to interact in this way. ------------- Source Layout ------------- AppArmor consists of several different parts: ``` binutils/ source for basic utilities written in compiled languages changehat/ source for using changehat with Apache, PAM and Tomcat common/ common makefile rules desktop/ empty documentation/ misc docs (not man pages), logos, ... init/ initscript fns and corresponding documentation kernel-patches/ compatibility patches for various kernel versions - deprecated libraries/ libapparmor source and language bindings parser/ source for parser profiles/ configuration files, reference profiles and abstractions tests/ regression and stress testsuites utils/ high-level utilities for working with AppArmor ``` -------------------------------------- Important note on AppArmor kernel code -------------------------------------- While most of the kernel AppArmor code has been accepted in the upstream Linux kernel, a few important pieces were not included. These missing pieces unfortunately are important bits for AppArmor userspace and kernel interaction; therefore we have included compatibility patches in the kernel-patches/ subdirectory, versioned by upstream kernel (2.6.37 patches should apply cleanly to 2.6.38 source). Without these patches applied to the kernel, the AppArmor userspace will not function correctly. ------------------------------------------ Building and Installing AppArmor Userspace ------------------------------------------ To build and install AppArmor userspace on your system, build and install in the following order. Some systems may need to export various python-related environment variables to complete the build. For example, before building anything on these systems, use something along the lines of: ``` $ export PYTHONPATH=$(realpath libraries/libapparmor/swig/python) $ export PYTHON=/usr/bin/python3 $ export PYTHON_VERSION=3 $ export PYTHON_VERSIONS=python3 ``` Note that, in general, the build steps can be run in parallel, while the test steps do not gain much speedup from being run in parallel. This is because the test steps spawn a handful of long-lived test runner processes that mostly run their tests sequentially and do not use `make`'s jobserver. Moreover, process spawning overhead constitutes a significant part of test runtime, so reworking the test harnesses to add parallelism (which would be a major undertaking for the harnesses that do not have it already) would not produce much of a speedup. ### libapparmor: ``` $ cd ./libraries/libapparmor $ sh ./autogen.sh $ sh ./configure --prefix=/usr --with-perl --with-python # see below $ make -j $(nproc) $ make check $ make install ``` [an additional optional argument to libapparmor's configure is --with-ruby, to generate Ruby bindings to libapparmor.] ### Binary Utilities: ``` $ cd binutils $ make -j $(nproc) $ make check $ make install ``` ### Parser: ``` $ cd parser $ make -j $(nproc) # depends on libapparmor having been built first $ make -j $(nproc) tst_binaries # a build step of make check that can be parallelized $ make check $ make install ``` ### Init: ``` $ make -j $(nproc) $ make check $ make install ``` ### Utilities: ``` $ cd utils $ make -j $(nproc) $ make check PYFLAKES=/usr/bin/pyflakes3 $ make install ``` ### Apache mod_apparmor: ``` $ cd changehat/mod_apparmor $ make -j $(nproc) # depends on libapparmor having been built first $ make install ``` ### PAM AppArmor: ``` $ cd changehat/pam_apparmor $ make -j $(nproc) # depends on libapparmor having been built first $ make install ``` ### Profiles: ``` $ cd profiles $ make $ make check # depends on the parser having been built first $ make install ``` Note that the empty local/* profile sniplets no longer get created by default. If you want them, run `make local` before running `make check`. [Note that for the parser, binutils, and utils, if you only wish to build/use some of the locale languages, you can override the default by passing the LANGS arguments to make; e.g. make all install "LANGS=en_US fr".] ### AppArmor in snapd snapd ships a vendored version of the AppArmor Userspace based on a tarball. To modify the AppArmor Userspace used by snapd, you can create your own by running: ```sh $ make tarball $ sha256sum apparmor-*.tar.gz 0d13f2cfa7da87a2284e45fb23c4820bca4939accf9a77f01fe0bb8cdec7038e apparmor-5.0.0.tar.gz ``` This will generate a tarball of the current AppArmor tree. Copy this tarball to the snapd tree and edit the `source` in the `snapcraft.yaml` file under `build-aux/snap/` to reference the copied `.tar.gz`. In addition, modify the tarball checksum under `source-checksum`. ```yaml apparmor: plugin: autotools build-packages: - autoconf-archive - bison - flex - gettext - g++ - pkg-config - wget source: ./apparmor-5.0.0.tar.gz source-checksum: sha256/0d13f2cfa7da87a2284e45fb23c4820bca4939accf9a77f01fe0bb8cdec7038e ``` Next, make sure that the version of `libapparmor` from the tarball matches the one on `cmd/configure.ac` under the snapd tree ```autoconf # Check if apparmor userspace library is available. AS_IF([test "x$enable_apparmor" = "xyes"], [ # Expect AppArmor5 when building as a snap under snapcraft AS_IF([test "x$SNAPCRAFT_PROJECT_NAME" = "xsnapd"], [ PKG_CHECK_MODULES([APPARMOR5], [libapparmor = 5.0.0], [ AC_DEFINE([HAVE_APPARMOR], [1], [Build with apparmor5 support])], [ AC_MSG_ERROR([unable to find apparmor5 for snap build of snapd])])], [ PKG_CHECK_MODULES([APPARMOR], [libapparmor], [ AC_DEFINE([HAVE_APPARMOR], [1], [Build with apparmor support])])]) ], [ AC_MSG_WARN([ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X Apparmor is disabled, all snaps will run in devmode X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]) ]) ``` Then, to create the snapd snap with your vendored AppArmor, run ```sh $ snapcraft pack ``` ------------------- AppArmor Testsuites ------------------- A number of testsuites are in the AppArmor sources. Most have documentation on usage and how to update and add tests. Below is a quick overview of their location and how to run them. Using spread with local virtual machines ---------------------------------------- It may be convenient to use the spread tool to provision and run the test suite in an ephemeral virtual machine. This allows testing in isolation from the host, as well as testing across different commonly used distributions and their real kernels. Image Garden is available as a snap. If you wish to use it this way then snap then install the snap with: ```sh sudo snap install image-garden ``` If you need to install snapd first, see https://snapcraft.io/docs/installing-snapd Alternatively you may build image-garden and spread from source, and install dependencies manually. ```sh sudo apt install git golang whois ovmf genisoimage qemu-utils qemu-system go install github.com/snapcore/spread/cmd/spread@latest git clone https://gitlab.com/zygoon/image-garden make -C image-garden sudo make -C image-garden install image-garden make ubuntu-cloud-24.10.x86_64.run cd $APPARMOR_PATH git clean -xdf ~/go/bin/spread -artifacts ./spread-artifacts -v ubuntu-cloud-24.10 # or ~/go/bin/spread -v garden:ubuntu-cloud-24.04:tests/regression/apparmor:at_secure ``` Running the `run_spread.sh` script, with `image-garden` snap installed or with `spread` on `PATH` will run all the tests across several supported systems (Debian, Ubuntu and openSUSE). If you include a `bzImage` file in the root of the repository then that kernel will be used in the integration test. Please look at `spread.yaml` for details. Regression tests ---------------- For details on structure and adding tests, see tests/regression/apparmor/README. To run: ### Regression tests - using apparmor userspace installed on host ``` $ cd tests/regression/apparmor (requires root) $ make -j $(nproc) USE_SYSTEM=1 $ sudo make tests USE_SYSTEM=1 $ sudo bash open.sh -r # runs and saves the last testcase from open.sh ``` ### Regression tests - using apparmor userspace from the tree. - [build libapparmor](#libapparmor) - [build binutils](#binary-utilities) - [build apparmor parser](#parser) - [build Pam apparmor](#pam-apparmor) ``` $ cd tests/regression/apparmor (requires root) $ make -j $(nproc) $ sudo make tests $ sudo bash open.sh -r # runs and saves the last testcase from open.sh ``` Parser tests ------------ For details on structure and adding tests, see parser/tst/README. To run: ``` $ cd parser/tst $ make $ make tests ``` Libapparmor ----------- For details on structure and adding tests, see libraries/libapparmor/README. ``` $ cd libraries/libapparmor $ make check ``` Utils ----- Tests for the Python utilities exist in the test/ subdirectory. ``` $ cd utils $ make check ``` The aa-decode utility to be tested can be overridden by setting up environment variable APPARMOR_DECODE; e.g.: ``` $ APPARMOR_DECODE=/usr/bin/aa-decode make check ``` Profile checks -------------- A basic consistency check to ensure that the parser and aa-logprof parse successfully the current set of shipped profiles. The system or other parser and logprof can be passed in by overriding the PARSER and LOGPROF variables. ``` $ cd profiles $ make && make check ``` To benchmark the parser CLI, install hyperfine, build the parser, and run ``` $ cd profiles $ make bench-parser-cli ``` The `bench-parser-outputsize` target can be similarly used to measure the final size of the compiled profiles. Stress Tests ------------ To run AppArmor stress tests: ``` $ make all ``` Use these: ``` $ ./change_hat $ ./child $ ./kill.sh $ ./open $ ./s.sh ``` Or run all at once: ``` $ ./stress.sh ``` Please note that the above will stress the system so much it may end up invoking the OOM killer. To run parser stress tests (requires /usr/bin/ruby): ``` $ ./stress.sh ``` (see stress.sh -h for options) Coverity Support ---------------- Coverity scans are available to AppArmor developers at https://scan.coverity.com/projects/apparmor. In order to submit a Coverity build for analysis, the cov-build binary must be discoverable from your PATH. See the "To Setup" section of https://scan.coverity.com/download?tab=cxx to obtain a pre-built copy of cov-build. To generate a compressed tarball of an intermediate Coverity directory: ``` $ make coverity ``` The compressed tarball is written to apparmor--cov-int.tar.gz, where is something like 2.10.90~3328, and must be uploaded to https://scan.coverity.com/projects/apparmor/builds/new for analysis. You must include the snapshot version in Coverity's project build submission form, in the "Project Version" field, so that it is quickly obvious to all AppArmor developers what snapshot of the AppArmor repository was used for the analysis. ----------------------------------------------- Building and Installing AppArmor Kernel Patches ----------------------------------------------- TODO ----------------- Required versions ----------------- The AppArmor userspace utilities are written with some assumptions about installed and available versions of other tools. This is a (possibly incomplete) list of known version dependencies: The Python utilities require a minimum of Python 3.3. The aa-notify tool's Python dependencies can be satisfied by installing the following packages (Debian package names, other distros may vary): * python3-notify2 * python3-psutil * python3-sqlite (part of the python3.NN-stdlib package) * python3-tk * python3-ttkthemes * python3-gi Perl is no longer needed since none of the utilities shipped to end users depend on it anymore. Most shell scripts are written for POSIX-compatible sh. aa-decode expects bash, probably version 3.2 and higher. apparmor-5.0.2/binutils/000077500000000000000000000000001522511161100151635ustar00rootroot00000000000000apparmor-5.0.2/binutils/Makefile000066400000000000000000000120241522511161100166220ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 2015 # Canonical Ltd. (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ---------------------------------------------------------------------- NAME=aa-binutils all: COMMONDIR=../common/ include $(COMMONDIR)/Make.rules DESTDIR=/ BINDIR=${DESTDIR}/usr/bin SBINDIR=${DESTDIR}/usr/sbin LOCALEDIR=/usr/share/locale MANPAGES=aa-enabled.1 aa-exec.1 aa-features-abi.1 aa-load.8 aa-status.8 WARNINGS = -Wall ifndef CFLAGS CFLAGS = -g -O2 -pipe ifdef DEBUG CFLAGS += -pg -D DEBUG endif ifdef COVERAGE CFLAGS = -g -pg -fprofile-arcs -ftest-coverage endif endif #CFLAGS # Internationalization support. Define a package and a LOCALEDIR EXTRA_DEFINES=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\" EXTRA_CFLAGS = ${CFLAGS} ${CPPFLAGS} ${EXTRA_CXXFLAGS} $(EXTRA_WARNINGS) ${EXTRA_DEFINES} SRCS = aa_enabled.c aa_load.c HDRS = BINTOOLS = aa-enabled aa-exec aa-features-abi SBINTOOLS = aa-status aa-load ifdef WITH_STATIC_LINKING AALIB = -Wl,-Bstatic -lapparmor -Wl,-Bdynamic -lpthread else AALIB = -lapparmor -lpthread endif ifdef WITH_LIBINTL AALIB += -lintl endif ifdef USE_SYSTEM # Using the system libapparmor so Makefile dependencies can't be used LIBAPPARMOR_A = INCLUDE_APPARMOR = APPARMOR_H = LIBAPPARMOR_LDFLAGS = else ifdef USE_INTREE LIBAPPARMOR_SRC = $(realpath -f ../libraries/libapparmor/) else LIBAPPARMOR_SRC = ../libraries/libapparmor/ endif LOCAL_LIBAPPARMOR_INCLUDE = $(LIBAPPARMOR_SRC)/include LOCAL_LIBAPPARMOR_LDPATH = $(LIBAPPARMOR_SRC)/src/.libs ifdef WITH_STATIC_LINKING LIBAPPARMOR_A = $(LOCAL_LIBAPPARMOR_LDPATH)/libapparmor.a else LIBAPPARMOR_A = $(LOCAL_LIBAPPARMOR_LDPATH)/libapparmor.so endif INCLUDE_APPARMOR = -I$(LOCAL_LIBAPPARMOR_INCLUDE) APPARMOR_H = $(LOCAL_LIBAPPARMOR_INCLUDE)/sys/apparmor.h LIBAPPARMOR_LDFLAGS = -L$(LOCAL_LIBAPPARMOR_LDPATH) ifdef USE_INTREE LIBAPPARMOR_LDFLAGS += -Wl,-rpath,$(LOCAL_LIBAPPARMOR_LDPATH) endif endif EXTRA_CFLAGS += $(INCLUDE_APPARMOR) LDFLAGS += $(LIBAPPARMOR_LDFLAGS) ifdef V VERBOSE = 1 endif ifndef VERBOSE VERBOSE = 0 endif ifeq ($(VERBOSE),1) BUILD_OUTPUT = Q = else BUILD_OUTPUT = > /dev/null 2>&1 Q = @ endif export Q VERBOSE BUILD_OUTPUT po/%.pot: %.c $(MAKE) -C po $(@F) NAME=$* SOURCES=$*.c # targets arranged this way so that people who don't want full docs can # pick specific targets they want. arch: $(BINTOOLS) $(SBINTOOLS) manpages: $(MANPAGES) docs: manpages indep: docs $(Q)$(MAKE) -C po all all: arch indep .PHONY: coverage coverage: $(MAKE) clean $(BINTOOLS) $(SBINTOOLS) COVERAGE=1 ifndef USE_SYSTEM $(LIBAPPARMOR_A): @if [ ! -f $@ ]; then \ echo "error: $@ is missing. Pick one of these possible solutions:" 1>&2; \ echo " 1) Build against the in-tree libapparmor by building it first and then trying again. See the top-level README for help." 1>&2; \ echo " 2) Build against the system libapparmor by adding USE_SYSTEM=1 to your make command." 1>&2;\ exit 1; \ fi endif aa-features-abi: aa_features_abi.c $(LIBAPPARMOR_A) $(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB) aa-load: aa_load.c $(LIBAPPARMOR_A) $(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB) aa-enabled: aa_enabled.c $(LIBAPPARMOR_A) $(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB) aa-exec: aa_exec.c $(LIBAPPARMOR_A) $(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB) aa-status: aa_status.c cJSON.o $(LIBAPPARMOR_A) $(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB) cJSON.o cJSON.o: cJSON.c cJSON.h $(CC) $(EXTRA_CFLAGS) -c -o $@ $< .SILENT: check .PHONY: check check: check_pod_files tests .PHONY: check-includes check-includes: # doesn't set $?, intended for manual review of the output ret=0; for file in *.c *.h ; do include-what-you-use -Xiwyu --error ${EXTRA_DEFINES} $(INCLUDE_APPARMOR) $$file || ret=$$? ; done; exit $$ret .SILENT: tests tests: $(BINTOOLS) $(SBINTOOLS) $(TESTS) echo "no tests atm" .PHONY: install install: install-indep install-arch .PHONY: install-arch install-arch: arch install -m 755 -d ${BINDIR} install -m 755 ${BINTOOLS} ${BINDIR} install -m 755 -d ${SBINDIR} ln -sf aa-status ${SBINDIR}/apparmor_status install -m 755 ${SBINTOOLS} ${SBINDIR} .PHONY: install-indep install-indep: indep $(MAKE) -C po install NAME=${NAME} DESTDIR=${DESTDIR} $(MAKE) install_manpages DESTDIR=${DESTDIR} ln -sf aa-status.8 ${DESTDIR}/${MANDIR}/man8/apparmor_status.8 ifndef VERBOSE .SILENT: clean endif .PHONY: clean clean: pod_clean rm -f core core.* *.o *.s *.a *~ *.gcda *.gcno rm -f gmon.out rm -f $(BINTOOLS) $(SBINTOOLS) $(TESTS) $(MAKE) -s -C po clean apparmor-5.0.2/binutils/aa-enabled.pod000066400000000000000000000044711522511161100176460ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd # essentially adheres to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa-enabled - test whether AppArmor is enabled =head1 SYNOPSIS B [options] =head1 DESCRIPTION B is used to determine if AppArmor is enabled. =head1 OPTIONS B accepts the following arguments: =over 4 =item -h, --help Display a brief usage guide. =item -q, --quiet Do not output anything to stdout. This option is intended to be used by scripts that simply want to use the exit code to determine if AppArmor is enabled. =item -x, --exclusive Require AppArmor to have exclusive access to shared LSM interfaces to be considered enabled. =back =head1 EXIT STATUS Upon exiting, B will set its exit status to the following values: =over 4 =item B<0> if AppArmor is enabled. =item B<1> if AppArmor is not enabled/loaded. =item B<2> intentionally not used as an B exit status. =item B<3> if the AppArmor control files aren't available under /sys/kernel/security/. =item B<4> if B doesn't have enough privileges to read the apparmor control files. =item B<10> AppArmor is enabled but does not have access to shared LSM interfaces. =item B<64> if any unexpected error or condition is encountered. =back =head1 BUGS If you find any bugs, please report them at L. =head1 SEE ALSO apparmor(7), apparmor.d(5), aa_is_enabled(2), and L. =cut apparmor-5.0.2/binutils/aa-exec.pod000066400000000000000000000145201522511161100171740ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd # essentially adheres to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa-exec - confine a program with the specified AppArmor profile =head1 SYNOPSIS B [options] [--] [IcommandE> ...] =head1 DESCRIPTION B is used to launch a program confined by the specified profile and or namespace. If both a profile and namespace are specified command will be confined by profile in the new policy namespace. If only a namespace is specified, the profile name of the current confinement will be used. If neither a profile or namespace is specified command will be run using standard profile attachment (ie. as if run without the aa-exec command). If the arguments are to be pasted to the IcommandE> being invoked by aa-exec then -- should be used to separate aa-exec arguments from the command. aa-exec -p profile1 -- ls -l =head1 OPTIONS B accepts the following arguments: =over 4 =item -p PROFILE, --profile=PROFILE confine IcommandE> with PROFILE. If the PROFILE is not specified use the current profile name (likely unconfined). =item -n NAMESPACE, --namespace=NAMESPACE use profiles in NAMESPACE. This will result in confinement transitioning to using the new profile namespace. =item -i, --immediate transition to PROFILE before doing executing IcommandE>. This subjects the running of IcommandE> to the exec transition rules of the current profile. =item -v, --verbose show commands being performed =item -d, --debug show commands and error codes =item -- Signal the end of options and disables further option processing. Any arguments after the -- are treated as arguments of the command. This is useful when passing arguments to the IcommandE> being invoked by aa-exec. =back =head1 RESTRICTIONS aa-exec uses I to change application confinement. The use of I may be restricted by policy in ways that will cause failure or results different than expected. Even when using I from unconfined restrictions in policy can causes failure or the confinement entered to be different than requested See the unpriviled unconfined restriction documentation for more detail. https://gitlab.com/apparmor/apparmor/-/wikis/unprivileged_unconfined_restriction =head1 STACKING aa-exec can be used to setup a stack of profiles as confinement. When an application is confined by a stack, all profiles in the stack are checked as if they were the profile confining the application. The resulting mediation is the intersection of what is allowed by each profile in the stack. The profiles in a stack are treated independently. Each profile can have its own flags and profile transitions. During an exec each profile gets to specify its transition and the results brought together to form a new canonicalized stack. The profile separator indicating a stack is the character sequence I. Thus a stack can be expressed using =over 4 $ aa-exec -p "unconfined//&firefox" -- bash $ ps -Z LABEL PID TTY TIME CMD unconfined 30714 pts/12 00:00:00 bash firefox//&unconfined (unconfined) 31160 pts/12 00:00:00 bash firefox//&unconfined (unconfined) 31171 pts/12 00:00:00 ps =back =head1 NAMESPACES aa-exec can be used to enter confinement in another policy namespace if the policy namespaces exists, is visible, and the profile exists in the namespace. Note applications launched within the namespace will not be able to exit the namespace, and may be restricted by additional confinement around namespacing. Files and resources visible to the parent that launches the application may not be visible in the policy namespace resulting in access denials. To enter a policy namespace the profile is prefixed with the namespace's name, using a I<:> prefix and suffix. Eg. =over 4 $ aa-exec -p :ex1:unconfined -- bash $ ps -Z LABEL PID TTY TIME CMD - 30714 pts/12 00:00:00 bash unconfined 34372 pts/12 00:00:00 bash unconfined 34379 pts/12 00:00:00 ps =back Confinement of processes outside of the namespace may not be visible in which case the confinement will be represented with a -. If policy is stacked only part of the confinement might be visible. However confinement is usually fully visible from the parent policy namespace. Eg. the confinement of the child can be queried in the parent to see =over 4 $ ps -Z 34372 LABEL PID TTY STAT TIME COMMAND :ex1:unconfined 34372 pts/12 S+ 0:00 bash =back And in the case of stacking with namespaces =over 4 $ aa-exec -p "unconfined//&:ex1:unconfined" -- bash $ ps -Z LABEL PID TTY TIME CMD - 30714 pts/12 00:00:00 bash unconfined 36298 pts/12 00:00:00 bash unconfined 36305 pts/12 00:00:00 ps =back while from the parent namespace the full confinement can be seen =over 4 $ ps -Z 36298 LABEL PID TTY STAT TIME COMMAND unconfined//&:ex1:unconfined 36298 pts/12 S+ 0:00 bash =back =head1 BUGS If you find any bugs, please report them at L =head1 SEE ALSO apparmor(7), apparmor.d(5), aa_change_profile(3), aa_change_onexec(3) and L. =cut apparmor-5.0.2/binutils/aa-features-abi.pod000066400000000000000000000042071522511161100206200ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd # essentially adheres to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa-features-abi - Extract, validate and manipulate AppArmor feature abis =head1 SYNOPSIS B [OPTIONS] [OUTPUT OPTIONS] =head1 DESCRIPTION B is used to extract a features abi and output to either stdout or a specified file. A SOURCE_OPTION must be specified. If an output option is not specified the features abi is written to stdout. =head1 OPTIONS B accepts the following arguments: =over 4 =item -h, --help Display a brief usage guide. =item -d, --debug show messages with debugging information =item -v, --verbose show messages with stats =back =head1 SOURCE =over 4 =item -x, --extract Extract the features abi for the kernel =item -f FILE, --file=FILE Load the features abi from FILE and send it to OUTPUT OPTIONS. =back =head1 OUTPUT OPTIONS =over 4 =item --stdout Write the features abi to I, this is the default if no output option is specified. =item -w FILE, --write FILE Write the features abi to I. =back =head1 BUGS If you find any bugs, please report them at L. =head1 SEE ALSO apparmor(7), apparmor.d(5), aa_features(3), and L. =cut apparmor-5.0.2/binutils/aa-load.pod000066400000000000000000000036341522511161100171730ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd # essentially adheres to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa-load - load precompiled AppArmor policy from cache location(s) =head1 SYNOPSIS B [options] (cache file|cache dir|cache base dir)+ =head1 DESCRIPTION B loads precompiled AppArmor policy from the specified locations. =head1 OPTIONS B accepts the following arguments: =over 4 =item -f, --force Force B to load a policy even if its abi does not match the kernel abi. =item -d, --debug Display debug messages. =item -v, --verbose Display progress and error messages. =item -n, --dry-run Do not actually load the specified policy/policies into the kernel. =item -h, --help Display a brief usage guide. =back =head1 EXIT STATUS Upon exiting, B returns 0 upon success and 1 upon an error loading the precompiled policy. =head1 BUGS If you find any bugs, please report them at L. =head1 SEE ALSO apparmor(7), apparmor.d(5), apparmor_parser(8), and L. =cut apparmor-5.0.2/binutils/aa-status.pod000066400000000000000000000115601522511161100175740ustar00rootroot00000000000000# This publication is intellectual property of Novell Inc. and Canonical # Ltd. Its contents can be duplicated, either in part or in whole, provided # that a copyright label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither SUSE LINUX GmbH, Canonical Ltd, the authors, nor the translators # shall be held liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. SUSE LINUX GmbH # and Canonical Ltd. essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa-status - display various information about the current AppArmor policy. =head1 SYNOPSIS B [option] =head1 DESCRIPTION B will report various aspects of the current state of AppArmor confinement. By default, it displays the same information as if the I<--verbose> argument were given. A sample of what this looks like is: apparmor module is loaded. 110 profiles are loaded. 102 profiles are in enforce mode. 8 profiles are in complain mode. Out of 129 processes running: 13 processes have profiles defined. 8 processes have profiles in enforce mode. 5 processes have profiles in complain mode. Other argument options are provided to report individual aspects, to support being used in scripts. =head1 OPTIONS B accepts only one argument at a time out of: =over 4 =item --enabled returns error code if AppArmor is not enabled. =item --profiled displays the number of loaded AppArmor policies. =item --enforced displays the number of loaded enforcing AppArmor policies. =item --complaining displays the number of loaded non-enforcing AppArmor policies. =item --kill displays the number of loaded enforcing AppArmor policies that will kill tasks on policy violations. =item --prompt displays the number of loaded enforcing AppArmor policies, with fallback to userspace mediation. =item --special-unconfined displays the number of loaded non-enforcing AppArmor policies that are in the special unconfined mode. =item --process-mixed displays the number of processes confined by profile stacks with profiles in different modes. =item --verbose displays multiple data points about loaded AppArmor policy set (the default action if no arguments are given). =item --json displays multiple data points about loaded AppArmor policy set in a JSON format, fit for machine consumption. =item --pretty-json same as --json, formatted to be readable by humans as well as by machines. =item --show what data sets to show information about. Currently I, I, I for both processes and profiles. The default is I. =item --count display only counts for selected information. =item --filter.mode=filter Allows specifying a posix regular expression filter that will be applied against the displayed processes and profiles apparmor profile mode, reducing the output. =item --filter.profiles=filter Allows specifying a posix regular expression filter that will be applied against the displayed processes and profiles confining profile, reducing the output. =item --filter.pid=filter Allows specifying a posix regular expression filter that will be applied against the displayed processes, so that only processes pids matching the expression will be displayed. =item --filter.exe=filter Allows specifying a posix regular expression filter that will be applied against the displayed processes, so that only processes executable name matching the expression will be displayed. =item --help displays a short usage statement. =back =head1 EXIT STATUS Upon exiting, B will set its exit status to the following values: =over 4 =item B<0> if apparmor is enabled and policy is loaded. =item B<1> if apparmor is not enabled/loaded. =item B<2> if apparmor is enabled but no policy is loaded. =item B<3> if the apparmor control files aren't available under /sys/kernel/security/. =item B<4> if the user running the script doesn't have enough privileges to read the apparmor control files. =item B<42> if an internal error occurred. =back =head1 BUGS B must be run as root to read the state of the loaded policy from the apparmor module. It uses the /proc filesystem to determine which processes are confined and so is susceptible to race conditions. If you find any additional bugs, please report them at L. =head1 SEE ALSO apparmor(7), apparmor.d(5), and L. =cut apparmor-5.0.2/binutils/aa_enabled.c000066400000000000000000000044311522511161100173640ustar00rootroot00000000000000/* * Copyright (C) 2015 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. */ #include #include #include #include #include #include #define _(s) gettext(s) #include void print_help(const char *command) { printf(_("%s: [options]\n" " options:\n" " -x | --exclusive Shared interfaces must be available\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n"), command); exit(1); } /* Exit statuses and meanings are documented in the aa-enabled.pod file */ static void exit_with_error(int saved_errno, int quiet) { switch(saved_errno) { case ENOSYS: if (!quiet) printf(_("No - not available on this system.\n")); exit(1); case ECANCELED: if (!quiet) printf(_("No - disabled at boot.\n")); exit(1); case ENOENT: if (!quiet) printf(_("Maybe - policy interface not available.\n")); exit(3); case EPERM: case EACCES: if (!quiet) printf(_("Maybe - insufficient permissions to determine availability.\n")); exit(4); case EBUSY: if (!quiet) printf(_("Partially - public shared interfaces are not available.\n")); exit(10); } if (!quiet) printf(_("Error - %s\n"), strerror(saved_errno)); exit(64); } int main(int argc, char **argv) { int i, enabled; int quiet = 0; int require_shared = 0; setlocale(LC_MESSAGES, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); if (argc > 3) { printf(_("unknown or incompatible options\n")); print_help(argv[0]); } for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--quiet") == 0 || strcmp(argv[i], "-q") == 0) { quiet = 1; } else if (strcmp(argv[i], "--exclusive") == 0 || strcmp(argv[i], "-x") == 0) { require_shared = 1; } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { print_help(argv[0]); } else { printf(_("unknown option '%s'\n"), argv[1]); print_help(argv[0]); } } enabled = aa_is_enabled(); if (!enabled) { if (require_shared || errno != EBUSY) exit_with_error(errno, quiet); } if (!quiet) printf(_("Yes\n")); exit(0); } apparmor-5.0.2/binutils/aa_exec.c000066400000000000000000000125731522511161100167240ustar00rootroot00000000000000/* * Copyright (c) 2015 * Canonical, Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #define _(s) gettext(s) static const char *opt_profile = NULL; static const char *opt_namespace = NULL; static bool opt_debug = false; static bool opt_immediate = false; static bool opt_verbose = false; static pid_t pid = 0; static void usage(const char *name, bool error) { FILE *stream = stdout; int status = EXIT_SUCCESS; if (error) { stream = stderr; status = EXIT_FAILURE; } fprintf(stream, _("USAGE: %s [OPTIONS] \n" "\n" "Confine with the specified PROFILE.\n" "\n" "OPTIONS:\n" " -p PROFILE, --profile=PROFILE PROFILE to confine with\n" " -n NAMESPACE, --namespace=NAMESPACE NAMESPACE to confine in\n" " -d, --debug show messages with debugging information\n" " -i, --immediate change profile immediately instead of at exec\n" " -v, --verbose show messages with stats\n" " -h, --help display this help\n" "\n"), name); exit(status); } #define error(fmt, args...) _error(_("[%ld] aa-exec: ERROR: " fmt "\n"), (long)pid, ## args) static void _error(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); exit(EXIT_FAILURE); } #define debug(fmt, args...) _debug(_("[%ld] aa-exec: DEBUG: " fmt "\n"), (long)pid, ## args) static void _debug(const char *fmt, ...) { va_list args; if (!opt_debug) return; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } #define verbose(fmt, args...) _verbose(_("[%ld] " fmt "\n"), (long)pid, ## args) static void _verbose(const char *fmt, ...) { va_list args; if (!opt_verbose) return; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } static void verbose_print_argv(char **argv) { if (!opt_verbose) return; fprintf(stderr, _("[%ld] exec"), (long)pid); for (; *argv; argv++) fprintf(stderr, " %s", *argv); fprintf(stderr, "\n"); } static char **parse_args(int argc, char **argv) { int opt; struct option long_opts[] = { {"debug", no_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"profile", required_argument, 0, 'p'}, {"namespace", required_argument, 0, 'n'}, {"immediate", no_argument, 0, 'i'}, {"verbose", no_argument, 0, 'v'}, }; while ((opt = getopt_long(argc, argv, "+dhp:n:iv", long_opts, NULL)) != -1) { switch (opt) { case 'd': opt_debug = true; break; case 'h': usage(argv[0], false); break; case 'p': if (opt_profile) error("Multiple -p/--profile parameters given"); opt_profile = optarg; break; case 'n': if (opt_namespace) error("Multiple -n/--namespace parameters given"); opt_namespace = optarg; break; case 'i': opt_immediate = true; break; case 'v': opt_verbose = true; break; default: usage(argv[0], true); break; } } if (optind >= argc) usage(argv[0], true); return argv + optind; } static void build_name(char *name, size_t name_len, const char *namespace, const char *profile) { size_t required_len = 1; /* reserve 1 byte for NUL-terminator */ if (namespace) required_len += 1 + strlen(namespace) + 3; /* ::// */ if (profile) required_len += strlen(profile); if (required_len > name_len) error("name too long (%zu > %zu)", required_len, name_len); name[0] = '\0'; if (namespace) { strcat(name, ":"); strcat(name, namespace); strcat(name, "://"); } if (profile) strcat(name, profile); } int main(int argc, char **argv) { char name[PATH_MAX]; int rc = 0; /* IMPORTANT: pid must be initialized before doing anything else since * it is used in a global context when printing messages */ pid = getpid(); argv = parse_args(argc, argv); if (opt_namespace || opt_profile) build_name(name, sizeof(name), opt_namespace, opt_profile); else goto exec; if (opt_immediate) { verbose("aa_change_profile(\"%s\")", name); rc = aa_change_profile(name); debug("%d = aa_change_profile(\"%s\")", rc, name); } else { verbose("aa_change_onexec(\"%s\")", name); rc = aa_change_onexec(name); debug("%d = aa_change_onexec(\"%s\")", rc, name); } if (rc) { if (errno == ENOENT) { error("%s '%s' does not exist", opt_profile ? "profile" : "namespace", name); } else if (errno == EACCES) { error("insufficient permissions to change to the %s '%s'", opt_profile ? "profile" : "namespace", name); } else if (errno == EINVAL) { error("AppArmor interface not available"); } else { error("%m"); } } exec: verbose_print_argv(argv); execvp(argv[0], argv); error("Failed to execute \"%s\": %m", argv[0]); } apparmor-5.0.2/binutils/aa_features_abi.c000066400000000000000000000111711522511161100204220ustar00rootroot00000000000000/* * Copyright (c) 2020 * Canonical, Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Canonical Ltd. */ #include #include #include #include #include #include #include #include #include #include #define _(s) gettext(s) #include "../libraries/libapparmor/src/private.h" static const char *progname = NULL; static const char *opt_file = NULL; static const char *opt_write = NULL; static bool opt_debug = false; static bool opt_verbose = false; static bool opt_extract = false; static void usage(const char *name, bool error) { FILE *stream = stdout; int status = EXIT_SUCCESS; if (error) { stream = stderr; status = EXIT_FAILURE; } fprintf(stream, _("USAGE: %s [OPTIONS] [OUTPUT OPTIONS]\n" "\n" "Output AppArmor feature abi from SOURCE to OUTPUT" "\n" "OPTIONS:\n" #if 0 " -d, --debug show messages with debugging information\n" " -v, --verbose show messages with stats\n" #endif " -h, --help display this help\n" "SOURCE:\n" " -f F, --file=F load features abi from file F\n" " -x, --extract extract features abi from the kernel\n" "OUTPUT OPTIONS:\n" " --stdout default, write features to stdout\n" " -w F, --write=F write features abi to the file F instead of stdout\n" "\n"), name); exit(status); } #define error(fmt, args...) _error(_("%s: ERROR: " fmt " - %m\n"), progname, ## args) static void _error(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); exit(EXIT_FAILURE); } #if 0 #define debug(fmt, args...) _debug(_("%s: DEBUG: " fmt "\n"), progname, ## args) static void _debug(const char *fmt, ...) { va_list args; if (!opt_debug) return; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } #define verbose(fmt, args...) _verbose(_(fmt "\n"), ## args) static void _verbose(const char *fmt, ...) { va_list args; if (!opt_verbose) return; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } #endif #define ARG_STDOUT 128 static char **parse_args(int argc, char **argv) { int opt; struct option long_opts[] = { {"debug", no_argument, 0, 'd'}, {"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"extract", no_argument, 0, 'x'}, {"file", required_argument, 0, 'f'}, {"write", required_argument, 0, 'w'}, {"stdout", no_argument, 0, ARG_STDOUT}, }; while ((opt = getopt_long(argc, argv, "+dvhxf:l:w:", long_opts, NULL)) != -1) { switch (opt) { case 'd': opt_debug = true; break; case 'v': opt_verbose = true; break; case 'h': usage(argv[0], false); break; case 'x': opt_extract = true; break; case 'f': opt_file = optarg; break; case 'w': opt_write = optarg; break; case ARG_STDOUT: opt_write = NULL; break; default: usage(argv[0], true); break; } } return argv + optind; } /* TODO: add features intersection and testing */ int main(int argc, char **argv) { struct aa_features *features; autoclose int in = -1; autoclose int out = -1; int rc = 0; progname = argv[0]; argv = parse_args(argc, argv); if (!opt_extract && !opt_file) usage(argv[0], true); if (opt_extract && opt_file) { error("options --extract and --file are mutually exclusive"); } if (opt_extract) { rc = aa_features_new_from_kernel(&features); if (rc == -1) error("failed to extract features abi from the kernel"); } if (opt_file) { in = open(opt_file, O_RDONLY); if (in == -1) error("failed to open file '%s'", opt_file); rc = aa_features_new_from_file(&features, in); if (rc == -1) error("failed to load features abi from file '%s'", opt_file); } if (opt_write) { out = open(opt_write, O_WRONLY | O_CREAT, 00600); if (out == -1) error("failed to open output file '%s'", opt_write); } else { out = fileno(stdout); if (out == -1) error("failed to get stdout"); } rc = aa_features_write_to_fd(features, out); if (rc == -1) error("failed to write features abi"); return 0; } apparmor-5.0.2/binutils/aa_load.c000066400000000000000000000225671522511161100167230ustar00rootroot00000000000000/* * Copyright (C) 2020 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. */ #define _GNU_SOURCE /* for asprintf() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _(s) gettext(s) /* TODO: implement config locations - value can change */ #define DEFAULT_CONFIG_LOCATIONS "/etc/apparmor/parser.conf" #define DEFAULT_POLICY_LOCATIONS "/var/cache/apparmor:/etc/apparmor.d/cache.d:/etc/apparmor.d/cache" #define CACHE_FEATURES_FILE ".features" bool opt_debug = false; bool opt_verbose = false; bool opt_dryrun = false; bool opt_force = false; bool opt_config = false; #define warning(fmt, args...) _error(_("aa-load: WARN: " fmt "\n"), ## args) #define error(fmt, args...) _error(_("aa-load: ERROR: " fmt "\n"), ## args) static void _error(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } #define verbose(fmt, args...) _debug(opt_verbose, _(fmt "\n"), ## args) #define debug(fmt, args...) _debug(opt_debug, _("aa-load: DEBUG: " fmt "\n"), ## args) static void _debug(bool opt_displayit, const char *fmt, ...) { va_list args; if (!opt_displayit) return; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } static int have_enough_privilege(const char *command) { uid_t uid, euid; uid = getuid(); euid = geteuid(); if (uid != 0 && euid != 0) { error("%s: Sorry. You need root privileges to run this program.\n", command); return EPERM; } if (uid != 0 && euid == 0) { error("%s: Aborting! You've set this program setuid root.\n" "Anybody who can run this program can update " "your AppArmor profiles.\n", command); exit(EXIT_FAILURE); } return 0; } static int load_config(const char *file) { /* TODO */ return ENOENT; } /** * load a single policy cache file to the kernel */ static int load_policy_file(const char *file) { int rc = 0; struct aa_kernel_interface *kernel_interface; // Todo: Check cache validity if (aa_kernel_interface_new(&kernel_interface, NULL, NULL)) { rc = -errno; error("Failed to open kernel interface '%s': %m", file); return rc; } if (!opt_dryrun && aa_kernel_interface_replace_policy_from_file(kernel_interface, AT_FDCWD, file)) { rc = -errno; error("Failed to load policy into kernel '%s': %m", file); } aa_kernel_interface_unref(kernel_interface); return rc; } static void validate_features(const char *dir_path) { aa_features *kernel_features; if (aa_features_new_from_kernel(&kernel_features) == -1) { error("Failed to obtain features: %m"); return; } if (aa_features_check(AT_FDCWD, dir_path, kernel_features) == -1) { if (errno == ENOENT) { /* features file does not exist * not an issue when loading cache policies from dir */ } else if (errno == EEXIST) { warning("Overlay features do not match kernel features"); } } aa_features_unref(kernel_features); } /** * load a directory of policy cache files to the kernel * This does not do a subdir search to find the kernel match but * tries to load the dir regardless of whether its features match * * The hierarchy looks like * * dir/ * .features * profile1 * ... */ static int load_policy_dir(const char *dir_path) { DIR *d; struct dirent *dir; int rc = 0; char *file; size_t len; validate_features(dir_path); d = opendir(dir_path); if (!d) { rc = -errno; error("Failed to open directory '%s': %m", dir_path); return rc; } while ((dir = readdir(d)) != NULL) { /* Only check regular files for now */ if (dir->d_type == DT_REG) { /* As per POSIX dir->d_name has at most NAME_MAX characters */ len = strnlen(dir->d_name, NAME_MAX); /* Ignores .features */ if (strncmp(dir->d_name, CACHE_FEATURES_FILE, len) == 0) { continue; } if (asprintf(&file, "%s/%s", dir_path, dir->d_name) == -1) { error("Failure allocating memory"); closedir(d); return -1; } load_policy_file(file); free(file); file = NULL; } } closedir(d); return 0; } /** * load_hashed_policy - find policy hashed dir and load it * * load/replace all policy from a policy hierarchy directory * * Returns: 0 on success < -errno * * It will find the subdir that matches the kernel and load all * precompiled policy files from it. * * The hierarchy looks something like * * location/ * kernel_hash1.0/ * .features * profile1 * ... * kernel_hash2.0/ * .features * profile1 * ... */ static int load_policy_by_hash(const char *location) { aa_policy_cache *policy_cache = NULL; int rc; if ((rc = aa_policy_cache_new(&policy_cache, NULL, AT_FDCWD, location, 0))) { rc = -errno; error("Failed to open policy cache '%s': %m", location); return rc; } if (opt_debug) { /* show hash directory under location that matches the * current kernel */ char *cache_loc = aa_policy_cache_dir_path_preview(NULL, AT_FDCWD, location); if (!cache_loc) { rc = -errno; error("Failed to find cache location '%s': %m", location); goto out; } debug("Loading cache from '%s'\n", cache_loc); free(cache_loc); } if (!opt_dryrun) { if ((rc = aa_policy_cache_replace_all(policy_cache, NULL)) < 0) { error("Failed to load policy cache '%s': %m", location); } else { verbose("Success - Loaded policy cache '%s'", location); } } out: aa_policy_cache_unref(policy_cache); return rc; } /** * load_arg - calls specific load functions for files and directories * * load/replace all policy files/dir in arg * * Returns: 0 on success, 1 on failure. * * It will load by hash subtree first, and fallback to a cache dir * If not a directory, it will try to load it as a cache file */ static int load_arg(char *arg) { char **location = NULL; int i, n, rc = 0; /* arg can specify an overlay of multiple cache locations */ if ((n = aa_split_overlay_str(arg, &location, 0, true)) == -1) { error("Failed to parse overlay locations: %m"); return 1; } for (i = 0; i < n; i++) { struct stat st; debug("Trying to open %s", location[i]); if (stat(location[i], &st) == -1) { error("Failed stat of '%s': %m", location[i]); rc = 1; continue; } if (S_ISDIR(st.st_mode)) { /* try hash dir subtree first */ if (load_policy_by_hash(location[i]) < 0) { error("Failed load policy by hash '%s': %m", location[i]); rc = 1; } /* fall back to cache dir */ if (load_policy_dir(location[i]) < 0) { error("Failed load policy by directory '%s': %m", location[i]); rc = 1; } } else if (load_policy_file(location[i]) < 0) { rc = 1; } } for (i = 0; i < n; i++) free(location[i]); free(location); return rc; } static void print_usage(const char *command) { printf("Usage: %s [OPTIONS] (cache file|cache dir|cache base dir)+\n" "Load precompiled AppArmor policy from cache location(s)\n\n" "Options:\n" " -f, --force load policy even if abi does not match the kernel\n" " -d, --debug display debug messages\n" " -v, --verbose display progress and error messages\n" " -n, --dry-run do everything except actual load\n" " -h, --help this message\n", command); } static const char *short_options = "c:dfvnh"; struct option long_options[] = { {"config", 1, 0, 'c'}, {"debug", 0, 0, 'd'}, {"force", 0, 0, 'f'}, {"verbose", 0, 0, 'v'}, {"dry-run", 0, 0, 'n'}, {"help", 0, 0, 'h'}, {NULL, 0, 0, 0}, }; static int process_args(int argc, char **argv) { int c, o; opterr = 1; while ((c = getopt_long(argc, argv, short_options, long_options, &o)) != -1) { switch(c) { case 0: error("error in argument processing\n"); exit(1); break; case 'd': opt_debug = true; break; case 'f': opt_force = true; break; case 'v': opt_verbose = true; break; case 'n': opt_dryrun = true; break; case 'h': print_usage(argv[0]); exit(0); break; case 'c': /* TODO: reserved config location, * act as a bad arg for now, when added update usage */ //opt_config = true; uncomment when implemented /* Fall through */ default: error("unknown argument: '%s'\n\n", optarg); print_usage(argv[1]); exit(1); break; } } return optind; } int main(int argc, char **argv) { int i, rc = 0; optind = process_args(argc, argv); if (!opt_dryrun && have_enough_privilege(argv[0])) return 1; /* if no location use the default one */ if (optind == argc) { if (!opt_config && load_config(DEFAULT_CONFIG_LOCATIONS) == 0) { verbose("Loaded policy config"); } if ((rc = load_arg(DEFAULT_POLICY_LOCATIONS))) verbose("Loading policy from default location '%s'", DEFAULT_POLICY_LOCATIONS); else debug("No policy specified, and no policy config or policy in default locations"); } for (i = optind; i < argc; i++) { /* Try to load all policy locations even if one fails * but always return an error if any fail */ int tmp = load_arg(argv[i]); if (!rc) rc = tmp; } return rc; } apparmor-5.0.2/binutils/aa_status.c000066400000000000000000000751711522511161100173260ustar00rootroot00000000000000/* * Copyright (C) 2020 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. */ #define _GNU_SOURCE /* for asprintf() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define _(s) gettext(s) #include #include #include "cJSON.h" #define autofree __attribute((cleanup(_aa_autofree))) #define autofclose __attribute((cleanup(_aa_autofclose))) #define AA_EXIT_ENABLED 0 #define AA_EXIT_DISABLED 1 #define AA_EXIT_NO_POLICY 2 #define AA_EXIT_NO_CONTROL 3 #define AA_EXIT_NO_PERM 4 #define AA_EXIT_INTERNAL_ERROR 42 /* NOTE: Increment this whenever the JSON format changes */ static const unsigned char aa_status_json_version[] = "2"; #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define __unused __attribute__ ((__unused__)) struct filter_set { regex_t mode; regex_t profile; regex_t pid; regex_t exe; }; typedef struct { regex_t *mode; regex_t *profile; regex_t *pid; regex_t *exe; } filters_t; static void init_filters(filters_t *filters, struct filter_set *base) { filters->mode = &base->mode; filters->profile = &base->profile; filters->pid = &base->pid; filters->exe = &base->exe; }; static void free_filters(filters_t *filters) { if (filters->mode) regfree(filters->mode); if (filters->profile) regfree(filters->profile); if (filters->pid) regfree(filters->pid); if (filters->exe) regfree(filters->exe); } struct profile { char *name; char *status; char *identities; }; static void free_profiles(struct profile *profiles, size_t n) { if (!profiles) return; while (n > 0) { n--; free(profiles[n].name); free(profiles[n].status); if (profiles[n].identities) free(profiles[n].identities); } free(profiles); } struct process { char *pid; char *profile; char *exe; char *mode; }; static void free_processes(struct process *processes, size_t n) { if (!processes) return; while (n > 0) { n--; free(processes[n].pid); free(processes[n].profile); free(processes[n].exe); free(processes[n].mode); } free(processes); } #define SHOW_PROFILES 1 #define SHOW_PROCESSES 2 static int verbose = 1; static bool quiet = false; int opt_show = SHOW_PROFILES | SHOW_PROCESSES; bool opt_json = false; bool opt_pretty = false; bool opt_count = false; const char *opt_mode = ".*"; const char *opt_profiles = ".*"; const char *opt_pid = ".*"; const char *opt_exe = ".*"; const char *profile_statuses[] = {"enforce", "complain", "prompt", "kill", "unconfined"}; const char *process_statuses[] = {"enforce", "complain", "prompt", "kill", "unconfined", "mixed"}; #define eprintf(...) \ do { \ if (!quiet) \ fprintf(stderr, __VA_ARGS__); \ } while (0) #define dprintf(...) \ do { \ if (verbose && !opt_json) \ printf(__VA_ARGS__); \ } while (0) #define dfprintf(...) \ do { \ if (verbose && !opt_json) \ fprintf(__VA_ARGS__); \ } while (0) static int open_profiles(FILE **fp) { autofree char *apparmorfs = NULL; autofree char *apparmor_profiles = NULL; struct stat st; int ret; ret = stat("/sys/module/apparmor", &st); if (ret != 0) { eprintf(_("apparmor not present.\n")); return AA_EXIT_DISABLED; } dprintf(_("apparmor module is loaded.\n")); ret = aa_find_mountpoint(&apparmorfs); if (ret == -1) { eprintf(_("apparmor filesystem is not mounted.\n")); return AA_EXIT_NO_CONTROL; } apparmor_profiles = malloc(strlen(apparmorfs) + 10); // /profiles\0 if (apparmor_profiles == NULL) { return AA_EXIT_INTERNAL_ERROR; } sprintf(apparmor_profiles, "%s/profiles", apparmorfs); *fp = fopen(apparmor_profiles, "r"); if (*fp == NULL) { if (errno == EACCES) { eprintf(_("You do not have enough privilege to read the profile set.\n")); } else { eprintf(_("Could not open %s: %s"), apparmor_profiles, strerror(errno)); } return AA_EXIT_NO_PERM; } return 0; } static char *get_identities(const char* profile_name) { autofree char *mountpoint = NULL; autofree char *base_path = NULL; DIR *dir = NULL; struct dirent *entry; const int size = 4096; int res_size; char *result = NULL; char identity_path[size]; FILE *f = NULL; if (aa_find_mountpoint(&mountpoint) == -1) { eprintf(_("Error: Could not find apparmor mountpoint.\n")); goto out; } if (asprintf(&base_path, "%s/policy/profiles", mountpoint) == -1) { eprintf(_("Error: Memory allocation failed for profiles path.\n")); goto out; } dir = opendir(base_path); if (!dir) { eprintf(_("Error: Could not open profiles directory %s: %s\n"), base_path, strerror(errno)); goto out; } while ((entry = readdir(dir)) != NULL) { size_t name_len = strlen(profile_name); if (strncmp(entry->d_name, profile_name, name_len) == 0 && entry->d_name[name_len] == '.') { snprintf(identity_path, sizeof(identity_path), "%s/%s/identity", base_path, entry->d_name); f = fopen(identity_path, "r"); if (f == NULL) goto out; result = malloc(size); if (!result) { eprintf("Error: Could not allocate %d bytes\n", size); goto out; } if (!fgets(result, size, f)) { eprintf("Error: cannot read identities\n"); free(result); result = NULL; goto out; } res_size = strlen(result); if (res_size <= 1) { /* no result or '\n': no identity */ free(result); result = NULL; } else { result[res_size - 1] = '\0'; /* To delete '\n' */ } goto out; } } out: if (f) fclose(f); if (dir) closedir(dir); return result; } /** * get_profiles - get a listing of profiles on the system * @fp: opened apparmor profiles file * @profiles: return: list of profiles * @n: return: number of elements in @profiles * * Return: 0 on success, shell error on failure */ static int get_profiles(FILE *fp, struct profile **profiles, size_t *n) { autofree char *line = NULL; size_t len = 0; *profiles = NULL; *n = 0; while (getline(&line, &len, fp) != -1) { struct profile *_profiles; autofree char *status = NULL; autofree char *name = NULL; char *tmpname = aa_splitcon(line, &status); if (!tmpname) { eprintf("Error: failed profile name split of '%s'.\n", line); // skip this entry and keep processing // else would be AA_EXIT_INTERNAL_ERROR; continue; } name = strdup(tmpname); if (status) { if (strcmp(status, "user") == 0) status = strdup("prompt"); else status = strdup(status); } // give up if out of memory if (name == NULL || status == NULL) goto err; _profiles = realloc(*profiles, (*n + 1) * sizeof(**profiles)); if (_profiles == NULL) goto err; // steal name and status _profiles[*n].name = name; _profiles[*n].status = status; _profiles[*n].identities = get_identities(name); name = NULL; status = NULL; *n = *n + 1; *profiles = _profiles; } return *n > 0 ? AA_EXIT_ENABLED : AA_EXIT_NO_POLICY; err: free_profiles(*profiles, *n); *profiles = NULL; *n = 0; return AA_EXIT_INTERNAL_ERROR; } static int compare_profiles(const void *a, const void *b) { return strcmp(((struct profile *)a)->name, ((struct profile *)b)->name); } /** * filter_profiles - create a filtered profile list * @profiles: list of profiles * @n: number of elements in @profiles * @filters: filters to apply * @filtered: return: new list of profiles that match the filter * @nfiltered: return: number of elements in @filtered * * Return: 0 on success, shell error on failure */ static int filter_profiles(struct profile *profiles, size_t n, filters_t *filters, struct profile **filtered, size_t *nfiltered) { int ret = 0; size_t i; *filtered = NULL; *nfiltered = 0; for (i = 0; i < n; i++) { if (regexec(filters->mode, profiles[i].status, 0, NULL, 0) != 0) continue; if (regexec(filters->profile, profiles[i].name, 0, NULL, 0) == 0) { struct profile *_filtered = realloc(*filtered, (*nfiltered + 1) * sizeof(**filtered)); if (_filtered == NULL) { free_profiles(*filtered, *nfiltered); *filtered = NULL; *nfiltered = 0; ret = AA_EXIT_INTERNAL_ERROR; break; } _filtered[*nfiltered].name = strdup(profiles[i].name); _filtered[*nfiltered].status = strdup(profiles[i].status); _filtered[*nfiltered].identities = profiles[i].identities ? strdup(profiles[i].identities): NULL; *filtered = _filtered; *nfiltered = *nfiltered + 1; } } if (*nfiltered != 0) { qsort(*filtered, *nfiltered, sizeof(*profiles), compare_profiles); } return ret; } /** * get_processes - get a list of processes that are confined * @profiles: list of profiles, used to filter out unconfined processes * @n: number of entries in @procfiles * @processes: return: list of confined processes * @nprocesses: return: number of entries in @processes * * Return: 0 on success, shell exit code on failure * * profiles is used to find prcesses that should be confined but aren't. */ static int get_processes(struct profile *profiles, size_t n, struct process **processes, size_t *nprocesses) { DIR *dir = NULL; struct dirent *entry = NULL; int ret = 0; *processes = NULL; *nprocesses = 0; dir = opendir("/proc"); if (dir == NULL) { ret = AA_EXIT_INTERNAL_ERROR; goto exit; } while ((entry = readdir(dir)) != NULL) { size_t i; int rc; int ispid = 1; autofree char *profile = NULL; autofree char *mode = NULL; /* be careful */ autofree char *exe = NULL; autofree char *real_exe = NULL; autofclose FILE *fp = NULL; autofree char *line = NULL; // ignore non-pid entries for (i = 0; ispid && i < strlen(entry->d_name); i++) { ispid = (isdigit(entry->d_name[i]) ? 1 : 0); } if (!ispid) { continue; } rc = aa_getprocattr(atoi(entry->d_name), "current", &profile, &mode); if (rc == -1 && errno != ENOMEM) { /* fail to access */ continue; } else if (rc == -1 || asprintf(&exe, "/proc/%s/exe", entry->d_name) == -1) { eprintf(_("ERROR: Failed to allocate memory\n")); ret = AA_EXIT_INTERNAL_ERROR; goto exit; } else if (mode) { /* TODO: make this not needed. Mode can now be autofreed */ if (strcmp(mode, "user") == 0) mode = strdup("prompt"); else mode = strdup(mode); } // get executable - readpath can allocate for us but seems // to fail in some cases with errno 2 - no such file or // directory - whereas readlink() can succeed in these // cases - and readpath() seems to have the same behaviour // as in python with better canonicalized results so try it // first and fallack to readlink if it fails // coverity[toctou] real_exe = realpath(exe, NULL); if (real_exe == NULL) { int res; // ensure enough space for NUL terminator real_exe = calloc(PATH_MAX + 1, sizeof(char)); if (real_exe == NULL) { eprintf(_("ERROR: Failed to allocate memory\n")); ret = AA_EXIT_INTERNAL_ERROR; goto exit; } res = readlink(exe, real_exe, PATH_MAX); if (res == -1) { continue; } real_exe[res] = '\0'; } if (mode == NULL) { // is unconfined so keep only if this has a // matching profile. TODO: fix to use attachment // ideally would walk process tree and apply // according to x rules and attachments for (i = 0; i < n; i++) { if (strcmp(profiles[i].name, real_exe) == 0) { profile = strdup(real_exe); mode = strdup("unconfined"); break; } } } if (profile != NULL && mode != NULL) { struct process *_processes = realloc(*processes, (*nprocesses + 1) * sizeof(**processes)); if (_processes == NULL) { free_processes(*processes, *nprocesses); *processes = NULL; *nprocesses = 0; ret = AA_EXIT_INTERNAL_ERROR; goto exit; } _processes[*nprocesses].pid = strdup(entry->d_name); _processes[*nprocesses].profile = profile; _processes[*nprocesses].exe = strdup(real_exe); _processes[*nprocesses].mode = mode; *processes = _processes; *nprocesses = *nprocesses + 1; profile = NULL; mode = NULL; ret = AA_EXIT_ENABLED; } } exit: if (dir != NULL) { closedir(dir); } return ret; } /** * filter_processes: create a new filtered process list by applying @filter * @processes: list of processes to filter * @n: number of entries in @processes * @filters: regex filters @processes against * @filtered: return: new list of processes matching filter * @nfiltered: number of entries in @filtered * * Return: 0 on success, shell exit value on failure */ static int filter_processes(struct process *processes, size_t n, filters_t *filters, struct process **filtered, size_t *nfiltered) { size_t i; int ret = 0; *filtered = NULL; *nfiltered = 0; for (i = 0; i < n; i++) { if (regexec(filters->mode, processes[i].mode, 0, NULL, 0) != 0) continue; if (regexec(filters->pid, processes[i].pid, 0, NULL, 0) != 0) continue; if (regexec(filters->exe, processes[i].exe, 0, NULL, 0) != 0) continue; if (regexec(filters->profile, processes[i].profile, 0, NULL, 0) == 0) { struct process *_filtered = realloc(*filtered, (*nfiltered + 1) * sizeof(**filtered)); if (_filtered == NULL) { free_processes(*filtered, *nfiltered); *filtered = NULL; *nfiltered = 0; ret = AA_EXIT_INTERNAL_ERROR; break; } _filtered[*nfiltered].pid = strdup(processes[i].pid); _filtered[*nfiltered].profile = strdup(processes[i].profile); _filtered[*nfiltered].exe = strdup(processes[i].exe); _filtered[*nfiltered].mode = strdup(processes[i].mode); *filtered = _filtered; *nfiltered = *nfiltered + 1; } } return ret; } /** * simple_filtered_count - count the number of profiles with mode == filter * @outf: output file destination * @filters: filters to filter profiles on * @profiles: profiles list to filter * @nprofiles: number of entries in @profiles * * Return: 0 on success, else shell error code */ static int simple_filtered_count(FILE *outf, filters_t *filters, bool json, struct profile *profiles, size_t nprofiles) { struct profile *filtered = NULL; size_t nfiltered; int ret; ret = filter_profiles(profiles, nprofiles, filters, &filtered, &nfiltered); if (!json) { fprintf(outf, "%zd\n", nfiltered); } else { fprintf(outf, "\"profile_count\": %zd", nfiltered); } free_profiles(filtered, nfiltered); return ret; } /** * simple_filtered_process_count - count processes with mode == filter * @outf: output file destination * @filters: filters to filter processes on * @processes: process list to filter * @nprocesses: number of entries in @processes * * Return: 0 on success, else shell error code */ static int simple_filtered_process_count(FILE *outf, filters_t *filters, bool json, struct process *processes, size_t nprocesses) { struct process *filtered = NULL; size_t nfiltered; int ret; ret = filter_processes(processes, nprocesses, filters, &filtered, &nfiltered); if (!json) { fprintf(outf, "%zd\n", nfiltered); } else { fprintf(outf, "\"process_count\": %zd", nfiltered); } free_processes(filtered, nfiltered); return ret; } static int compare_processes_by_profile(const void *a, const void *b) { return strcmp(((struct process *)a)->profile, ((struct process *)b)->profile); } static int compare_processes_by_executable(const void *a, const void *b) { return strcmp(((struct process *)a)->exe, ((struct process *)b)->exe); } static void json_header(FILE *outf) { fprintf(outf, "{\"version\": \"%s\"", aa_status_json_version); } static void json_seperator(FILE *outf) { fprintf(outf, ", "); } static void json_footer(FILE *outf) { fprintf(outf, "}\n"); } /** * detailed_profiles - output a detailed listing of apparmor profile status * @outf: output file * @filters: filters to apply * @json: whether output should be in json format * @profiles: list of profiles to output * @nprofiles: number of profiles in @profiles * * Return: 0 on success, else shell error */ static int detailed_profiles(FILE *outf, filters_t *filters, bool json, struct profile *profiles, size_t nprofiles) { int ret; size_t i; int is_first = 1; if (json) { fprintf(outf, "\"profiles\": {"); } else { dfprintf(outf, "%zd profiles are loaded.\n", nprofiles); } for (i = 0; i < ARRAY_SIZE(profile_statuses); i++) { size_t nfiltered = 0, j; struct profile *filtered = NULL; filters_t subfilters = *filters; regex_t mode_filter; if (regexec(filters->mode, profile_statuses[i], 0, NULL, 0) == REG_NOMATCH) /* skip processing for entries that don't match filter*/ continue; /* need subfilter as we want to split on matches to specific * status */ subfilters.mode = &mode_filter; if (regcomp(&mode_filter, profile_statuses[i], REG_NOSUB) != 0) { eprintf(_("Error: failed to compile sub filter '%s'\n"), profile_statuses[i]); return AA_EXIT_INTERNAL_ERROR; } ret = filter_profiles(profiles, nprofiles, &subfilters, &filtered, &nfiltered); regfree(&mode_filter); if (ret != 0) { return ret; } if (!json) { dfprintf(outf, "%zd profiles are in %s mode.\n", nfiltered, profile_statuses[i]); } for (j = 0; j < nfiltered; j++) { if (json) { fprintf(outf, "%s\"%s\": \"%s\"", is_first ? "" : ", ", filtered[j].name, profile_statuses[i]); is_first = 0; } else { if(filtered[j].identities) dfprintf(outf, " %s (%s)\n", filtered[j].name, filtered[j].identities); else dfprintf(outf, " %s\n", filtered[j].name); } } free_profiles(filtered, nfiltered); } if (json) fprintf(outf, "}"); return AA_EXIT_ENABLED; } /** * detailed_processses - output a detailed listing of apparmor process status * @outf: output file * @filters: filter regexs * @json: whether output should be in json format * @processes: list of processes to output * @nprocesses: number of processes in @processes * * Return: 0 on success, else shell error */ static int detailed_processes(FILE *outf, filters_t *filters, bool json, struct process *processes, size_t nprocesses) { int ret = 0; size_t i; int need_finish = 0; if (json) { fprintf(outf, "\"processes\": {"); } else { dfprintf(outf, "%zd processes have profiles defined.\n", nprocesses); } for (i = 0; i < ARRAY_SIZE(process_statuses); i++) { size_t nfiltered = 0, j; struct process *filtered = NULL; filters_t subfilters = *filters; regex_t mode_filter; if (regexec(filters->mode, process_statuses[i], 0, NULL, 0) == REG_NOMATCH) /* skip processing for entries that don't match filter*/ continue; /* need sub_filter as we want to split on matches to specific * status */ subfilters.mode = &mode_filter; if (regcomp(&mode_filter, process_statuses[i], REG_NOSUB) != 0) { eprintf(_("Error: failed to compile sub filter '%s'\n"), profile_statuses[i]); return AA_EXIT_INTERNAL_ERROR; } ret = filter_processes(processes, nprocesses, &subfilters, &filtered, &nfiltered); regfree(&mode_filter); if (ret != 0) goto exit; if (!json) { if (strcmp(process_statuses[i], "unconfined") == 0) { dfprintf(outf, "%zd processes are unconfined but have a profile defined.\n", nfiltered); } else { dfprintf(outf, "%zd processes are in %s mode.\n", nfiltered, process_statuses[i]); } } if (!json) { qsort(filtered, nfiltered, sizeof(*filtered), compare_processes_by_profile); for (j = 0; j < nfiltered; j++) { dfprintf(outf, " %s (%s) %s\n", filtered[j].exe, filtered[j].pid, // hide profile name if matches executable (strcmp(filtered[j].profile, filtered[j].exe) == 0 ? "" : filtered[j].profile)); } } else { // json output requires processes to be grouped per executable qsort(filtered, nfiltered, sizeof(*filtered), compare_processes_by_executable); for (j = 0; j < nfiltered; j++) { if (j > 0 && strcmp(filtered[j].exe, filtered[j - 1].exe) == 0) { // same executable fprintf(outf, ", {\"profile\": \"%s\", \"pid\": \"%s\", \"status\": \"%s\"}", filtered[j].profile, filtered[j].pid, filtered[j].mode); } else { fprintf(outf, "%s\"%s\": [{\"profile\": \"%s\", \"pid\": \"%s\", \"status\": \"%s\"}", // first element will be a unique executable !need_finish ? "" : "], ", filtered[j].exe, filtered[j].profile, filtered[j].pid, filtered[j].mode); need_finish = 1; } } } free_processes(filtered, nfiltered); } if (json) { if (need_finish > 0) { fprintf(outf, "]"); } fprintf(outf, "}"); } exit: return ret; } static int print_legacy(const char *command) { printf(_("Usage: %s [OPTIONS]\n" "Legacy options and their equivalent command\n" " --profiled --count --profiles\n" " --enforced --count --profiles --mode=enforced\n" " --complaining --count --profiles --mode=complain\n" " --kill --count --profiles --mode=kill\n" " --prompt --count --profiles --mode=prompt\n" " --special-unconfined --count --profiles --mode=unconfined\n" " --process-mixed --count --ps --mode=mixed\n"), command); exit(0); return 0; } static int usage_filters(void) { long unsigned int i; printf(_("Usage of filters\n" "Filters are used to reduce the output of information to only\n" "those entries that will match the filter. Filters use posix\n" "regular expression syntax. The possible values for exes that\n" "support filters are below\n\n" " --filter.mode: regular expression to match the profile mode" " modes: enforce, complain, kill, unconfined, mixed\n" " --filter.profiles: regular expression to match displayed profile names\n" " --filter.pid: regular expression to match displayed processes pids\n" " --filter.exe: regular expression to match executable\n" )); for (i = 0; i < ARRAY_SIZE(process_statuses); i++) { printf("%s%s", i ? ", " : "", process_statuses[i]); } printf("\n"); exit(0); return 0; } static int print_usage(const char *command, bool error) { int status = EXIT_SUCCESS; if (error) { status = EXIT_FAILURE; } printf(_("Usage: %s [OPTIONS]\n" "Displays various information about the currently loaded AppArmor policy.\n" "Default if no options given\n" " --show=all\n\n" "OPTIONS (one only):\n" " --enabled returns error code if AppArmor not enabled\n" " --show=X What information to show. {profiles,processes,all}\n" " --count print the number of entries. Implies --quiet\n" " --filter.mode=filter see filters\n" " --filter.profiles=filter see filters\n" " --filter.pid=filter see filters\n" " --filter.exe=filter see filters\n" " --json displays multiple data points in machine-readable JSON format\n" " --pretty-json same data as --json, formatted for human consumption as well\n" " --verbose (default) displays data points about loaded policy set\n" " --quiet don't output error messages\n" " -h[(legacy|filters)] this message, or info on the specified option\n" " --help[=(legacy|filters)] this message, or info on the specified option\n"), command); exit(status); return 0; } #define ARG_ENABLED 129 #define ARG_PROFILED 130 #define ARG_ENFORCED 131 #define ARG_COMPLAIN 132 #define ARG_KILL 133 #define ARG_UNCONFINED 134 #define ARG_PS_MIXED 135 #define ARG_JSON 136 #define ARG_PRETTY 137 #define ARG_COUNT 138 #define ARG_SHOW 139 #define ARG_MODE 140 #define ARG_PROFILES 141 #define ARG_PID 142 #define ARG_EXE 143 #define ARG_PROMPT 144 #define ARG_VERBOSE 'v' #define ARG_QUIET 'q' #define ARG_HELP 'h' static int parse_args(int argc, char **argv) { int opt; struct option long_opts[] = { {"enabled", no_argument, 0, ARG_ENABLED}, {"profiled", no_argument, 0, ARG_PROFILED}, {"enforced", no_argument, 0, ARG_ENFORCED}, {"complaining", no_argument, 0, ARG_COMPLAIN}, {"prompt", no_argument, 0, ARG_PROMPT}, {"kill", no_argument, 0, ARG_KILL}, {"special-unconfined", no_argument, 0, ARG_UNCONFINED}, {"process-mixed", no_argument, 0, ARG_PS_MIXED}, {"json", no_argument, 0, ARG_JSON}, {"pretty-json", no_argument, 0, ARG_PRETTY}, {"verbose", no_argument, 0, ARG_VERBOSE}, {"quiet", no_argument, 0, ARG_QUIET}, {"help", 2, 0, ARG_HELP}, {"count", no_argument, 0, ARG_COUNT}, {"show", 1, 0, ARG_SHOW}, {"filter.profiles", 1, 0, ARG_PROFILES}, {"filter.pid", 1, 0, ARG_PID}, {"filter.exe", 1, 0, ARG_EXE}, {"filter.mode", 1, 0, ARG_MODE}, {NULL, 0, 0, 0}, }; // Using exit here is temporary while ((opt = getopt_long(argc, argv, "+vh::", long_opts, NULL)) != -1) { switch (opt) { case ARG_ENABLED: exit(aa_is_enabled() == 1 ? 0 : AA_EXIT_DISABLED); break; case ARG_VERBOSE: verbose = 1; /* default opt_mode */ /* default opt_show */ break; case ARG_QUIET: quiet = true; break; case ARG_HELP: if (!optarg) { print_usage(argv[0], false); } else if (strcmp(optarg, "legacy") == 0) { print_legacy(argv[0]); } else if (strcmp(optarg, "filters") == 0) { usage_filters(); } else { eprintf(_("Error: Invalid --help option '%s'.\n"), optarg); print_usage(argv[0], true); break; } break; case ARG_PROFILED: verbose = false; opt_count = true; opt_show = SHOW_PROFILES; /* default opt_mode */ break; case ARG_ENFORCED: verbose = false; opt_count = true; opt_show = SHOW_PROFILES; opt_mode = "enforce"; break; case ARG_COMPLAIN: verbose = false; opt_count = true; opt_show = SHOW_PROFILES; opt_mode = "complain"; break; case ARG_PROMPT: verbose = false; opt_count = true; opt_show = SHOW_PROFILES; opt_mode = "prompt"; break; case ARG_UNCONFINED: verbose = false; opt_count = true; opt_show = SHOW_PROFILES; opt_mode = "unconfined"; break; case ARG_KILL: verbose = false; opt_count = true; opt_show = SHOW_PROFILES; opt_mode = "kill"; break; case ARG_PS_MIXED: verbose = false; opt_count = true; opt_show = SHOW_PROCESSES; opt_mode = "mixed"; break; case ARG_JSON: opt_json = true; /* default opt_show */ break; case ARG_PRETTY: opt_pretty = true; opt_json = true; /* default opt_show */ break; case ARG_COUNT: opt_count = true; /* default opt_show */ break; case ARG_SHOW: if (strcmp(optarg, "all") == 0) { opt_show = SHOW_PROFILES | SHOW_PROCESSES; } else if (strcmp(optarg, "profiles") == 0) { opt_show = SHOW_PROFILES; } else if (strcmp(optarg, "processes") == 0) { opt_show = SHOW_PROCESSES; } else { eprintf(_("Error: Invalid --show option '%s'.\n"), optarg); print_usage(argv[0], true); break; } break; case ARG_PROFILES: opt_profiles = optarg; /* default opt_mode */ break; case ARG_PID: opt_pid = optarg; /* default opt_mode */ break; case ARG_MODE: opt_mode = optarg; break; case ARG_EXE: opt_exe = optarg; /* default opt_mode */ break; default: eprintf(_("Error: Invalid command.\n")); print_usage(argv[0], true); break; } } return optind; } int main(int argc, char **argv) { autofree char *buffer = NULL; /* pretty print buffer */ size_t buffer_size; autofclose FILE *fp = NULL; size_t nprofiles = 0; struct profile *profiles = NULL; int ret = EXIT_SUCCESS; const char *progname = argv[0]; FILE *outf = stdout, *outf_save = NULL; struct filter_set filter_set; filters_t filters; if (argc > 1) { int pos = parse_args(argc, argv); if (pos < argc) { eprintf(_("Error: Unknown options.\n")); print_usage(progname, true); } } else { verbose = 1; /* default opt_show */ /* default opt_mode */ /* default opt_json */ } init_filters(&filters, &filter_set); if (regcomp(filters.mode, opt_mode, REG_NOSUB) != 0) { eprintf(_("Error: failed to compile mode filter '%s'\n"), opt_mode); return AA_EXIT_INTERNAL_ERROR; } if (regcomp(filters.profile, opt_profiles, REG_NOSUB) != 0) { eprintf(_("Error: failed to compile profiles filter '%s'\n"), opt_profiles); ret = AA_EXIT_INTERNAL_ERROR; goto out; } if (regcomp(filters.pid, opt_pid, REG_NOSUB) != 0) { eprintf(_("Error: failed to compile ps filter '%s'\n"), opt_pid); ret = AA_EXIT_INTERNAL_ERROR; goto out; } if (regcomp(filters.exe, opt_exe, REG_NOSUB) != 0) { eprintf(_("Error: failed to compile exe filter '%s'\n"), opt_exe); ret = AA_EXIT_INTERNAL_ERROR; goto out; } /* check apparmor is available and we have permissions */ ret = open_profiles(&fp); if (ret != 0) goto out; if (opt_pretty) { outf_save = outf; outf = open_memstream(&buffer, &buffer_size); if (!outf) { eprintf(_("Failed to open memstream: %m\n")); return AA_EXIT_INTERNAL_ERROR; } } /* always get policy even if not displayed because getting processes * requires it to filter out unconfined tasks that don't or shouldn't * have policy associated. */ ret = get_profiles(fp, &profiles, &nprofiles); if (ret == AA_EXIT_NO_POLICY && !opt_json) { eprintf(_("No policy loaded into the kernel\n")); goto out; } else if (ret != 0 && !opt_json) { eprintf(_("Failed to retrieve profiles from kernel: %d....\n"), ret); goto out; } if (opt_json) json_header(outf); if (opt_show & SHOW_PROFILES) { if (opt_json) json_seperator(outf); if (opt_count) { ret = simple_filtered_count(outf, &filters, opt_json, profiles, nprofiles); } else { ret = detailed_profiles(outf, &filters, opt_json, profiles, nprofiles); } if (ret != 0) goto out; } if (opt_show & SHOW_PROCESSES) { if (opt_json) json_seperator(outf); struct process *processes = NULL; size_t nprocesses = 0; ret = get_processes(profiles, nprofiles, &processes, &nprocesses); if (ret != 0) { eprintf(_("Failed to get confinement information from processes: %d....\n"), ret); } else if (opt_count) { ret = simple_filtered_process_count(outf, &filters, opt_json, processes, nprocesses); } else { ret = detailed_processes(outf, &filters, opt_json, processes, nprocesses); } free_processes(processes, nprocesses); if (ret != 0) goto out; } if (opt_json) json_footer(outf); if (opt_pretty) { autofree char *pretty = NULL; cJSON *json; /* explicit close to sync */ fclose(outf); outf = outf_save; json = cJSON_Parse(buffer); if (!json) { eprintf(_("Failed to parse json output")); ret = AA_EXIT_INTERNAL_ERROR; goto out; } pretty = cJSON_Print(json); if (!pretty) { eprintf(_("Failed to print pretty json")); ret = AA_EXIT_INTERNAL_ERROR; goto out; } fprintf(outf, "%s\n", pretty); ret = AA_EXIT_ENABLED; } out: free_profiles(profiles, nprofiles); free_filters(&filters); exit(ret); } apparmor-5.0.2/binutils/cJSON.c000066400000000000000000002265371522511161100162620ustar00rootroot00000000000000/* Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* cJSON */ /* JSON parser in C. */ /* disable warnings about old C89 functions in MSVC */ #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) #define _CRT_SECURE_NO_DEPRECATE #endif #ifdef __GNUC__ #pragma GCC visibility push(default) #endif #if defined(_MSC_VER) #pragma warning (push) /* disable warning about single line comments in system headers */ #pragma warning (disable : 4001) #endif #include #include #include #include #include #include #include #ifdef ENABLE_LOCALES #include #endif #if defined(_MSC_VER) #pragma warning (pop) #endif #ifdef __GNUC__ #pragma GCC visibility pop #endif #include "cJSON.h" /* define our own boolean type */ #ifdef true #undef true #endif #define true ((cJSON_bool)1) #ifdef false #undef false #endif #define false ((cJSON_bool)0) /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ #ifndef isinf #define isinf(d) (isnan((d - d)) && !isnan(d)) #endif #ifndef isnan #define isnan(d) (d != d) #endif #ifndef NAN #define NAN 0.0/0.0 #endif typedef struct { const unsigned char *json; size_t position; } error; static error global_error = { NULL, 0 }; CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { return (const char*) (global_error.json + global_error.position); } CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { if (!cJSON_IsString(item)) { return NULL; } return item->valuestring; } CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item) { if (!cJSON_IsNumber(item)) { return NAN; } return item->valuedouble; } /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 13) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif CJSON_PUBLIC(const char*) cJSON_Version(void) { static char version[15]; sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); return version; } /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { if ((string1 == NULL) || (string2 == NULL)) { return 1; } if (string1 == string2) { return 0; } for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { if (*string1 == '\0') { return 0; } } return tolower(*string1) - tolower(*string2); } typedef struct internal_hooks { void *(CJSON_CDECL *allocate)(size_t size); void (CJSON_CDECL *deallocate)(void *pointer); void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); } internal_hooks; #if defined(_MSC_VER) /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ static void * CJSON_CDECL internal_malloc(size_t size) { return malloc(size); } static void CJSON_CDECL internal_free(void *pointer) { free(pointer); } static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) { return realloc(pointer, size); } #else #define internal_malloc malloc #define internal_free free #define internal_realloc realloc #endif /* strlen of character literals resolved at compile time */ #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) { size_t length = 0; unsigned char *copy = NULL; if (string == NULL) { return NULL; } length = strlen((const char*)string) + sizeof(""); copy = (unsigned char*)hooks->allocate(length); if (copy == NULL) { return NULL; } memcpy(copy, string, length); return copy; } CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { if (hooks == NULL) { /* Reset hooks */ global_hooks.allocate = malloc; global_hooks.deallocate = free; global_hooks.reallocate = realloc; return; } global_hooks.allocate = malloc; if (hooks->malloc_fn != NULL) { global_hooks.allocate = hooks->malloc_fn; } global_hooks.deallocate = free; if (hooks->free_fn != NULL) { global_hooks.deallocate = hooks->free_fn; } /* use realloc only if both free and malloc are used */ global_hooks.reallocate = NULL; if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { global_hooks.reallocate = realloc; } } /* Internal constructor. */ static cJSON *cJSON_New_Item(const internal_hooks * const hooks) { cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); if (node) { memset(node, '\0', sizeof(cJSON)); } return node; } /* Delete a cJSON structure. */ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { cJSON *next = NULL; while (item != NULL) { next = item->next; if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { cJSON_Delete(item->child); } if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { global_hooks.deallocate(item->valuestring); } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { global_hooks.deallocate(item->string); } global_hooks.deallocate(item); item = next; } } /* get the decimal point character of the current locale */ static unsigned char get_decimal_point(void) { #ifdef ENABLE_LOCALES struct lconv *lconv = localeconv(); return (unsigned char) lconv->decimal_point[0]; #else return '.'; #endif } typedef struct { const unsigned char *content; size_t length; size_t offset; size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ internal_hooks hooks; } parse_buffer; /* check if the given size is left to read in a given parse buffer (starting with 1) */ #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) /* check if the buffer can be accessed at the given index (starting with 0) */ #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) /* get a pointer to the buffer at the position */ #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) /* Parse the input text to generate a number, and populate the result into item. */ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { double number = 0; unsigned char *after_end = NULL; unsigned char number_c_string[64]; unsigned char decimal_point = get_decimal_point(); size_t i = 0; if ((input_buffer == NULL) || (input_buffer->content == NULL)) { return false; } /* copy the number into a temporary buffer and replace '.' with the decimal point * of the current locale (for strtod) * This also takes care of '\0' not necessarily being available for marking the end of the input */ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { switch (buffer_at_offset(input_buffer)[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '+': case '-': case 'e': case 'E': number_c_string[i] = buffer_at_offset(input_buffer)[i]; break; case '.': number_c_string[i] = decimal_point; break; default: goto loop_end; } } loop_end: number_c_string[i] = '\0'; number = strtod((const char*)number_c_string, (char**)&after_end); if (number_c_string == after_end) { return false; /* parse_error */ } item->valuedouble = number; /* use saturation in case of overflow */ if (number >= INT_MAX) { item->valueint = INT_MAX; } else if (number <= (double)INT_MIN) { item->valueint = INT_MIN; } else { item->valueint = (int)number; } item->type = cJSON_Number; input_buffer->offset += (size_t)(after_end - number_c_string); return true; } /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { if (number >= INT_MAX) { object->valueint = INT_MAX; } else if (number <= (double)INT_MIN) { object->valueint = INT_MIN; } else { object->valueint = (int)number; } return object->valuedouble = number; } CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) { char *copy = NULL; /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { return NULL; } if (strlen(valuestring) <= strlen(object->valuestring)) { strcpy(object->valuestring, valuestring); return object->valuestring; } copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); if (copy == NULL) { return NULL; } if (object->valuestring != NULL) { cJSON_free(object->valuestring); } object->valuestring = copy; return copy; } typedef struct { unsigned char *buffer; size_t length; size_t offset; size_t depth; /* current nesting depth (for formatted printing) */ cJSON_bool noalloc; cJSON_bool format; /* is this print a formatted print */ internal_hooks hooks; } printbuffer; /* realloc printbuffer if necessary to have at least "needed" bytes more */ static unsigned char* ensure(printbuffer * const p, size_t needed) { unsigned char *newbuffer = NULL; size_t newsize = 0; if ((p == NULL) || (p->buffer == NULL)) { return NULL; } if ((p->length > 0) && (p->offset >= p->length)) { /* make sure that offset is valid */ return NULL; } if (needed > INT_MAX) { /* sizes bigger than INT_MAX are currently not supported */ return NULL; } needed += p->offset + 1; if (needed <= p->length) { return p->buffer + p->offset; } if (p->noalloc) { return NULL; } /* calculate new buffer size */ if (needed > (INT_MAX / 2)) { /* overflow of int, use INT_MAX if possible */ if (needed <= INT_MAX) { newsize = INT_MAX; } else { return NULL; } } else { newsize = needed * 2; } if (p->hooks.reallocate != NULL) { /* reallocate with realloc if available */ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); if (newbuffer == NULL) { p->hooks.deallocate(p->buffer); p->length = 0; p->buffer = NULL; return NULL; } } else { /* otherwise reallocate manually */ newbuffer = (unsigned char*)p->hooks.allocate(newsize); if (!newbuffer) { p->hooks.deallocate(p->buffer); p->length = 0; p->buffer = NULL; return NULL; } if (newbuffer) { memcpy(newbuffer, p->buffer, p->offset + 1); } p->hooks.deallocate(p->buffer); } p->length = newsize; p->buffer = newbuffer; return newbuffer + p->offset; } /* calculate the new length of the string in a printbuffer and update the offset */ static void update_offset(printbuffer * const buffer) { const unsigned char *buffer_pointer = NULL; if ((buffer == NULL) || (buffer->buffer == NULL)) { return; } buffer_pointer = buffer->buffer + buffer->offset; buffer->offset += strlen((const char*)buffer_pointer); } /* securely comparison of floating-point variables */ static cJSON_bool compare_double(double a, double b) { double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } /* Render the number nicely from the given item into a string. */ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output_pointer = NULL; double d = item->valuedouble; int length = 0; size_t i = 0; unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ unsigned char decimal_point = get_decimal_point(); double test = 0.0; if (output_buffer == NULL) { return false; } /* This checks for NaN and Infinity */ if (isnan(d) || isinf(d)) { length = sprintf((char*)number_buffer, "null"); } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ length = sprintf((char*)number_buffer, "%1.15g", d); /* Check whether the original double can be recovered */ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { /* If not, print with 17 decimal places of precision */ length = sprintf((char*)number_buffer, "%1.17g", d); } } /* sprintf failed or buffer overrun occurred */ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { return false; } /* reserve appropriate space in the output */ output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); if (output_pointer == NULL) { return false; } /* copy the printed number to the output and replace locale * dependent decimal point with '.' */ for (i = 0; i < ((size_t)length); i++) { if (number_buffer[i] == decimal_point) { output_pointer[i] = '.'; continue; } output_pointer[i] = number_buffer[i]; } output_pointer[i] = '\0'; output_buffer->offset += (size_t)length; return true; } /* parse 4 digit hexadecimal number */ static unsigned parse_hex4(const unsigned char * const input) { unsigned int h = 0; size_t i = 0; for (i = 0; i < 4; i++) { /* parse digit */ if ((input[i] >= '0') && (input[i] <= '9')) { h += (unsigned int) input[i] - '0'; } else if ((input[i] >= 'A') && (input[i] <= 'F')) { h += (unsigned int) 10 + input[i] - 'A'; } else if ((input[i] >= 'a') && (input[i] <= 'f')) { h += (unsigned int) 10 + input[i] - 'a'; } else /* invalid */ { return 0; } if (i < 3) { /* shift left to make place for the next nibble */ h = h << 4; } } return h; } /* converts a UTF-16 literal to UTF-8 * A literal can be one or two sequences of the form \uXXXX */ static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) { long unsigned int codepoint = 0; unsigned int first_code = 0; const unsigned char *first_sequence = input_pointer; unsigned char utf8_length = 0; unsigned char utf8_position = 0; unsigned char sequence_length = 0; unsigned char first_byte_mark = 0; if ((input_end - first_sequence) < 6) { /* input ends unexpectedly */ goto fail; } /* get the first utf16 sequence */ first_code = parse_hex4(first_sequence + 2); /* check that the code is valid */ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { goto fail; } /* UTF16 surrogate pair */ if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { const unsigned char *second_sequence = first_sequence + 6; unsigned int second_code = 0; sequence_length = 12; /* \uXXXX\uXXXX */ if ((input_end - second_sequence) < 6) { /* input ends unexpectedly */ goto fail; } if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { /* missing second half of the surrogate pair */ goto fail; } /* get the second utf16 sequence */ second_code = parse_hex4(second_sequence + 2); /* check that the code is valid */ if ((second_code < 0xDC00) || (second_code > 0xDFFF)) { /* invalid second half of the surrogate pair */ goto fail; } /* calculate the unicode codepoint from the surrogate pair */ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); } else { sequence_length = 6; /* \uXXXX */ codepoint = first_code; } /* encode as UTF-8 * takes at maximum 4 bytes to encode: * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (codepoint < 0x80) { /* normal ascii, encoding 0xxxxxxx */ utf8_length = 1; } else if (codepoint < 0x800) { /* two bytes, encoding 110xxxxx 10xxxxxx */ utf8_length = 2; first_byte_mark = 0xC0; /* 11000000 */ } else if (codepoint < 0x10000) { /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ utf8_length = 3; first_byte_mark = 0xE0; /* 11100000 */ } else if (codepoint <= 0x10FFFF) { /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ utf8_length = 4; first_byte_mark = 0xF0; /* 11110000 */ } else { /* invalid unicode codepoint */ goto fail; } /* encode as utf8 */ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { /* 10xxxxxx */ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); codepoint >>= 6; } /* encode first byte */ if (utf8_length > 1) { (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); } else { (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); } *output_pointer += utf8_length; return sequence_length; fail: return 0; } /* Parse the input text into an unescaped cinput, and populate item. */ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) { const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; unsigned char *output_pointer = NULL; unsigned char *output = NULL; /* not a string */ if (buffer_at_offset(input_buffer)[0] != '\"') { goto fail; } { /* calculate approximate size of the output (overestimate) */ size_t allocation_length = 0; size_t skipped_bytes = 0; while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) { /* is escape sequence */ if (input_end[0] == '\\') { if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { /* prevent buffer overflow when last input character is a backslash */ goto fail; } skipped_bytes++; input_end++; } input_end++; } if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) { goto fail; /* string ended unexpectedly */ } /* This is at most how much we need for the output */ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); if (output == NULL) { goto fail; /* allocation failure */ } } output_pointer = output; /* loop through the string literal */ while (input_pointer < input_end) { if (*input_pointer != '\\') { *output_pointer++ = *input_pointer++; } /* escape sequence */ else { unsigned char sequence_length = 2; if ((input_end - input_pointer) < 1) { goto fail; } switch (input_pointer[1]) { case 'b': *output_pointer++ = '\b'; break; case 'f': *output_pointer++ = '\f'; break; case 'n': *output_pointer++ = '\n'; break; case 'r': *output_pointer++ = '\r'; break; case 't': *output_pointer++ = '\t'; break; case '\"': case '\\': case '/': *output_pointer++ = input_pointer[1]; break; /* UTF-16 literal */ case 'u': sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); if (sequence_length == 0) { /* failed to convert UTF16-literal to UTF-8 */ goto fail; } break; default: goto fail; } input_pointer += sequence_length; } } /* zero terminate the output */ *output_pointer = '\0'; item->type = cJSON_String; item->valuestring = (char*)output; input_buffer->offset = (size_t) (input_end - input_buffer->content); input_buffer->offset++; return true; fail: if (output != NULL) { input_buffer->hooks.deallocate(output); } if (input_pointer != NULL) { input_buffer->offset = (size_t)(input_pointer - input_buffer->content); } return false; } /* Render the cstring provided to an escaped version that can be printed. */ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) { const unsigned char *input_pointer = NULL; unsigned char *output = NULL; unsigned char *output_pointer = NULL; size_t output_length = 0; /* numbers of additional characters needed for escaping */ size_t escape_characters = 0; if (output_buffer == NULL) { return false; } /* empty string */ if (input == NULL) { output = ensure(output_buffer, sizeof("\"\"")); if (output == NULL) { return false; } strcpy((char*)output, "\"\""); return true; } /* set "flag" to 1 if something needs to be escaped */ for (input_pointer = input; *input_pointer; input_pointer++) { switch (*input_pointer) { case '\"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': /* one character escape sequence */ escape_characters++; break; default: if (*input_pointer < 32) { /* UTF-16 escape sequence uXXXX */ escape_characters += 5; } break; } } output_length = (size_t)(input_pointer - input) + escape_characters; output = ensure(output_buffer, output_length + sizeof("\"\"")); if (output == NULL) { return false; } /* no characters have to be escaped */ if (escape_characters == 0) { output[0] = '\"'; memcpy(output + 1, input, output_length); output[output_length + 1] = '\"'; output[output_length + 2] = '\0'; return true; } output[0] = '\"'; output_pointer = output + 1; /* copy the string */ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { /* normal character, copy */ *output_pointer = *input_pointer; } else { /* character needs to be escaped */ *output_pointer++ = '\\'; switch (*input_pointer) { case '\\': *output_pointer = '\\'; break; case '\"': *output_pointer = '\"'; break; case '\b': *output_pointer = 'b'; break; case '\f': *output_pointer = 'f'; break; case '\n': *output_pointer = 'n'; break; case '\r': *output_pointer = 'r'; break; case '\t': *output_pointer = 't'; break; default: /* escape and print as unicode codepoint */ sprintf((char*)output_pointer, "u%04x", *input_pointer); output_pointer += 4; break; } } } output[output_length + 1] = '\"'; output[output_length + 2] = '\0'; return true; } /* Invoke print_string_ptr (which is useful) on an item. */ static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) { return print_string_ptr((unsigned char*)item->valuestring, p); } /* Predeclare these prototypes. */ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); /* Utility to jump whitespace and cr/lf */ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) { if ((buffer == NULL) || (buffer->content == NULL)) { return NULL; } if (cannot_access_at_index(buffer, 0)) { return buffer; } while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { buffer->offset++; } if (buffer->offset == buffer->length) { buffer->offset--; } return buffer; } /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) { if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { return NULL; } if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { buffer->offset += 3; } return buffer; } CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { size_t buffer_length; if (NULL == value) { return NULL; } /* Adding null character size due to require_null_terminated. */ buffer_length = strlen(value) + sizeof(""); return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); } /* Parse an object - create a new root, and populate. */ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) { parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; cJSON *item = NULL; /* reset error position */ global_error.json = NULL; global_error.position = 0; if (value == NULL || 0 == buffer_length) { goto fail; } buffer.content = (const unsigned char*)value; buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; item = cJSON_New_Item(&global_hooks); if (item == NULL) /* memory fail */ { goto fail; } if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { /* parse failure. ep is set. */ goto fail; } /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ if (require_null_terminated) { buffer_skip_whitespace(&buffer); if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { goto fail; } } if (return_parse_end) { *return_parse_end = (const char*)buffer_at_offset(&buffer); } return item; fail: if (item != NULL) { cJSON_Delete(item); } if (value != NULL) { error local_error; local_error.json = (const unsigned char*)value; local_error.position = 0; if (buffer.offset < buffer.length) { local_error.position = buffer.offset; } else if (buffer.length > 0) { local_error.position = buffer.length - 1; } if (return_parse_end != NULL) { *return_parse_end = (const char*)local_error.json + local_error.position; } global_error = local_error; } return NULL; } /* Default options for cJSON_Parse */ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) { return cJSON_ParseWithOpts(value, 0, 0); } CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) { return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); } #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) { static const size_t default_buffer_size = 256; printbuffer buffer[1]; unsigned char *printed = NULL; memset(buffer, 0, sizeof(buffer)); /* create buffer */ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); buffer->length = default_buffer_size; buffer->format = format; buffer->hooks = *hooks; if (buffer->buffer == NULL) { goto fail; } /* print the value */ if (!print_value(item, buffer)) { goto fail; } update_offset(buffer); /* check if reallocate is available */ if (hooks->reallocate != NULL) { printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); if (printed == NULL) { goto fail; } buffer->buffer = NULL; } else /* otherwise copy the JSON over to a new buffer */ { printed = (unsigned char*) hooks->allocate(buffer->offset + 1); if (printed == NULL) { goto fail; } memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); printed[buffer->offset] = '\0'; /* just to be sure */ /* free the buffer */ hooks->deallocate(buffer->buffer); } return printed; fail: if (buffer->buffer != NULL) { hooks->deallocate(buffer->buffer); } if (printed != NULL) { hooks->deallocate(printed); } return NULL; } /* Render a cJSON item/entity/structure to text. */ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { return (char*)print(item, true, &global_hooks); } CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) { return (char*)print(item, false, &global_hooks); } CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; if (prebuffer < 0) { return NULL; } p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); if (!p.buffer) { return NULL; } p.length = (size_t)prebuffer; p.offset = 0; p.noalloc = false; p.format = fmt; p.hooks = global_hooks; if (!print_value(item, &p)) { global_hooks.deallocate(p.buffer); return NULL; } return (char*)p.buffer; } CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; if ((length < 0) || (buffer == NULL)) { return false; } p.buffer = (unsigned char*)buffer; p.length = (size_t)length; p.offset = 0; p.noalloc = true; p.format = format; p.hooks = global_hooks; return print_value(item, &p); } /* Parser core - when encountering text, process appropriately. */ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) { if ((input_buffer == NULL) || (input_buffer->content == NULL)) { return false; /* no input */ } /* parse the different types of values */ /* null */ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) { item->type = cJSON_NULL; input_buffer->offset += 4; return true; } /* false */ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) { item->type = cJSON_False; input_buffer->offset += 5; return true; } /* true */ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) { item->type = cJSON_True; item->valueint = 1; input_buffer->offset += 4; return true; } /* string */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { return parse_string(item, input_buffer); } /* number */ if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) { return parse_number(item, input_buffer); } /* array */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { return parse_array(item, input_buffer); } /* object */ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { return parse_object(item, input_buffer); } return false; } /* Render a value to text. */ static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output = NULL; if ((item == NULL) || (output_buffer == NULL)) { return false; } switch ((item->type) & 0xFF) { case cJSON_NULL: output = ensure(output_buffer, 5); if (output == NULL) { return false; } strcpy((char*)output, "null"); return true; case cJSON_False: output = ensure(output_buffer, 6); if (output == NULL) { return false; } strcpy((char*)output, "false"); return true; case cJSON_True: output = ensure(output_buffer, 5); if (output == NULL) { return false; } strcpy((char*)output, "true"); return true; case cJSON_Number: return print_number(item, output_buffer); case cJSON_Raw: { size_t raw_length = 0; if (item->valuestring == NULL) { return false; } raw_length = strlen(item->valuestring) + sizeof(""); output = ensure(output_buffer, raw_length); if (output == NULL) { return false; } memcpy(output, item->valuestring, raw_length); return true; } case cJSON_String: return print_string(item, output_buffer); case cJSON_Array: return print_array(item, output_buffer); case cJSON_Object: return print_object(item, output_buffer); default: return false; } } /* Build an array from input text. */ static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) { cJSON *head = NULL; /* head of the linked list */ cJSON *current_item = NULL; if (input_buffer->depth >= CJSON_NESTING_LIMIT) { return false; /* to deeply nested */ } input_buffer->depth++; if (buffer_at_offset(input_buffer)[0] != '[') { /* not an array */ goto fail; } input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { /* empty array */ goto success; } /* check if we skipped to the end of the buffer */ if (cannot_access_at_index(input_buffer, 0)) { input_buffer->offset--; goto fail; } /* step back to character in front of the first element */ input_buffer->offset--; /* loop through the comma separated array elements */ do { /* allocate next item */ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); if (new_item == NULL) { goto fail; /* allocation failure */ } /* attach next item to list */ if (head == NULL) { /* start the linked list */ current_item = head = new_item; } else { /* add to the end and advance */ current_item->next = new_item; new_item->prev = current_item; current_item = new_item; } /* parse next value */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (!parse_value(current_item, input_buffer)) { goto fail; /* failed to parse value */ } buffer_skip_whitespace(input_buffer); } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { goto fail; /* expected end of array */ } success: input_buffer->depth--; item->type = cJSON_Array; item->child = head; input_buffer->offset++; return true; fail: if (head != NULL) { cJSON_Delete(head); } return false; } /* Render an array to text */ static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output_pointer = NULL; size_t length = 0; cJSON *current_element = item->child; if (output_buffer == NULL) { return false; } /* Compose the output array. */ /* opening square bracket */ output_pointer = ensure(output_buffer, 1); if (output_pointer == NULL) { return false; } *output_pointer = '['; output_buffer->offset++; output_buffer->depth++; while (current_element != NULL) { if (!print_value(current_element, output_buffer)) { return false; } update_offset(output_buffer); if (current_element->next) { length = (size_t) (output_buffer->format ? 2 : 1); output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { return false; } *output_pointer++ = ','; if(output_buffer->format) { *output_pointer++ = ' '; } *output_pointer = '\0'; output_buffer->offset += length; } current_element = current_element->next; } output_pointer = ensure(output_buffer, 2); if (output_pointer == NULL) { return false; } *output_pointer++ = ']'; *output_pointer = '\0'; output_buffer->depth--; return true; } /* Build an object from the text. */ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) { cJSON *head = NULL; /* linked list head */ cJSON *current_item = NULL; if (input_buffer->depth >= CJSON_NESTING_LIMIT) { return false; /* to deeply nested */ } input_buffer->depth++; if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { goto fail; /* not an object */ } input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { goto success; /* empty object */ } /* check if we skipped to the end of the buffer */ if (cannot_access_at_index(input_buffer, 0)) { input_buffer->offset--; goto fail; } /* step back to character in front of the first element */ input_buffer->offset--; /* loop through the comma separated array elements */ do { /* allocate next item */ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); if (new_item == NULL) { goto fail; /* allocation failure */ } /* attach next item to list */ if (head == NULL) { /* start the linked list */ current_item = head = new_item; } else { /* add to the end and advance */ current_item->next = new_item; new_item->prev = current_item; current_item = new_item; } /* parse the name of the child */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (!parse_string(current_item, input_buffer)) { goto fail; /* failed to parse name */ } buffer_skip_whitespace(input_buffer); /* swap valuestring and string, because we parsed the name */ current_item->string = current_item->valuestring; current_item->valuestring = NULL; if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { goto fail; /* invalid object */ } /* parse the value */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (!parse_value(current_item, input_buffer)) { goto fail; /* failed to parse value */ } buffer_skip_whitespace(input_buffer); } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { goto fail; /* expected end of object */ } success: input_buffer->depth--; item->type = cJSON_Object; item->child = head; input_buffer->offset++; return true; fail: if (head != NULL) { cJSON_Delete(head); } return false; } /* Render an object to text. */ static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output_pointer = NULL; size_t length = 0; cJSON *current_item = item->child; if (output_buffer == NULL) { return false; } /* Compose the output: */ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { return false; } *output_pointer++ = '{'; output_buffer->depth++; if (output_buffer->format) { *output_pointer++ = '\n'; } output_buffer->offset += length; while (current_item) { if (output_buffer->format) { size_t i; output_pointer = ensure(output_buffer, output_buffer->depth); if (output_pointer == NULL) { return false; } for (i = 0; i < output_buffer->depth; i++) { *output_pointer++ = '\t'; } output_buffer->offset += output_buffer->depth; } /* print key */ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) { return false; } update_offset(output_buffer); length = (size_t) (output_buffer->format ? 2 : 1); output_pointer = ensure(output_buffer, length); if (output_pointer == NULL) { return false; } *output_pointer++ = ':'; if (output_buffer->format) { *output_pointer++ = '\t'; } output_buffer->offset += length; /* print value */ if (!print_value(current_item, output_buffer)) { return false; } update_offset(output_buffer); /* print comma if not last */ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { return false; } if (current_item->next) { *output_pointer++ = ','; } if (output_buffer->format) { *output_pointer++ = '\n'; } *output_pointer = '\0'; output_buffer->offset += length; current_item = current_item->next; } output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); if (output_pointer == NULL) { return false; } if (output_buffer->format) { size_t i; for (i = 0; i < (output_buffer->depth - 1); i++) { *output_pointer++ = '\t'; } } *output_pointer++ = '}'; *output_pointer = '\0'; output_buffer->depth--; return true; } /* Get Array size/item / object item. */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { cJSON *child = NULL; size_t size = 0; if (array == NULL) { return 0; } child = array->child; while(child != NULL) { size++; child = child->next; } /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ return (int)size; } static cJSON* get_array_item(const cJSON *array, size_t index) { cJSON *current_child = NULL; if (array == NULL) { return NULL; } current_child = array->child; while ((current_child != NULL) && (index > 0)) { index--; current_child = current_child->next; } return current_child; } CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) { if (index < 0) { return NULL; } return get_array_item(array, (size_t)index); } static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) { cJSON *current_element = NULL; if ((object == NULL) || (name == NULL)) { return NULL; } current_element = object->child; if (case_sensitive) { while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { current_element = current_element->next; } } else { while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) { current_element = current_element->next; } } if ((current_element == NULL) || (current_element->string == NULL)) { return NULL; } return current_element; } CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) { return get_object_item(object, string, false); } CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) { return get_object_item(object, string, true); } CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) { return cJSON_GetObjectItem(object, string) ? 1 : 0; } /* Utility for array list handling. */ static void suffix_object(cJSON *prev, cJSON *item) { prev->next = item; item->prev = prev; } /* Utility for handling references. */ static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) { cJSON *reference = NULL; if (item == NULL) { return NULL; } reference = cJSON_New_Item(hooks); if (reference == NULL) { return NULL; } memcpy(reference, item, sizeof(cJSON)); reference->string = NULL; reference->type |= cJSON_IsReference; reference->next = reference->prev = NULL; return reference; } static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { cJSON *child = NULL; if ((item == NULL) || (array == NULL) || (array == item)) { return false; } child = array->child; /* * To find the last item in array quickly, we use prev in array */ if (child == NULL) { /* list is empty, start new one */ array->child = item; item->prev = item; item->next = NULL; } else { /* append to the end */ if (child->prev) { suffix_object(child->prev, item); array->child->prev = item; } else { while (child->next) { child = child->next; } suffix_object(child, item); array->child->prev = item; } } return true; } /* Add item to array/object. */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) { return add_item_to_array(array, item); } #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) #pragma GCC diagnostic push #endif #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-qual" #endif /* helper function to cast away const */ static void* cast_away_const(const void* string) { return (void*)string; } #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) #pragma GCC diagnostic pop #endif static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) { char *new_key = NULL; int new_type = cJSON_Invalid; if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { return false; } if (constant_key) { new_key = (char*)cast_away_const(string); new_type = item->type | cJSON_StringIsConst; } else { new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); if (new_key == NULL) { return false; } new_type = item->type & ~cJSON_StringIsConst; } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { hooks->deallocate(item->string); } item->string = new_key; item->type = new_type; return add_item_to_array(object, item); } CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { return add_item_to_object(object, string, item, &global_hooks, false); } /* Add an item to an object with constant string as key */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { return add_item_to_object(object, string, item, &global_hooks, true); } CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) { if (array == NULL) { return false; } return add_item_to_array(array, create_reference(item, &global_hooks)); } CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { if ((object == NULL) || (string == NULL)) { return false; } return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); } CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) { cJSON *null = cJSON_CreateNull(); if (add_item_to_object(object, name, null, &global_hooks, false)) { return null; } cJSON_Delete(null); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) { cJSON *true_item = cJSON_CreateTrue(); if (add_item_to_object(object, name, true_item, &global_hooks, false)) { return true_item; } cJSON_Delete(true_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) { cJSON *false_item = cJSON_CreateFalse(); if (add_item_to_object(object, name, false_item, &global_hooks, false)) { return false_item; } cJSON_Delete(false_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) { cJSON *bool_item = cJSON_CreateBool(boolean); if (add_item_to_object(object, name, bool_item, &global_hooks, false)) { return bool_item; } cJSON_Delete(bool_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) { cJSON *number_item = cJSON_CreateNumber(number); if (add_item_to_object(object, name, number_item, &global_hooks, false)) { return number_item; } cJSON_Delete(number_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) { cJSON *string_item = cJSON_CreateString(string); if (add_item_to_object(object, name, string_item, &global_hooks, false)) { return string_item; } cJSON_Delete(string_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) { cJSON *raw_item = cJSON_CreateRaw(raw); if (add_item_to_object(object, name, raw_item, &global_hooks, false)) { return raw_item; } cJSON_Delete(raw_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) { cJSON *object_item = cJSON_CreateObject(); if (add_item_to_object(object, name, object_item, &global_hooks, false)) { return object_item; } cJSON_Delete(object_item); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) { cJSON *array = cJSON_CreateArray(); if (add_item_to_object(object, name, array, &global_hooks, false)) { return array; } cJSON_Delete(array); return NULL; } CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) { if ((parent == NULL) || (item == NULL)) { return NULL; } if (item != parent->child) { /* not the first element */ item->prev->next = item->next; } if (item->next != NULL) { /* not the last element */ item->next->prev = item->prev; } if (item == parent->child) { /* first element */ parent->child = item->next; } /* make sure the detached item doesn't point anywhere anymore */ item->prev = NULL; item->next = NULL; return item; } CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) { if (which < 0) { return NULL; } return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); } CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) { cJSON_Delete(cJSON_DetachItemFromArray(array, which)); } CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) { cJSON *to_detach = cJSON_GetObjectItem(object, string); return cJSON_DetachItemViaPointer(object, to_detach); } CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) { cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); return cJSON_DetachItemViaPointer(object, to_detach); } CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) { cJSON_Delete(cJSON_DetachItemFromObject(object, string)); } CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) { cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); } /* Replace array/object items with new ones. */ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { cJSON *after_inserted = NULL; if (which < 0) { return false; } after_inserted = get_array_item(array, (size_t)which); if (after_inserted == NULL) { return add_item_to_array(array, newitem); } newitem->next = after_inserted; newitem->prev = after_inserted->prev; after_inserted->prev = newitem; if (after_inserted == array->child) { array->child = newitem; } else { newitem->prev->next = newitem; } return true; } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) { if ((parent == NULL) || (replacement == NULL) || (item == NULL)) { return false; } if (replacement == item) { return true; } replacement->next = item->next; replacement->prev = item->prev; if (replacement->next != NULL) { replacement->next->prev = replacement; } if (parent->child == item) { parent->child = replacement; } else { /* * To find the last item in array quickly, we use prev in array. * We can't modify the last item's next pointer where this item was the parent's child */ if (replacement->prev != NULL) { replacement->prev->next = replacement; } } item->next = NULL; item->prev = NULL; cJSON_Delete(item); return true; } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { if (which < 0) { return false; } return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); } static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) { if ((replacement == NULL) || (string == NULL)) { return false; } /* replace the name in the replacement */ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { cJSON_free(replacement->string); } replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); replacement->type &= ~cJSON_StringIsConst; return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { return replace_item_in_object(object, string, newitem, false); } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { return replace_item_in_object(object, string, newitem, true); } /* Create basic types: */ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_NULL; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_True; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_False; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = boolean ? cJSON_True : cJSON_False; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_Number; item->valuedouble = num; /* use saturation in case of overflow */ if (num >= INT_MAX) { item->valueint = INT_MAX; } else if (num <= (double)INT_MIN) { item->valueint = INT_MIN; } else { item->valueint = (int)num; } } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_String; item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); if(!item->valuestring) { cJSON_Delete(item); return NULL; } } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) { cJSON *item = cJSON_New_Item(&global_hooks); if (item != NULL) { item->type = cJSON_String | cJSON_IsReference; item->valuestring = (char*)cast_away_const(string); } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) { cJSON *item = cJSON_New_Item(&global_hooks); if (item != NULL) { item->type = cJSON_Object | cJSON_IsReference; item->child = (cJSON*)cast_away_const(child); } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { cJSON *item = cJSON_New_Item(&global_hooks); if (item != NULL) { item->type = cJSON_Array | cJSON_IsReference; item->child = (cJSON*)cast_away_const(child); } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_Raw; item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); if(!item->valuestring) { cJSON_Delete(item); return NULL; } } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type=cJSON_Array; } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) { cJSON *item = cJSON_New_Item(&global_hooks); if (item) { item->type = cJSON_Object; } return item; } /* Create Arrays: */ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; if ((count < 0) || (numbers == NULL)) { return NULL; } a = cJSON_CreateArray(); for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); if (!n) { cJSON_Delete(a); return NULL; } if(!i) { a->child = n; } else { suffix_object(p, n); } p = n; } return a; } CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; if ((count < 0) || (numbers == NULL)) { return NULL; } a = cJSON_CreateArray(); for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber((double)numbers[i]); if(!n) { cJSON_Delete(a); return NULL; } if(!i) { a->child = n; } else { suffix_object(p, n); } p = n; } return a; } CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; if ((count < 0) || (numbers == NULL)) { return NULL; } a = cJSON_CreateArray(); for(i = 0;a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); if(!n) { cJSON_Delete(a); return NULL; } if(!i) { a->child = n; } else { suffix_object(p, n); } p = n; } return a; } CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; if ((count < 0) || (strings == NULL)) { return NULL; } a = cJSON_CreateArray(); for (i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateString(strings[i]); if(!n) { cJSON_Delete(a); return NULL; } if(!i) { a->child = n; } else { suffix_object(p,n); } p = n; } return a; } /* Duplication */ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { cJSON *newitem = NULL; cJSON *child = NULL; cJSON *next = NULL; cJSON *newchild = NULL; /* Bail on bad ptr */ if (!item) { goto fail; } /* Create new item */ newitem = cJSON_New_Item(&global_hooks); if (!newitem) { goto fail; } /* Copy over all vars */ newitem->type = item->type & (~cJSON_IsReference); newitem->valueint = item->valueint; newitem->valuedouble = item->valuedouble; if (item->valuestring) { newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); if (!newitem->valuestring) { goto fail; } } if (item->string) { newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); if (!newitem->string) { goto fail; } } /* If non-recursive, then we're done! */ if (!recurse) { return newitem; } /* Walk the ->next chain for the child. */ child = item->child; while (child != NULL) { newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ if (!newchild) { goto fail; } if (next != NULL) { /* If newitem->child already set, then crosswire ->prev and ->next and move on */ next->next = newchild; newchild->prev = next; next = newchild; } else { /* Set newitem->child and move to it */ newitem->child = newchild; next = newchild; } child = child->next; } return newitem; fail: if (newitem != NULL) { cJSON_Delete(newitem); } return NULL; } static void skip_oneline_comment(char **input) { *input += static_strlen("//"); for (; (*input)[0] != '\0'; ++(*input)) { if ((*input)[0] == '\n') { *input += static_strlen("\n"); return; } } } static void skip_multiline_comment(char **input) { *input += static_strlen("/*"); for (; (*input)[0] != '\0'; ++(*input)) { if (((*input)[0] == '*') && ((*input)[1] == '/')) { *input += static_strlen("*/"); return; } } } static void minify_string(char **input, char **output) { (*output)[0] = (*input)[0]; *input += static_strlen("\""); *output += static_strlen("\""); for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { (*output)[0] = (*input)[0]; if ((*input)[0] == '\"') { (*output)[0] = '\"'; *input += static_strlen("\""); *output += static_strlen("\""); return; } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { (*output)[1] = (*input)[1]; *input += static_strlen("\""); *output += static_strlen("\""); } } } CJSON_PUBLIC(void) cJSON_Minify(char *json) { char *into = json; if (json == NULL) { return; } while (json[0] != '\0') { switch (json[0]) { case ' ': case '\t': case '\r': case '\n': json++; break; case '/': if (json[1] == '/') { skip_oneline_comment(&json); } else if (json[1] == '*') { skip_multiline_comment(&json); } else { json++; } break; case '\"': minify_string(&json, (char**)&into); break; default: into[0] = json[0]; json++; into++; } } /* and null-terminate. */ *into = '\0'; } CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Invalid; } CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_False; } CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xff) == cJSON_True; } CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & (cJSON_True | cJSON_False)) != 0; } CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_NULL; } CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Number; } CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_String; } CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Array; } CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Object; } CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) { if (item == NULL) { return false; } return (item->type & 0xFF) == cJSON_Raw; } CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) { if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) { return false; } /* check if type is valid */ switch (a->type & 0xFF) { case cJSON_False: case cJSON_True: case cJSON_NULL: case cJSON_Number: case cJSON_String: case cJSON_Raw: case cJSON_Array: case cJSON_Object: break; default: return false; } /* identical objects are equal */ if (a == b) { return true; } switch (a->type & 0xFF) { /* in these cases and equal type is enough */ case cJSON_False: case cJSON_True: case cJSON_NULL: return true; case cJSON_Number: if (compare_double(a->valuedouble, b->valuedouble)) { return true; } return false; case cJSON_String: case cJSON_Raw: if ((a->valuestring == NULL) || (b->valuestring == NULL)) { return false; } if (strcmp(a->valuestring, b->valuestring) == 0) { return true; } return false; case cJSON_Array: { cJSON *a_element = a->child; cJSON *b_element = b->child; for (; (a_element != NULL) && (b_element != NULL);) { if (!cJSON_Compare(a_element, b_element, case_sensitive)) { return false; } a_element = a_element->next; b_element = b_element->next; } /* one of the arrays is longer than the other */ if (a_element != b_element) { return false; } return true; } case cJSON_Object: { cJSON *a_element = NULL; cJSON *b_element = NULL; cJSON_ArrayForEach(a_element, a) { /* TODO This has O(n^2) runtime, which is horrible! */ b_element = get_object_item(b, a_element->string, case_sensitive); if (b_element == NULL) { return false; } if (!cJSON_Compare(a_element, b_element, case_sensitive)) { return false; } } /* doing this twice, once on a and b to prevent true comparison if a subset of b * TODO: Do this the proper way, this is just a fix for now */ cJSON_ArrayForEach(b_element, b) { a_element = get_object_item(a, b_element->string, case_sensitive); if (a_element == NULL) { return false; } if (!cJSON_Compare(b_element, a_element, case_sensitive)) { return false; } } return true; } default: return false; } } CJSON_PUBLIC(void *) cJSON_malloc(size_t size) { return global_hooks.allocate(size); } CJSON_PUBLIC(void) cJSON_free(void *object) { global_hooks.deallocate(object); } apparmor-5.0.2/binutils/cJSON.h000066400000000000000000000366721522511161100162660ustar00rootroot00000000000000/* Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef cJSON__h #define cJSON__h #ifdef __cplusplus extern "C" { #endif #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) #define __WINDOWS__ #endif #ifdef __WINDOWS__ /* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol For *nix builds that support visibility attribute, you can define similar behavior by setting default visibility to hidden by adding -fvisibility=hidden (for gcc) or -xldscope=hidden (for sun cc) to CFLAGS then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does */ #define CJSON_CDECL __cdecl #define CJSON_STDCALL __stdcall /* export symbols by default, this is necessary for copy pasting the C and header file */ #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) #define CJSON_EXPORT_SYMBOLS #endif #if defined(CJSON_HIDE_SYMBOLS) #define CJSON_PUBLIC(type) type CJSON_STDCALL #elif defined(CJSON_EXPORT_SYMBOLS) #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL #elif defined(CJSON_IMPORT_SYMBOLS) #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL #endif #else /* !__WINDOWS__ */ #define CJSON_CDECL #define CJSON_STDCALL #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type #else #define CJSON_PUBLIC(type) type #endif #endif /* project version */ #define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MINOR 7 #define CJSON_VERSION_PATCH 13 #include /* cJSON Types: */ #define cJSON_Invalid (0) #define cJSON_False (1 << 0) #define cJSON_True (1 << 1) #define cJSON_NULL (1 << 2) #define cJSON_Number (1 << 3) #define cJSON_String (1 << 4) #define cJSON_Array (1 << 5) #define cJSON_Object (1 << 6) #define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 /* The cJSON structure: */ typedef struct cJSON { /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ struct cJSON *next; struct cJSON *prev; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ struct cJSON *child; /* The type of the item, as above. */ int type; /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ char *string; } cJSON; typedef struct cJSON_Hooks { /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ void *(CJSON_CDECL *malloc_fn)(size_t sz); void (CJSON_CDECL *free_fn)(void *ptr); } cJSON_Hooks; typedef int cJSON_bool; /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. * This is to prevent stack overflows. */ #ifndef CJSON_NESTING_LIMIT #define CJSON_NESTING_LIMIT 1000 #endif /* returns the version of cJSON as a string */ CJSON_PUBLIC(const char*) cJSON_Version(void); /* Supply malloc, realloc and free functions to cJSON */ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); /* Render a cJSON entity to text for transfer/storage. */ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. */ CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); /* Check item type and return its value */ CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item); /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); /* These calls create a cJSON item of the appropriate type. */ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); /* raw json */ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); /* Create a string where valuestring references a string so * it will not be freed by cJSON_Delete */ CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); /* Create an object/array that only references it's elements so * they will not be freed by cJSON_Delete */ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); /* These utilities create an Array of count items. * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); /* Append item to the specified array/object. */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before * writing to `item->string` */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); /* Remove/Detach items from Arrays/Objects. */ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); /* Update array items. */ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will * need to be released. With recurse!=0, it will duplicate any children connected to the item. * The item->next and ->prev pointers are always zero on return from Duplicate. */ /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. * The input pointer json cannot point to a read-only address area, such as a string constant, * but should point to a readable and writable adress area. */ CJSON_PUBLIC(void) cJSON_Minify(char *json); /* Helper functions for creating and adding items to an object at the same time. * They return the added item or NULL on failure. */ CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) /* helper for the cJSON_SetNumberValue macro */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ CJSON_PUBLIC(void *) cJSON_malloc(size_t size); CJSON_PUBLIC(void) cJSON_free(void *object); #ifdef __cplusplus } #endif #endif apparmor-5.0.2/binutils/po/000077500000000000000000000000001522511161100156015ustar00rootroot00000000000000apparmor-5.0.2/binutils/po/Makefile000066400000000000000000000013031522511161100172360ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (C) 2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # ---------------------------------------------------------------------- all: # As translations get added, they will automatically be included, unless # the lang is explicitly added to DISABLED_LANGS; e.g. DISABLED_LANGS=en es DISABLED_LANGS= COMMONDIR=../../common include $(COMMONDIR)/Make-po.rules XGETTEXT_ARGS+=--language=C --keyword=_ $(shell if [ -f ${NAME}.pot ] ; then echo -n -j ; fi) apparmor-5.0.2/binutils/po/aa-enabled.pot000066400000000000000000000026221522511161100203000ustar00rootroot00000000000000# Copyright (C) 2015 Canonical Ltd # This file is distributed under the same license as the AppArmor package. # John Johansen , 2015. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/aa_enabled.pot000066400000000000000000000031331522511161100203600ustar00rootroot00000000000000# Translations for aa_enabled # Copyright (C) 2024 Canonical Ltd # This file is distributed under the same license as the AppArmor package. # John Johansen , 2020. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n" "POT-Creation-Date: 2024-08-31 15:59-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../aa_enabled.c:21 #, c-format msgid "" "%s: [options]\n" " options:\n" " -x | --exclusive Shared interfaces must be available\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:37 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:41 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:50 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:54 #, c-format msgid "Partially - public shared interfaces are not available.\n" msgstr "" #: ../aa_enabled.c:58 #, c-format msgid "Error - %s\n" msgstr "" #: ../aa_enabled.c:73 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:87 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:98 #, c-format msgid "Yes\n" msgstr "" apparmor-5.0.2/binutils/po/aa_exec.pot000066400000000000000000000026051522511161100177150ustar00rootroot00000000000000# Translations for aa_exec # Copyright (C) 2024 Canonical Ltd # This file is distributed under the same license as the AppArmor package. # John Johansen , 2020. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n" "POT-Creation-Date: 2024-08-31 15:59-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../aa_exec.c:50 #, c-format msgid "" "USAGE: %s [OPTIONS] \n" "\n" "Confine with the specified PROFILE.\n" "\n" "OPTIONS:\n" " -p PROFILE, --profile=PROFILE\t\tPROFILE to confine with\n" " -n NAMESPACE, --namespace=NAMESPACE\tNAMESPACE to confine in\n" " -d, --debug\t\t\t\tshow messages with debugging information\n" " -i, --immediate\t\t\tchange profile immediately instead of at exec\n" " -v, --verbose\t\t\t\tshow messages with stats\n" " -h, --help\t\t\t\tdisplay this help\n" "\n" msgstr "" #: ../aa_exec.c:65 #, c-format msgid "[%ld] aa-exec: ERROR: " msgstr "" #: ../aa_exec.c:76 #, c-format msgid "[%ld] aa-exec: DEBUG: " msgstr "" #: ../aa_exec.c:89 #, c-format msgid "[%ld] " msgstr "" #: ../aa_exec.c:107 #, c-format msgid "[%ld] exec" msgstr "" apparmor-5.0.2/binutils/po/aa_features_abi.pot000066400000000000000000000026131522511161100214210ustar00rootroot00000000000000# Translations for aa_features_abi # Copyright (C) 2024 Canonical Ltd # This file is distributed under the same license as the AppArmor package. # John Johansen , 2011. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n" "POT-Creation-Date: 2024-08-31 15:59-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../aa_features_abi.c:53 #, c-format msgid "" "USAGE: %s [OPTIONS] [OUTPUT OPTIONS]\n" "\n" "Output AppArmor feature abi from SOURCE to OUTPUT\n" "OPTIONS:\n" " -d, --debug show messages with debugging information\n" " -v, --verbose show messages with stats\n" " -h, --help display this help\n" "SOURCE:\n" " -f F, --file=F load features abi from file F\n" " -x, --extract extract features abi from the kernel\n" "OUTPUT OPTIONS:\n" " --stdout default, write features to stdout\n" " -w F, --write=F write features abi to the file F instead of stdout\n" "\n" msgstr "" #: ../aa_features_abi.c:73 #, c-format msgid "%s: ERROR: " msgstr "" #: ../aa_features_abi.c:85 #, c-format msgid "%s: DEBUG: " msgstr "" #: ../aa_features_abi.c:98 msgid "\n" msgstr "" apparmor-5.0.2/binutils/po/aa_load.pot000066400000000000000000000014541522511161100177110ustar00rootroot00000000000000# Translations for aa_load # Copyright (C) 2024 Canonical Ltd # This file is distributed under the same license as the AppArmor package. # John Johansen , 2020. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n" "POT-Creation-Date: 2024-08-31 15:59-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../aa_load.c:40 msgid "aa-load: WARN: " msgstr "" #: ../aa_load.c:41 msgid "aa-load: ERROR: " msgstr "" #: ../aa_load.c:51 msgid "\n" msgstr "" #: ../aa_load.c:52 msgid "aa-load: DEBUG: " msgstr "" apparmor-5.0.2/binutils/po/aa_status.pot000066400000000000000000000120571522511161100203160ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Canonical Ltd # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n" "POT-Creation-Date: 2025-04-26 11:12-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../aa_status.c:161 msgid "apparmor not present.\n" msgstr "" #: ../aa_status.c:164 msgid "apparmor module is loaded.\n" msgstr "" #: ../aa_status.c:168 msgid "apparmor filesystem is not mounted.\n" msgstr "" #: ../aa_status.c:181 msgid "You do not have enough privilege to read the profile set.\n" msgstr "" #: ../aa_status.c:183 #, c-format msgid "Could not open %s: %s" msgstr "" #: ../aa_status.c:356 ../aa_status.c:379 msgid "ERROR: Failed to allocate memory\n" msgstr "" #: ../aa_status.c:587 ../aa_status.c:653 ../aa_status.c:603 ../aa_status.c:669 #, c-format msgid "Error: failed to compile sub filter '%s'\n" msgstr "" #: ../aa_status.c:715 ../aa_status.c:731 #, c-format msgid "" "Usage: %s [OPTIONS]\n" "Legacy options and their equivalent command\n" " --profiled --count --profiles\n" " --enforced --count --profiles --mode=enforced\n" " --complaining --count --profiles --mode=complain\n" " --kill --count --profiles --mode=kill\n" " --prompt --count --profiles --mode=prompt\n" " --special-unconfined --count --profiles --mode=unconfined\n" " --process-mixed --count --ps --mode=mixed\n" msgstr "" #: ../aa_status.c:734 ../aa_status.c:750 #, c-format msgid "" "Usage of filters\n" "Filters are used to reduce the output of information to only\n" "those entries that will match the filter. Filters use posix\n" "regular expression syntax. The possible values for exes that\n" "support filters are below\n" "\n" " --filter.mode: regular expression to match the profile " "mode modes: enforce, complain, kill, unconfined, mixed\n" " --filter.profiles: regular expression to match displayed profile names\n" " --filter.pid: regular expression to match displayed processes pids\n" " --filter.exe: regular expression to match executable\n" msgstr "" #: ../aa_status.c:762 ../aa_status.c:778 #, c-format msgid "" "Usage: %s [OPTIONS]\n" "Displays various information about the currently loaded AppArmor policy.\n" "Default if no options given\n" " --show=all\n" "\n" "OPTIONS (one only):\n" " --enabled returns error code if AppArmor not enabled\n" " --show=X What information to show. {profiles,processes,all}\n" " --count print the number of entries. Implies --quiet\n" " --filter.mode=filter see filters\n" " --filter.profiles=filter see filters\n" " --filter.pid=filter see filters\n" " --filter.exe=filter see filters\n" " --json displays multiple data points in machine-readable JSON " "format\n" " --pretty-json same data as --json, formatted for human consumption as " "well\n" " --verbose (default) displays data points about loaded policy set\n" " --quiet don't output error messages\n" " -h[(legacy|filters)] this message, or info on the specified option\n" " --help[=(legacy|filters)] this message, or info on the specified option\n" msgstr "" #: ../aa_status.c:856 ../aa_status.c:872 #, c-format msgid "Error: Invalid --help option '%s'.\n" msgstr "" #: ../aa_status.c:924 ../aa_status.c:940 #, c-format msgid "Error: Invalid --show option '%s'.\n" msgstr "" #: ../aa_status.c:946 ../aa_status.c:962 msgid "Error: Invalid command.\n" msgstr "" #: ../aa_status.c:971 ../aa_status.c:987 msgid "Error: Unknown options.\n" msgstr "" #: ../aa_status.c:983 ../aa_status.c:999 #, c-format msgid "Error: failed to compile mode filter '%s'\n" msgstr "" #: ../aa_status.c:988 ../aa_status.c:1004 #, c-format msgid "Error: failed to compile profiles filter '%s'\n" msgstr "" #: ../aa_status.c:994 ../aa_status.c:1010 #, c-format msgid "Error: failed to compile ps filter '%s'\n" msgstr "" #: ../aa_status.c:1000 ../aa_status.c:1016 #, c-format msgid "Error: failed to compile exe filter '%s'\n" msgstr "" #: ../aa_status.c:1015 ../aa_status.c:1031 #, c-format msgid "Failed to open memstream: %m\n" msgstr "" #: ../aa_status.c:1026 #, c-format msgid "Failed to get profiles: %d....\n" msgstr "" #: ../aa_status.c:1050 #, c-format msgid "Failed to get processes: %d....\n" msgstr "" #: ../aa_status.c:1076 ../aa_status.c:1099 msgid "Failed to parse json output" msgstr "" #: ../aa_status.c:1083 ../aa_status.c:1106 msgid "Failed to print pretty json" msgstr "" #: ../aa_status.c:1044 #, c-format msgid "Failed to retrieve profiles from kernel: %d....\n" msgstr "" #: ../aa_status.c:1073 #, c-format msgid "Failed to get confinement information from processes: %d....\n" msgstr "" #: ../aa_status.c:1042 msgid "No policy loaded into the kernel\n" msgstr "" apparmor-5.0.2/binutils/po/af.po000066400000000000000000000036261522511161100165360ustar00rootroot00000000000000# Afrikaans translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-03-04 17:55+0000\n" "Last-Translator: bernard stafford \n" "Language-Team: Afrikaans \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-03-05 05:40+0000\n" "X-Generator: Launchpad (build e0878392dc799b267dea80578fa65500a5d74155)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [opsies]\n" " opsies:\n" " -q | --quiet Moenie druk uit enige boodskappe\n" " -h | --help Afdruk hulp\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "onbekende of onversoenbare opsies\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "onbekende opsie '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Ja\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Geen - nie beskikbaar op hierdie stelsel.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Nee - gestremde by stewel.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Miskien - beleid koppelvlak nie beskikbaar.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Miskien - onvoldoende toestemmings om beskikbaarheid te bepaal.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Fout - '%s'\n" apparmor-5.0.2/binutils/po/be.po000066400000000000000000000030471522511161100165330ustar00rootroot00000000000000# Belarusian translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-05-05 21:55+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Belarusian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-05-06 05:41+0000\n" "X-Generator: Launchpad (build fbdff7602bd10fb883bf7e2ddcc7fd5a16f60398)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/ca.po000066400000000000000000000037151522511161100165320ustar00rootroot00000000000000# Catalan translation for apparmor # Copyright (c) 2024 Rosetta Contributors and Canonical Ltd 2024 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2024. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2024-09-14 10:17+0000\n" "Last-Translator: Walter Garcia-Fontes \n" "Language-Team: Catalan \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2024-09-15 07:16+0000\n" "X-Generator: Launchpad (build 1b1ed1ad2dbfc71ee62b5c5491c975135a771bf0)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [opcions]\n" " opcions:\n" " -q | --quiet No imprimeixis cap missatge\n" " -h | --help Imprimeix l'ajuda\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "opcions desconegudes o incompatibles\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "opció desconeguda «%s»\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Sí\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "No - no esta disponible a aquest sistema\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "No - desactivat a l'inici.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Potser - la interfície de política no està disponible.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Potser - permisos insuficient per determinar la disponibilitat.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Error - '%s'\n" apparmor-5.0.2/binutils/po/cs.po000066400000000000000000000034521522511161100165520ustar00rootroot00000000000000# Czech translation for apparmor # Copyright (c) 2022 Rosetta Contributors and Canonical Ltd 2022 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2022. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2022-01-09 11:59+0000\n" "Last-Translator: Marek Hladík \n" "Language-Team: Czech \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2022-01-10 06:32+0000\n" "X-Generator: Launchpad (build 1682fd44eec4f62371f0bed122a83482daf08e23)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "neznámé nebo nekompatibilní volby\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "neznámá volba '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Ano\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Ne - není v tomto systému k dispozici.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Ne - zakázáno při startu.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Možná - rozhraní zásad není k dispozici.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Možná - nedostatečná oprávnění k určení dostupnosti.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Chyba - '%s'\n" apparmor-5.0.2/binutils/po/de.po000066400000000000000000000037451522511161100165420ustar00rootroot00000000000000# German translation for apparmor # Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2016. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: AppArmor list \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2018-02-09 23:55+0000\n" "Last-Translator: Tobias Bannert \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-04-18 05:33+0000\n" "X-Generator: Launchpad (build 18928)\n" "Language: de\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [Optionen]\n" " Optionen:\n" " -q | --quiet Keine Nachrichten anzeigen\n" " -h | --help Hilfetext anzeigen\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "unbekannte oder nicht kompatible Optionen\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "unbekannte Option »%s«\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Ja\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Nein – auf diesem System nicht verfügbar.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Nein – beim Start deaktiviert.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Vielleicht – Richtlinienschnittstelle nicht verfügbar.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" "Vielleicht – ungenügende Berechtigungen, um die Verfügbarkeit zu prüfen\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Fehler – »%s«\n" apparmor-5.0.2/binutils/po/en_AU.po000066400000000000000000000030741522511161100171340ustar00rootroot00000000000000# English (Australia) translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-11-28 04:45+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: English (Australia) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-11-29 06:26+0000\n" "X-Generator: Launchpad (build 12d09381f8e8eee3115395875b132e165fa96574)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/en_CA.po000066400000000000000000000030661522511161100171130ustar00rootroot00000000000000# English (Canada) translation for apparmor # Copyright (c) 2021 Rosetta Contributors and Canonical Ltd 2021 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2021. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2021-10-01 04:55+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: English (Canada) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2021-10-02 06:17+0000\n" "X-Generator: Launchpad (build 1ce78163f6a09ed42b4201fe7d3f0e3a2eba7d02)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/en_GB.po000066400000000000000000000036551522511161100171240ustar00rootroot00000000000000# English (United Kingdom) translation for apparmor # Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2016. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: AppArmor list \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2016-02-18 06:22+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-04-18 05:33+0000\n" "X-Generator: Launchpad (build 18928)\n" "Language: en_GB\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "unknown or incompatible options\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "unknown option '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Yes\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "No - not available on this system.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "No - disabled at boot.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Maybe - policy interface not available.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Maybe - insufficient permissions to determine availability.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Error - '%s'\n" apparmor-5.0.2/binutils/po/es.po000066400000000000000000000036351522511161100165570ustar00rootroot00000000000000# Spanish translation for apparmor # Copyright (c) 2019 Rosetta Contributors and Canonical Ltd 2019 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2019. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2019-06-09 14:01+0000\n" "Last-Translator: Adolfo Jayme \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-06-10 04:32+0000\n" "X-Generator: Launchpad (build 18978)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [opciones]\n" " opciones:\n" " -q | --quiet No emitir ningún mensaje\n" " -h | --help Mostrar la ayuda\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "opciones desconocidas o incompatibles\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "se desconoce la opción «%s»\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Sí\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "No; no disponible en este sistema.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "No; desactivado durante el arranque.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Quizá; interfaz de directiva no disponible.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Quizá; permisos insuficientes para determinar disponibilidad.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Error: «%s»\n" apparmor-5.0.2/binutils/po/et.po000066400000000000000000000036431522511161100165570ustar00rootroot00000000000000# Estonian translation for apparmor # Copyright (c) 2023 Rosetta Contributors and Canonical Ltd 2023 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2023. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2023-07-04 08:52+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Estonian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2023-07-05 04:31+0000\n" "X-Generator: Launchpad (build beda0e9dd2b131780db60fe479d4b43618b27243)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [valikud]\n" " valikud:\n" " -q | --quiet Ärge printige sõnumeid välja\n" " -h | --help Prindi abiinfo\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "tundmatud või ühildumatud valikud\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "tundmatu valik '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Jah\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Ei – pole selles süsteemis saadaval.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Ei – käivitamisel keelatud.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Võib-olla – poliisiliides pole saadaval.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Võib-olla - kättesaadavuse määramiseks pole piisavalt õigusi.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Viga – '%s'\n" apparmor-5.0.2/binutils/po/fa.po000066400000000000000000000036161522511161100165350ustar00rootroot00000000000000# Persian translation for apparmor # Copyright (c) 2019 Rosetta Contributors and Canonical Ltd 2019 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2019. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2019-12-27 08:16+0000\n" "Last-Translator: VahidNameni \n" "Language-Team: Persian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-12-28 05:38+0000\n" "X-Generator: Launchpad (build bceb5ef013b87ef7aafe0755545ceb689ca7ac60)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "تنظیم نامعلوم یا ناسازگار\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "تنظیم '%s' ناشناخته است\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "بله\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "خیر- در این سیستم موجود نیست.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "خیر - غیرفعال در زمان boot.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "شاید - رابط سیاست گذاری در دسترس نیست.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "شاید - دسترسی ناکافی جهت شناسایی در دسترس پذیری.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "خطا - '%s'\n" apparmor-5.0.2/binutils/po/fi.po000066400000000000000000000033531522511161100165430ustar00rootroot00000000000000# Finnish translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-01-29 07:44+0000\n" "Last-Translator: Jiri Grönroos \n" "Language-Team: Finnish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-01-30 05:40+0000\n" "X-Generator: Launchpad (build b8d1327fd820d6bf500589d6da587d5037c7d88e)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "tuntemattomat tai yhteensopimattomat valinnat\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "tuntematon valinta '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Kyllä\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Ei - ei käytettävissä tässä järjestelmässä.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Ei - poistettu käytöstä käynnistyksen yhteydessä.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Virhe - '%s'\n" apparmor-5.0.2/binutils/po/gl.po000066400000000000000000000030431522511161100165430ustar00rootroot00000000000000# Galician translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-04-21 14:59+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Galician \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-04-22 06:10+0000\n" "X-Generator: Launchpad (build aad6b57d58e2f621954298e262c1cc904860f5d2)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/he.po000066400000000000000000000040031522511161100165320ustar00rootroot00000000000000# Hebrew translation for apparmor # Copyright (c) 2023 Rosetta Contributors and Canonical Ltd 2023 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2023. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2023-10-05 05:12+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Hebrew \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2023-10-06 08:32+0000\n" "X-Generator: Launchpad (build bd6cfd0cfc024dbe1dcd7d5d91165fb4f6a6c596)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [אפשרויות]\n" " אפשרויות:\n" " ‎-q | --quiet לא להציג הודעות\n" " ‎-h | --help הצגת עזרה\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "אפשרויות לא ידועות או לא נתמכות\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "האפשרות ‚%s’ לא מוכרת\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "כן\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "לא - לא זמין במערכת הזאת.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "לא - מושבת בעלייה.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "אולי - מנשק המדיניות לא זמין.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "אולי - אין מספיק הרשאות לקבוע זמינות.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "שגיאה - ‚%s’\n" apparmor-5.0.2/binutils/po/hi.po000066400000000000000000000030351522511161100165420ustar00rootroot00000000000000# Hindi translation for apparmor # Copyright (c) 2023 Rosetta Contributors and Canonical Ltd 2023 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2023. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2023-01-09 07:39+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Hindi \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2023-01-10 06:22+0000\n" "X-Generator: Launchpad (build 87bfee1fd14ea3245297d63eeec1e4c8a1d203a8)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/hr.po000066400000000000000000000036321522511161100165560ustar00rootroot00000000000000# Croatian translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2021-10-03 10:17+0000\n" "Last-Translator: gogo \n" "Language-Team: Croatian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2021-10-04 06:23+0000\n" "X-Generator: Launchpad (build 1ce78163f6a09ed42b4201fe7d3f0e3a2eba7d02)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [mogućnosti]\n" " options:\n" " -q | --quiet Ne prikazuj poruke\n" " -h | --help Prikaži pomoć\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "nepoznata ili nepotpuna mogućnost\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "nepoznata mogućnost '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Da\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Ne - nedostupno na ovom sustavu.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Ne - onemogućeno pri pokretanju.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Možda - pravilo sučelja nedostupno.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Možda - nedovoljna dozvola za određivanje dostupnosti.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Greška - '%s'\n" apparmor-5.0.2/binutils/po/id.po000066400000000000000000000037221522511161100165410ustar00rootroot00000000000000# Indonesian translation for apparmor # Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2016. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: AppArmor list \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2016-01-20 08:59+0000\n" "Last-Translator: Ari Setyo Wibowo \n" "Language-Team: Indonesian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-04-18 05:33+0000\n" "X-Generator: Launchpad (build 18928)\n" "Language: id\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [options]\n" " pilihan:\n" " -q | --quiet Jangan tampilkan pesan apapun\n" " -h | --help Tampilkan bantuan\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "pilihan yang tidak dikenali atau tidak kompatibel\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "pilihan tidak dikenali '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Ya\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Tidak - tidak tersedia di sistem ini.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Tidak - nonaktifkan saat boot.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Mungkin - kebijakan antarmuka tidak tersedia.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Mungkin - izin tidak memadai untuk menentukan ketersediaan.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Kesalahan - '%s'\n" apparmor-5.0.2/binutils/po/it.po000066400000000000000000000036521522511161100165630ustar00rootroot00000000000000# Italian translation for apparmor # Copyright (c) 2022 Rosetta Contributors and Canonical Ltd 2022 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2022. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2022-06-30 17:54+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2022-07-01 04:30+0000\n" "X-Generator: Launchpad (build f48158886a49da429840bcd298f0c7ed60f9ad7b)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [opzioni]\n" " opzioni:\n" " -q | --quiet Non stampa nessun messaggio\n" " -h | --help Stampa la guida\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "opzioni sconosciute o incompatibili\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "opzione sconosciuta '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Si\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "No - non disponibile su questo sistema.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "No - disabilitato all'avvio.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Forse - interfaccia dei criteri non disponibile.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" "Forse - autorizzazioni insufficienti per determinare la disponibilità.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Errore - '%s'\n" apparmor-5.0.2/binutils/po/ka.po000066400000000000000000000050311522511161100165330ustar00rootroot00000000000000# Georgian translation for apparmor # Copyright (c) 2023 Rosetta Contributors and Canonical Ltd 2023 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2023. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2023-06-26 15:06+0000\n" "Last-Translator: NorwayFun \n" "Language-Team: Georgian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2023-06-27 04:31+0000\n" "X-Generator: Launchpad (build aedf8597c50c1abc5fb7f9e871e686dfcb381fde)\n" "Language: aa\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [პარამეტრები]\n" " პარამეტრები:\n" " -q | --quiet შეტყობინებები გამოტანილი არ იქნება\n" " -h | --help დახმარების გამოტანა\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "უცნობი ან შეუთავსებელი პარამეტრები\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "უცნობი პარამეტრი \"%s\"-სთვის\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "დიახ\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "არა - მიუწვდომელია ამ სისტემაზე\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "არა - გამორთულია ჩატვირთვისას\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "შეიძლება - პოლიტიკის ინტერფეისი ხელმისაწვდომი არაა.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "შეიძლება - არასაკმარისი წვდომები ხელმისაწვდომობის დასადგენად.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "შეცდომა - \"%s\"\n" apparmor-5.0.2/binutils/po/kab.po000066400000000000000000000030401522511161100166730ustar00rootroot00000000000000# Kabyle translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-04-29 14:31+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Kabyle \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-04-30 05:44+0000\n" "X-Generator: Launchpad (build fbdff7602bd10fb883bf7e2ddcc7fd5a16f60398)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/my.po000066400000000000000000000050411522511161100165660ustar00rootroot00000000000000# Burmese translation for apparmor # Copyright (c) 2022 Rosetta Contributors and Canonical Ltd 2022 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2022. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2022-06-26 11:50+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Burmese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2022-06-27 04:30+0000\n" "X-Generator: Launchpad (build 51a2e4fa2e9b8e45f00904ad7f53546f45ac48a5)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s- [options]\n" " ရွေးချယ်စရာများ-\n" " -q | --quiet မည်သည့်စာတိုကိုမှ ပရင့်မထုတ်ပါနှင့်။\n" " -h | --help ပရင့်အကူအညီ\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "အမည်မသိ သို့မဟုတ် သဟဇာတမဖြစ်သော ရွေးချယ်စရာများ\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "အမည်မသိရွေးချယ်မှု '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "ဟုတ်\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "မဟုတ်ပါ - ဤစနစ်တွင် မရနိုင်ပါ။\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "မဟုတ်ပါ - boot တွင် ပိတ်ထားပါသည်။\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "ဖြစ်နိုင်ပါသည် - မူဝါဒ interface ကို မရနိုင်ပါ။\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" "ဖြစ်နိုင်ပါသည် - ရရှိနိုင်မှုကို ဆုံးဖြတ်ရန်အတွက် ခွင့်ပြုချက်များမှာ " "လုံလောက်မှုမရှိပါ။\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "အမှား- '%s'\n" apparmor-5.0.2/binutils/po/oc.po000066400000000000000000000030711522511161100165430ustar00rootroot00000000000000# Occitan (post 1500) translation for apparmor # Copyright (c) 2021 Rosetta Contributors and Canonical Ltd 2021 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2021. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2021-01-14 18:26+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Occitan (post 1500) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2021-01-15 07:59+0000\n" "X-Generator: Launchpad (build 511b4a3b6512aa3d421c5f7d74f3527e78bff26e)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/pl.po000066400000000000000000000036461522511161100165650ustar00rootroot00000000000000# Polish translation for apparmor # Copyright (c) 2021 Rosetta Contributors and Canonical Ltd 2021 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2021. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2021-07-22 20:10+0000\n" "Last-Translator: Marek Adamski \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2021-07-23 06:03+0000\n" "X-Generator: Launchpad (build 7edebbcd0516593cf020aaa3c59299732a7c73cc)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [opcje]\n" " opcje:\n" " -q | --quiet Nie wyświetlaj żadnych komunikatów\n" " -h | --help Wyświetl pomoc\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "nieznane lub niekompatybilne opcje\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "nieznana opcja '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Tak\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Nie - nie jest dostępne w tym systemie.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Nie - wyłączone podczas rozruchu.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Może - interfejs zasad nie jest dostępny.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Może - brak wystarczających uprawnień do określenia dostępności.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Błąd - '%s'\n" apparmor-5.0.2/binutils/po/pt.po000066400000000000000000000037071522511161100165730ustar00rootroot00000000000000# Portuguese translation for apparmor # Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2016. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: AppArmor list \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2016-03-03 08:34+0000\n" "Last-Translator: Ivo Xavier \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-04-18 05:33+0000\n" "X-Generator: Launchpad (build 18928)\n" "Language: pt\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [opções]\n" " opções:\n" " -q | --silencioso Não mostrar mensagens\n" " -h | --ajuda Mostar ajuda\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "opções desconhecidas ou incompatíveis\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "opção desconhecida '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Sim\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Não - não disponível neste sistema.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Não - desligado ao iniciar.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Talvez - política de interface não disponível.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Talvez - permissões insuficientes para determinar disponibilidade.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Erro - '%s'\n" apparmor-5.0.2/binutils/po/pt_BR.po000066400000000000000000000037521522511161100171560ustar00rootroot00000000000000# Brazilian Portuguese translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-04-27 20:32+0000\n" "Last-Translator: Rodrigo Farias \n" "Language-Team: Brazilian Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-04-28 05:52+0000\n" "X-Generator: Launchpad (build d1105341713c5be348effe2a5142c4a210ce4cde)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [options]\n" " opções:\n" " -q | --quiet Não imprimir nenhum mensagem\n" " -h | --help Assistente de impressão\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "opções incompatíveis ou desconhecidas\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "opção desconhecida '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Sim\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Não - não disponível neste sistema.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Não - desabilitado na inicialização.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Talvez - interface de política não disponível.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Talvez - permissões insuficientes para determinar disponibilidade.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Erro - '%s'\n" apparmor-5.0.2/binutils/po/ro.po000066400000000000000000000044021522511161100165610ustar00rootroot00000000000000# Romanian translation for apparmor, "apparmor-binutils" component. # Mesajele în limba română pentru pachetul „apparmor”, componenta „apparmor-binutils”. # Copyright © 2020 Rosetta Contributors and Canonical Ltd. # Copyright © 2024 Canonical Ltd. # This file is distributed under the same license as the apparmor package. # # Daniel Slavu , feb-2020. # Remus-Gabriel Chelu , sep-2024. # msgid "" msgstr "" "Project-Id-Version: apparmor-binutils\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2024-09-23 22:45+0000\n" "Last-Translator: Remus-Gabriel Chelu \n" "Language-Team: Romanian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2024-09-25 04:33+0000\n" "X-Generator: Launchpad (build 1b1ed1ad2dbfc71ee62b5c5491c975135a771bf0)\n" "Language: ro\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [opțiuni]\n" " opțiuni:\n" " -q | --quiet nu afișează niciun mesaj\n" " -h | --help imprimă ajutorul\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "opțiuni necunoscute sau incompatibile\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "opțiune necunoscută „%s”\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Da\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Nu - nu este disponibil pe acest sistem.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Nu - dezactivat la pornire.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" "Poate - interfața politică (de directive politice) nu este disponibilă.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" "Poate - permisiuni insuficiente pentru a determina disponibilitatea.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Eroare - „%s”\n" apparmor-5.0.2/binutils/po/ru.po000066400000000000000000000042561522511161100165760ustar00rootroot00000000000000# Russian translation for apparmor # Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2016. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: AppArmor list \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2016-03-29 14:46+0000\n" "Last-Translator: Eugene Roskin \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-04-18 05:33+0000\n" "X-Generator: Launchpad (build 18928)\n" "Language: ru\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [параметры]\n" " параметры:\n" " -q | --quiet не выводить никакие сообщения\n" " -h | --help вывести справку\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "неизвестные или несовместимые параметры\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "неизвестный параметр '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Да\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Нет - недоступно на этой системе.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Нет - выключено при загрузке.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Возможно - интерфейс политики недоступен.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Возможно - недостаточно разрешений для определения доступности.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Ошибка - '%s'\n" apparmor-5.0.2/binutils/po/sr.po000066400000000000000000000030411522511161100165630ustar00rootroot00000000000000# Serbian translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-11-23 18:06+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Serbian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-11-24 05:55+0000\n" "X-Generator: Launchpad (build c35ff22711d15549e2303ae18ae521fd91f6bf00)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "" apparmor-5.0.2/binutils/po/sv.po000066400000000000000000000036301522511161100165730ustar00rootroot00000000000000# Swedish translation for apparmor # Copyright (c) 2018 Rosetta Contributors and Canonical Ltd 2018 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2018. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2018-09-08 03:51+0000\n" "Last-Translator: Jonatan Nyberg \n" "Language-Team: Swedish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-04-18 05:33+0000\n" "X-Generator: Launchpad (build 18928)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [options]\n" " flaggor:\n" " -q | --quiet Skriv inte ut några meddelanden\n" " -h | --help Skriv ut hjälp\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "okända eller inkompatibla flaggor\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "okänd flagga '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Ja\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Nej - inte tillgänglig på detta system.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Nej - inaktiverad vid uppstart.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Kanske - policy gränssnitt inte tillgängliga.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" "Kanske - otillräckliga behörigheter för att bestämma tillgängligheten.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Fel - '%s'\n" apparmor-5.0.2/binutils/po/sw.po000066400000000000000000000036241522511161100165770ustar00rootroot00000000000000# Swahili translation for apparmor # Copyright (c) 2019 Rosetta Contributors and Canonical Ltd 2019 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2019. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2019-11-14 12:33+0000\n" "Last-Translator: Swahilinux Administration \n" "Language-Team: Swahili \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-11-15 04:30+0000\n" "X-Generator: Launchpad (build c597c3229eb023b1e626162d5947141bf7befb13)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [chaguzi]\n" " chaguzi:\n" " -q | --quiet Usichapishe jumbe yoyote\n" " -h | --help Chapisha msaada\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "chaguo lisilojulikana au lisilofaa\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "chaguo lisilojulikana '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Ndio\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "La - haipo kwenye mfumo huu.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "La - ilizimwa kwenye washi.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Labda - kiolesura cha faragha hakipo.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Labda - hamna ruhusa ya kutosha ili kuamua kama ipo.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Dosari - '%s'\n" apparmor-5.0.2/binutils/po/tr.po000066400000000000000000000037161522511161100165750ustar00rootroot00000000000000# Turkish translation for apparmor # Copyright (c) 2018 Rosetta Contributors and Canonical Ltd 2018 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2018. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2018-05-19 23:10+0000\n" "Last-Translator: Kudret EMRE \n" "Language-Team: Turkish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2019-04-18 05:33+0000\n" "X-Generator: Launchpad (build 18928)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [seçenekler]\n" " seçenekler:\n" " -q | --quiet Hiçbir mesajı gösterme\n" " -h | --help Yardımı görüntüler\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "bilinmeyen veya uyumsuz seçenekler\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "bilinmeyen seçenek '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Evet\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Hayır - Bu sistemde kullanılabilir değil.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Hayır - önyüklemede devredışı bırakıldı.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Belki - policy arayüzü kullanılabilir değil.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "" "Belki - kullanılabilir olup olmadığını denetlemek için yetersiz yetki.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Hata - '%s'\n" apparmor-5.0.2/binutils/po/uk.po000066400000000000000000000042251522511161100165630ustar00rootroot00000000000000# Ukrainian translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-05-19 21:48+0000\n" "Last-Translator: Nazarii Ritter \n" "Language-Team: Ukrainian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-05-20 05:42+0000\n" "X-Generator: Launchpad (build 0385b538081bc4718df6fb844a3afc89729c94ce)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [опції]\n" " опції:\n" " -q | --quiet Не виводити жодних повідомлень\n" " -h | --help Вивести довідку\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "невідомі або несумісні опції\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "невідомий параметр «%s»\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "Так\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "Ні – недоступно на цій системі.\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "Ні – вимкнено під час завантаження.\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "Можливо – інтерфейс політики недоступний.\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "Можливо – недостатньо дозволів для визначення наявності.\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "Помилка - '%s'\n" apparmor-5.0.2/binutils/po/zh_CN.po000066400000000000000000000036201522511161100171430ustar00rootroot00000000000000# Chinese (Simplified) translation for apparmor # Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020 # This file is distributed under the same license as the apparmor package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: apparmor\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-11-28 10:23-0800\n" "PO-Revision-Date: 2020-05-14 09:16+0000\n" "Last-Translator: 玉堂白鹤 \n" "Language-Team: Chinese (Simplified) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-05-15 05:51+0000\n" "X-Generator: Launchpad (build 0385b538081bc4718df6fb844a3afc89729c94ce)\n" #: ../aa_enabled.c:26 #, c-format msgid "" "%s: [options]\n" " options:\n" " -q | --quiet Don't print out any messages\n" " -h | --help Print help\n" msgstr "" "%s: [选项]\n" " 选项:\n" " -q | --quiet 不要打印任何消息\n" " -h | --help 打印帮助\n" #: ../aa_enabled.c:45 #, c-format msgid "unknown or incompatible options\n" msgstr "未知或不兼容的选项\n" #: ../aa_enabled.c:55 #, c-format msgid "unknown option '%s'\n" msgstr "未知选项 '%s'\n" #: ../aa_enabled.c:64 #, c-format msgid "Yes\n" msgstr "是\n" #: ../aa_enabled.c:71 #, c-format msgid "No - not available on this system.\n" msgstr "否 - 在此系统上不可用。\n" #: ../aa_enabled.c:74 #, c-format msgid "No - disabled at boot.\n" msgstr "否 - 引导时被禁用。\n" #: ../aa_enabled.c:77 #, c-format msgid "Maybe - policy interface not available.\n" msgstr "也许 - 策略界面不可用\n" #: ../aa_enabled.c:81 #, c-format msgid "Maybe - insufficient permissions to determine availability.\n" msgstr "也许 - 没有足够的权限确定可用性。\n" #: ../aa_enabled.c:84 #, c-format msgid "Error - '%s'\n" msgstr "错误 - '%s'\n" apparmor-5.0.2/changehat/000077500000000000000000000000001522511161100152545ustar00rootroot00000000000000apparmor-5.0.2/changehat/mod_apparmor/000077500000000000000000000000001522511161100177345ustar00rootroot00000000000000apparmor-5.0.2/changehat/mod_apparmor/COPYING.LGPL000066400000000000000000000635001522511161100215300ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! apparmor-5.0.2/changehat/mod_apparmor/Makefile000066400000000000000000000073121522511161100213770ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 2004, 2005 NOVELL (All rights reserved) # Copyright (c) 2016 Canonical, Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- NAME:=apache2-mod_apparmor all: COMMONDIR=../../common/ include $(COMMONDIR)/Make.rules TARGET:=mod_apparmor.so MANPAGES=mod_apparmor.8 APXS:=$(shell if [ -x "/usr/sbin/apxs2" ] ; then \ echo "/usr/sbin/apxs2" ; \ elif [ -x "/usr/sbin/apxs" ] ; then \ echo "/usr/sbin/apxs" ; \ elif [ -x "/usr/bin/apxs2" ] ; then \ echo "/usr/bin/apxs2" ; \ elif [ -x "/usr/bin/apxs" ] ; then \ echo "/usr/bin/apxs" ; \ else \ echo "apxs" ; \ fi ) APXS_INSTALL_DIR=$(shell ${APXS} -q LIBEXECDIR) DESTDIR= ifdef USE_SYSTEM LIBAPPARMOR = $(shell if pkg-config --exists libapparmor ; then \ pkg-config --silence-errors --libs libapparmor ; \ elif ldconfig -p | grep -q libapparmor\.so$$ ; then \ echo -lapparmor ; \ fi ) ifeq ($(strip $(LIBAPPARMOR)),) ERROR_MESSAGE = $(error ${nl}\ ************************************************************************${nl}\ Unable to find libapparmor installed on this system; either${nl}\ install libapparmor devel packages, set the LIBAPPARMOR variable${nl}\ manually, or build against in-tree libapparmor.${nl}\ ************************************************************************${nl}) endif # LIBAPPARMOR not set LDLIBS += $(LIBAPPARMOR) else LIBAPPARMOR_SRC := ../../libraries/libapparmor/ LIBAPPARMOR_INCLUDE = $(LIBAPPARMOR_SRC)/include LIBAPPARMOR_PATH := $(LIBAPPARMOR_SRC)/src/.libs/ ifeq ($(realpath $(LIBAPPARMOR_PATH)/libapparmor.a),) ERROR_MESSAGE = $(error ${nl}\ ************************************************************************${nl}\ $(LIBAPPARMOR_PATH)/libapparmor.a is missing; either build against${nl}\ the in-tree libapparmor by building it first and then trying again${nl}\ (see the top-level README for help) or build against the system${nl}\ libapparmor by adding USE_SYSTEM=1 to your make command.${nl}\ ************************************************************************${nl}) endif # Need to pass -Wl twice here to get past both apxs2 and libtool, as # libtool will add the path to the RPATH of the library if passed -L/some/path LIBAPPARMOR_FLAGS = -I$(LIBAPPARMOR_INCLUDE) -Wl,-Wl,-L$(LIBAPPARMOR_PATH) LDLIBS = -lapparmor endif APXS_CFLAGS="-Wc,$(EXTRA_WARNINGS)" .PHONY: libapparmor_check .SILENT: libapparmor_check libapparmor_check: ; $(ERROR_MESSAGE) all: libapparmor_check $(TARGET) docs .PHONY: docs docs: ${MANPAGES} ${HTMLMANPAGES} %.so: %.c ${APXS} ${LIBAPPARMOR_FLAGS} ${APXS_CFLAGS} -c $< ${LDLIBS} mv .libs/$@ . .PHONY: install install: ${TARGET} ${MANPAGES} mkdir -p ${DESTDIR}/${APXS_INSTALL_DIR} install -m 755 $< ${DESTDIR}/${APXS_INSTALL_DIR} $(MAKE) install_manpages DESTDIR=${DESTDIR} .PHONY: clean clean: pod_clean rm -rf .libs rm -f *.la *.lo *.so *.o *.slo .PHONY: check check: check_pod_files apparmor-5.0.2/changehat/mod_apparmor/frob_sysconfig000077500000000000000000000042321522511161100226770ustar00rootroot00000000000000#!/usr/bin/perl -w # # ---------------------------------------------------------------------- # Copyright (c) 2004, 2005 NOVELL (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- # read /etc/sysconfig/apache2 and add "change_hat" to the apache # modules found there use Getopt::Long; use File::Temp qw/ :mktemp /; my $module="apparmor"; sub usage() { print "$0\t--file= modify \n"; print "\t\t\t--remove remove option from config file\n"; print "\t\t\t--help this help\n"; exit(0); } my ($conffile,$help,$remove); GetOptions( "file=s" => \$conffile, "remove" => \$remove, "help!" => \$help ) or usage(); usage() if $help; if (defined $conffile) { $old = $conffile; chomp($old); } else { $old="/etc/sysconfig/apache2"; } open(MENU,"<$old") or die "Fatal: can't open $old: $!"; ($fh, $file) = mkstemp($old . "XXXXXX" ); while () { # ok, we rely on the '="' to site the changes ; if (! defined $remove) { if ( /^\s*APACHE_MODULES="/ ) { if ( ! /$module/ ) { s/="/="$module /; } } } else { # remove the option if ( /^\s*APACHE_MODULES=".*$module/ ) { s/$module\s*//; } # remove old versions of the module if ( /^\s*APACHE_MODULES=".*change_hat/ ) { s/change_hat\s*//; } } print $fh $_; } rename $old, "$old.orig" || system("/bin/mv", $old, "$old.orig") && die "$old could not be renamed to $old.orig ($!); see $file for modifications"; rename $file, "$old" || system("/bin/mv", $file, "$old") && die "$file could not be renamed to $old ($!); see $file for modifications"; apparmor-5.0.2/changehat/mod_apparmor/mod_apparmor.c000066400000000000000000000334041522511161100225640ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005, 2006 NOVELL (All rights reserved) * Copyright (c) 2014 Canonical, Ltd. (All rights reserved) * * The mod_apparmor module is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * mod_apparmor - (apache 2.0.x) * Author: Steve Beattie * * This currently only implements change_hat functionality, but could be * extended for other stuff we decide to do. */ #include "ap_config.h" #include "httpd.h" #include "http_config.h" #include "http_request.h" #include "http_log.h" #include "http_main.h" #include "http_protocol.h" #include "util_filter.h" #include "apr.h" #include "apr_strings.h" #include "apr_lib.h" #include #include /* #define DEBUG */ #ifndef unused_ #define unused_ __attribute__ ((unused)) #endif /* should the following be configurable? */ #define DEFAULT_HAT "HANDLING_UNTRUSTED_INPUT" #define DEFAULT_URI_HAT "DEFAULT_URI" /* Compatibility with apache 2.2 */ #if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER < 3 #define APLOG_TRACE1 APLOG_DEBUG server_rec *ap_server_conf = NULL; #endif #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(apparmor); #endif module AP_MODULE_DECLARE_DATA apparmor_module; static unsigned long magic_token = 0; static int inside_default_hat = 0; typedef struct { const char *hat_name; char *path; } apparmor_dir_cfg; typedef struct { const char *hat_name; int is_initialized; } apparmor_srv_cfg; /* aa_init() gets invoked in the post_config stage of apache. * Unfortunately, apache reads its config once when it starts up, then * it re-reads it when goes into its restart loop, where it starts it's * children. This means we cannot call change_hat here, as the modules * memory will be wiped out, and the magic_token will be lost, so apache * wouldn't be able to change_hat back out. */ static int aa_init(apr_pool_t *p, unused_ apr_pool_t *plog, unused_ apr_pool_t *ptemp, unused_ server_rec *s) { apr_file_t *file; apr_size_t size = sizeof(magic_token); int ret; ret = apr_file_open (&file, "/dev/urandom", APR_READ, APR_OS_DEFAULT, p); if (!ret) { apr_file_read(file, (void *) &magic_token, &size); apr_file_close(file); } else { ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf, "Failed to open /dev/urandom"); } ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "Opened /dev/urandom successfully"); return OK; } /* As each child starts up, we'll change_hat into a default hat, mostly * to protect ourselves from bugs in parsing network input, but before * we change_hat to the uri specific hat. */ static void aa_child_init(unused_ apr_pool_t *p, unused_ server_rec *s) { int ret; ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "init: calling change_hat with '%s'", DEFAULT_HAT); ret = aa_change_hat(DEFAULT_HAT, magic_token); if (ret < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf, "Failed to change_hat to '%s'", DEFAULT_HAT); } else { inside_default_hat = 1; } } static void debug_dump_uri(request_rec *r) { apr_uri_t *uri = &r->parsed_uri; if (uri) ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Dumping uri info " "scheme='%s' host='%s' path='%s' query='%s' fragment='%s'", uri->scheme, uri->hostname, uri->path, uri->query, uri->fragment); else ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Asked to dump NULL uri"); } /* aa_enter_hat will attempt to change_hat in the following order: (1) to a hatname in a location directive (2) to the server name or a defined per-server default (3) to the server name + "-" + uri (4) to the uri (5) to DEFAULT_URI (6) back to the parent profile */ static int aa_enter_hat(request_rec *r) { int aa_ret = -1; apparmor_dir_cfg *dcfg = (apparmor_dir_cfg *) ap_get_module_config(r->per_dir_config, &apparmor_module); apparmor_srv_cfg *scfg = (apparmor_srv_cfg *) ap_get_module_config(r->server->module_config, &apparmor_module); const char *aa_hat_array[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; int i = 0; char *aa_label, *aa_mode, *aa_hat; const char *vhost_uri; debug_dump_uri(r); ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "aa_enter_hat (%s) n:0x%lx p:0x%lx main:0x%lx", dcfg->path, (unsigned long) r->next, (unsigned long) r->prev, (unsigned long) r->main); /* We only call change_hat for the main request, not subrequests */ if (r->main) return OK; if (inside_default_hat) { aa_change_hat(NULL, magic_token); inside_default_hat = 0; } if (dcfg != NULL && dcfg->hat_name != NULL) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[dcfg] adding hat '%s' to aa_change_hat vector", dcfg->hat_name); aa_hat_array[i++] = dcfg->hat_name; } if (scfg) { ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Dumping scfg info: " "scfg='0x%lx' scfg->hat_name='%s'", (unsigned long) scfg, scfg->hat_name); } else { ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "scfg is null"); } if (scfg != NULL) { if (scfg->hat_name != NULL) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[scfg] adding hat '%s' to aa_change_hat vector", scfg->hat_name); aa_hat_array[i++] = scfg->hat_name; } else { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[scfg] adding server_name '%s' to aa_change_hat vector", r->server->server_hostname); aa_hat_array[i++] = r->server->server_hostname; } vhost_uri = apr_pstrcat(r->pool, r->server->server_hostname, "-", r->uri, NULL); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[vhost+uri] adding vhost+uri '%s' to aa_change_hat vector", vhost_uri); aa_hat_array[i++] = vhost_uri; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[uri] adding uri '%s' to aa_change_hat vector", r->uri); aa_hat_array[i++] = r->uri; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[default] adding '%s' to aa_change_hat vector", DEFAULT_URI_HAT); aa_hat_array[i++] = DEFAULT_URI_HAT; aa_ret = aa_change_hatv(aa_hat_array, magic_token); if (aa_ret < 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, r, "aa_change_hatv call failed"); } /* Check to see if a defined AAHatName or AADefaultHatName would * apply, but wasn't the hat we landed up in; report a warning if * that's the case. */ aa_ret = aa_getcon(&aa_label, &aa_mode); if (aa_ret < 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, r, "aa_getcon call failed"); } else { ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "AA checks: aa_getcon result is '%s', mode '%s'", aa_label, aa_mode); /* TODO: use libapparmor get hat_name fn here once it is implemented */ aa_hat = strstr(aa_label, "//"); if (aa_hat != NULL && strcmp(aa_mode, "enforce") == 0) { aa_hat += 2; /* skip "//" */ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "AA checks: apache is in hat '%s', mode '%s'", aa_hat, aa_mode); if (dcfg != NULL && dcfg->hat_name != NULL) { if (strcmp(aa_hat, dcfg->hat_name) != 0) ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AAHatName '%s' applies, but does not appear to be a hat in the apache apparmor policy", dcfg->hat_name); } else if (scfg != NULL && scfg->hat_name != NULL) { if (strcmp(aa_hat, scfg->hat_name) != 0 && strcmp(aa_hat, r->uri) != 0) ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AADefaultHatName '%s' applies, but does not appear to be a hat in the apache apparmor policy", scfg->hat_name); } } free(aa_label); } return OK; } static int aa_exit_hat(request_rec *r) { int aa_ret; apparmor_dir_cfg *dcfg = (apparmor_dir_cfg *) ap_get_module_config(r->per_dir_config, &apparmor_module); /* apparmor_srv_cfg *scfg = (apparmor_srv_cfg *) ap_get_module_config(r->server->module_config, &apparmor_module); */ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "exiting change_hat: dir hat %s dir path %s", dcfg->hat_name, dcfg->path); /* can convert the following back to aa_change_hat() when the * aa_change_hat() bug addressed in trunk commit 2329 lands in most * system libapparmors */ aa_change_hatv(NULL, magic_token); aa_ret = aa_change_hat(DEFAULT_HAT, magic_token); if (aa_ret < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "Failed to change_hat to '%s'", DEFAULT_HAT); } else { inside_default_hat = 1; } return OK; } static const char * aa_cmd_ch_path(unused_ cmd_parms *cmd, unused_ void *mconfig, const char *parm1) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "directory config change hat %s", parm1 ? parm1 : "DEFAULT"); apparmor_dir_cfg *dcfg = mconfig; if (parm1 != NULL) { dcfg->hat_name = parm1; } else { dcfg->hat_name = "DEFAULT"; } return NULL; } static int path_warn_once; static const char * immunix_cmd_ch_path(cmd_parms *cmd, void *mconfig, const char *parm1) { if (path_warn_once == 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "ImmHatName is " "deprecated, please use AAHatName instead"); path_warn_once = 1; } return aa_cmd_ch_path(cmd, mconfig, parm1); } static const char * aa_cmd_ch_srv(cmd_parms *cmd, unused_ void *mconfig, const char *parm1) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "server config change hat %s", parm1 ? parm1 : "DEFAULT"); apparmor_srv_cfg *scfg = (apparmor_srv_cfg *) ap_get_module_config(cmd->server->module_config, &apparmor_module); if (parm1 != NULL) { scfg->hat_name = parm1; } else { scfg->hat_name = "DEFAULT"; } return NULL; } static int srv_warn_once; static const char * immunix_cmd_ch_srv(cmd_parms *cmd, void *mconfig, const char *parm1) { if (srv_warn_once == 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "ImmDefaultHatName is " "deprecated, please use AADefaultHatName instead"); srv_warn_once = 1; } return aa_cmd_ch_srv(cmd, mconfig, parm1); } static void * aa_create_dir_config(apr_pool_t *p, char *path) { apparmor_dir_cfg *newcfg = (apparmor_dir_cfg *) apr_pcalloc(p, sizeof(*newcfg)); ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "aa_create_dir_cfg (%s)", path ? path : ":no path:"); if (newcfg == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "aa_create_dir_config: couldn't alloc dir config"); return NULL; } newcfg->path = apr_pstrdup(p, path ? path : ":no path:"); return newcfg; } /* XXX: Should figure out an appropriate action to take here, if any static void * aa_merge_dir_config(apr_pool_t *p, void *parent, void *child) { apparmor_dir_cfg *newcfg = (apparmor_dir_cfg *) apr_pcalloc(p, sizeof(*newcfg)); ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "in immunix_merge_dir ()"); if (newcfg == NULL) return NULL; return newcfg; } */ static void * aa_create_srv_config(apr_pool_t *p, unused_ server_rec *srv) { apparmor_srv_cfg *newcfg = (apparmor_srv_cfg *) apr_pcalloc(p, sizeof(*newcfg)); ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "in aa_create_srv_config"); if (newcfg == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "aa_create_srv_config: couldn't alloc srv config"); return NULL; } return newcfg; } static const command_rec mod_apparmor_cmds[] = { AP_INIT_TAKE1( "ImmHatName", immunix_cmd_ch_path, NULL, ACCESS_CONF, "" ), AP_INIT_TAKE1( "ImmDefaultHatName", immunix_cmd_ch_srv, NULL, RSRC_CONF, "" ), AP_INIT_TAKE1( "AAHatName", aa_cmd_ch_path, NULL, ACCESS_CONF, "" ), AP_INIT_TAKE1( "AADefaultHatName", aa_cmd_ch_srv, NULL, RSRC_CONF, "" ), { NULL } }; static void register_hooks(unused_ apr_pool_t *p) { ap_hook_post_config(aa_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(aa_child_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_access_checker(aa_enter_hat, NULL, NULL, APR_HOOK_FIRST); /* ap_hook_post_read_request(aa_enter_hat, NULL, NULL, APR_HOOK_FIRST); */ ap_hook_log_transaction(aa_exit_hat, NULL, NULL, APR_HOOK_LAST); } module AP_MODULE_DECLARE_DATA apparmor_module = { STANDARD20_MODULE_STUFF, aa_create_dir_config, /* dir config creator */ NULL, /* dir merger --- default is to override */ /* immunix_merge_dir_config, */ /* dir merger --- default is to override */ aa_create_srv_config, /* server config */ NULL, /* merge server config */ mod_apparmor_cmds, /* command table */ register_hooks /* register hooks */ }; apparmor-5.0.2/changehat/mod_apparmor/mod_apparmor.conf000066400000000000000000000001561522511161100232650ustar00rootroot00000000000000 # # Load the Immunix SubDomain change_hat module # LoadModule apparmor_module modules/mod_apparmor.so apparmor-5.0.2/changehat/mod_apparmor/mod_apparmor.pod000066400000000000000000000125601522511161100231240ustar00rootroot00000000000000# This publication is intellectual property of Novell Inc. and Canonical # Ltd. Its contents can be duplicated, either in part or in whole, provided # that a copyright label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither SUSE LINUX GmbH, Canonical Ltd, the authors, nor the translators # shall be held liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. SUSE LINUX GmbH # and Canonical Ltd. essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME mod_apparmor - fine-grained AppArmor confinement for Apache =head1 DESCRIPTION An AppArmor profile applies to an executable program; if a portion of the program needs different access permissions than other portions, the program can "change hats" via aa_change_hat(2) to a different role, also known as a subprofile. The mod_apparmor Apache module uses the aa_change_hat(2) mechanism to offer more fine-grained confinement of dynamic elements within Apache such as individual php and perl scripts, while still allowing the performance benefits of using mod_php and mod_perl. To use mod_apparmor with Apache, ensure that mod_apparmor is configured to be loaded into Apache, either via a2enmod, yast or manual editing of the apache2(8)/httpd(8) configuration files, and restart Apache. Make sure that apparmor is also functioning. Once mod_apparmor is loaded within Apache, all requests to Apache will cause mod_apparmor to attempt to change into a hat that matches the ServerName for the server/vhost. If no such hat is found, it will first fall back by attempting to change into a hat composed of the ServerName-URI (e.g. "www.example.com-/app/some.cgi"). If that hat is not found, it will fall back to attempting to use the hat named by the URI (e.g. "/app/some.cgi"). If that hat is not found, it will fall back to attempting to use the hat DEFAULT_URI; if that also does not exist, it will fall back to using the global Apache profile. Most static web pages can simply make use of the DEFAULT_URI hat. Additionally, before any requests come in to Apache, mod_apparmor will attempt to change hat into the HANDLING_UNTRUSTED_INPUT hat. mod_apparmor will attempt to use this hat while Apache is doing the initial parsing of a given http request, before its given to a specific handler (like mod_php) for processing. Because defining hats for every URI/URL often becomes tedious, mod_apparmor provides the AAHatName and AADefaultHatName Apache configuration options. =over 4 =item B AAHatName allows you to specify a hat to be used for a given Apache EDirectoryE, EDirectoryMatchE, ELocationE or ELocationMatchE directive (see the Apache documentation for more details). Note that mod_apparmor behavior can become confused if EDirectory*E and ELocation*E directives are intermingled and it is recommended to use one type of directive. If the hat specified by AAHatName does not exist in the Apache profile, then it falls back to the behavior described above. =item B AADefaultHatName allows you to specify a default hat to be used for virtual hosts and other Apache server directives, so that you can have different defaults for different virtual hosts. This can be overridden by the AAHatName directive and is checked for only if there isn't a matching AAHatName. The default value of AADefaultHatName is the ServerName for the server/vhost configuration. If the AADefaultHatName hat does not exist, then it falls back to the behavior described above. =back =head1 URI REQUEST SUMMARY When profiling with mod_apparmor, it is helpful to keep the following order of operations in mind: On each URI request, mod_apparmor will first aa_change_hat(2) into ^HANDLING_UNTRUSTED_INPUT, if it exists. Then, after performing the initial parsing of the request, mod_apparmor will: =over 4 =item 1 try to aa_change_hat(2) into a matching AAHatName hat if it exists and applies, otherwise it will =item 2 try to aa_change_hat(2) into an AADefaultHatName hat, either the ServerName (the default) or the configuration value specified by the AADefaultHatName directive, for the server/vhost, otherwise it will =item 3 try to aa_change_hat(2) into the ServerName-URI, otherwise it will =item 4 try to aa_change_hat(2) into the URI itself, otherwise it will =item 5 try to aa_change_hat(2) into the DEFAULT_URI hat, if it exists, otherwise it will =item 6 fall back to the global Apache policy =back =head1 BUGS mod_apparmor() currently only supports apache2, and has only been tested with the prefork MPM configuration -- threaded configurations of Apache may not work correctly. For Apache 2.4 users, you should enable the mpm_prefork module. There are likely other bugs lurking about; if you find any, please report them at L. =head1 SEE ALSO apparmor(7), apparmor_parser(8), aa_change_hat(2) and L. =cut apparmor-5.0.2/changehat/pam_apparmor/000077500000000000000000000000001522511161100177325ustar00rootroot00000000000000apparmor-5.0.2/changehat/pam_apparmor/COPYING000066400000000000000000000037401522511161100207710ustar00rootroot00000000000000The pam_apparmor package is licensed under the same license as Linux-PAM , quoted below: ------------------------------------------------------------------------- Redistribution and use in source and binary forms of Linux-PAM, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain any existing copyright notice, and this entire permission notice in its entirety, including the disclaimer of warranties. 2. Redistributions in binary form must reproduce all prior and current copyright notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of any author may not be used to endorse or promote products derived from this software without their specific prior written permission. ALTERNATIVELY, this product may be distributed under the terms of the GNU General Public License, in which case the provisions of the GNU GPL are required INSTEAD OF the above restrictions. (This clause is necessary due to a potential conflict between the GNU GPL and the restrictions contained in a BSD-style copyright.) THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------- apparmor-5.0.2/changehat/pam_apparmor/Makefile000066400000000000000000000064131522511161100213760ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 1999, 2004, 2005 NOVELL (All rights reserved) # Copyright (c) 2016 Canonical, Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- NAME=pam_apparmor all: COMMONDIR=../../common/ include $(COMMONDIR)/Make.rules ifdef USE_SYSTEM LIBAPPARMOR = $(shell if pkg-config --exists libapparmor ; then \ pkg-config --silence-errors --libs libapparmor ; \ elif ldconfig -p | grep -q libapparmor\.so$$ ; then \ echo -lapparmor ; \ fi ) ifeq ($(strip $(LIBAPPARMOR)),) ERROR_MESSAGE = $(error ${nl}\ ************************************************************************${nl}\ Unable to find libapparmor installed on this system; either${nl}\ install libapparmor devel packages, set the LIBAPPARMOR variable${nl}\ manually, or build against in-tree libapparmor.${nl}\ ************************************************************************${nl}) endif LIBAPPARMOR_INCLUDE = AA_LDLIBS = $(LIBAPPARMOR) AA_LINK_FLAGS = else LIBAPPARMOR_SRC := ../../libraries/libapparmor/ LIBAPPARMOR_INCLUDE_PATH = $(LIBAPPARMOR_SRC)/include LIBAPPARMOR_PATH := $(LIBAPPARMOR_SRC)/src/.libs/ ifeq ($(realpath $(LIBAPPARMOR_PATH)/libapparmor.a),) ERROR_MESSAGE = $(error ${nl}\ ************************************************************************${nl}\ $(LIBAPPARMOR_PATH)/libapparmor.a is missing; either build against${nl}\ the in-tree libapparmor by building it first and then trying again${nl}\ (see the top-level README for help) or build against the system${nl}\ libapparmor by adding USE_SYSTEM=1 to your make command.${nl}\ ************************************************************************${nl}) endif LIBAPPARMOR_INCLUDE = -I$(LIBAPPARMOR_INCLUDE_PATH) AA_LINK_FLAGS = -L$(LIBAPPARMOR_PATH) AA_LDLIBS = -lapparmor endif EXTRA_CFLAGS=$(CFLAGS) $(CPPFLAGS) -fPIC -shared -Wall $(EXTRA_WARNINGS) $(LIBAPPARMOR_INCLUDE) LINK_FLAGS=-Xlinker -x $(AA_LINK_FLAGS) $(LDFLAGS) LIBS=-lpam $(AA_LDLIBS) OBJECTS=${NAME}.o get_options.o .PHONY: libapparmor_check .SILENT: libapparmor_check libapparmor_check: ; $(ERROR_MESSAGE) all: libapparmor_check $(NAME).so docs .PHONY: docs # docs: we should have some docs: $(NAME).so: ${OBJECTS} $(CC) $(EXTRA_CFLAGS) $(LINK_FLAGS) -o $@ ${OBJECTS} $(LIBS) %.o: %.c $(CC) $(EXTRA_CFLAGS) -c -o $@ $< # need some better way of determining this DESTDIR=/ SECDIR ?= ${DESTDIR}/lib/security .PHONY: install install: $(NAME).so install -m 755 -d $(SECDIR) install -m 755 $(NAME).so $(SECDIR)/ .PHONY: clean clean: rm -f core core.* *.so *.o *.s *.a *~ apparmor-5.0.2/changehat/pam_apparmor/README000066400000000000000000000063351522511161100206210ustar00rootroot00000000000000pam_apparmor - a (linux specific) PAM module to add support for apparmor's subprocess confinement. An apparmor profile applies to an executable program; if a portion of the program needs different access permissions than other portions, the program can "change hats" via change_hat(2) to a different role, also known as a subprofile. The pam_apparmor PAM module allows applications to confine authenticated users into subprofiles based on groupnames, usernames, or a default profile. To accomplish this, pam_apparmor needs to be registered as a PAM session module. Compiling pam_apparmor ---------------------- The pam-development libraries and libapparmor need to be installed on the build system. 'make' should be all that is needed to build pam_apparmor.so; 'make rpm' should work on RPM-based systems. Configuring pam_apparmor ------------------------ To add pam_apparmor support to a pam enabled application, add a line like the following to the pam configuration file for the application (usually stored in /etc/pam.d/): session optional pam_apparmor.so Likely you will want add the pam_apparmor after other session management modules. If you make the pam_apparmor module 'required' instead of 'optional', the session will abort if pam_apparmor is not able to successfully find a hat to change_hat into. Be careful when making it required; it is possible to cause all attempted logins to the service to fail if the apparmor policy is insufficient. By default, pam_apparmor will attempt to change_hat into a hat based on the primary group name of the user logging in. If that hat fails to exist, the module will attempt to change_hat into a hat named DEFAULT (it is recommended to ensure this hat exists in the apparmor profiles for applications using pam_apparmor). However, this is configurable by adding an option to the pam configuration line to modify what order and what attributes pam_apparmor will attempt to use when attempting to change_hat. To do so, add 'order=' followed by a comma separated list of types of hats to try. The type of hats available are: * 'user' - the username will be used as the hatname * 'group' - the primary group will be used as the hatname * 'default' - the string 'DEFAULT' will be used as the hatname. Generally, this should be the hat of last resort. The order in the list determines the order the hat will be attempted. Some example configurations: # the default behavior session optional pam_apparmor.so order=group,default # attempt to use only the username session optional pam_apparmor.so order=user # use the username, followed by the primary groupname, followed by # DEFAULT if the prior hats do not exist in the apparmor profile session optional pam_apparmor.so order=user,group,default You can also add a 'debug' flag to the pam_apparmor session line; this will cause the pam module to report more of what it is attempting to do to syslog. References ---------- Project webpage: https://apparmor.net/ To provide feedback or ask questions please contact the apparmor@lists.ubuntu.com mail list. This is the development list for the AppArmor team. See also: change_hat(3), and the Linux-PAM online documentation at http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/ apparmor-5.0.2/changehat/pam_apparmor/get_options.c000066400000000000000000000110571522511161100224340ustar00rootroot00000000000000/* * Written by Steve Beattie 2006/10/25 * * Modeled after the option parsing code in pam_unix2 by: * Copyright (c) 2006 SUSE Linux Products GmbH, Nuernberg, Germany. * Copyright (c) 2002, 2003, 2004 SuSE GmbH Nuernberg, Germany. * Author: Thorsten Kukuk * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of * the GNU Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE /* for strndup() */ #include #include #include #include #include #include #define PAM_SM_SESSION #include #include "pam_apparmor.h" #define DEBUG_STRING "debug" #define ORDER_PREFIX "order=" static int parse_option(pam_handle_t *pamh, struct config **config, const char *argv) { const char *opts; if (argv == NULL || argv[0] == '\0') return 0; if (strcasecmp(argv, DEBUG_STRING) == 0) { debug_flag = 1; return 0; } else if (strncasecmp(argv, ORDER_PREFIX, strlen(ORDER_PREFIX)) != 0) { pam_syslog (pamh, LOG_ERR, "Unknown option: `%s'\n", argv); return PAM_SESSION_ERR; } opts = argv + strlen(ORDER_PREFIX); while (*opts != '\0') { hat_t hat; char *opt, *comma; int i; comma = index(opts, ','); if (comma) opt = strndup(opts, comma - opts); else opt = strdup(opts); if (!opt) { pam_syslog(pamh, LOG_ERR, "Memory allocation error: %s", strerror(errno)); return PAM_SESSION_ERR; } if (strcasecmp(opt, "group") == 0) hat = eGroupname; else if (strcasecmp(opt, "user") == 0) hat = eUsername; else if (strcasecmp(opt, "default") == 0) hat = eDefault; else { pam_syslog (pamh, LOG_ERR, "Unknown option: `%s'\n", opt); free(opt); return PAM_SESSION_ERR; } if (!(*config)) { struct config *new_cfg = malloc(sizeof(**config)); if (!new_cfg) { pam_syslog(pamh, LOG_ERR, "Memory allocation error: %s", strerror(errno)); free(opt); return PAM_SESSION_ERR; } new_cfg->hat_type[0] = eNoEntry; new_cfg->hat_type[1] = eNoEntry; new_cfg->hat_type[2] = eNoEntry; (*config) = new_cfg; } /* Find free table entry, looking for duplicates */ for (i = 0; i < MAX_HAT_TYPES && (*config)->hat_type[i] != eNoEntry; i++) { if ((*config)->hat_type[i] == hat) { pam_syslog(pamh, LOG_ERR, "Duplicate hat type: %s\n", opt); free(opt); free(*config); (*config) = NULL; return PAM_SESSION_ERR; } } if (i >= MAX_HAT_TYPES) { pam_syslog(pamh, LOG_ERR, "Unable to add hat type '%s'\n", opt); return PAM_SESSION_ERR; } (*config)->hat_type[i] = hat; free(opt); if (comma) opts = comma + 1; else opts += strlen(opts); } return 0; } int get_options(pam_handle_t *pamh, struct config **config, int argc, const char **argv) { int retval = 0; /* Parse parameters for module */ for ( ; argc-- > 0; argv++) { int rc = parse_option(pamh, config, *argv); if (rc != 0) retval = rc; } return retval; } apparmor-5.0.2/changehat/pam_apparmor/pam_apparmor.c000066400000000000000000000133741522511161100225640ustar00rootroot00000000000000/* pam_apparmor module */ /* * Copyright (c) 2006 * NOVELL (All rights reserved) * * Copyright (c) 2010 * Canonical, Ltd. (All rights reserved) * * Written by Jesse Michael 2006/08/24 * and Steve Beattie 2006/10/25 * * Based off of pam_motd by: * Ben Collins 2005/10/04 * Michael K. Johnson 1996/10/24 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * here, we make a definition for the externally accessible function * in this file (this definition is required for static a module * but strongly encouraged generally) it is used to instruct the * modules include file to define the function prototypes. */ #define PAM_SM_SESSION #include #include "pam_apparmor.h" int debug_flag = 0; #ifndef unused_ #define unused_ __attribute__ ((unused)) #endif static struct config default_config = { .hat_type[0] = eGroupname, .hat_type[1] = eDefault, .hat_type[2] = eNoEntry, }; /* --- session management functions (only) --- */ PAM_EXTERN int pam_sm_close_session (unused_ pam_handle_t *pamh, unused_ int flags, unused_ int argc, unused_ const char **argv) { return PAM_IGNORE; } PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, unused_ int flags, int argc, const char **argv) { int fd, retval, pam_retval = PAM_SUCCESS; unsigned int magic_token; const char *user; struct passwd *pw; struct group *gr; struct config *config = NULL; int i; if ((retval = get_options(pamh, &config, argc, argv)) != 0) return retval; if (!config) config = &default_config; /* grab the target user name */ retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS || user == NULL || *user == '\0') { pam_syslog(pamh, LOG_ERR, "Can't determine user\n"); return PAM_USER_UNKNOWN; } pw = pam_modutil_getpwnam(pamh, user); if (!pw) { pam_syslog(pamh, LOG_ERR, "Can't determine group for user %s\n", user); return PAM_PERM_DENIED; } gr = pam_modutil_getgrgid(pamh, pw->pw_gid); if (!gr || !gr->gr_name) { pam_syslog(pamh, LOG_ERR, "Can't read info for group %d\n", pw->pw_gid); return PAM_PERM_DENIED; } fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { pam_syslog(pamh, LOG_ERR, "Can't open /dev/urandom\n"); return PAM_PERM_DENIED; } /* the magic token needs to be non-zero otherwise, we won't be able * to probe for hats */ do { retval = pam_modutil_read(fd, (void *)&magic_token, sizeof(magic_token)); if (retval < 0) { pam_syslog(pamh, LOG_ERR, "Can't read from /dev/urandom\n"); close(fd); return PAM_PERM_DENIED; } } while ((magic_token == 0) || (retval != sizeof(magic_token))); close(fd); pam_retval = PAM_SUCCESS; for (i = 0; i < MAX_HAT_TYPES && config->hat_type[i] != eNoEntry; i++) { const char *hat = NULL; switch (config->hat_type[i]) { case eGroupname: hat = gr->gr_name; if (debug_flag) pam_syslog(pamh, LOG_DEBUG, "Using groupname '%s'\n", hat); break; case eUsername: hat = user; if (debug_flag) pam_syslog(pamh, LOG_DEBUG, "Using username '%s'\n", hat); break; case eDefault: if (debug_flag) pam_syslog(pamh, LOG_DEBUG, "Using DEFAULT\n"); hat = "DEFAULT"; break; default: pam_syslog(pamh, LOG_ERR, "Unknown value in hat table: %x\n", config->hat_type[i]); goto nodefault; break; } retval = change_hat(hat, magic_token); if (retval == 0) { /* success, let's bail */ if (debug_flag) pam_syslog(pamh, LOG_DEBUG, "Successfully changed to hat '%s'\n", hat); goto out; } switch (errno) { /* case EPERM: */ /* Can't enable until ECHILD patch gets accepted, and we can * distinguish between unconfined and confined-but-no-hats */ case EINVAL: /* apparmor is not loaded or application is unconfined, * stop attempting to use change_hat */ if (debug_flag) pam_syslog(pamh, LOG_DEBUG, "AppArmor not loaded, or application is unconfined\n"); pam_retval = PAM_SUCCESS; goto out; break; case ECHILD: /* application is confined but has no hats, * stop attempting to use change_hat */ goto nodefault; break; case EACCES: case ENOENT: /* failed to change into attempted hat, so we'll * jump back out and try the next one */ break; default: pam_syslog(pamh, LOG_ERR, "Unknown error occurred changing to %s hat: %s\n", hat, strerror(errno)); /* give up? */ pam_retval = PAM_SYSTEM_ERR; goto out; } retval = change_hat(NULL, magic_token); if (retval != 0) { /* changing into the specific hat and attempting to * jump back out both failed. that most likely * means that either apparmor is not loaded or we * don't have a profile loaded for this application. * in this case, we want to allow the pam operation * to succeed. */ goto out; } } nodefault: /* if we got here, we were unable to change into any of the hats * we attempted. */ pam_syslog(pamh, LOG_ERR, "Can't change to any hat\n"); pam_retval = PAM_SESSION_ERR; out: /* zero out the magic token so an attacker wouldn't be able to * just grab it out of process memory and instead would need to * brute force it */ memset(&magic_token, 0, sizeof(magic_token)); if (config && config != &default_config) free(config); return pam_retval; } #ifdef PAM_STATIC /* static module data */ struct pam_module _pam_apparmor_modstruct = { "pam_apparmor", NULL, NULL, NULL, pam_sm_open_session, pam_sm_close_session, NULL, }; #endif /* end of module definition */ apparmor-5.0.2/changehat/pam_apparmor/pam_apparmor.changes000066400000000000000000000030651522511161100237460ustar00rootroot00000000000000------------------------------------------------------------------- Mon Jul 30 08:16:39 CEST 2007 - sbeattie@suse.de - Convert libapparmor builddep to libapparmor-devel ------------------------------------------------------------------- Tue Mar 13 10:27:34 PDT 2007 - jmichael@suse.de - Use pam_modutil_* wrapper functions when possible ------------------------------------------------------------------- Tue Oct 31 12:00:00 UTC 2006 - jmichael@suse.de - Add debug option ------------------------------------------------------------------- Tue Oct 31 12:00:00 UTC 2006 - sbeattie@suse.de - Add configuration options to order attempted hat changes ------------------------------------------------------------------- Wed Oct 25 12:00:00 UTC 2006 - sbeattie@suse.de - remove auto-editing of pam's common-session - honor RPM's CFLAGS when building - add license (same as Linux PAM package). ------------------------------------------------------------------- Thu Sep 14 12:00:00 UTC 2006 - jmichael@suse.de - header comment was incorrect - use pam_get_user() instead of pam_get_item() - fix read from urandom if 0 ------------------------------------------------------------------- Fri Jan 13 12:00:00 UTC 2006 - sbeattie@suse.de - Add svn repo number to tarball ------------------------------------------------------------------- Fri Jan 13 12:00:00 UTC 2006 - jmichael@suse.de - Make magic tokens harder to guess by pulling them from /dev/urandom ------------------------------------------------------------------- Wed Dec 21 10:31:40 PST 2005 - jmichael@suse.de - initial apparmor-5.0.2/changehat/pam_apparmor/pam_apparmor.h000066400000000000000000000042401522511161100225610ustar00rootroot00000000000000/* pam_apparmor module */ /* * Written by Jesse Michael 2006/08/24 * and Steve Beattie 2006/10/25 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of * the GNU Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ enum hat_t { eNoEntry, eUsername, eGroupname, eDefault, }; typedef enum hat_t hat_t; #define MAX_HAT_TYPES 3 struct config { hat_t hat_type[MAX_HAT_TYPES]; }; extern int debug_flag; extern int get_options(pam_handle_t *pamh, struct config **config, int argc, const char **argv); apparmor-5.0.2/changehat/tomcat_apparmor/000077500000000000000000000000001522511161100204445ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/000077500000000000000000000000001522511161100223765ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/Makefile000066400000000000000000000022151522511161100240360ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 1999, 2004, 2005, 2006 NOVELL (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- NAME = tomcat_apparmor all: COMMONDIR=../../../common/ include $(COMMONDIR)/Make.rules LIB = lib CATALINA_HOME = /usr/share/tomcat5 # By default build 1.4 bytecode all: ant -Dtarget=1.4 jar jni_so clean: ant clean install: ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/Manifest000066400000000000000000000000331522511161100240630ustar00rootroot00000000000000Main-Class: ChangeHatValve apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/README.tomcat_apparmor000066400000000000000000000147621522511161100264570ustar00rootroot00000000000000# ------------------------------------------------------------------ # # Copyright (C) 2002-2006 Novell/SUSE # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # ------------------------------------------------------------------ ---------------------- 1. Overview 2. Requirements 3. Compiling the code 4. Installation 5. Generating a basic Tomcat profile 6. Implementation Notes 7. Profile Generation Tools and change_hat 8. Feedback/Resources ----------------------- 1. Overview -------- This package provides an implementation of a Tomcat 5.0 Valve that calls out to the change_hat(2) function provided by libapparmor to allow a process to change its security context. Any feedback is greatly appreciated. 2. Requirements ------------ AppArmor version 1.2 or later Tomcat version 5.0.X JDK 1.4.2 or later 3. Compiling the Code ----------------- From the top level directory execute: ant jar jni_so This will create a jar file and a shared library (for the JNI interface to the libapparmor library) and place the jar file under dist/ and the shared library under src/jni_src. 4. Installation ------------ - Copy the jar file to $TOMCAT_HOME/server/lib: [SLES10 example] cp dist/changeHatValve.jar /usr/share/tomcat5/server/lib - Copy the shared library to somehere in your library search path: cp dist/libJNIChangeHat.so /usr/lib [Note: you must ensure that the target directory is passed to tomcat via the java.library.path property. This can be accomplished by setting the JAVA_OPTS environment variable, export JAVA_OPTS=-Djava.library.path, or set via the env variable LD_LIBRARY_PATH to include this directory so that tomcat can find this library at startup] - Configure the Tomcat server to use ChangeHatValve: Place the configuration directive below in your server.xml file. The valve definition should be the initial configuration option declared in the top-level container in the container hierarchy. [Note: The mediationType attribute may be set to ServletPath or URI depending on the granularity of containers that you wish to create. URI will prompt the user to create containers for every URI it processes. This is not recommended for most deployment scenarios and so the default "ServletPath" should be used. This maps to containers identified by the ServletPath header defined in the HttpRequest.] - Defining a default and required hat for the tomcat profile Edit the file /etc/apparmor/logprof.conf and add the following line to the section [required_hats]: ^.+/catalina.sh$ = DEFAULT Edit the file /etc/apparmor/logprof.conf and add the following line to the section [default_hat]: ^.+/catalina.sh$ = DEFAULT 5. Generating a basic Tomcat profile ------------------------------- Once the installation steps above have been started you are ready to begin creating a profile for your application. The profile creation tool genprof will guide you through generating a profile and its support for change_hat will prompt you create discrete hats as requested by the changeHatValve during tomcat execution. 1. Create a basic profile for the tomcat server. - Run the command "genprof PATH_TO_CATALINA.SH" - In a separate window start tomcat and then stop tomcat - In the genprof window press "S" to scan for events - Answer the questions about the initial profile for tomcat 2. Extending the profile to include containers for your web-app - Stop the tomcat server - Deploy your WAR file or equivalent files under the container. - execute "genprof PATH_TO_CATALINA.SH" - In a separate window start tomcat and then exercise your web application - In the genprof window press "S" to scan for events During the prompting you will be asked questions similar to: ----------------------------------------------- Profile: /usr/share/tomcat5/bin/catalina.sh Default Hat: DEFAULT Requested Hat: /servlet/CookieExample (A)dd Requested Hat / (U)se Default Hat / (D)eny / Abo(r)t / (F)inish ------------------------------------------------ This example shows the tomcat valve for changehat attempting to change to the hat "/servlet/CookieExample". You can choose to create this hat, and subsequently fill it with resources, use the Default hat, named "DEFAULT" and is the default hat for request processing. 6. Implementation Notes -------------------- - Selecting the hat during request processing This implementation follows the following pattern to decide what hat to execute in for an incoming request: Try #1: Request data (URI or ServletInfo) if #1 fails (because the hat does not exist) then try #2 Try #2: DEFAULT - this is the default hat for request processing if #2 fails (because of any error) then.. Error: report that change_hat calls failed and remain in current security context. - Java 1.4.2 Notes This library uses java.security.SecureRandom to generate random numbers used as cookies in the change_hat(2) interface. This class on java version 1.4.2 uses /dev/random which is a blocking call and can adversely effect performance. Java can be configured to use /dev/urandom instead. For details: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4705093 7. Profile Tools and change_hat ---------------------------- When using the profile generation tool, genprof, you will be prompted to add a new hat when you exercise your program and requests are processed by the changeHatValve. You can choose to Add the hat or use the Default hat. If you choose to add the requested hat: genprof will create the hat and then all subsequent resource requests will be mediated in this hew hat (or security context). If you choose to use the default hat: genprof will mediate all resource requests in the default hat for the duration of processing this request. When the request processing is complete the valve will change_hat back to the parent context. 8. Feedback/Resources ----------------- Project webpage: https://apparmor.net/ To provide feedback or ask questions please contact the apparmor@lists.ubuntu.com mail list. This is the development list for the AppArmor team. apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/build.xml000066400000000000000000000067621522511161100242320ustar00rootroot00000000000000 apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/000077500000000000000000000000001522511161100231655ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/com/000077500000000000000000000000001522511161100237435ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/com/novell/000077500000000000000000000000001522511161100252425ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/com/novell/apparmor/000077500000000000000000000000001522511161100270635ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/com/novell/apparmor/JNIChangeHat.java000066400000000000000000000020101522511161100321020ustar00rootroot00000000000000/* ------------------------------------------------------------------ * * Copyright (C) 2002-2005 Novell/SUSE * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * ------------------------------------------------------------------ */ package com.novell.apparmor; import java.nio.*; /** * * JNI interface to AppArmor change_hat(2) call * **/ public class JNIChangeHat { public static int EPERM = 1; public static int ENOMEM = 12; public static int EACCES = 13; public static int EFAULT = 14; // Native 'c' function delcaration public native int changehat_in(String subdomain, int magic_token); // Native 'c' function delcaration public native int changehat_out(int magic_token); static { // The runtime system executes a class's static initializer // when it loads the class. System.loadLibrary("JNIChangeHat"); } } apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/com/novell/apparmor/catalina/000077500000000000000000000000001522511161100306375ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/com/novell/apparmor/catalina/valves/000077500000000000000000000000001522511161100321375ustar00rootroot00000000000000ChangeHatValve.java000066400000000000000000000164221522511161100355500ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/com/novell/apparmor/catalina/valves/* ------------------------------------------------------------------ * * Copyright (C) 2002-2005 Novell/SUSE * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * ------------------------------------------------------------------ */ package com.novell.apparmor.catalina.valves; import com.novell.apparmor.JNIChangeHat; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.apache.catalina.HttpRequest; import org.apache.catalina.Container; import org.apache.catalina.HttpResponse; import org.apache.catalina.valves.ValveBase; import java.security.SecureRandom; public final class ChangeHatValve extends ValveBase { // JNI interface class for AppArmor change_hat private static JNIChangeHat changehat_wrapper = new JNIChangeHat(); private static SecureRandom randomNumberGenerator = null; private static String DEFAULT_HAT = "DEFAULT"; private static int SERVLET_PATH_MEDIATION = 0; private static int URI_MEDIATION = 1; private int mediationType = ChangeHatValve.SERVLET_PATH_MEDIATION; /* * * Property setter called during the parsing of the server.xml. * If the mediationType is an attribute of the * Valve definition for * com.novell.apparmor.catalina.valves.ChangeHatValve * then this setter will be called to set the value for this property. * * @param type URI|ServletPath * * Controls what granularity of security confinement when used with * AppArmor change_hat(2). Either based upon getServletPath() * or getRequestURI() called against on the request. * */ public void setMediationType( String type ) { if ( type.equalsIgnoreCase("URI") ) { this.mediationType = ChangeHatValve.URI_MEDIATION; } else if ( type.equalsIgnoreCase("servletPath") ) { this.mediationType = ChangeHatValve.SERVLET_PATH_MEDIATION; } } /* * * Return an int value representing the currently configured * mediationType for this instance. * */ int getMediationType() { return this.mediationType; } /* * * Return an instance of SecureRandom creating one if necessary * */ SecureRandom getRndGen() { if ( ChangeHatValve.randomNumberGenerator == null) { ChangeHatValve.randomNumberGenerator = new java.security.SecureRandom(); } return ChangeHatValve.randomNumberGenerator; } /* * * Call to return a random cookie from the SecureRandom PRNG * */ int getCookie() { SecureRandom rnd = getRndGen(); if ( rnd == null ) { this.getContainer().getLogger().log( "[APPARMOR] can't initialize SecureRandom for cookie generation for change_hat() call.", container.getLogger().ERROR); return 0; } return rnd.nextInt(); } /* * * Call out to AppArmor change_hat(2) to change the security * context for the processing of the request by subsequent valves. * Returns to the current security context when processing is complete. * The security context that is chosen is govern by the * mediationType property - which can be set in the * server.xml file. * * @param request Request being processed * @param response Response being processed * @param context The valve context used to invoke the next valve * in the current processing pipeline * * @exception IOException if an input/output error has occurred * @exception ServletException if a servlet error has occurred * */ public void invoke( org.apache.catalina.Request request, org.apache.catalina.Response response, org.apache.catalina.ValveContext context ) throws IOException, ServletException { Container container = this.getContainer(); int cookie, result; boolean inSubHat = false; container.getLogger().log(this.getClass().toString() + "[APPARMOR] Request received [" + request.getInfo() + "]", container.getLogger().DEBUG); if ( !( request instanceof HttpRequest) || !(response instanceof HttpResponse) ) { container.getLogger().log(this.getClass().toString() + "[APPARMOR] Non HttpRequest received. Not changing context. " + "[" + request.getInfo() + "]", container.getLogger().ERROR); context.invokeNext(request, response); return; } HttpRequest httpRequest = (HttpRequest) request; HttpServletRequest servletRequest = (HttpServletRequest) httpRequest.getRequest(); String hatname = ChangeHatValve.DEFAULT_HAT;; if ( getMediationType() == ChangeHatValve.SERVLET_PATH_MEDIATION ) { hatname = servletRequest.getServletPath(); } else if ( getMediationType() == ChangeHatValve.URI_MEDIATION ) { hatname = servletRequest.getRequestURI(); } /* * Select the AppArmor container for this request: * * 1. try hat name from either URI or ServletPath * (based on configuration) * * 2. try hat name of the defined DEFAULT_HAT * * 3. run in the current AppArmor context */ cookie = getCookie(); if ( hatname == null || "".equals(hatname) ) { hatname = ChangeHatValve.DEFAULT_HAT; } container.getLogger().log("[APPARMOR] ChangeHat to [" + hatname + "] cookie [" + cookie + "]", container.getLogger().DEBUG); result = changehat_wrapper.changehat_in(hatname, cookie); if ( result == JNIChangeHat.EPERM ) { container.getLogger().log("[APPARMOR] change_hat valve " + "configured but Tomcat process is not confined by an " + "AppArmor profile.", container.getLogger().ERROR); context.invokeNext(request, response); } else { if ( result == JNIChangeHat.EACCES ) { changehat_wrapper.changehat_out(cookie); result = changehat_wrapper.changehat_in(ChangeHatValve.DEFAULT_HAT, cookie); if ( result != 0 ) { changehat_wrapper.changehat_out(cookie); container.getLogger().log("[APPARMOR] ChangeHat to [" + hatname + "] failed. Running in parent context.", container.getLogger().ERROR); } else { inSubHat = true; } } else if ( result != 0 ) { changehat_wrapper.changehat_out(cookie); container.getLogger().log("[APPARMOR] ChangeHat to [" + hatname + "] failed. Running in parent context.", container.getLogger().ERROR); } else { inSubHat = true; } context.invokeNext(request, response); if ( inSubHat ) changehat_wrapper.changehat_out(cookie); } } } apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/jni_src/000077500000000000000000000000001522511161100246145ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/jni_src/JNIChangeHat.c000066400000000000000000000027031522511161100271450ustar00rootroot00000000000000/* ------------------------------------------------------------------ Copyright (C) 2002-2005 Novell/SUSE This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License published by the Free Software Foundation. ------------------------------------------------------------------ */ #include "jni.h" #include #include #include "com_novell_apparmor_JNIChangeHat.h" /* c intermediate lib call for Java -> JNI -> c library execution of the change_hat call */ JNIEXPORT jint Java_com_novell_apparmor_JNIChangeHat_changehat_1in (JNIEnv *env, jobject obj, jstring hatnameUTF, jint token) { int len; jint result = 0; if ( hatnameUTF == NULL ) { return ( EINVAL ); } len = (*env)->GetStringLength(env, hatnameUTF); if ( len > 0 ) { if ( len > 128 ) { len = 128; } char hatname[128]; (*env)->GetStringUTFRegion(env, hatnameUTF, 0, len, hatname); result = (jint) change_hat(hatname, (unsigned int) token); if ( result ) { return errno; } else { return result; } } return (jint) result; } JNIEXPORT jint JNICALL Java_com_novell_apparmor_JNIChangeHat_changehat_1out (JNIEnv *env, jobject obj, jint token) { jint result = 0; result = (jint) change_hat(NULL, (unsigned int) token); if ( result ) { return errno; } else { return result; } } apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_0/src/jni_src/Makefile000066400000000000000000000023671522511161100262640ustar00rootroot00000000000000INCLUDE=/usr/lib/jvm/java/include TOP=../.. CLASSPATH=${TOP}/build CFLAGS=-g -O2 -Wall -Wstrict-prototypes -Wl,-soname,$@.${SO_VERS} -pipe -fpic -D_REENTRANT INCLUDES=-I$(INCLUDE) -I$(INCLUDE)/linux CLASSFILE=${CLASSPATH}/com/novell/apparmor/${JAVA_CLASSNAME}.class DESTDIR=${TOP}/dist SO_VERS = 1 LIB = lib/ LIBDIR = /usr/${LIB} JAVA_CLASSNAME=JNIChangeHat TARGET=lib${JAVA_CLASSNAME} all: ${TARGET}.so clean: rm -f *.so *.so.${SO_VERS} ${JAVA_CLASSNAME}.java com_novell_apparmor_${JAVA_CLASSNAME}.h ${JAVA_CLASSNAME}.java com_novell_apparmor_${JAVA_CLASSNAME}.h: ${CLASSFILE} javah -jni -classpath ${CLASSPATH} com.novell.apparmor.${JAVA_CLASSNAME} ${TARGET}.so: ${JAVA_CLASSNAME}.c ${JAVA_CLASSNAME}.java com_novell_apparmor_${JAVA_CLASSNAME}.h gcc ${INCLUDES} ${CFLAGS} -shared -o ${TARGET}.so ${JAVA_CLASSNAME}.c -lapparmor install: ${TARGET}.so install -d $(DESTDIR)/${LIB} $(DESTDIR)${LIBDIR} mv -f $(TARGET).so $(TARGET)-$(VERSION)-$(RELEASE).so.$(SO_VERS) install -m 755 $(TARGET)-$(VERSION)-$(RELEASE).so.$(SO_VERS) ${DESTDIR}/${LIB} ln -sf /${LIB}/$(TARGET)-$(VERSION)-$(RELEASE).so.$(SO_VERS) ${DESTDIR}/${LIB}/$(TARGET).so.$(SO_VERS) ln -sf /${LIB}/$(TARGET).so.$(SO_VERS) ${DESTDIR}${LIBDIR}/$(TARGET).so apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/000077500000000000000000000000001522511161100224035ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/Makefile000066400000000000000000000022201522511161100240370ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 1999, 2004, 2005, 2006 NOVELL (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- NAME = tomcat_apparmor all: COMMONDIR=../../../common/ include $(COMMONDIR)/Make.rules LIB = lib CATALINA_HOME = /usr/share/tomcat55 all: ant -Dcatalina_home=${CATALINA_HOME} -Dtarget=1.5 jar jni_so clean: ant clean install: ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/Manifest000066400000000000000000000000331522511161100240700ustar00rootroot00000000000000Main-Class: ChangeHatValve apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/README.tomcat_apparmor000066400000000000000000000147621522511161100264640ustar00rootroot00000000000000# ------------------------------------------------------------------ # # Copyright (C) 2002-2006 Novell/SUSE # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # ------------------------------------------------------------------ ---------------------- 1. Overview 2. Requirements 3. Compiling the code 4. Installation 5. Generating a basic Tomcat profile 6. Implementation Notes 7. Profile Generation Tools and change_hat 8. Feedback/Resources ----------------------- 1. Overview -------- This package provides an implementation of a Tomcat 5.0 Valve that calls out to the change_hat(2) function provided by libapparmor to allow a process to change its security context. Any feedback is greatly appreciated. 2. Requirements ------------ AppArmor version 1.2 or later Tomcat version 5.0.X JDK 1.4.2 or later 3. Compiling the Code ----------------- From the top level directory execute: ant jar jni_so This will create a jar file and a shared library (for the JNI interface to the libapparmor library) and place the jar file under dist/ and the shared library under src/jni_src. 4. Installation ------------ - Copy the jar file to $TOMCAT_HOME/server/lib: [SLES10 example] cp dist/changeHatValve.jar /usr/share/tomcat5/server/lib - Copy the shared library to somehere in your library search path: cp dist/libJNIChangeHat.so /usr/lib [Note: you must ensure that the target directory is passed to tomcat via the java.library.path property. This can be accomplished by setting the JAVA_OPTS environment variable, export JAVA_OPTS=-Djava.library.path, or set via the env variable LD_LIBRARY_PATH to include this directory so that tomcat can find this library at startup] - Configure the Tomcat server to use ChangeHatValve: Place the configuration directive below in your server.xml file. The valve definition should be the initial configuration option declared in the top-level container in the container hierarchy. [Note: The mediationType attribute may be set to ServletPath or URI depending on the granularity of containers that you wish to create. URI will prompt the user to create containers for every URI it processes. This is not recommended for most deployment scenarios and so the default "ServletPath" should be used. This maps to containers identified by the ServletPath header defined in the HttpRequest.] - Defining a default and required hat for the tomcat profile Edit the file /etc/apparmor/logprof.conf and add the following line to the section [required_hats]: ^.+/catalina.sh$ = DEFAULT Edit the file /etc/apparmor/logprof.conf and add the following line to the section [default_hat]: ^.+/catalina.sh$ = DEFAULT 5. Generating a basic Tomcat profile ------------------------------- Once the installation steps above have been started you are ready to begin creating a profile for your application. The profile creation tool genprof will guide you through generating a profile and its support for change_hat will prompt you create discrete hats as requested by the changeHatValve during tomcat execution. 1. Create a basic profile for the tomcat server. - Run the command "genprof PATH_TO_CATALINA.SH" - In a separate window start tomcat and then stop tomcat - In the genprof window press "S" to scan for events - Answer the questions about the initial profile for tomcat 2. Extending the profile to include containers for your web-app - Stop the tomcat server - Deploy your WAR file or equivalent files under the container. - execute "genprof PATH_TO_CATALINA.SH" - In a separate window start tomcat and then exercise your web application - In the genprof window press "S" to scan for events During the prompting you will be asked questions similar to: ----------------------------------------------- Profile: /usr/share/tomcat5/bin/catalina.sh Default Hat: DEFAULT Requested Hat: /servlet/CookieExample (A)dd Requested Hat / (U)se Default Hat / (D)eny / Abo(r)t / (F)inish ------------------------------------------------ This example shows the tomcat valve for changehat attempting to change to the hat "/servlet/CookieExample". You can choose to create this hat, and subsequently fill it with resources, use the Default hat, named "DEFAULT" and is the default hat for request processing. 6. Implementation Notes -------------------- - Selecting the hat during request processing This implementation follows the following pattern to decide what hat to execute in for an incoming request: Try #1: Request data (URI or ServletInfo) if #1 fails (because the hat does not exist) then try #2 Try #2: DEFAULT - this is the default hat for request processing if #2 fails (because of any error) then.. Error: report that change_hat calls failed and remain in current security context. - Java 1.4.2 Notes This library uses java.security.SecureRandom to generate random numbers used as cookies in the change_hat(2) interface. This class on java version 1.4.2 uses /dev/random which is a blocking call and can adversely effect performance. Java can be configured to use /dev/urandom instead. For details: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4705093 7. Profile Tools and change_hat ---------------------------- When using the profile generation tool, genprof, you will be prompted to add a new hat when you exercise your program and requests are processed by the changeHatValve. You can choose to Add the hat or use the Default hat. If you choose to add the requested hat: genprof will create the hat and then all subsequent resource requests will be mediated in this hew hat (or security context). If you choose to use the default hat: genprof will mediate all resource requests in the default hat for the duration of processing this request. When the request processing is complete the valve will change_hat back to the parent context. 8. Feedback/Resources ----------------- Project webpage: https://apparmor.net/ To provide feedback or ask questions please contact the apparmor@lists.ubuntu.com mail list. This is the development list for the AppArmor team. apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/build.xml000066400000000000000000000070171522511161100242310ustar00rootroot00000000000000 apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/000077500000000000000000000000001522511161100231725ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/com/000077500000000000000000000000001522511161100237505ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/com/novell/000077500000000000000000000000001522511161100252475ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/com/novell/apparmor/000077500000000000000000000000001522511161100270705ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/com/novell/apparmor/JNIChangeHat.java000066400000000000000000000020101522511161100321070ustar00rootroot00000000000000/* ------------------------------------------------------------------ * * Copyright (C) 2002-2005 Novell/SUSE * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * ------------------------------------------------------------------ */ package com.novell.apparmor; import java.nio.*; /** * * JNI interface to AppArmor change_hat(2) call * **/ public class JNIChangeHat { public static int EPERM = 1; public static int ENOMEM = 12; public static int EACCES = 13; public static int EFAULT = 14; // Native 'c' function delcaration public native int changehat_in(String subdomain, int magic_token); // Native 'c' function delcaration public native int changehat_out(int magic_token); static { // The runtime system executes a class's static initializer // when it loads the class. System.loadLibrary("JNIChangeHat"); } } apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/com/novell/apparmor/catalina/000077500000000000000000000000001522511161100306445ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/com/novell/apparmor/catalina/valves/000077500000000000000000000000001522511161100321445ustar00rootroot00000000000000ChangeHatValve.java000066400000000000000000000145371522511161100355620ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/com/novell/apparmor/catalina/valves/* ------------------------------------------------------------------ * * Copyright (C) 2002-2007 Novell/SUSE * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * ------------------------------------------------------------------ */ package com.novell.apparmor.catalina.valves; import com.novell.apparmor.JNIChangeHat; import java.io.IOException; import javax.servlet.ServletException; import org.apache.catalina.Container; import org.apache.catalina.valves.ValveBase; import java.security.SecureRandom; public final class ChangeHatValve extends ValveBase { // JNI interface class for AppArmor change_hat private static JNIChangeHat changehat_wrapper = new JNIChangeHat(); private static SecureRandom randomNumberGenerator = null; private static String DEFAULT_HAT = "DEFAULT"; private static int SERVLET_PATH_MEDIATION = 0; private static int URI_MEDIATION = 1; private int mediationType = ChangeHatValve.SERVLET_PATH_MEDIATION; /* * * Property setter called during the parsing of the server.xml. * If the mediationType is an attribute of the * Valve definition for * com.novell.apparmor.catalina.valves.ChangeHatValve * then this setter will be called to set the value for this property. * * @param type URI|ServletPath * * Controls what granularity of security confinement when used with * AppArmor change_hat(2). Either based upon getServletPath() * or getRequestURI() called against on the request. * */ public void setMediationType( String type ) { if ( type.equalsIgnoreCase("URI") ) { this.mediationType = ChangeHatValve.URI_MEDIATION; } else if ( type.equalsIgnoreCase("servletPath") ) { this.mediationType = ChangeHatValve.SERVLET_PATH_MEDIATION; } } /* * * Return an int value representing the currently configured * mediationType for this instance. * */ public int getMediationType() { return this.mediationType; } /* * * Return an instance of SecureRandom creating one if necessary * */ SecureRandom getRndGen() { if ( ChangeHatValve.randomNumberGenerator == null) { ChangeHatValve.randomNumberGenerator = new java.security.SecureRandom(); } return ChangeHatValve.randomNumberGenerator; } /* * * Call to return a random cookie from the SecureRandom PRNG * */ int getCookie() { SecureRandom rnd = getRndGen(); if ( rnd == null ) { this.getContainer().getLogger().error( "[APPARMOR] can't initialize SecureRandom for cookie" + " generation for change_hat() call."); return 0; } return rnd.nextInt(); } /* * * Call out to AppArmor change_hat(2) to change the security * context for the processing of the request by subsequent valves. * Returns to the current security context when processing is complete. * The security context that is chosen is govern by the * mediationType property - which can be set in the * server.xml file. * * @param request Request being processed * @param response Response being processed * @param context The valve context used to invoke the next valve * in the current processing pipeline * * @exception IOException if an input/output error has occurred * @exception ServletException if a servlet error has occurred * */ public void invoke( org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response ) throws IOException, ServletException { Container container = this.getContainer(); int cookie, result; boolean inSubHat = false; container.getLogger().debug(this.getClass().toString() + "[APPARMOR] Request received [" + request.getInfo() + "]"); String hatname = ChangeHatValve.DEFAULT_HAT;; if ( getMediationType() == ChangeHatValve.SERVLET_PATH_MEDIATION ) { hatname = request.getServletPath(); } else if ( getMediationType() == ChangeHatValve.URI_MEDIATION ) { hatname = request.getRequestURI(); } /* * Select the AppArmor container for this request: * * 1. try hat name from either URI or ServletPath * (based on configuration) * * 2. try hat name of the defined DEFAULT_HAT * * 3. run in the current AppArmor context */ cookie = getCookie(); if ( hatname == null || "".equals(hatname) ) { hatname = ChangeHatValve.DEFAULT_HAT; } container.getLogger().debug("[APPARMOR] ChangeHat to [" + hatname + "] cookie [" + cookie + "]"); result = changehat_wrapper.changehat_in(hatname, cookie); if ( result == JNIChangeHat.EPERM ) { container.getLogger().error("[APPARMOR] change_hat valve " + "configured but Tomcat process is not confined by an " + "AppArmor profile."); getNext().invoke(request, response); } else { if ( result == JNIChangeHat.EACCES ) { changehat_wrapper.changehat_out(cookie); result = changehat_wrapper.changehat_in(ChangeHatValve.DEFAULT_HAT, cookie); if ( result != 0 ) { changehat_wrapper.changehat_out(cookie); container.getLogger().error("[APPARMOR] ChangeHat to [" + hatname + "] failed. Running in parent context."); } else { inSubHat = true; } } else if ( result != 0 ) { changehat_wrapper.changehat_out(cookie); container.getLogger().error("[APPARMOR] ChangeHat to [" + hatname + "] failed. Running in parent context."); } else { inSubHat = true; } getNext().invoke(request, response); if ( inSubHat ) changehat_wrapper.changehat_out(cookie); } } } apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/jni_src/000077500000000000000000000000001522511161100246215ustar00rootroot00000000000000apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/jni_src/JNIChangeHat.c000066400000000000000000000027031522511161100271520ustar00rootroot00000000000000/* ------------------------------------------------------------------ Copyright (C) 2002-2005 Novell/SUSE This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License published by the Free Software Foundation. ------------------------------------------------------------------ */ #include "jni.h" #include #include #include "com_novell_apparmor_JNIChangeHat.h" /* c intermediate lib call for Java -> JNI -> c library execution of the change_hat call */ JNIEXPORT jint Java_com_novell_apparmor_JNIChangeHat_changehat_1in (JNIEnv *env, jobject obj, jstring hatnameUTF, jint token) { int len; jint result = 0; if ( hatnameUTF == NULL ) { return ( EINVAL ); } len = (*env)->GetStringLength(env, hatnameUTF); if ( len > 0 ) { if ( len > 128 ) { len = 128; } char hatname[128]; (*env)->GetStringUTFRegion(env, hatnameUTF, 0, len, hatname); result = (jint) change_hat(hatname, (unsigned int) token); if ( result ) { return errno; } else { return result; } } return (jint) result; } JNIEXPORT jint JNICALL Java_com_novell_apparmor_JNIChangeHat_changehat_1out (JNIEnv *env, jobject obj, jint token) { jint result = 0; result = (jint) change_hat(NULL, (unsigned int) token); if ( result ) { return errno; } else { return result; } } apparmor-5.0.2/changehat/tomcat_apparmor/tomcat_5_5/src/jni_src/Makefile000066400000000000000000000026371522511161100262710ustar00rootroot00000000000000TOP = ../.. CLASSPATH = ${TOP}/build LIB = lib/ LIBDIR = /usr/${LIB} INCLUDE = ${LIBDIR}/jvm/java/include CFLAGS = -g -O2 -Wall -Wstrict-prototypes -Wl,-soname,$@.${SO_VERS} -pipe -fpic -D_REENTRANT INCLUDES = -I$(INCLUDE) -I$(INCLUDE)/linux -I$(TOP)/../../../libraries/libapparmor/src/ CLASSFILE = ${CLASSPATH}/com/novell/apparmor/${JAVA_CLASSNAME}.class DESTDIR = ${TOP}/dist SO_VERS = 1 JAVA_CLASSNAME = JNIChangeHat TARGET = lib${JAVA_CLASSNAME} all: ${TARGET}.so clean: rm -f *.so *.so.${SO_VERS} ${JAVA_CLASSNAME}.java com_novell_apparmor_${JAVA_CLASSNAME}.h ${JAVA_CLASSNAME}.java com_novell_apparmor_${JAVA_CLASSNAME}.h: ${CLASSFILE} javah -jni -classpath ${CLASSPATH} com.novell.apparmor.${JAVA_CLASSNAME} ${TARGET}.so: ${JAVA_CLASSNAME}.c ${JAVA_CLASSNAME}.java com_novell_apparmor_${JAVA_CLASSNAME}.h gcc ${INCLUDES} ${CFLAGS} -shared -o ${TARGET}.so ${JAVA_CLASSNAME}.c -L$(TOP)/../../../libraries/libapparmor/src/.libs -lapparmor install: ${TARGET}.so install -d $(DESTDIR)/${LIB} $(DESTDIR)${LIBDIR} mv -f $(TARGET).so $(TARGET)-$(VERSION)-$(RELEASE).so.$(SO_VERS) install -m 755 $(TARGET)-$(VERSION)-$(RELEASE).so.$(SO_VERS) ${DESTDIR}/${LIB} ln -sf /${LIB}/$(TARGET)-$(VERSION)-$(RELEASE).so.$(SO_VERS) ${DESTDIR}/${LIB}/$(TARGET).so.$(SO_VERS) ln -sf /${LIB}/$(TARGET).so.$(SO_VERS) ${DESTDIR}${LIBDIR}/$(TARGET).so apparmor-5.0.2/common/000077500000000000000000000000001522511161100146225ustar00rootroot00000000000000apparmor-5.0.2/common/Make-po.rules000066400000000000000000000035631522511161100171760ustar00rootroot00000000000000# ------------------------------------------------------------------ # # Copyright (c) 1999-2008 NOVELL (All rights reserved) # Copyright 2009-2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # ------------------------------------------------------------------ # # The including makefile needs to define LANG, which lists the lang # files to include; e.g. LANG=en_US de_DE, where en_US.po and de_DE.po # exist LOCALEDIR=/usr/share/locale XGETTEXT_ARGS=--copyright-holder="Canonical Ltd" --msgid-bugs-address=apparmor@lists.ubuntu.com -d ${NAME} # When making the .pot file, it's expected that the parent Makefile will # pass in the list of sources in the SOURCES variable PARENT_SOURCES=$(foreach source, ${SOURCES}, ../${source}) # Can override by passing LANGS=whatever here LANGS?=$(patsubst %.po, %, $(wildcard *.po)) TARGET_MOS=$(foreach lang, $(filter-out $(DISABLED_LANGS),$(LANGS)), ${lang}.mo) .PHONY: all all: ${TARGET_MOS} ${NAME}.pot: ${PARENT_SOURCES} xgettext ${XGETTEXT_ARGS} ${PARENT_SOURCES} -o $@ %.mo: %.po msgfmt -c -o $@ $< .PHONY: install install: ${TARGET_MOS} mkdir -p $(DESTDIR)/${LOCALEDIR} for lang in ${LANGS} ; do \ mkdir -p ${DESTDIR}/${LOCALEDIR}/$${lang}/LC_MESSAGES ; \ install -m 644 $${lang}.mo ${DESTDIR}/${LOCALEDIR}/$${lang}/LC_MESSAGES/${NAME}.mo ; \ done .PHONY: clean clean: rm -f *.mo Make.rules apparmor-5.0.2/common/Make.rules000066400000000000000000000112201522511161100165470ustar00rootroot00000000000000# ------------------------------------------------------------------ # # Copyright (C) 2002-2005 Novell/SUSE # Copyright (C) 2010-2015 Canonical, Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ------------------------------------------------------------------ # Make.rules - common make targets and variables for building AppArmor # # NOTES: # Before including this file in your Makefile, you should # - define COMMONDIR (the location of the common/ directory) # - define the default rule (usually 'all:'). (Note: you can redefine # it later in your Makefile) .PHONY: common_Make.rules_is_a_bad_target common_Make.rules_is_a_bad_target: @echo "*** default target in common/Make.rules hit - either you did something strange, or something is broken... ***" exit 1 DISTRIBUTION=AppArmor VERSION=$(shell cat $(COMMONDIR)/Version) # Convenience functions pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(PATH))))) map = $(foreach a,$(2),$(call $(1),$(a))) AWK?=$(or $(shell which awk),$(error awk utility required for build but not available)) define nl endef REPO_VERSION_CMD=[ -x /usr/bin/git ] && /usr/bin/git describe --tags --long --abbrev=16 --match 'v*' 2> /dev/null || $(AWK) '{ print $2 }' common/.stamp_rev ifndef PYTHON_VERSIONS PYTHON_VERSIONS = $(call map, pathsearch, python3) endif ifndef PYTHON PYTHON = $(firstword ${PYTHON_VERSIONS}) endif #Helper function to be used with $(call pyalldo, run_test_with_all.py) pyalldo=set -e; $(foreach py, $(PYTHON_VERSIONS), $(py) $(1);) # Common set of compiler warnings _EXTRA_WARNINGS = -Wall -Wsign-compare -Wmissing-field-initializers -Wformat -Wformat-security -Wunused-parameter -Wimplicit-fallthrough EXTRA_WARNINGS := $(shell for warning in ${_EXTRA_WARNINGS} ; do \ if ${CC} $${warning} -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then \ echo "$${warning}"; \ else \ echo "***" >&2 ; \ echo "WARNING: unable to use $${warning} with ${CC}, dropping" >&2 ; \ echo "***" >&2 ; \ fi ; \ done) .PHONY: version .SILENT: version version: echo $(VERSION) .PHONY: repo_version .SILENT: repo_version repo_version: echo $(shell $(value REPO_VERSION_CMD)) .PHONY: pod_clean ifndef VERBOSE .SILENT: pod_clean endif pod_clean: -rm -f ${MANPAGES} *.[0-9].gz ${HTMLMANPAGES} pod2htm*.tmp # ===================== # manpages # ===================== POD2MAN = /usr/bin/pod2man POD2HTML = /usr/bin/pod2html MANDIR = /usr/share/man DOCDIR = /usr/share/doc/${NAME}-${VERSION} # get list of directory numbers based on definition of MANPAGES variable MANDIRS=$(sort $(foreach dir, 1 2 3 4 5 6 7 8, $(patsubst %.${dir}, ${dir}, $(filter %.${dir}, ${MANPAGES})))) HTMLMANPAGES=$(foreach manpage, ${MANPAGES}, ${manpage}.html) .PHONY: install_manpages install_manpages: $(MANPAGES) $(foreach dir, ${MANDIRS}, \ install -d ${DESTDIR}/${MANDIR}/man${dir} ; \ install -m 644 $(filter %.${dir}, ${MANPAGES}) ${DESTDIR}/${MANDIR}/man${dir}; \ ) MAN_RELEASE="AppArmor ${VERSION}" %.1 %.2 %.3 %.4 %.5 %.6 %.7 %.8: %.pod $(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --stderr --section=$(subst .,,$(suffix $@)) > $@ %.1.html: %.pod $(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@ %.2.html: %.pod $(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@ %.3.html: %.pod $(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@ %.4.html: %.pod $(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@ %.5.html: %.pod $(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@ %.6.html: %.pod $(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@ %.7.html: %.pod $(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@ %.8.html: %.pod $(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@ A2PS_ARGS=-Ec -g --line-numbers=1 ENSCRIPT_ARGS=-C -2jGr -f Courier6 -E %.c.ps: %.c #a2ps ${A2PS_ARGS} $< -o $@ enscript ${ENSCRIPT_ARGS} -o $@ $< %.pm.ps: %.pm enscript ${ENSCRIPT_ARGS} -o $@ $< .PHONY: check_pod_files check_pod_files: LANG=C podchecker -warning -warning *.pod apparmor-5.0.2/common/Version000066400000000000000000000000061522511161100161660ustar00rootroot000000000000005.0.2 apparmor-5.0.2/common/apparmor.css000066400000000000000000000017541522511161100171640ustar00rootroot00000000000000BODY {background:rgb(000,000,000); color:rgb(225,225,225); margin-left: 1%; margin-right: 5%} H1 {color:rgb(240,240,240); font-size:115%;} H2 {color:rgb(240,240,240); font-size:109%;} H3 {color:rgb(240,240,240); font-size:104%;} TD.sidebar {width:18em; background:rgb(020,020,020); vertical-align:top;} TD.main {width:250em; background:rgb(020,020,020); padding-top:5px; padding-bottom:5px; padding-left:10px; padding-right:10px; } TD.sidebarhead {background:rgb(038,038,038);} TD.footer {background:rgb(020,020,020); padding:5px; } TD.block {background: #9c9c9c; color:rgb(000,000,000)} P {font-size:102%} P {margin-left:.5em; margin-right:.5em} P {color:rgb(225,225,225)} P.sidebar {font-size:98% } P.sidebarhead {font-size:98%; font-weight:bold; } UL {font-size:102%} UL {margin-left:.5em; margin-right:.5em} UL {color:rgb(225,225,225)} IMG {border:none} :link, :visited, :active { text-decoration:underline; } :link { color: white } :visited { color: rgb(225,225,225)} :active { color: gray } apparmor-5.0.2/common/list_af_names.sh000077500000000000000000000010721522511161100177650ustar00rootroot00000000000000#!/bin/bash -e # ===================== # generate list of network protocols based on # sys/socket.h for use in multiple locations in # the source tree # ===================== # It doesn't make sense for AppArmor to mediate PF_UNIX, filter it out. Search # for "PF_" constants since that is what is required in bits/socket.h, but # rewrite as "AF_". echo "#include " | \ cpp -dM | \ LC_ALL=C sed -n \ -e '/PF_UNIX/d' \ -e 's/PF_LOCAL/PF_UNIX/' \ -e 's/^#define[ \t]\+PF_\([A-Z0-9_]\+\)[ \t]\+\([0-9]\+\).*$/AF_\1 \2,/p' | \ sort -n -k2 apparmor-5.0.2/common/list_capabilities.sh000077500000000000000000000006211522511161100206440ustar00rootroot00000000000000#!/bin/bash -e # ===================== # generate list of capabilities based on # /usr/include/linux/capabilities.h for use in multiple locations in # the source tree # ===================== echo "#include " | \ cpp -dM | \ LC_ALL=C sed -n \ -e '/CAP_EMPTY_SET/d' \ -e 's/^\#define[ \t]\+CAP_\([A-Z0-9_]\+\)[ \t]\+\([0-9xa-f]\+\)\(.*\)$/CAP_\1/p' | \ LC_ALL=C sort apparmor-5.0.2/documentation/000077500000000000000000000000001522511161100162035ustar00rootroot00000000000000apparmor-5.0.2/documentation/AppArmor_Developer_1-Kernel_Notes.odt000066400000000000000000001421531522511161100252550ustar00rootroot00000000000000PKԳC^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKԳCg7˽ Thumbnails/thumbnail.pngPNG  IHDRg? IDATx{Pesᦀefe],.*ReHZ^[6fPP-+mj6qТđa99Gh&ߠ#ό}>{|UUUK}ҐH b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FRkꤪΤ>cr kc-g<`ȭ^K={M_Ok,5OSxJc kp0a'?)^"5qRI*6cQ$ϪG\v#S ;ehĴQ9=6h}7G>fsJnve'BW,|nEșB7<.JtZ:~d?3{.׷eW[7 5yon`qHu%_.L*K}Urx53z9 Vg2H9£CHTlFn<;t~/v9ᑩԎl骻x>cZT~-vl*wzŘ'Mш ~:}:]ۈ?[8f!/k#9V,Lm'$!EWcOmt5/Papֲtm%jal2e ўwԶORwJ_p6w}V|h-'.9ZvLzzK%iKz[_j0m/vg2G׏|dWe2o`5l'Fmkr᧙ewƴ2fyp:0c[Y^WlN[tn Nm9h`TUT[MF~((*-; xĥOjU+\ĦЄIUg/| oytËdSђ;{u[9F6:w߽4}ImSW1{>gSܔo}*{Xv5ao̝mv]N(r6}51o;y[ilYl?l0*lތmۙPΜAWĦqIPU|mK{Ќܽ\vO#)H b$1XsIcN'eL^Lעn5]M8?WeO80a`>)Ь4l,?e8}G”-߬^xtq:h'Fݷo}xYzV,e?M[oէ05#o[k}Wev0\w#xEi>Iy}փ'AW"/_]㫯^oEY_ ~&/WW8|I ݕLg/_ԟ~Ik'I<c|6[wzk/v1> @>dy;ꫵ]|ZWW4]t{wQ|߱'IU֛xIOyqcOc[ 7 7u<M;@3[ѴAGor;?—f] ́j룏O뿏Hmt]{qql3w9SVe@v |$ޕ|1r5E?N?Dz 57ͯoBg~li{=׶G6k 6o'{6?hpbWRw3o~ԦW6Km~>μgE FC -ڽ3ugn_J:w߈g*bBv6OuSΥ3CTĬ3y؏][eBW=/%hML3]Js)Y+\{*jK"=?Saî;螟omRV_J7p잟Kڣv_ޤ{:Kp63vߌGՑm{^htqFbL_L7s!Tt|$g*R~Lx3v)?Hs|P ?I 泍]mf~H%Kn4? ᵏQ ˤgp6~FuOR/N)Ʉ$[gRsT y'u?>Ov>dg?y>'U8g*<OVitRɚUnTJ8hL]h,H>hMu*#?UJ|h}fGњ*$՛gᴞ3?3U֔P)H3{DSEgZh:ph*GkȏQ }{0>0L*c}4LAPrJGk}f'~*Іmv(.r*djT9ZSH5>{Vʼn;$*GkHt#7^rL+C??SevSCȠR=L5T8nCA3UwVrzU2g$ͤ5=pZZ'QwZ+H }ZSP}{'iWfUxZX]:h>k=G,>*f4x-Z~zx4|ZceЪW G:WU WW/Oy*WFJ=WUDsnim!O^ژ4 wx\:oVn|K~5n}FϦQFe owjWwSqz a'O7Gtr-Uo| aIqM/]~8{uFz+`.޹ab_kL+^t…נ/6pvۅ|ݣ|rۏ~}Ov.{t~o{\q'6=T#з"H?΂=dBw߽z20%z(J2Btqhl{} Gџr~O֫dCg2imhf9ɸUGv߮j;x2k:stVmR]G/It?{ZMe"._M*J4[7nȃv}ô쵅GĻy\Fw;}WORwz-Gk>7H n +7BX}S{λ 8qN'H]++v%ؗGK\v#QŸ_ +/w}Yy2"9WUyb_XDTBsI|DT'΅K]NEy\V ũGT5rOX琙9M||zuuWI-~/8%c0y\D .@8%Xfy D >`qA5c@qW97N >k̆ 0&Dѿ܆Gѿ̬Gѿ܆VGѿ̆Gѿ܆Gѿ̬Gѿ܆VGѿ̆D?j"szpһ|VCo\5cӿX1aLVbŘÉ1ۇc o%V>ӿhi{Z1^ӿe0+bLĘÊ1[c+oNB=ܘa{qrw̗i-w|1_zw̗i-w|1_LZ73ĉ1ۇc o%V>}X1aVbŘ-D073ĉ1ۇc o%V>}X1aVbŘ-D073ĉ1ۇc o%V>}X1aVbŘ-D0G73ĉ1ۇc o%V>}X1aVbŘ-D073ĉ1ۇc o%V>}X1aVbŘ-D0'73ĉ1ۇc o%V>}X1aVbŘ-Dʳ0]cĘÊ1[c+oN>e0+b"y1 qbaŘ-Ç1[c'oV2|X1 <ǘ81bØĊ1ۇc+o>JHyϘ81bØĊ1ۇc+o>JHyߘ81bØĊ1ۇc+o>JHy81bØĊ1ۇc+o>JHyИ81bØĊ1ۇc+o>JHyȘ81bØĊ1ۇc+o>JHyؘ81bØĊ1ۇc+o>JHyĘ81bØĊ1ۇc+o>JHykc?C}X1aVbŘÉ1ۇc o%VB<61!N>e0+bĘÊ1[c+o!Rߘ'oV2|X1pbaŘ-Ç1[c)ߘ'oV2|X1pbaŘ-Ç1[c)ߘ'oV2|X1pbaŘ-Ç1[c)m1mۦ} <0wW ?r׋`#C܋GxY}WQ;'.jϢeݸw/cإK>To=|#3?^e˥<@k6Gƾ{oQ^]= dgS> Ң'R"oQF_J˷; _"uz㫫lx O[R{fn8oI=Gj"Qľ~}܈74fHErI>ɕ~8C1al\<)6B*]#Z)8Σؿ?.*`I`/bdk7v˯5_JsX Fq? 6q,Xp)-y26x9'pώ*04ܬ5ׄ|/iK_Ms_-ΕhI *"K<[&{L&IӱBr7it vM2qؾV 1G̃//ó{l844dnOssiAzO^]y <}t~ /(0 p8|Kyzyg}}Wk/N\afv#eƭ,p\mXwE>%:#9\7Ma:Fɏ9fYRljM2 _7w;pvT!̄m'yiw'\W4_yI$klB?&"7ϟ-M^mI nEʯr L.v1q޻ӥ[g[$ e7?l:z8zf~A6m0#Hz}5ŏCi~rO멯S?ؔnTIpIᯘ|HV47%j [q x $ia6 Rmꯀ~S{wr1^omE앻 ZR/q?s˷B]+ ?(\$ n>a% A/n PHt]⬜Y5 byu;[z~x@ n^V}h^?*u zYz@/ց>1@e9k;c9Ċ~3~NJ㠒~muI(uֵ>!*`_?u~b> 9ݮSv?I4Mc#Qy't_9 ed;cO]mf\N#+8X)qS9O{?ɶ'#9 뫯zy=lܶ8u/fdx~{pc>v_{\D5g=عf:wK.wWg9Ľפm_<}Ei@¦ķq'|s9~܇/ W~#5#=zw2=UYkFBײַsj߯ h,8}]5l®"\}8օ؁nڄ(,휟|v#<!Mc]oB^|S6]qӹbҌ?tM>ɚQGpM^߲C:T37?~ߌ?bC3u iDӉ i@3(vqjN܈d ed lqu'ӊ֑˧}umƚw;xz9CYM>Nt9C‰,9Jk`D {،HTNFIG?>瘃fD$}yK,']= ?h۩f,u|! hأR ٳ2w%O +cx=Jc7ŹcV3:f3s)kb+ zp $: 1a{4Ȼ+ho86?YTp8 9<" 2g7]$t=7t?7:qL,[?$\ g:h3l?cp:j3|G?Zo7Y KymA}5HLϣn<߬=r3tQ-?% ?>Wn*oc /:6qXk:U]VEO| 3$)Z4<`3M9TVZ2}2cEFd } Jv3]ꯑ{wRB\Q^E*ZCE@muFGO,t_)[z֧e?;8N2{ΰ괷I)|}7QK1~tÍ,"W3`~FaL/~qpT&;36xG10lf9Ђ7!=7Z Kb=72qU Śe4u}/2Gm\1;L K:7k DZH+_*ʭDeğm }h G"}en%FV1}/ VmE lō).!4Ai>~rk%Ҿ$-0a1kV (p)pF֠ܥSoCx*Z`BtM U)Fۉ>O =3xp];/j4n/7|IwJIG R{͝ H6=2|/:Gr_%'R<6F;F_; _sJJ8gkFr\G uHJ,wQDȇzge3D)j9/QgaOa|̏Sś&{Vʂ)[&c8OReZ+4H`=AoMOb"jz&:Jk7]I6%jL@>5o4uI"U:Fh#e>7I>^'jHK3p廰G_>3o`)DTD!){9#t@8L;ߟIL#wDH|.6Zxޑ/QIzp{H"vxg߁V0*HEbK.Wŝ@&'m's 7d٥t.$B[LS(Yp( of2ɭ(3cqwЛ(TU)S_'3$;"6%2oAG=sѽ A]'ߠ;CEFΧ> M[I 41!.B.-':hS5^~Ġ!,`p&{7h>^ p>O|MP9P i0vVL`ucX #톨_ ~w}R{/-1GK?G@'\w  %*|n,L-F"A Qpx݅%- G fhoGȂQ <zfπǥ̪Qä~|G.nbdxb΍R9G$iūoְﮈF! ~#4Z!{Q |w?- .G|鿸(X_^N" Z`YI#xl{ @?#^ZHyKKV5B>0y]HlHOJN:DP=R7Bn 2qu{ρΜD7ƺ~%]8<VK=j?,7 +c9X=o\R} |DǤ'L5UY6<&d|Iu{.[kـ-b%Ⱂxxt (^?ySq4uyc>vI+d^|&/y^("/F33i#@:Y3<7pFٺIlKLz^+&+sfXB;èvC+\3/. ,UR8_$~2a9f@qL՚-93 TKw>a @ \c ]T}P7:YʇPF2utg, {NU7T}s}^ e.ۏ^<ˆa<[ g!| < R?I$TD /YLD|"_/۱c&@#%(ho VJ`”0=$7^ i h%? /)M3PK jWp)?w`[PjP윮.3zKK)zĒ2|d*[  㓢.fh%or>&e|6ᒸ\H"࣏&# M,BTA41ztz^&p?ZlN[븖GZVO_؊Dmc V>Zc$,nϖraƬJ*$LHJ/1%F:%#F+Fb?On/}.iJ5NTz42'ωq3LeP`*8 ڼ2;_ix}(XmY\|- aJ3j5YTa7!.hD>R"LJ bSd2B>wu/ׅEyMA Kq(-ekCA}x8=(1`W; RfPjd+iHt6Na2RMly9ˉ}*Gaj->Rp(EZGIqˬ,MVL6IUE^T6&\t=#uՉcz9;ݑwn,]zC"NاK@d8zʁU!tylh6!yY$!eaE%xʡO:G+:Q%D?z=K(>zsLܣ?VOǿf[[/;L /390%*@w-FB#BnY^؈Fh~,)qAf[?s +yJ(zƔytcFAtvV@$ 9.GGQ*;Gt?o>T4&?a< 6{LT1 EAЋ0[(99Mgq4y ajdžbr ljsu[ؽuxۛMbzHŚ\/""|?c]%q &:!`%*)"2ۆLFj/hѬT!=&fdo{wFrl"ڀ}G ml $Kx LȕT ߈(s?MD^' {H`iz PFO|w'IW( T/]QDyp"̌#\TmRWCKr%MU3Ӌ8R%|1LX LJMXPֻCfK63Js8LLq?YM&7 DG@{&sgOh4LѠ7*k*gŘq 笃H}5{^O^4L-ků}t5,d(u-(Vb>Mw7Qqr1p>̌fV151: z!fv|jiP'Ue ]:8u]Ps2w((hp*ETElNM2_kܥ 8uɷ6Dg'F'Hs0rSPߣD{$IV1t&7[wO1C#A(fA kqunfT,|L~ް9|hK_RuPK^z'r׫D+І( 1LFhC OQ=/ y) क,-u3H;wU#ynǵ\XpЏZԳ ^DWS,.ҡIB2},\j?|oQ3؃ZchP U =| ݼ":4$8BAPQl=UѺ9Y~ kkMgРX9# GJ&ΧK:H3gqMw.FO.#G7d"kYa $X )CѴOVH '$~+F}}}FhbY}-S_||9GT.儌 Á(U@e3pVNY5Fz̃O*vJ; c7 \\aX#(qpѠx .DN.¨i/cMg`_GI5#zVah&KqxgD3 Wn] O!{2M*nqW%F'TaIt>B,cݑQB㔽O,Gg']K53-ϤrLAxH5 (ol7:՞(iIehsBMJRj8pȂl꜊T?R 2EJƏEοLIYrf"m(TTMj URg7@5?ľh߻z8Dj#wRB%/s(|ɲ[;7e nmd1"n\GLVsHP:LDr o351U@tM0eHE3ڻBcP==Y@l"݄c6B ~ڲLq:QOnғ{S^CP`C)$u|h(7Opᑪ&1F4˜˝p av EY"VeGRAIuP}A>E"3ċu\/8u)me?ߚ׉7ÿ}k#E&b M6z Rc̜{8&8[Fh}Bڷio"hJgA/rօ*䍧4"lwq'{+%*er{GNM\WՋݰ[b0{rEqhVq$d/4}$a(صV?؟t)OA(~$B4'vHٝA"xAzK!K,$J\#vƢ*$Gu"ԹӤy7 %d^ c.̻dGYNjۘIpF,‹xd+JkH0Q ?9j3e5]K4g7~+g GQouoƣo{J(H]Y y.8}t/ W cd6|7zPЄÚ.*}yUʼnSeFi3ˀ|â/&K$r.q$Gkr*t ʥM<`3Y{nGDA)~6Cho]]b4Au4QRyCT=p0Fr7҄`O`]EUфmQ yRS4Sǃ[E!)Vr/_'ުB/Գ ;o=R>pHz+_ڔ4 {՛Σ%]h!{ӁgxMsw( C*H'ٱu;ڔ5pZ"EPKg ufJūW-'gTAC8ЋNcٚ^Zʲ?vՐ_N\2=/R'_ru+1vRl0u 'R^i *))-\t7έ/nM> @>hO[g%,{wg5ة_Ac"1PTg}2uצlD Loљ v69uO$[W%||a'߉ '>@,N&<@iU[.tmɚ+$ ʲ/w"4wI  -壊Ho=r?TQ#R\Glg鿮u"΀ ;FqCGH-9;lZO/=Iвh;`F17I< jO~ܡPrӛVaʻ?/wozHymߓﯵc1@=eƴI枈58H7BV`E#"<J `_ b:.x6h*VhZ 2l B+FkͱVܢ.DdRH$U×ZS R|pjhZ$r(E ݉kZ%_ )1ie2C,YC:NߞL`F7e8K^s%)I^i5ɗ\ςx R^u1C\81 SAHf=l([/S+mQAѴ^i|ȫC Z|&r,;M#>p(҇ubCnkw]W_Z.SsgݷD-Ԍl@Caя4vUN]U/a^3vŃn3WzDR.֎*Ig эd:޶?/= $ ;, (WΧp ΃h7UnMPe*^z[A-3pTS:Is =Ϊ,s-/CIl Ah;d`s]7G!I~F$'b{QOFZ (XgcGi,`]c.Hޗ|qN=2"nDZ~ 1v_}fu65^g${3T$Wfh9MA1~1&ᜠ3i:F=@{uz4YJQ(ҳ-j_c?[IDyR4xXIDaM^)3E0i]S$~fGo? F# e-1+09#8 +Yѐ;B#4ߥGZΙLx%x3Pqy70e\ޜd_}g!ᓟaaq#r[L5S%S-_7ӛLlNi!Hr莯t{xQA?яY&θ;5{-UV-s9$:Oq I< _A>'Mˌޓr3p3UJUjf@h yZ{Fܿ. lIRL5 6r"Kr)GX:n.BM(́'BA(z(7 YUֈUk*7urs|xRS 핒]LԯM*׫*[+-/& 1Y.sxP>Aa6,fw*,z|s@)yif՟K;8oN$8LQYnQ<+ccyyp,Ky FC8i vu͓jEznCܾL.pn* )`ǯ#9{\o$Yʓ G>#qKBTh{D4Tx%RF"!ܣ/{5`P UoRU{dIhC[TR˄3N*ڽ%)jLo Lh%,`[F,7݃&Hu A{b͸ǸyR윉c?BhB_||6O?ު3`XklOK?64#5 8#iJb<7L+Β M^ /S)HjHYB>[fdsDcW+d˒Kh'4_v?ђp)KxÔ&7$|Ubڏ'!LH9[;|fQYDu,%Dc fqRKbXFZQḌ><8dEhDO_S!̠x~_DϢXH%@Qkw `rO=zWD gnKlq;Zm~zgDN6Rיd S2@ ꏡ zt+pPD>v=wlͯ+;emu*W2PH")Q.H1* ϫ[׀Ų9[-+cdY޽.@e 7C9$lH,ghJ&ՁVh &TʅS:7OuaKREa)i%.S<_N}׉`Rb#RI䙆]K1q+~"\ }C*-_ϗ`H5mИ|cE8*YA ) QU*g@+ŗL^q(e4_,TN,I)S+x8fD`yA*zPuڶ&Ct|T_dv6HYtM}W/p͇#e>Q.6/i6pKJ|`[ZVvX#(BqfD9LR  d8B){Ga2҉os:1*\C#6JܦL$(1u/Q^U@:UYj8m6`4sP]Y}P*-<"QJe&lE}'/bo4á4'rrȠP`Rix\FhoC/g|.`#;z/vd];= w/Ϥt&"4d $ x#I~tw`Я2E?Ct?@$%FƍíNFrvE% myWrH+CmR vሎ#މW }![ȟ ؊dWW8-p~K$kO/D6Է *eVN9?l’֧pI/hZ|P ͯ9J^X;vw/G)hL pB$P>mJTIo'ي`k(0;s컡IlbC~rJ &Fm$PWƞFG)r]QlU3Ǒri2^ḟwv }7Gqorp_]ޚ wpfr ^\0#'qxK## JQsOp&X_\j.Yڙs]`;FC G_^2AY O 6h5`o t ESnb *oie [#q<+C#6>&0}MUnd7\^tZa=꽟f 3jLN_lbByD2jוuV[/(Z`D! @"7CN'@~QHOE#"£k!)VYJS?Ǩqa,b+7&>p]DrApgT>RL֐779r?W熱k-je6I$d{J9W_½My8סU ;;NrjUwVNZ-CJmo#V4K^ߒ:AwQG+mquD;$bwwd2G%2D,Mt`:$41ͅ^"QY+y2sٕLc[ҟ_gC=p(k{h%)=t,1@R3Kt7ݧ|8j)Wp$LYi)@DL5 PFU-7HO,HŔ߇ZQ viYHܠՎl\ { RJ.=Yn@24%oUrr)5)p\2GȦtF t:.;>w/8(ge<7HMżcNb2 7b,eH=ZӚl$ @O’uv2(qG9A"BS@M;27cBZ1Vr&zQ(jO!ʲòe*AaK44`jI=Jl G8qB/-E_]\eamUUf#,\@PIlz+] ,[t!roPqcv?r"|_+&\e*,vV6 1Vk[s>ϼJF<692N&^XF#Xn9eY@>KùQm8qBtօ#J;R"ӀE 0&$,Y)\Z0&Wt}(PLXp p #QhqkoInAGݯW8^NWݣYSD|/}5ox?4]!Mi7/g.$ond,2!(њLԡ/{Օ۲oOn!)iVKS_kjʃ^(3@C|Z@L}93mTF<%Nr5>kde ʳ8çfR[m+Bhu28{gaFKZb>Rr*歠eUݼrb&7?igMѼp}[%Sذ^LGq RA.ZLVwEn7M^T:y/wBy;0kX@~np[b]Ru6Wla<#8ǥ2jQQJ9XA̯Z,;$`U UգMZh䢦Vsh4&2=զC37FgVz̎k[k:ݡRO3OI;pNoRzG+Mk6ewlL_e[PP$\R,罈+]SHaDZaW (/oF+hBq2r= 49!GŌ簴\gw/瞐n"6 zQڅfA/7%̕BXc- bU*P \2}Lh7AqfZK-xyOE^m=՜=y@s˗N𣣏z`=7jdbKѤV:){sizlds(vLT(8md 6Vn/2=:drЀ Ii[\P7JMMxGg6A`|@|@&RBsgu_ld)n$ZeV\{&z H}Rgsrm5!YA=x9hJ6lV9 B%ņ.`SG'dH]]# e'SŖ\͋|q=Skuz40u\]K/8%?+ "nc2"QvJ ^kͣijviN%{m=]|U{gUT<7 r#h6d@{{UC[h*3!uP\F~P7G}(GáE7(~o}.e$[Zi@.:,3RW5)٬qWS8rdC*h_p 悋_гaz3'j4xJqR! ZڈOǟ78u5*ϒ(8QOf *1]:*U}1Ve d}uǟTT<"mF1fJV^{PfiUc.p^?zQĝD F&E\o@$\^\1K "rtU M4[3!O% l||s"E ()GDc,y].L<0jѺ}V!eOsT3RE?&r{$Z8q.Ed[qjn^|UC1b]{A2sc671RI6̠Ԙ76t+ge{.cCR]1I?׹7b~ jED>_5N!S+א[Ƣ\mt1g"NJ'gsJ+jWR&_ܗ0)哄＀S!J҃WIm幜kنgc5D [`-To'e容4^3o0uB'S`^W#Fpo-*-]fIQb/ON4g`c%s!wq6V.H Bdjzz"HM)5 a ؊ޯfۃOF]۵SfUl1XFls39.\ :CjB5> #U#Ry4:X§r)형Ja!p1JNޱʫM(U>fQIXKx-E* }' M@^7Tm]syDOT)r(]џži"?Y\zVWx@e+V} z,'Dqjp|Ν˭ވ :dB;T %:)1>>т$!G צT/'2ow:⥚0"*B:ҿ @L@{( ތj%B=xqrE:}Ԏ |W0!fR\K0}Hw7A/ - +椁@>n\m_^/bRkN%:YhE䶖o{ {wѠ\z;դ_$eKR }[!ZI&vcxO({XɯnB=\WABC$yc uy P,5Iv r[״x zq` zx w ғDՈ@ޣ_0aqetaZV(8gDpJ)H&p,·\\@ wWYKr$@X_={R PQWBS]T|/ lΩj#~J ڮB~g>/ zSdtJ+y4a HVW@s537?d>ʸDs$o VFohVK{Hoy!%[.`=fEg-ݿaC]e".<ruK0T8*qh!Za1zXݎ?t U1束@};=C22f6wW3f1Vј>k1ae8!RB`Rq

/FJ8:`o AJ, υ 8 crjkHcX] 9)F\W<ED9jP7$洠iKyliM}bP?R%+5,FX%VjwGKk6F/ oT3cUҁ )%vBIYɕ"4QBsh?zE]Mg(u²dBBe F)fEFZ"!ChgWJ7BKe:TK=nSoH=ـ~ݢͪ$ rIV;a.^%d W+{:k8ʞTlxg@Ŝ#!X͡U3[p^ep*%=: QOL|k=Oga^s)GzASlc4qv9cOȿOn2QR)Jh8rF?do>_1ofc[!&5TQnD6u K{q"d>~X-ݝ= 0Ӽ c>CnЬbqP֚+̹+б^q0.vt gG/SDtO!kd2T]5~_W!鈀}H!; >gQ ؟YlR]J =q|_gtY@1Ћy9yaie%$:;з^ A4Cs4J}pkR[xmxJ ~ٿr߭_8LLԊo_*DTxag˛db)#0Q ȹ;Io0L,]\/+D+^y'\BGJ[<%lG7љ}*xn@5;:bâk}9m ' tw.9`^cg0*8W-Ξ~aTXb]nypsً~frDq `aEεJRN$Ok@gkbƀ]'Rh@D2( ϐTOe$Dy-sk?(tSJ.{0?O1"­끩H:5.)5uMww&8b_ȮtAm<~h#YT 0Er9-Sٙ6y'0`eMbHZ -Jü3eS*?Ͻ۵O+t~>dd_ .Crtd#ƃ Os7L 3^>?%]q;Gh2ލO9N=dCN!~DxãU}Py6u}G]<`~n븾㣯o|ȶί4ԌM2&C3O^pڒ]N?Ah+o<.G\]l(C9wR{B41!&/ ixk.@.goI>Z@QrFzg& νKu~m)3EXGrBcq „QM|̊UrUɍipUT'Q4aeMǃs[ꔬ$xՊ( x b} էx6] -$!m,EnTD?Ƴi_PnSs6a)A|{5[mw-UcF{VmTK`۽ň1Y2o +XJ¯ P[uJ#w5@ӻdo*ARýR?Z?Ӹ$|6#cm! 8]lru6f>+l]Ȉyy Fk[ņQ<ޢb^m9ۊ*]Zn9x{& Mu-w鉺gK=_J\R-`ɑnCLl,D<#ċY#}_-zW=GZ3: կo:Xz<}?=i.y^@~PKEw1PKԳC settings.xmlZs8"{GhZ$CB˕H37a/Cz$9+h3liWO* UTP)]1<\~.}Їf~З %ꂶ L_ߖ)TEob bvu2Kx(q\^.WPFQoK}pv,t[Vcd6jJ/]lF5V[n6 ҟPCdtsylv[";{ZI`cK7zbVM{0ՙ+} =$\}4N# <صǂGq993P{&(i@1]ђTQ2&2 8TV6>N`J(VܔثkOJZ '.Ja-[-?Q>(I֘6$}.-ټP/kgV Ft$=(poLM^@ܚ>H$ӔKP(z:|G@mg4xYm$e"w,ADGs1pr&D?"Dp'!ڑlҧjY(M Z]ȲpEL2{ɖ6ٳP-`HJ:1E6Oўp{}5~gbPMI΍f`H4 ObiA2O=`{w`6cOQ$ /_ AD:pO ʆAZ 0! d%||w&,A3PimnW:_Ɓ1ƱIx&ĺowL(wjܘfZX;osPpXSb5H5t aiWT#(͂&ÑimCll]c93Eܾ\ aR4wK3'\51_!@DlJ n7g tmSp"H䀄pPGz+LN"@˦FQԪÙP3ocf"t (JBiSڰfAɕb &p~ċc&:ˍh|sɃdGb)΀M>pRU~S!D:q5pH#b@9̑׻bմF|'LQ')K+Hө7S VcbL[M극꽖+AjEJo ~`gEBIzb. MIYr8VPhcbP}gXN?tZ@K SxO{mpv+ [7u弯 PKB(PKԳCmeta.xmlK0Ɲ,ɖ[8(2]Pd֑,_:^iV[⺔w@?or,I( N2ϵNE5keK;ZNu#B4&\Kkm(}}hSAe Z tCQ8paj`בBv!BNLY62"EJZj`V>* LB.3u36pwјPtiJAv8Qɢ($ 6ȶLDr cL>ۜuZEg}+/B ÓTy#M V1N]'lި[vSv뻇4ЉUD}nfEE429.$RM)YG­0K$hrmøse49z/PK5NB:PKԳC-Pictures/20000906000053DA000060418B63A95C.svm] tUŹDB #HH HDVP++DiUZ\jmW[Vk-VZW{}%*9gCf>Ny|?~5%OB9ÄK?t! {q-Jĭ;vΝJbuc&f[̙Q?kYsdi3=֣YFz/5IG I5|W!߂D֓ BEo=S%/mu0+?<.S|$yMi{8m! 9YѦH@V SL~dM 3p)Ңj!fx)ҥ  LdKA!" ;x)һA]x)!Y%Sgl>ni#kxEWM | {dc|}uQHY}itT}@vp)A6 d1E)*@V _Vc$l0++uTO~üZ964))DRk3J--"P -{xgZOnF`xOB!bq8j)RY _7W{󡐒 Itw[5o1Ez>eft+^xi5^9WM7@ H õ)3AV}dC[@v(Ⱦ6EZ8d󀕦H׀d"Nᰅ"=dgl|SO2} a ;x)k@v?~ )ASGat~o"}dM7ȦE!K@6{"+ȊA1p`RUo_H Wj,RU{c#rox/lx JTȒ]J4z%-+/{V~=gd=0{VYg55kzј*kO;)vn_:u۰e&=CV%\ Q_D+Q[(,2^9\ VU_D+VU[(fjz@kjJDr1hVV[(VW #Я+ B>"U^ x ׅa_Uj9YLWb"ZbEaUu x@k& nK]]Ä*LWbZ}hXQh?x#tiW,`)xR:杪VuK{6(d*UъU+ +X+GZk=eCñOb%VV_D+Z-VZ+DFub4c Hj@Suuaتť=Ճl2]hŪjUV,#4Tny xŠ"ZZmZ]!:,0#ЄXpfCB8K}A&_d3\.%.Æ{.m~Id*ъ+ ë+p+GZ{KσlÓtU.ъj aKkrD?;>m|_፣x$2]AkEbEBt wFѲ R W%Nw\k22lx'^Xޮ\ W_D+W[( hmy?IUwDr1hVV[(VW[Z#McmnNAB|PDr1hVV[(VWM!]A ) }d\וA.sFza W+$2]hjV#=A*zkH"UV_D+Z-VZ+-v;M&נg88 Y/LPұߒɶl]-!=xAR<qQNDRԳDWȪ b%=${3K0Q$&Xl/u5-p-k% dy%Bx='LC {x)@d#"&0Ez . eHٙ x)-H$8{ ch g;2DmȎj"=nu=}]od< H \npY%p)ҳ@vniEՁl'p)Ҳp gt sH?َa$d_3E d6E:A6)ҕ d?5E] C_ft:Ȗ2"S?K@&p)AԌO>L^8RF|"X"< >] {d0EZ0ZL!\WN9/*;؈i $zVD} !n?qlΚhY)5謍Y)5謍Y)5謍Y)5謍Y)5謍Y:i~]_m_$~5ؤ[^&ꅰo$) 8jHUgd3E_'{XG4~ѯUѯ#_%1RUѯ#_%1RUѯ#_%1RUѯ#_%Y8vu<};`f}B٨+i➡\6/mxlH{,Y%?2yނT ';*ST~z,Bd_$,/ և)ẑ~)&*SYDvG{5"DVIY\/ɞhgA}M  B=Zxr}f<˳*/#s'x':ӏhdW:I6(sdGH3 (gc$;AR.2H'-HF?iI$^IƻB aL?+<R@!^-J"dn^¢<ޮflWA8Ğ-v(bybo|TOSXZ8ZX-7m>19ɽK(j9P>݅xd ¯qYٳe*̞={֞Yl>l2ƥ}9u}/!1tOQU޲U|zQ/ų' /8X${`]>L!7g^épͽ;}O >G[dX|鏒4o-m J$]$.٘^IdkM>0I:$ &I?s5V6vfL!GnnN!#⓳6$>\ 7]-mߴG.훮v!v$/MھuRa؊aj_SKCr,*Խ !M)KXU7RmoTxXv 4_ Z/FmxxMvjr"il S.Xi8wx"Tk$>W~Wȏ*#koc}#bE #q5GL Y狋SM|@7ZAT ;f!-(6n+㓟ިvyTs ZTE6wD]+xv,M.6@bcćKd9b!UPkhcϾybsBˁۀKol~ 8 } <c5:X+^% cE\iW]$y)sd;ˊdWI9n*r]$T$I3E_-|H sW"*H{dl%R%RJKܷ~["D ΠCL8Ǎ8Ḓv8l@98/D99o~p^/3yge|gj){hBۅSn|)n} 2"]F$ @r]ES1NVkd5YEݯVӿV;V1V5Vy|ZُE_Kje?yu :tqXH+y9lv9B8OF9ο^ٵp h-wt^T[_y=P!U.hXQ]®=Y m\2.>D{ɮK'q&D7.q\;BуebB];ͮɗ͋ }osW3WȹǺ߰0tp#tU./-VNCWxyNc!fosr2"VdAD0 9u9%p!r[\SjWWI~ey'Id*ъ+ +W9kfi]ipHvm&<Dk(d*њ+*#Dt@۶iNb4^tg9r,Br1*-:Y#D7{W I=u)bhw&Z#,e")޽r=[DZtU./-VfOWm.c93R |)Lsh'hdpM%3U׎mkFޫ/e-i%)D$R?YC}ZۡU]^9<;D;ٖQ#ĸPƭjzJvJ*C_rġFLvjR3j4Pi׈7He7Bl8DbW#i_;.FF!=gԈ#^7NdR#UeB3TbW#WNz&;QYqxBb5RY k.>S諑c(mٺH`LrH.H<!>/Χ7Q yT։;/Xbj|H2*6 lŨ Hqʀѷr;c_[WȫIe]0B?tirVyVL~jWR3Ma)buRY/&qUuw3٩]JB{X'^%I.u%ٯ,;D&RYUB,#1QZsT} gSZqz_TJgCbTMUru,?W8Sp'=Ũ(ĸ"5!2B.*C_r]j_;.FF!ݐEp_ b{Р\Lh-/EGУ+)~ s8_-8_}LV-M)ތ<rK{195"`"fcCeU;%&+Gwlw-[`BPKgő"كPKԳC styles.xml\m6~BpqM$/ h$.}-hȢ@k;HQ$kכM-Ɯp /_̻njߍq0pӄۻїxE7UBaA眯4ng"N*G;W"^^t6bv{ |C;KZ_>bv{' v}Cv>P? ☑(XM&a|)Nr9QTpl=WOp`|Éa'y]n`h@gVq&Nni2ܼC"b+_`cIT1#ijn?Ԫ*;UFAp3ѿC/(-ttS;%&  fxt"^e%u˒ԙN {jˆTE1yCL$ 0IT֦ 0 X*JHTUMB-96p>w"i͐}Ykl(=c?q_y6{Tn'LiY8 IN.u9Rd=\S')$OH0Bvt%5zT< }B9QX*dK~s84;8 "bHz} Dn&[9 q5rxtﮂ:MBٮׯdv5}Sj1z ˴=ލ/*]m~((&퓗b/XB-!oxQa8 eU70SITѯ0iSWw$RЎV5$WU8ñ0=!d`|+k` ܐ!Fuިprc  6geZVuVl$xYw2]Ȗ"%bf_zCR Fކ ʧPiN}|X"Ot%; ŰS7*`}/BQ6Xo s "q2$1HG{A `^`YQV X3`G"4ERM{|e7ȋX֢rw(it<\n-l6{\WW=mc3oiBe(pbN }HR] tf)eP#YYa1 [j(8Dhېh)iʴ?>*@k:$|F"Ź % tQbbІY¼.i\w2USЬح'4nT/QKǮa>\mwFgLŞ +DI7HOhH C gcY] U:EU' @lJVR[:rb8=Od猗v185B.=;iDhɊkFFa205 H^ط\U.8`MdX,NCԽ o-?w#M4F^a7a3Rz:z_oH6RTFjZ{mا2K4LUw]gktnWeZ7gnUQe-~] 5V,pvl0{x͢J8BFQ!ySv8i1Aaíٱ[k>j1~*khM:˟ݤ=_gOYg x½xp)D6e' %W\COf~6[,& o jwoߎ[Ns{ŦiFI uQjS$]˸c8W󫴖~z>N}a{eKMNqJ!Up3vu4 ~:M na{k!3sz\ҴZ*QjCjHbϯyFJ4ɼ!̴8#0V<3lջ-!ƹz|F/P,(S5žSq⩰ sS4ù)*-JvKkȾ|O֏5HٵJ.\|'-h d؂os2Ml74 > XFB(B9ܶU{zϲ&=j1^7ߴc|O_ƳgϞy;x0x0^t`hx߾0/1^>Ƌq؊p؆o._|0xa G G GW\'T`.ݗ -/>7 a9W=Qxh:rgI'7Gzg!Γ.IFDҠm Lzƺ;o)T0HӖ4L}O/:5%4MzL`^`ntv]8jaj)Jy՚a9R:'|ʈ43e!"Zyn-`usNқ{dHl Vb9J/ Ap`PthaBA+O'JsB{ngץRQu֎-o&:\PNYv[vQJj顫Nl4Ow#PKn, [ DPKԳC manifest.rdf͓n0`{q6euGDŽތV9IjC7v;n*D-?PKYXPKԳC^2 ''mimetypePKԳCg7˽ MThumbnails/thumbnail.pngPKԳC8 @ layout-cachePKԳCEw1  content.xmlPKԳCB( ߃settings.xmlPKԳC5NB:3meta.xmlPKԳCgő"ك-^Pictures/20000906000053DA000060418B63A95C.svmPKԳCn, [ D ˮstyles.xmlPKԳCh ^manifest.rdfPKԳCConfigurations2/statusbar/PKԳC'ջConfigurations2/accelerator/current.xmlPKԳC,Configurations2/floater/PKԳCbConfigurations2/images/Bitmaps/PKԳCConfigurations2/toolbar/PKԳCռConfigurations2/menubar/PKԳC Configurations2/toolpanel/PKԳCCConfigurations2/progressbar/PKԳC}Configurations2/popupmenu/PKԳCYXMETA-INF/manifest.xmlPKPapparmor-5.0.2/documentation/AppArmor_Developer_2-policy_layout_and_encoding.odt000066400000000000000000004350211522511161100302510ustar00rootroot00000000000000PKiGV^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKiGVConfigurations2/floater/PKiGVConfigurations2/menubar/PKiGVConfigurations2/popupmenu/PKiGVConfigurations2/toolbar/PKiGV'Configurations2/accelerator/current.xmlPKPKiGVConfigurations2/statusbar/PKiGVConfigurations2/images/Bitmaps/PKiGVConfigurations2/toolpanel/PKiGVConfigurations2/progressbar/PKiGV manifest.rdfj@>Ųu^DC\'j5}7RPqf=y;9È!9ԝls:&[ɞ5jr1FeMNw!i(aIXE46#yȮ^`49E3Cp*-F8Pa(!t aa*u?;u ;Hbm*|9`;?%ROOv:Z2Bi$xWJ:;<•ye:_oZ%Ͼe~PKh&PKiGVmeta.xmlSM0W W$ajVj*9%6͒d7+xf<?I+چI @(T6]%k֞rI8ReIe8ѹu]X 1FORtI1akJK= K#j \c5gUӚqD-I{s+YlTBRc`wiw~b8,X*$#fuڔ_Q*+TnrF~D0ALv׼32 P5LI  e` 7]tD9Ux;K?g,1yŹL0|#Jo F|[ǫ=H՞>O ¾1``x5d|xrCZ'YqGnۆpV O> N3 '`429Ν 5؋fnzvQ P9B0e#PK(n'8PKiGV settings.xml[QSH~_a"rJ[Pkݾ IsNS3=p]I%U]ݓLO_w_8x9ʋRQd!Ӌè}xZr9N&UO˛r݅~TL#,SpZT >evZ9Շ3 $G֌)7ƈ,]n::pCȓ>aBo-0b!!, 4Vgr;w7֡oeU?W _nr5 8~܉)ݭd7߼vz|c@F$Pe%&"^I/v&ނ{.mT^=6=肇WAvC~G0=J,S,3.˼W}%)Bz!QVwB6҃X*oi87`ϼv'} o!! _],x*LdTrܹMA hyPCJ4.<3`XI=`OB~@A~L1Cu-ᖇL1zݛLlpQs_0E-z?qaZpF\ic/q,vų;6'$ƀjM#*EW9+%MHG/?p/s*Z@ؼiw/t3_M _Pӓb@ X e!%|Q@8(/Ȍ}|ۇ9!Ԭ̇[`DV@[0m<廆  o1P|D贀xpDm9x<|=A #P (s #;DlMt3_ܹbPmL##?!3`5}hA Ӕ0aE ޹ .&رC+Y[ |ԕH 8ZO1٭ × A[#`.Sf.vycfw H$JЭUVE")I>j;F9bF .Qm568AD2%Zײ'Jݕ R+DZo6RG}cDP_iBlϑ0qp4o@M ^c>d?W7^}8* "Qk'M.}de aAs[6(nV9;99\BkuUK=҅gҬhl#mk:8SSI~44uEb\UT!OF0[y*_Iƿh镒mJ}7wH6Ǝ0iZT[#&rbmdBOrTGTn0RT&`~"ܬ&i3e;Ds>L%/ÿx3Y&`o2+G5цOvuL5`M=y34B)nhOzpdB:4u;|C)ЊKE(Eɳ"GFXlf fշ] ϻ ; A[PÎ>02 VO1dGDӼ21;53:6:>95;;<*7D7AE@=K@6LB|Att V{H w3Cc`ޏnw>MV6FV^n#aG?0 (?y\>^P? 'P?{7{f'?{fgGt_ |::鋷Gڿ,L_?g+a0.KyƄv<3io>cgij?'禧ug)0"';v~A?d2~OYXv=cM]; Lƿ=zw?._oǓo_S]\p=C_G*~:PgxFxV gyy37 i "1d?W7pb?? }M!P ??6W?gL *U]qFdwϸY +xܷ&W|,BkdKeXAhR} lSh[d8㉵8J#_[K$hۋ9ȝt qXA^K<"(@FOCO߶lnOooۍ~7ɥcT8C B;e2&ёH UTÂX_H+0\ UjEFR#>VxpTVB8T. |>~rpF1R Aw؁<b{ kQg-CuI y 1#v2ǀ~зOUǞq4փr{YvKuϢ cJ{=7J}0UZК1@;ۙs] ڣA]=j+b,}6bopbNWM@e_uG|*"q|Ə[u'u@4#,Ώ:! 1S3GSAXcTw QovfT_vUj&o/o !ۍOL">lluwl[H~ ZKu:P>3S {WaW3,w;r'5qaվgbAPkoR]{0L,s$f P/" ULW9XHv JZH=dxCV}i"a'kϩ|#3y{G,}ZvX{%r'V,[ݎ TWWܿ` vvu\[9ڕS\[1C@a3n&._mꍮpq'zv^6bucqfz71Q0ʎZ1;*F,"8[`~Q0^'I&/IF1pKfD?J8aX`!E}O$Jӏ><V$2AZ+'7%( ;(Pu7̳5 0'L/81! I#(.>snA _ $+i$7@C,^4BJd '9 xqAz^o32+HHU݊ a{BhH>C|Q  >6HnSPr%Մy䊔#/n:zj=IvR* }B{0@y!7cLsrc (2]IfJHMn<JO+ShnYw:gǪSsYw4Mn}/VMhWô! = i3!$﯀hԴ/TC؋ꋭ 2#U5da1\S1[EpoX:Dhx4@G!,[<{C{?qXt`_ܔɃP^j4\BeUxoD]>y{26,-ѯ\=W &|Iڄb^ )Y[SvSs|"ҵOڡ>&j}09d>iv2RZ}=z+0BtoYt B@BAnJ>#,y1B=@xa>91#f\hww|AHe- 3~{6[iR/ g!BYnYPReM7Ms5s&"Z,(˅NfQHТJu$$+⽟'Y0bK2 D>3bvRTtVnǺj [y=`b> ,|@Yxie(2W# B~Xd&=J31N>d QR2ʢğTNJ{1AEv_W hQK#IpV.gTF ^JCY/At\DY:6PS^ (wHePԢP1FrY q(u~8p PQLrON;ڃ3m%grH/lPXYi|RK!C M_mkծwoՃ;5V,TeC ,;D׿by{[Ar*~wb&B ?\pwM1x_e4)t<D7}m:TU4;+f5 ^({>ai~wdٵ5PiNͻ>`E~)]Z3duM^*g9աYõZ@u?̳T?\zx~`d`mfc5$^o~bE̱*opOm(v.qGqЍwdr ;I*90SN%`dޥ[Gz&I<9\!M&o4i_^oOr__aaS!,gn?mD[a3k?cpK2[ ULd/} D1_I<c$p X0*?jqZyMEC? 5j^'E>dDR'(f9;{=aSY}ņ&\+dX?ȝʇ􎙴ԂxO67V&SP7. .܋(%[|"z&W o\vB։| ?ڟ>kxy龫a =milqU5닍sŞVV BBU;4f,G9b;Æ;]Ȱj"y+?_GcVpF͝Mln'&;hg:K*vy*\uwt}KO8Uז|8MN&iy&x(>6 x+-a 0D| tu8|O?~#;H jtG7M8  \3A, h++PEyQLdcN8H6ʃ8̈́ cT㧐(Og!aؘȄDG kD@'ǃ@&8R5q!KTQU RJ yQL.@6*d0NV%3"ڸ"~yN)4sK`h` 4J3 s厎]9LhbSZP)n|b>Gi7UWד>#,WNξHJ25獺Q ֓%pacڪ3gl* ot8o0}9?X_PsUlGs@qF@PB+࿎߶9:sS7;89[%?3 g:-}ld/8b-14g11< 3oQ$iXU)$RmUK*1=Qoz!re88*%KSfKY!~є9+aٻZ,SZw@zO?Y'5줪6?M~֒ڨ1M;Eβ@ْKG>X앓Am4ڈ%džuRNpZDQN35Ɲ4BRƝ2\)6t d$O,<Ԋ\on=g搼ф8K0.F6ɯ19lsPĚ5O!JM؀\ ( ք&& .&TTh)PN3,,b`Xi1ǵrP4a{Zs2zW4uwoƊP!u:&o6.M>7z;Ql6@<ak<ϽKɨny]peBQAO*X~<[ k~>{ľ˾kz|G}Nڣ}:E;_ ?M_o¹,nk缾yvgYiԲ/;mŷbh_SRkl ~n&}n͒+yܮaZTgwt&~^Sb<^%~׏,AxNbV| %YPR|2TLy' ~7&6)\|i=O!1jF0!:EqFBd"zrS"NuRxizzёGD8JB1<|M J)AQcO&:h7Dh p0OɞBR+D27'}La/9 OM%6L _KgH~tL"<ťt %}-\?Er#=͑>! wr-x24ZNlYu(4EdL 5c9tWZ銸uȾ_z `#mgyo?~mm=,?臮_ߖCmB&ȋL_h MN_~н?;8CG*'{u'e?zV|mYxF3C^ ﳝYd_X( eC[K3}{5SwW-~Tc(Xg\pVl+oT_>i(eFLd'bFd0@./A>f8TḶ[^7p)#tdߘE_m#Bv? tnAW>c1|e֒oJ:Zu4Co7,DpXw@`G37B [RUx𓑷no|36/s8Ԍ&5{g-&9Y|:oO_gψdqَ:f:L*Pngw|`#xwp1ʣ{oGߚ'Y~a hb*,/ػ¹7pc!Jw>h{pъ 6QK.&LS6]r^u)"+E>|%QdhʕxkK%:Om5k͠Yxb+:(\(Ҳ{!sjGg@3ak'熌P7Xp֎ke4rOqGfӃ2pc=nh9LK:NoŔ%;AܸTR7RCDWGBU/vM>91} +b  KY<{l.Z }ؗ>8@b`-k6Mk@KדaK,k8-Ę"1!`:#1m8OKiL\  @Ik/#|.]ߦL;yJcuDw҈&W@_Ieb3'M]-%,2XdMPOH.ApCN/S<9 4{/BNҐ|1t R4ߟNTUiH4jr~ijR#E'd3 zң[͍>⹺:!zJw&7 ,ca ԐPu|4΀yc3{͗ۢ`n`si@6>:b6HW?VΫa}9c}l{bjiѝUKu Ўd29iJohHQ.}l,}QH|K6LN=fOFL+XA7t5ZMWbPC+'e {#-/HLa)7p :2_Npų$-ke[ø\">y.O^Om->C$@轵5$OB$*n@[$!*%\lĩDp -+!;C ں&6IDZ $DL:%ri֚MCY'yL2u^i佽,:U8tXH Aix$[>}G8#:Gip:5$HGI JarDdDT9buS],vg^wg d-%4e2?k#ՙXƬ.۸QL ;, <`/vw ߼{,T>0u3`}6al&DmؼЁ+`]j腖LCJS{\@]|R@Ͽhж ZY߫5Xt[9fω?"\aD_UOw81!<_;>#rx{UQeGa9s-ΝbK,;kmz&|f=GwX:#-^)Hx_*냼>'#WgQ;WY'_8еUs][;pv좵lBPEzhh!}_qch'pG& &܀VKkCa^FW<Ɲ˚XWaH2ݾXPuWU]͡U*33{Q}`.ެ:mK_vޫgͻ$Dzzλ >K v>=9>8[[Cڝjjlvռni*@d'DyB~d_^_T:nw!hb |$@{''.XBes|0'&]L5HD&0yS)^N',FƼ<yRMJ1>|Ҍ)[_0Q`@!WKw+8ct,7u+#`ڸq`?`dpwVC +_Xmvǚh::Z#գ*c ԝ :KWk|%^21Ǿ`ՠxhR(a 6 < Lq${"I"<蜜lBOLI yX~dKBy\y@qgmS8@/Aty72?YpbpYeA@+c0bꀨ#,<;JA TVbmމG\&cum8F|PV%]{9 +@l&r_o3@PB+qOhd&8xɹ4Tt$$ZP:BVOmtɋ5e >p߶GA*FlX6Xs]~èQ_Kn|.>'s@{n&$%vqFxzB@ .} uq aLg~WCuo@!:?n{ۮyOqmHzTьYa @70 n$d}3xկFԐEXHk٭ZT\_/ո#p-E{}%/V I55qŽUoXgiɉ>|Zy2ϯ ]/~x<ڥQ㿻?h{mxL1U3oLgj wafm/f]%jeqD0<^CgEa?3|8'l:aNZ&Fc[ teY?[?SW{>d% BwS7e]\&g4_lKeRd!1>c`0mckQ(¬]A?<],{=~Gn P|!,h8^M -*&/NC,H%`DrKMeM $P%.&]'1qڠha_r(k!aWgFw* -e3DrQ?,MrPFKzQ(>&S&R *^,3enBZ25L_:&gSgeʲvEPNL2#nBƓlbo=g3/5,ؙsF`cCl( iPw ZCx\sC?Sj_joV}`  yeLF{BBзR,u 1957D Fb>uܖl2idR&]x3>@B‘Pio[}t5sڣEz?<|8')m|O@Y n)vhXK=8/$wpRbLN`Ϗ)!з+\rR[J:LyC*i<ĆSƅw`^~vSm[,NT'n~zY_^e-8}p8L[[0Y'CB]ᤌ`;1eddD*CBx"S@z#c\zc G"!~뺀WJlId<lgb{0lO,j)\66$tY1uԅ=@X5!kĖWɄ"j'8\??yUs,YE$k2)J#ba6R9C_jNd]wARL $T~.7eěTӷ6y,G2BkaےnLv6kw5`iyj|Fe@1/{bXb> ZY$F"!Id d?$\,<9 'i#yz\1\̢)n񷙞z,$5S)nH|oD}G5w"NQI13QQ* =ajIT [6(Boh*@Pz|`o)C=1z055;/y{{{GD73ɊOF31{Mr8/8gQY_֣'wHNsM WuipU7 xeIԂUgy!G47Uk:Wv^kJ/-? nJç-gLVMU\`|GDLWst{n$Kܠp"%!=/%)7>,E:("7tɯ|qcay޿=6UnV#AmV]Ю*. v2#"t).„,HY#-\ "ibH=DbX\Ygk5$$k _G27Ef85$!BxڬDzM+4JruE #_͗Z0^A?/x%[_#4>2M/擮XJGxKwb9gI<}JGU_l3:ޢ]5#ԎX{G27{AGTz #_7D S;mo1=LWqFuX"Z ܇U![15{ƶ %d-"qNlj}Js|yHԁbZ }e~TsS;;k_vC1jGz3RtH+9 0_~BCʴZh榸QcUS~\WY~͎,B=c^[oreͫ?hhT6S:S2YbѝŢ"J\z SGԷ sTED%Dr4l2e:8IIMTjX!IH-D)#u[B*Nѝ}.h :o[HCŽD S|DsSt1Z#ʢDdFӁK8Waև|OʠH'}L*x MFSfM_\n}-6.NnJF %P+b˾_ScM )"Q!127&=/۽}Bn~xT?矢f`p)j_@A`+3l0K@\lG7|瞢An&sSjj/ v, 0@Gvbp Wm=ZwێU괎[E[ }Mժ[09w9:̳SٖfկcwyiaωޝSO孅M&6'_>渐]J^+'2gmoB/ f*<&rO$~bhjlsUuCv)hҋKyr(zhP>c vU@L~:tw++"yP,MIH1ݽRjR(w`qXId"j%kQs*[:YH1r 4 <榀0ZK?蘖8LkkĔ\4:1].*.Obo= &<"PR!Ϻ2yLZ/"3SF?IO28b-Lwf[ӗr4Ң=Zc4}$ ߋ}ңiuX.Nlr*Nu6߉'=Y>)  M01 h,d6PawqUd `DC^0hFS kϨE~Gs_[굚Z=y&l% o͚yO"pk_?)TahSsBW뎨';WM{RXߌgYjgk3K3{M[fH,ez4)bQ8n-Yӝ䯐t2a"KK@5Q*Sŧ'`ƜnشgG? A4Q'<7c^řXNr@ XXM yX/z8[!{BX߂)JPǃ̧bd &{,h<|4kcxXա+U `k! bk~[cVzcбhGN,C@:j^hvQ_ա&%\q3?*do8Ov+0TG$hpp%p_\d$b-#{`{!\8#W~;ibqAȑd4E=@ZL h5@{,-Ӂ3H!1:DzSHWO\/SJOy:Z<<1+L>$/e*:|@ZGO|yZXQ+} Hn]$ XH>@E_<}y*H|rxr%!* r6holULiMOnࣚoX1IY(TڡpOyʚW#ƫ:NdT/y&ߟP8yzm1ͣCG  ]Joh 7z*sxȰOKsέkKWu ܎ /S/B-] 7&;yd%;qS(It)<|RK.%!+eEY0THNY0Δ*rK•Ec|DsSd. De !G'~F{ @JJ@9SΈӗ *,0AA)6aڢC>-4ORFʧ?5}BYv "OQKw)BM&Bɯ⭧KGtXllE(a!*W<OAVG57u)3a>ְ:YߍG?#TN0)xOI[`<)2ai);(B'N| Pے먴{>uO^RMQ胋@߄ {Ƭ: a|U>Ac~#,34}|{P*H3 X,iа},? i*N!U(E_`J0RҗY7g6U6>YpVZ3iЧ(x6 ~5} SckNWSn v*?ZsFN@ LX0?_ W[Gh8k^8j ]>I湳bRA>LCIw|=>$+F>D Sg~Ѥd0m8} ,9pHuFaLM8 A u|P!zWyi s-WE<ѧjj-sS5GcF_S#SJexe,6>wvp ¾m6;O $ssP i90d>Lf%NgFmjL%>~ubS,v>y+/@)U .g$*AO聾(ɬ/\h<DG });+\YoyxM!~%!uC8#IM"8` %ŔxjHC $^8äh n73O9ۆ0p]dC)hs)QjJĄ| Hu}oMN$X Q2CbShIFO4r|vSsYTތ_9oU,QqEb!I~V\}r*qFs5F`=w@D&GSdg_0?uOgZ{ acjZuz> fܥ3x` 30M-$o_G?[&tHJo&D^`pD?4uz(n :tlҚS߭?.sSQ FzA0ti t |%Cy%,d''s~t HK}gAKBw |<">s3  _(Yմ&Ҭ]Xwf :g9O M0w^.S W6Džݭ/=-ȃ?歁uѹTk_?.[Ϊ /ukA>~28K1?# Z+7~8a@|ʒk5Ab,@ zs4wsgD/TnfEysBc wk`+;{h}3Zio-8~8S)*-88)ê@XUQCL{US:Va].$ zv*uaԓ<$܅RԣRֲR c t<;E=5>P-S"^RWOl$ iyv $hzt7۽+΁GwrSɞ`3 r7㋝ F{1iT<<3ԿFZ> NaCDF)\a+ |taRУQOB]=QYFFG Y{}=82:q?S$zIThF{d#X$ɎczD7L}4:L5.!94:hf$HM>nb IW!zB8 ef.?: mmMԍ rP2~tGqO LdNѲVPKLY*nsƗM᯶w ($,><^+g&NvxBrJe79 >_^xL{p2~ )ûhtvlOTHGՐ8{OUlk.!976},ֳU葌c*n;/wJ{m[ #x@߰Vkc+'/7-= P]y#=`'͙J3| xaeHS#])5\afܪUDu}'J{n[kuWa$gv$ˢ{h+[z-'.YwPz7z y?>Qc;t.wEӽXo'`Ǵ;TE[X^!L8h›Xܻl~"1R;ך T:{]dϨ.{AEŰ]y;jzl`qKXrJ ^o=|z젽ܐx-.7[ZJKz)^͑#qEH,8_NDֆ? وBH$Ţ͢ϔXU-s҈tAID$t<jo@tԊFCkHO@,A.%k3$Tׇ:Sr^_R,%&O[}EI0i`SqY%; 0+$yB(S[<(9pA(H_5݆/97ţ&ոr aPp6S SRiVGo ֟#7,#>y(+@z9={-/xU[nǭ; j # OF;FKͧfӯ犧u_@~|^ȋ㮚"W_F_ժ~&`-5@z%wvw>Xs;z?<:Xl;T/5Aa}p/y v ׸-@L׏d4~"L|s,xNӽ^j`5PþӐ/\ϿZzqϫm/L={LLWs~^jׂ~K~~pq9uxBɀ|%7P:˂{ q0CB s,hfEwu!۠K*{Z:bK?s+f}ҷ|?GZ?x}^bȋpwv d΍-kcpU}Ψ;wlfK;;FGwѲB=^xsޙ2=ڳjy`=^WtJ[}!tVX}d^ai$OE'GEs鐕c(}HAP㞦 Lϒ9%z&a8!Æ $]334:L'MB)DxX_ԙwnF8" :#yw93X4Q4P2vԓVkqӕLM"Yn08xc 'Dh]v4XuW<0xLc |r!#ЩXٰ޲cE1fpn` nOߥgc%KI:cn?)uR[g˹QEP=/DWe36T1 =IOPjyeh<:A9'X r'w?X67=QƔ;c _!rC@r|*蟲 ]W3PXo9 _z(չZn䷎T@@$p~23OZ-ʛ{݃7X5 &Wѻ'eΰgY"9hO*Cbcf]\l3,XcX:(\ʃ ,uIˀp,0ˉ2 X%).%FAOK }% TϑbJK}e}͵%e8o9l5}\5R Xutk?[~OY0bY[ϗxit ܱ9nOO8y #`)n&2|Q8D%' -A\IZZrT&KR8?ZhD'%) GqIٳRɕ7rAΟ}bO2dx|8dP4DT$z P@"6Q-#<ڃIxkPI(s+ZFrre LtSlmS~b7PNǟ]&GOkOI[[AUH86[kR;:!K^jI@HxZ M)}&oCT+1O&?a\?}O{I +30RvRr]@/U~r\>QE/i1Y}wшd8c`)hM~@C<4Qw];l5ќ,i=} +yj~ZCs?x4QJ~kbW۟Ub0l Nc,K+`%l bMKd!KuH?["!`[[>`O{Q[;N h4-0[j;ULٟ|,Z 8=ěc%pd7v#B?A9 PH|*GekP%Hf(2kk a0zQr׍ZMH G<C'p\[[F!z|FK*Bi/'9+^'-&0ڲmL@u>87U;j\%sYЀ5Uiړ% 9!u.P R)KtA%SC[m޹an*WNʾUmzs@mB?+kPǴGH VimZGtMxU A.0+2V>-%wp _YJIDI(^f ^Tl~_XD>;{.FϻdDE\t7Afe_n9,Bt)oC_ sS0T<@lm{$&qvo5ԫ$\q#LE *  ] ~K6~ eAr"T'*Q$ʸWzr] ,d Vc$$_Oo3<78 iq<LS6uaM4J@z&=GPn\O4|c397oW_alXp 4iazn)5LY'N_$HkV,A1A; cqˎ$L hmyo(C$yQ' ؼTi\IpYY+IlTP.;$ŵGqbZ6*= a \{)Sa|?&dDq/4]P"ȧ@ )o">JWץVhjo&nsQz>N;g㌔Yg%m> e{ʈO* 6F.p֜G0ʦTZ n? j029B)>}BYz(<5ΌO1`ÍUh|y_x ѱ-pV{7^F MtMGѸGs^?|2Љ7gTDhY : ֲc{ oםki\j3<*6HP{_sVA;[erys[]a}<{.暻7{[qacCK{7sSP8SO>A,B :̮W->K_gMå, y7ځٞ*TQ-ݽL<5U q]ȿy.sqQoN.C&k47ϖ;>"q.Hc' J{xG͑]-3i MxPvaAQclώO4y5iOy ^ϗO]Tn^- +pb>r0v]ƨEi"L]S8q"G 8qK"'F@c(z >Y'Iq2oܟ.TeSRǢ!)ĵuDm6@\64/F9@O[W9~@ R;Z=6~WKm6\-X?f E;3Kd$"^`Ai4%흝]ͣ+ǸrjŚsS({i4,:aƸw1'RY7[VojVC߆y` @F<:Wr` xc$gniLMp3i]m5>m]nXoS F[""8OD蚢z7,FЂss9\Z@ &W6dl&a_k;pa >ց@B_D>Rd ,r .eå\0T7ŵab bSB$9x>iXpA?e g; 䕁S$"L\o@.ӣ~c61惒( 1c HT6I Jg2m ߟ7rJT򥸂%vy2hEn;[:pPS_5tM9Ğ 5MzU}կlvVnAGmoH8xd'xs;:υZk95V`aKR;_>c1@ub]*Zɟ~;"Y 1Xvžbj`nԝ/gfuxn95\6?XbR½oX_;9qX?dSAm哱xpϮܚ@5oػL@ž,uhܚM+@X,t5>U Yta(3~<@+Jj@Ow=/$Y0b['D3H}}sͲX&||["c\9sȏ!"CYEMz#߇ f5O>Ƹm%< Q5n7t0+@ ۀo  V8a8dqTq>>? J GNX,&E΂ ! >Qa !B>}dI' $ւϗE<*A,D Kk!ebD3l,M1Vm&xr/y /ꛬP{HRSNkEt"z\A M܋6Dp(ʬ`s]Q:I 0g.m!huPiq@/a(t}b"`U yG'+[NTtSZc81=A ',sO˜b>Um:FljP1N !cӭ=N@UcMմ[h8m=nqǜKmG#BYzKT]YeXTH!i|Pu]CaoAW]Ndw[ }HeBy'&tG*+č N_JZU$=;Yc jK :KQ; >m偈[iL7uvߙj9< ''A!.&{-X 9ݢzX=ֹAⶱA/y~. ۱Xdiy:aq c;j$it"uD2Я(Ru7w`'\2)߻H:Vdun/jO2 { n}6Vm|Eޱ0棾Oݘz2TA'`grJ"U)a )JbH;w H2q2T%csd各~"[r2a>el+}뇚8obb\@ ϗ jJeX2#B7(m?衇;>&n8DϠb*dI>Eɵp]؈Lp2bl&G{S9 剤E/P ?Ư!ɒR$dd9HO gwO"9rcd79˽7Vs_ѓ[㭪7?I7yIiJӂ^@2U]?h;4ubj(X Ur;Uv~FX ‘XtG|:Gjow~ SlbfM$/>K_}YM~SAƒۃڅϿsz{k`H?zpO֤Yd/^c^SA!:伨:{fm%bپfI"9UQ:{Ky2Bսoqc|!^2Ӯi̭.ӳ YCd1@ wAC<Jgu[JZmUuLUX\6SyuHU݃PvNDe-!eJme}๻ G RL:C3L-AGA` nu]~-->r}Q 'YQ̇Ɣ~ F- {QaӇJL`Л9DcȸGDʸGu#Y ց(1+ԃցSUs-kK ?9=|6W\yg04DDzķiH,G<P=U{TKB)1BҌ}q=Jԁ(R`26me̥…Ƽi`KoZ[CqyPTo 3E$$uC@ICԹdAfq{!fM^Wɍ6?MsIDf@8dtzߋ DM/ Aps9+tx BiEe" Mz?MW-DJ9π-.>W|a}I1:ʢL<By׮1o(KN6ޕB 6!R.+PxxYCF[_XxHwwENUiiAݦ K>Ү* }R>Zoju-:*X3[X~Cт!YywڎMtj 5~{G9e}xЏ鏹՘Bp yM_9[њ~=xot'Dw{k (zq!P?P=޹"}blF%dPlɝ`/g[уXaM|/~ΣQJXGEU>Ou~j:,ҁfd1@7,ZWu݄Xk`~E[ϵ7ir4ۭ #sWsb#e4-g>/,}LP]ͳ5..;| {7oFa&RW/JϨobyK~Y PH_`d4CF:KT$3]7 wpc pcʛDe­ L4# o|Fi-} {Q˽|l-v=4K1˗ @F:Ɠ9d>kЩ8gvb^ מ,;}ezN5Ǟ{XRɺ]<[pgUwJE+pR']j1tN`jd캗цU1I3Igg pJu(ϓD?C:=]& }:U@b!b@_q.Q9qelHHO Ih$։!A:p. PKC0NL &c. nr0AehJizMO2Ѻpt2LJ=Pif(?e#1L2'ٷEf}yL!4$RqܷÇ&䘻zMroS iQ((ǯE“/ꏢ @, fbBgF,-C7qP O>).VC&>@bJc@xSi}Ot6V9sƉU'daNLօT>OM_WvQiW;,O{^wSDU5N` &ҌLI1зa5EWgvxo+B ZeGS|M3|&9'&﹇E-LYLbzX!I{x!le"wprє8ʽa*Pah [[O9=gwjU[3}Xy@ځlFKb+ZһT6=")JٴMisșd"g8!j(X{h\pR X|y)cuGo8 }yB[ }/ O1!@*r^<1Ӂ Okk#&k Oz\M!"F:E-.0ebhylCQ!NH. 7YHE4v*v?$ASBb iC_M Q]ZGR Au.\w1~ܔD5{ě\#{)zFNp<OoM|_$]x%4pK2lFWpq"sM#yk)9S3ka-)1ZҴz{pʋA0TW1x׊9-]!a\HO7όi6NU\ގfK_j`ђ%/7uwt ic PT$xH>%Eh nOx$9BHW^~p}ɪ VL|%VHM+Vs  \K-b[ܷu+ n7Q;Q;&Xz%¸uL })5}~r AR Je컳oDyZRdM_D¾[(UN4}aQ'bE.M Hό/Z؉F'2@ &zO Ұ떄!E6_u; {HIu &\aRGr,p:9.!7?Nl fB=ޟgȤ@n4tzR@D*bvcl}XCYF\OFN=(/ܑ#{& t(W:Vc@r(n"}P{z:p g͙3`aȹӽqv: _6f-p)-fu HNηB3 FPkq_$G(wخa~BXXaϔ Zƶ=PQF ;"9, =TA͠.&aQ" PbSp`9i6ܧg& S816a|.k K>GIϛ;!棢H!XWvTQ2'rRdTQ.VnkjgzHuܪO72)g)BhF~*Kz@%t,tޱpa,AI ͋X.M\2QWUǰ,ppN;D>4FH,>Yd()>EQ6>DFdp-}>H/N9ٙ, Zم$Ygsyj!}€#PBⰂxD`u} Np -Kņ5b2+=ǃ@2y^xJo=ɥ()p0)Mc9)b'z$ 6 xJET~ u0^nU-kG[kOMh/V4۲ft~C2gPn ә}Ut߇o\)a9k;A 0__.O} }[ vbP!g*`UAX.]O1IXt"ZeًfciJeK]'2b=+eK%2)jC0{֐>v d~EE`x5Fjnl{iM_վ~> '"S^M$$e(9zv\dS.S݃7 */sCIɷ#6 t4|Jv }!c}x{1nb&Q!XZ(pcD-B7 WnfCNj T^а^3j*:Gj8Ģ=G|_2l|Jc!)SHAaQ#i]ada4!,M/YÐO>4kRi2<^v 6tieEfU̬Ŕ)P67/k4{Ǽ.]Ln`2zm;SW\&7SSÙt MWwwJ*.몞 旙-,/[|1S͖_@9a ٌqFP͡sP L_0@J)=u+,K>Cy7MZyMHT)w>7mǸ1t,eFrXGD@jIt\2'"*舟ĕք2ZP-%QkB*zGOTM:n #2wy[{( 'Ru0w>8-ȵ[~bgu3x.+z0qwH;΁~vd{*p} f[4N$Vh:V mtG)s@Ed`)O`\  آնVE & @RE.\U';6PKK?'0c}F[_Y /O8L$@H[NzW egZ΍F.K4=KѺhPaX?"b٘O)LIA38JIr޲ <+,3gl,g퀃ǒ»ĊG!C]}PΛH8¦)^dCWo戽] t -(Y=KjjcjWsU{=mG#Oco1XWw #93+sŽҒ ˪|./ ,f7*H^^K;T@u/QEU'6ܜ1S iT8Pa&} }j(wfk? q.n'f3;@_)[Æ^ϗkgOgMQU^#yH@+uLPEaAdpB^S&;%c0`1Һ2" fd z0]iOIгt\>QOaztՃ8Ih1Oƶ^H\z<\= Nğz!)ɕQa$^vZ|'qDǯ^Z>Ks=$%UtAJR(RB=HEfT؏0hIB O^\7Ijq_z̧Ez"Sޭuj!esqP< @hGDJ6(;R(*<n()PP9F_aB;(`3?]@ct鎉G 7HoRh^AG>zXf"O<Чoa0kPjŒ|\@f~ۉp  vT{ӡcБ /jxd%Euz*ɼ ىJ+U`鳘-}fK3HOTݟ+>F>%R]&<aR{cvP~Zb=^>ȓ1P|ס;`-}WM7J/?rEtfC0 Ss/jI}Y>{qp #%B;}#*vuums,/AT]qj^l2ID%Yi^}?i{:B>w~z˽˽OzG' ; /cNa9 $y|JḨX,PЊxQYa9Q'"觬ľOC<% . *\LSHIy! I()ΌEP?ww35J1Y擖Qk$[DWkJaY1]6:LNPLMA7hz՞\e XSO}A@Q'3~/{v81I88 Iˇ_7/,q#k27gtN{ $< Vep*[.GFA'17p3q6X7#3BŽ 9+h"ТpyGЯFWM >Ek߳QkBI!\ nHb< Ep\0ޝ+owB-RNR3L3ԗIm8`-6T.H_"! t? pa7{=\Өg+r 䚣g"㻨`&% )9Hc h9 uټ}0a []Ť②9xrWv+t4EA )7.0Z# g_7-y^ZLvo"\I WU&]~qP3$N XN E0$xWHCVy)AnPNs2 nmOڬ' \A\1)^8YRV6^,ג1&KZBN}.?'wW ΃\._ 5CK+?`,1[P̛ð(ҁu8hN1]3+)x=4ܜD;[!:!U2jaЛBPJveHz%%.K ;zv:֏OX%QZ [4s8TW,V!* uԱ} Mqp G'[!o`q 'C;}$o쭎WH/h{#YW\gbG7!b5={Nd bؖ潷mJemo1VKz;}^ub1^\ȿ|m^qw9mц8R$KJbrKp”"gb5$^)ʵmiBo;* 6*1Lkv9`B ~[>t-ZZ7W8+nORS}:grl9T;PO`ji?@ybb~G1B!6E>T*88uv䱼};7A=2e7gZw߃kQy1y4:oMAKe͓NZ.ejqIMzLǟI-5ʤ'!.fR&dR˦2If&|ZǓ߻IL[?hBD$z.~gɍ#Of[(=|/ zZW7A:n97hyWG9m` )N'cOa9:Ju"?gh^[ox8qmHIZYMP( P2J$qdAn(Y&]mh/(t_?U0ѳ [$4|k(pzL6sv9X/J58ZT@ ewY:[OҘU+6Rp=H70$T5ؐ4\ńV6߻C-vC}osX#,&K Z$Huaul@y!z/;ׯSe$A[b|W̷y=vAK>ErF>Ӳ0 ~"FR| &o33g+ C' I.K`*9%]q"/< P5W>&m7Aޅ83X,UzLöPʱE$ ـ3w,iMi#=|?6-`?b?~nƬkb֢[oo,3jԬ5gs̢Ykjy7j7޿gvϞ^Eױ3-fI 9ߔ/qWܲr MFKYGQRoc /P #>5?8l f\#5&2k3oUM)GM[4wfk`@j`<VѺ*+rKrtPs K3(v7kq>ZARr4/z 4y`Q^+wӚgfҵ5d^eJqE>~5+8G%h<^gd#%`JJ䲑n5YuiG:ɬeՑw?PK/;.:gPKiGV content.xml]Y6~_QQ7Ioֺ=aWyHPű$*HVWҁC(=㦒`8>b~e/_]u}Eqd٫>|; }i$dY|Yw/˛<_]Vf2Wd^lb[V4i~SmN#Iۨ"d-=+dO>ٙ& cF?[p[-ށ(4EӼX<+7oxA"EVo0ʓt7wh绷[e)VyZK'FxuW$94Fŷ2+Uq޵Qoz֔gRQJ WtNI |Z{MkźMcOsZɫ(q Rawղ[g˿߼/y(4?`}ҿۿs[q~8[߈pbJ wӑg)}R&>2m4[7Az߸TvmFYހI-;e|qpa6 <Ѣ|:|wo {tb\׆O?,e\d+jX@IU[\ڧͅdx^~y6ڿH_]Yd~xb-oȒE-UVT1f&]rߞL{(SY"LWe@RX=mFGW9`'[+Bl}V﷕_n$/7W2Bm)ZĪ,Nt6ͪG3zapO2/}-F#yaeG"Eh^wa:?U~^=< bsF~||H_%{ 9/wxWv. 9{^z!mDWخH@y#ouh%t%IPY2Z(JGPf: W?tb2?EB/X-Q7l/$nuPd-jɋΟ.%ߟ Y}*hkܯ͂Yp5 wf}c{`ܷf}g͂Yp6 /f,͂,wa?͂Ype\lܩYpc&f%fM͂;3 WQp_k~co͂Ypߘf,?YpߙGdܟ͂Yp5 {~0 of,Oewj,YpYpSLks|4''󶆥'JzPtNWWNeNj:c+RѲ*geMJ.qDK}}U!FlIZM\j?Y+yU՛ѥ;hly*B> ߾Xn.7Й7q&ٙ-{~U5_!y |zQ1 ͱG$3Lɔnn?#Ym+]  #@ѱ;pPbzewD K۩VBG+[4vi0*华V$gS]킟=(?{-d[??@?g;x6ӒtH܍xRxzr%;y;PɻӠ|'r/w}-X٬k5؎ |-/$M߬iԴ`Dejz>/9Z^@CFe/^fTFIvyQYQ٠2JBs~mS'4vډCsB-Γ}PTW˓w$ӄ఼PA9,@@r' a%d{yld{@=+!mYVn? dY]ڻ\߶oDM?Cuܔ}'IuϳS%_8( dY]b8l&JgWfgyYцQƖ5)6%F0pٰEaXe=ӕR,~([&T⣭uYy`WIgok99H}ٌ.AVmDJ/x~s}Ւ`ۀkjo?}2? W;'UE QyL+^?s=Y<{),ʱla7%;CD/QCga0G9;\s.9sv/3|<%;]xz/wy,_0M43gg s>Pԃ4O>w.;;#;ш`<ܹT8n ;pq' h*鵀<@ڂu]8_a#"`8u)7GrfB9PjB ©i׻߅s<]pZ xs'!|ᴲ};Q8Nn8d~\}r'ا^=Ȟ":VQ7=e=/É( gS-8Ї)Cf^٩U'O '>TO\'O'O=~Op^[$U u0\CDg|tRR :@uq JREҁR (}67TJTIX^54˃5*IkJ%N"gW!! i&1O04Q'i`?0g4n)g_BէzB[/H\2O7 0 )Sj’%`؁ilHaֹ&I.+U7DiRߊKܚH}Y7ZԶԵ wQ{4T:M%g , ȯꋘShv1+ݚm3;4?mcO%r$89L"r9[p➖7;渟9+m?zT3 ֘>箬%rFO?"}/a'KDG+^JJgG2MG]Nyi*7:w1ſ PZ8ZχZ@P9XPfƾE8(|<q*)O篲ٖ׉*J-'*>a#R2? \ E#C{}^?$Tm:Kh\si-lZ##ȦTfFMտMbƼVF@Mo5*'+UDAm*Wm7RqՅ4E:C0LPl׿E(`迕?Lֿ~+#TITn%@eHszNQ9w׀vඏyi :UmB:D_S9^\BgD7NXAJ+#b 1adD .JiB}Ɔ@s;v0 ɒNF 091EdD p8 GONF Rqz?UzM>!_\X$/*i #۳ڐm34/Jo>_]{!˭99[;t МTdh2 МTל\shyu'"}uݚ]S|-Kmc!(6]o9u9Ԛ"Whs$@IqhRLk_G(;}t=IݯE^=g0Y9N">F]$=D[vJβ,H 9$wT9Tk#c!Rp`Aϫ< 둂PUH1](AOo>/ƈnc4;D/3Dvg|8qسf$r+ `#2xdlR:So/WEi8{Ἒ82ΗugUa3DdDxG_:́e Cb:\N =P F;eP0*…*JRlBqI .f|2C lNDDN঑9܄ƅssͻrS]mb&ՅznB¹a3a犄0G#04qu2"ӘgyӘe3=B'C3Ɉ(Ocnr@ GZEj&(ubNF xꅉP KZ6IXs++E0rUBBnS'#buMNx'#8 kT9Q#"'N <{aȎv2-mX'#b%UbO5ymAۻ ]FɈT N5DX-YyX IxH޶4G'kUd qt+#!y Y3) a0N6sH<5G5ʈhHszV (NFDCɳj($H6u2"UCNRևK12z \5Aqt2"p^jb(mG6Ɉ@]4Qmbg2"MPp4Ã?ێH_H |(׈A Qm&r[O\pc$'J%ntտ~Wg9c\k9]q 9&.# s&N_{ &okVK3khT&l7߸ 't/~TKY>~,O&}D\Pm#3Eg?Cv;Ӗj?ϿzuE;oj;e:K(>OZ׭W/U٘TF^u_I?\Z?# j6A,1sGH!@sS')s")eD*є9JIVM*HSDf1)ubdb'恊)d)wbzi)c ؍2DcJ8`:=2ǔ=q: 0><SQ$3Ö"c7pI$kY\3PT#c;R)}0IvqoTKI ڊIFIGoRd"҈KS$rH #hz^h(0O*dFZȈ[1Hx%bo)R36$1ڼH-I+/qUx[QR3F\VdJkJKA^jz7HSdA8m\{wxn]ϟMnϭ#nc 8֟^ܽJ3mkw w5ܙ|3ŵWw\Y4zq nޏCh\RҞӀc4r0[lpHWl?6_Bg7Z^<vM^W(oֵqzIycLvoZoT?VbYM1+k8CKC@_GGB:h6okچqD}4DFuF.`ChZ]Z56bz m!8&|9c^|G;R˴TWYX\-(OO~_?߭9]&>ujh&{=/tw4]Yfj u 2纍˼`>S{_ۼ{RD->7RP`C$_h_uZAu~.=ٳ/6J:lH}9͗1߲tZ_zs.՟Ӿ~Et04*&Տ-}ǑreNc_8_fj{yXk}UOVONxW>\GumRvaVLgͺH^Oa]:@O~=y։@ukH[۫!,gQ0Nѥl)zfFS 8fqGBTFYb``,<΋yQQDeC#)΋yQQ8/nrQԙff;|a y 򱂧|*,Dc Qa|`c gVPy 򱄨ð||`vB3t򱃨|),+ g>,X>|`X;:OaATX>VT3t򱃨|,!0,,X>|Pa | *,+x :OaATX>u,k'TX>C),; 򱂪|SX>v%D*,DcOaXAUX>C),; |`c gVPy 򱄨ð|t<lb̺=Ⱥ\f^yEUgU]dRg2o'] Dq/?>2OJ v']f$O{N}es]I95}=q;w{E06BU uˡnnnnnnnnnnQ.---------ԭ5փ!q^^^^^^^^{^}סWxPPPPPPPP;u@]›zzzzzzzzPCWfnnnnnnnnwv௥7 :*:7lB›}ouv:%{2Y>` 裸weHۧr0g%,7 ~nnnnn]n#R%{%{%{%{%{%{%{*{\y:+]MP6_v7P|xU:;;#_"{.r=a/'#ʋe; ??rv(-+[ٞO\o9,ד|_ 7/ه"9}ɿfՑ] ~>Q'LwI/+T(܇unlcա?|AGN/ OTgrb(V*aˊIx3{i??Z/O_ee{5:Tq>ـT]kbf{T҂I8|<[6/JW;Y S[ϕ\>oYͳq0W8Y|̧ӕ?u|4ʿ'=zNƠx4 fET*P** BUETA@UBT}F7:HUa UOaUCAhp_OFW?x2oio-6x$ Nެi\V?=2CtD 8@) :Bť@l;saD\79mx!*.Jd:yS_`͋Ȭsݜ;)ܖ4Eq/&QA_{%>ź|bx\ +mDk:֜+%h8/'t[@:Z8MN<ړmҁt8. K#H0_I@f a#<:m:@ǬX8:rI*&/d,,#v(4: UVp驻g{FA:fS8xܯG3*Glգ#m#2*i:F֖J#Oi1c U]h@OX:p(QcȚpĘt*UflnܶŭЀr٦ʡV` @|& ddcHEhg%UQ2 UځMhY%Ud6;**U LS%w4=a;-uR9+- TOKE6b=TAa,*2MK LSP6q(G踻rtMU :zQA6U4s*9]FQ81,嬸'Ɖf1.g<]%J dɜ*E\R9+Q~" [6Z%lD:4JaU"_OFW?əwuF؍I{R\@#*3҅nF8BFk89y)3>?8&'A#9giMb;RĿz+SĴ(+ NbǣQƐbl38@|[Q;#ȱoCN;c 1Ab\I'ƛŸ_1N7q9} qbQ̋}B~$3 N:Zh `(Vb6"V'VޅVcv`VƱbP6qfɉqcYrbמ%g@wY#V7aŞa6 9[ ZwM[c]Zv3Og~İn~D\ !\J2qJ0)3oH/TL0`Y^V9&aouL=\y+2UB~+\W]y+2}NW~@W.jwy4̓K|*`RşSMz_9g;wZLt,:{ˊeeeN0qwur 7q qfl@}\"q G+ŕl$|yv& ~[4ߕlpU!W͑ ꓫ`W\qɕ,Hu8hԠ7DCиu rPQS /u\N\A+`I(rE,:(rE,2rC vk(ٰLo:jxNL:npBWrr$RહW\ݺk;+"0W"Ƚu`=8W\5 \xG \vk>Jc#I]yp:([F8_ yM Bij^}ⱶOOB$;Ju QO­~x qxbx. x'Gxxt'N>IH ᄪ@9\Oµ$\p Uֿ gOs^+`׹y: s'>S$uXD g v D< vx `צFJ\O¥\OU uU9QV"ם1|"w^ ^}<ʖNKQOKcz=<4gxLŇ  mŒx25wqSGP<E[xx\ zx ѯ WHonFߥH.ɥ.0:P\Wvme;geTMz>=35Oᆖ<gʘ3ya''vgZ/r;%%F#ʖ3uVӛ.ؒ%\t:T+:"7 Q<'wxy <3!\ <3I\hGH&Lp&t3)q[7A''/rCDD'9p؁Kg&W^9Ӹ΋h86̄'9 5S: N"G3EΆD^er6Lоw1^J)8>p%縚C@jAr2"L֑WJ +s.D\戼z#E΃~ 38Wp%ĕ6Dp zûhܷƉ9⣎QO\^)WSm W:c\CNvmd76vFc+JrNMb&/Z\*?W\ej`*?W(\\1Wp+:* *?W[hsMqV>W;tnVOeG߯ݞ*>lV2wx)ͣO~|/b8gCǗOv-CS_>Bح ٽYpy?p1"0aA;L N8@%2XDF,Vl o.]m;j^.ʈLqZ%cyzLԨdqP{[ц:BTB$F'N{uవG0' rD>S,?!ވ=OHrՈpk 8fQyS;c\\_Wϩr  r䝥wF/w-׋Y_4S0N$[.U_ÛNALNix'w-w1ʝ#l+/R{b .W"ӭ^u6RG&WLNvm1?4N`Oz)u$~.1t>Mg;p!wp?>|n{QCLIth1b<+<~^&11|'1tp912{qپ^<^#<꾺'pb|/w?m7=;]Tw|铐ڽPgg?Yn8iq? /Cmܝ91*Tv<5O6~x퇋ߢGJV=ƒH R}ꇷzӻV?WWkUZi#fqk;`-2^گyk0yNǘ2'Ių}qZfeSH"ޔK>3ǡ;9g[z7ܫB[;:F;xp\2IG>Qz76y?<|>W2OCyVOޮ(2|}E BN)jɍ=t.'3 6}RF4EKo>^{?_/ oåjcu3Ɣh2棾 } %|%UicN㔾~6G mu2iTAʅ3Uvonf:32%FA'9}Yzb<x?/i&^e!Y\"@tfid'Xy1}zI>W#+\nٛSX )Uw"nlҗ{v074}|צPekՉgb:O1X{$sU7ZY,+;Ηdpz/sW۞>ьOo2.^ԑKr߻fտb}9Nl{2Gmsz.<:ݤ\xtN\?&x꟟wt6oΣIfl:So–G{:_{83.@6;c>~?ܥ>{?${Oh=n~5Mg?`ؔs@Sh/^!m4q[O#lMJH,qZK"49b7h<l^tpݨF践Y^^7 e+-91bzPu73$bzI\~9"rfbPSDއ+IIo8ȻwGy+bph>e:#/YnN?\rbs_pRPQ2fg{ӟCr07҈ً wEIb:!uj2~YUA#5G?4t:=^]t){eiA ё'u2?y_$1I]yEdO0HDfF~>GĹga7^K}b|:;bC:^/cI%whYU"ۑ/qn>0Eܳ aښ7ǎIe0;ŀ(~ޛ/Fd֦Gh 6oFӁS ꨛ2/?͒I*ԍS֋G:(жl7/]zmA.slJAw.rB^y}MyM,||p5fy|ʩj۾48.,n/N\׍BI>;|t3圡ܳwԥFnL>[q7Fߌ}<ԋeMl8I{xv2P M'w黇vT5:1N?\ႝq4Nhdk輭 2}<$N=Aa7Xxu}~LGSx~fnH(!ͻOFVCf)|Iv|aY>(ZAFc<ij#xdnOI2|ݨKaUv GkG ^p`7UHkF'׀9) SOFsisl70_8Ve 8X:|R&F ɿNAsF/Ga4V9#*%b_No畑ٰsHV:L'*:Y r4MW*mSױuLG hnOq~ȺQXN'ܖjЯHͬ.ijy3iOYmyйl\OyEHeIu޶iyٔWGw^@Aixٍ0˥u#+nܳvsИQI :#2(/ iuU:/N QHun!8FGUUWP2(5'PSvzӨd$mC?ZĻb dѣF_at%Pʔ5YZ[uI6ւ +}:_<giݴZiC_rRĩFzvVB o!TbB?TPf1蠟L=U`-0KˎF$Fɨ\_kgNu.[Zx%ƖyUf4nؗy_cF{Lf"YG ]n=J;o|&ip^gQ4/lG}RycQ W׼Oͦ{w~NYʝgdr]uNXr;2v6w 1#Mw+Q{c&}CF[7 pໍ}~o+(#SP攨Y0f:}*;wkov՟ߕ^|7ؒhHMlӘן~ዷ},/1%݆-,07)Xd)WpgٴXK2oD"1+%;GqK2c&.+% ƁF}^ :k=ew3G҈S>eB&^e>,yn~;esU쉜LyDQlx;s!jYr{MG\uНχ䡎2j"M( {lq4 r0/x0妏DVrs?y=eCͼb <,i'}8~X*x76:4(BÐ٠!xm76|mޏ? _DBml&Tf}Wb'IJޤ<4^N^}ynK9=7wtROp*KSxC3R(,tMGr{I_ngl)K\yx2[ E Co&ț{CkA#Fs9J@B?' S+lA:!yl~A%Ô'G/9%^>t a}?liDSџ/eِ^v@|HIJ2^{|[AXDWOAk:8MWOm'“}bXz)g&oYz;LjW큓BsD~&4_FС͢ºLȈPC$oLOL'iR%INM<ͅxҴzM/?qL?LV/u&$ v5Sp%tLN_ 9=t1Q!xr!8 yᕝUxg~}u`6Q0@x:x,33hΒ">ʵpFw 683'Ӝ`EkO /R߅n4WQ:Ӕ kIGB%+/eeȟso[QW`Ւ:QhtIrD,PH+_ 6,כ*% BQ;!žZ YFEB.IIcJbXƄq\MP:EOٗ4b/3%BZi.JB)g1Nb:~_~gŽZpZP,3F*׷\ rI;*Yt.4`^).U-vQ\C#;|}zۢ /_T _AiDVRjnPvʝr ǖ`K҃jZ2"rZWsۖ,mw,2JPhTUd e:j-5p@hqz?Ln5Rꇅ⇅uu4 bQF_mxg~Xs4Us+IDaஜIӕw!tGP J.QthK[j]ĵɤd!d;F?LꙷД;QJ$r,8ɥ&I2$GCϙD\CB1S;3\!fZ:טYL:Jxzo@[ȴoFu[ t,`v2 N ;Mn<<( &'\ngb(Un ^D%wX݊Q-W+23RmeUCa^蚐|E[Uep۹hϰh[P JXsè|泹h6jx2W e;q`슶5SV3NJݸ`zyMxnsbsudƺ_Z; ΟeTeY_WjկkjCk%\==njтu*-BD5m+ivzkN d~mbOOfYO^`Aٺ1A'Z,!UZw\&󱾳A9K~aOH?&_$ ;!hOn}¨ņHgfKe jν'M^$o}G^HXa QEsy\ܨ@#{t@g< ( n6hE6(kp1IPEt{$aC*cRߎHspN~'nt%Xޤ[!#2u`$|NH 1{06i%l†=t$hm=~TH [a*bI.!JY\ <լy?нs351_Qg粭<푞?Сѭkd;e{id-n@tozy'P7- 7wZd7fP^_nGpݧ h>o|;F1c?ь*i(KdN϶H)(WFXӤ#^:RurYvDdɗ}?G,5[ia3r>'kYWБS6'B._[LWeN->[\j\e!>l_[A֍m 7PKRO) !:V"/kJQ4֝kDֈ+c4k.{ժ"6JI=u9zAʖdtG{jJ޺rĕϦ7)]K`pNf* C젢UE?zឈ!A/qw{}X-{#֫Ѧja0a\Y㲘{.P=WW׶n.N&E1_Jąg>za}IShV>k>B]TvS_Gzbz @ѣJFjNCjs7uz'(@,?UjXKW#jZKFfҚA]Z~t5ؑOwPVgE-rwxl$5N~hDB,@?6S mC4ZgzRKvԸ=YLW}I&J/91u>o p{c4M-a 2*Չ*[UXByDz4YRfKUvVr ]"# KY'FVA]D nW박޻Fyz}#^Y/ DzP˞ӄP@Ȩ LyuS؝QPapVkVe_Duߓ$~^3&dWu蠎A]ZEGh) !Ok!Ƃʍ\t7k&$7O=J:Rՠ M&j0)MQuز WMpU2z,gγCKviOJluASZfq:F4̚0*iuxƴl(ie(0  pg You8ά̵ 6kv%Pi[f?(zC[ Q2Zqksh9 Q#\r$/dA96HlAq LPX jyd(!)ɖgέj^НDˇ.@^0J=ri^;^(Sxg$3-_si{ c 6ƋO鶥M Ix]ְLTkn`A:TRnciꑐ<6{ͅR d3'؉mCJgeMdZQ2A}{ A@˸bI،}Ias!fT"UchMxBG څCC l7RnƠFOTHIzWW侽ܽ>[@..C9c:/&1 {cQb Fw M=nsY:I9Y0|F~&@Zyg?ڀ?QbT3JRC()%BMcZ͒AOoo}`n L3n C0CZ98b6@$a"FZƍqz?@mߐqWfs1[p':A0'9`-U({̼D/ݦWB$Y}j[i ;LN~XAfLwh>܋tT9XW'rYn 8|/@=,+F9)XHKĬD61KoNC)ʦihi#b0 _[ljd n= T[gP>% TZבN%.g%H~H~mv oj ~7!iN+%hقBxD67'f8@L2&c&{$k6gpjYen?6Nn1Yf=/Gʬ5DEy:bOV}z'֓A ۋ7%T<'>ke 053ZGٻȼ)4݈!~fFzMɾuف70 cths,NM&=^ɫb ś|R=7qݟ46"lT0у,[ жKvW^ sŜ7_2,Oqv\V}-bmUr[+6Z]}zwYTc2%Nܶ+_˲;f'w;P MWlEKҥ41S4&mǣs=LIl"77qyo `2{#@ğ5!Trrg?ЬGA5F9EAK~(ϋ6/x9V4Ͳ7Duk~,l!L>R!| y ̮ Dv euK4k$Ӫ-=l- S 9_ڊcyI- -^fJYqIXr|~>W{8| )Ao? o UX- PWl-*m*j,X]}ݝ* Vju~9+/VjuiJō'6h0a>t8PZY)X)z: ?OC}mun *FeQbp61 n)R~HZO|F|}T(t<JeLO1=\M_c[/Ł[ҶΔ Diz1sէ]ЂT ƣ<)YdhkP?Lڏ<(L0xE|Zѭ@77t'ڡc=/Gq(ܾē>ɓQ|E/EW&c?Ծ4|yFkyeq:Jˬ5UU^q+W$9_:a¹+'x|.r"ѡCвt]6=,nuuoD,#lP{JN"bYɌIJ%3 b 0}IH =>ԀBg9 HX" uA4T1*J$8Be #y.A' ߄P#-BkMRobg,v%i[RCjْDR; CJb} PfShDF.&jhX W}}K#A C'C5l5m f#Mddƕ&&5n$?% 4'ҍw^$?aU9,$?٬Ff{1?eo|Paȏʧm9MT;`UeP)dPd"c!TDL\$^/h hP% gIVt 2âueAhBmzƆcMq@aux䅰ME3:h!P0k!:w+WԤrG4DGR&3 լ )XG7#R䤐~ɪT QC?X"M(/IJ4/$7nѡ~m;H^ nk:H^nP92W9wrl[CQp)Ht ddW•EŠ 11Ek,ˣD'  ӕh눋e[IgNɠ _I+1.2}_hJ(<8dۣw!/Mt Z+ԏBA+b7Ql)v,. JH:)ЏX6@">Eg*\!Ps~BtbeM@v9 #v$BGt,4LD~}C-EԻP $T1nI U@Wy(,VcUIoɰs ƗSA\J43o%g$l#5Bra]6)K-і(XtU!-2D]25 S&fS"c)!|%RxјBfsUf& X$f76p 9.M(^/SK!NJScтC$7A`[Z Ag:6jmp[%ڊ5AGu%X%|lΔL$S¾KJ" 3xySBbO؟ rc43X7P-t("#ԁkvcLF: >HW{t4w<~R1f3/pܫ<.%qU#^Yxhci+\'aK0^ygQ_(,øBGMY%oP?Q 9D!W0T.cE5<YaW˅QAA \l1ә4#+kc@Y< hTDtμ/sMg*t# ωgckE]yirjO"x@Abvֲu/ȥaE<;֠>WLfiM}^ x,V4O7(,8!#!U?"9ҺbP߇^DŴ<пt%1VcYM@tgBrɾp:XV z^t)pəIzODN%s(fXq69TȼZoGgE0c(hu\vZIIeDQ P]cvʣ]¶>CK%\'JiZ`mm.k*&^@t% k樋`dy:%`,wspnY,=uѠ^վe*h$7\r', 8;ѳe(un:*pnBv;ψΗ}"%B#XԹPL6 ۆ6bh+<+2E@SqWG:ï45X) ӑLVJ/])=:pԆ&wr!R$|qsyhhݴEًKKhC.8CqO^}#y u Cz6/4r^hDIڼЪz,[ۿ&zjHlw]U.w]w:iC{F`a[N5ԝ2xڨ0260FDFVˍ'%/0ar'z1yZG$3C7+NKW NWڱK6h&uluYBzuXvkyv8PY j $l)e*.˞ uUmNm]Ͽ%[+ik3<$U[m7t۪ݮJk~_Eu)#_֝Nyn{y}0y}~K;_~~[O֏Oڷ}UӾ=ֻZn﫣Mrn՟ Nӿ?#\{t{KCRuxؽljEWcqRˇ孞OXy#ՏwQ?wC.O!}ntyѻj%aQ͟Eʸ~r2:Ou*7[nW}}$?zZ˧cc15b:社z{ߪ$K4m}Y:(jP/gS!{ڽvuoBO?z>w'$V-b'=nޮ咶+/ j4 ;X;<tceԿn$<}3H >Jv 6ک}uN6DDw۟џvgԗL-\n|tvY?ӛq_eJU~s,< nAJwtc!Cl=fmaj&EARgD]i]_{V5_Hc:Ó˹ՠUk7T!g$HИȞnL1iԟyp 0dH@ p!پ 0eI<ؼm H!Ԗ`:e+.=#C{ݜD`tۃ p#c/эD?z_1[l,*P-H@0E 49.7g$<9jFfaugӽc.U'aS"H7k(no'BD:#%@l%L#G˻%b '!>]# H/AhO~DeVË?rx/Ͷ]8p=eTZt5yk*xC9VdA(A PrL(t" }HR@_$B x#'JLhz3 fgI\8BQ ;./ͦzX~n+)Wǵb19)N\#˪__z˃X _\=.B3$Z/;m^?Yr"eEi/rVp-Q%Y0,tzN8}  ) eh06 %anx48jOP LlE87E8L+ m&Ao^$ (>QHL'x ڣyuw#*#_c90c2* zG oNat„^ۄ/PY<[+HWK&#KHgN4 (02t/}0$!7l 7  #7Pp_p!:ٹf0_{@]{c~Mq^"l<NiDSbw IԴQ4#q)SٟrEBKܑg\ -9nTH˳Ijy:|ӬK)9$E+f{ ),\{+qQJQmF=D"ݑ#JyYE/wT}'嘍4, MK;3;ޝ87$TBhE܇ U"sUf sW:co '0nNĿ\g"fXª3 +Opj%p/  Y(`ӗk<L7l$\W1cdJbg>afdmE6s!n6B ^h!)f NdPȟ0}af d@dGNTIX7۷L1W}|[Q޸˓Lj vQ7T궋"V ǎB't5h_֕BBl i(3/-w:alv~b ${SWo^ňMz;,'ꮈ8RǼRA} D5J%`SCE T4beP̌Yen*lH՜;5LL1Z@^K÷DieV WDIpDE_Nd%tquJ7԰11rSQKSQn,kE0b/u6S61;If?W_o7+ٱ앤ñ[7JDJmIu`+ʹόl}otL †7:ff/O3ۮMiNjΒD Xl7%0eĪ9,q"Gk^!ZTE#xLil/זgC̞wI߸Դ+Ib޵|$7@UHg]+v501U?Nf%CS1jf~GyEKׁʩ6>(]Eҗtτ2/AiR$7bT"? i%/ Dq)Vy :A^"ZX\ +"_Xlk"c`A @d0'M|K[JG@`K kl)L4ഥpo{%Q:ژZ괴J-$N1ȮHr0rqsN36;HϨ[>/?H8@Ұ@pb?ҐuB #Q+Klka1P>%<sv,~n.[V&%?//'\:էI\_^ݍL1ќbwxS\wծ{eW+V bV?V-J w]OnkyZA ǃ\Dz.]Бr߷z4{95V%)%?XvVϋOBUϱR7}ɧ/mdCm~Ӱ D(F_Z/oGzYLjSD!'aZWRzfN_;@4qq1~{'[йm/Ǹ(♗c\X/js_BV1QT+y|J"u[{\(sP`4c 0m{vUQX[J, y 72YR̶ѩ̢m;<ߦuX`#O%nXpk[tQ4l]+$ `.<Mt6H>68vm@B8ʬ + ˋ&`Y!eA L7C)1C|R&jKm9x1ۏ`ЎCz?b#KuPg?pVSSy6z m>%M i$ 4J?J\$a8! q7r2sU/:BWr^ԒXąs$hy/ 'fTNLBJZ0BڲXv25Εt0tK[ۘ~>bU(rM6?KCFc5AkxJRr% 1oEETԍ)I{"Z֓ 0[}@'"˚_}h0TQN] $ӓ_`[Vj Umj ,+ I坪-gﰅ 8ɴJ8H (`w8-`!<}cXp(,n^-NI)Y^#zy̏rd*+t k`!'AЈĴT`YvCV^)qNo NILU!XL9`60fxЗ3C#:7)#9 ʁ寐bAM+R*GɲԨHpy"nk"xK #IZږ/Ȳ:1%0 6x&˳1u!TTlc׾(oߞmO1 _y.GMDŽgaYttZZ79V˽wow"<8dC 9'+ĸH\mm"kzIuhY{ 3.&KFsV_#kӯES&0nkM-2l=4d_#EW6GhzD@ |m8gF4ڼ6u^r/1kk” &[^5ds!gO„}{ۄטYYI`:chv74.ƴPoPM e^@̩av ?…ez0m DNQ|T!W$_h>}F;Qv5:{6V[̊ĉg@6u5浱xjҡ.,u+uXcPM ?'Gg/nrӜDڜBP̌Yen*LM h*_SRS&j [c"44"PDID˩UHQ.NBb1cbbK#番'1 lmcv,(v$d! [odDzPQ\Ǝ-HmmR4J;N:>5 oġ٘Y53i?ԋc~P<`&lC(]Eҗtτ2/CiR$7J*)vxBb~dq-\g^NW+:= ͕*"Y!5;=`E]4 g0'M|K[Jnk ƖDN[ D-Щ0) wrko煉? >fo}Ծ@8Aۗpf?ҐuB #QK׭,Jrh"!S.ur*hwW~ v&I+N,䠼s%ĬIM`߼e[5i}6%0fMB!qn_?wrӛ^ކ8s[oXHǔ6Zx̠VO!6TMJr kj9GW@9 ܔvx ܡ]*U_&x~qoxХMH2#,?ƾSáW0/Z0Ե +#%^J]v U@#'Y,=9b$e~p(jPR5*?5>~}^^nQiWjE _$h+Gi}|l҄c_Qa}VC[Cpbv[sVVE]Qo;VA~cӕy A-6ayB.P-O!czXcwUv/ z8Wǎ\W{im6joT;9kOJHBv8?25 7ׁ?b&rcEm.pƻPrGRehTZ]jL_qkVUMH+R j(݋Bו.Q'm-{re-iNi+䵮J7I&~Ldz'MNtI U,,{|m"O8SuLvpoi  Ʒ&SA Iuxo 9r#'[ S+Yo[x4k&j/5K j& ] 4} d<&I>> ԝ9ݷn.x@ҀF~'m5Y!g3 @hMZm%78owm+A.v3<8&+`N)xr"~J=8뇩 R0sEU*8f$ҿI3f!$]y@wI.*-je$qP(N(7r*72P YC8e"vYC;N/GَMK&fTNKB "Ϯ-U"s0s<s3iLhZ*Ogd¹02b&dA);G#Z^#fk}r%CrH/ϛmR-?O l$6m=[qIZS6_,JlBy??kJ^Z"Ɩ]RRג HKP,ۥܥj%K^[C&&TT&*&8(uӏd֚qCj˓H 2L1gAiMPx /q bV5\-ook"w:݁SGA Ox%-LQpBf*AWLC w:*^B{&wC AF+IU U`vBf!ˁ r^HWPC04v/ʗZ֦ޫ30E5!0XQNۅ.MF;VNO~onV+Tekq]{o.2E%w67KD(.+=y@,40|eXOnJ)<&1;u3`2xxS{כ={m)x<M}j]2^$71 zZd!x^n`5BTB !5H\5{iD)|t.Rr!;3Wp&1\k c /];rקNϪ>_꥜rss~7N?ȘJcS{/qw4qw"S<}޳[[ Kg=b~l;z_7muuDC>Tf"DG.&,RWmWjH*p>'7j|>loOϟ<0OxhRxͻc7ma??<=z%{CEk>=<^!OO?Ѳ[:4{E7~zs_==hJ˼W+$ $x)2*Kz$$)~[~-8^cwGe*#E h䲙A#mlmYm^QACw?YD\cyP-ߑHZAj jiLjj 0$jPKk)ɫ+,?U#%FtAh4o1Ѵ :tT& +uTQ e/䍄)㊂:VM^)aA>_HKArPNcۈ&%~/:=قlPOqH菓a; RBt;3DX=Æd,70}og)$ .Z+ƶD}ddcpJA>^?O1lHuT)0bn4g#9eؘ2סg#"[ez,C]YyuT.|DAG`!s;0141}7z yYBb$*t- zU5Xz`pzLZ7<8nRXhv]{nz@|N [poyCy_-CH`=sk!Xh ̰"NjvHIIG,|S m1K}*U [{ýeeu.JS}#v/V@ئv)%Hy%[-[gw5M'3Dh6ul/j9ά{oeL $Њ2J3xU x4Su7,0u 3?/IVrpF}P${Sv)[ܿL ÑʧX&_SDdL1m (EXH4)KNArt<`DbwGLع}RMPieO 7E{ $ mf R)ČI"wkoM]"@ Nk$u24$PHN|)XQdt7Lb$ʪjگ7p ,& FƬ#HH&qn_?wrӛcU^F0sYo;*wF!_tl".U띎j!5:v?'EoS|):n( $pM!AI: xj/]1Rϣ0MAT&J ټ fyc鵗,5W rXTLV6ԑ?衯Jh~7=z4ʚcdT,p9U{B.`TFT!<AuROYT([ )$߿JRBPQ[͝G9Y ҒY7y]q~,r2ؗdCL!fuU>5t5זΞX%';Q%}%cm#nWeJ\`s׈HK+seX1=e=βP܈G:q'w%^x٬(i*V!3Du"s{OJ:3URrw#dKŒbĚ/}s\18$`wеnas2Yfx2),noUSӛZrKQEwL76Lm'*Bbsr^ y,_\D)|?&x+m7/|ȡʲJ7pD ԚDF̯0{'= K2<yd Z+_Ki!"=T>DgvKE|OPPT1p&߾믗m/c]JWkvWGkf4ϫw Զ?s녶Ӫ_8wۂ7] /OWNGWп[36MźoZXu^N/klP# dT$ʛ4.};/*zލ- ,fEr,Uϲr\{59L/ Rl;! iyuqk]_fdOE<'!ofU,-r3vc|W } m;+ӆ$?}<ܬYwj{,.[YX߼S>Gz2#)-7JH@$iq\5o\opt9uhEwןyKDwBm̝卢*[a4˲ʉe~frtZSt`M,L,zj|2┉-Oyl-b^?MҷiD›ֆ׽z/Z|1I{Y7:^m _=~B4%%;<^z$4/O-{|VOUIfSմܬ<WIjA$dX1~I"8u@&C %܎MAqA6O'mX zv.KGCH*iTC܆UߎmYiz> N"g#=-eJh ؃x}PGk9 ޭOzWPՒaUMS~޾:~[.} X /Oc6oa UyV,CX0cӭ0*6_Em嬍u{ J+c,vif<-|4<񠤾 6H5J9w+vC[V[a83?t.&1N-2ݪu@24N$^P\6'"2ŭ ɂME9-إPpDI45ZS`VCvEGhߏ}+Fxܷ}+rl'X{ 2/JR RL|e@~2qh$17̗rY'wՠ>0^>S(ZC'Y2]tn#ʞunKǤ- (z[E:חbh;g1L}ߞnI3b8 #h|`QHxKݴXUl=<}fm-1,Z#6iaRȈ⟕w ҈O&^IɲR='d)_]:DPRi#%kyM @|q5mp\/zoʫ P${Un~^W/Jl/<vYtnΑ8A| 8NRuTz7W /IDK%V_9YBNibm:<ʼn CmZ)B͊cS|#yb " *]onk(Il;%UARNpHf8z+E07q/aMQ@z '1]z/$"_HXq[oͳYa1ܰ uln%6vZz-u~ɺ]e˦9%9_ȇmՉs#⮸vI?Ţ'QG:~61<ǟl Z𞿽<g(IHTq76V:n(&=+ҪoẺZD̎uڇ&jc1jm1) *#> oUyhX{.)>VmEXQ.cz1'`ވ:-{jUhυ)}ܛ6vFu|ĥ#vl,E٣-ܾ!]S}CVa&0*'0T̂_4ߡl!BP}P}Än<"t/4`6,$'+qO"ʢ0adh^6-թ.rmΗK ʕhJդ[G B`]PmN VBx?䨈Uf~=QSR޺#4{VgWńFL2YW잝n_5aߗٛM+4 &"w]A &l?>⎕x7..s{lm"Gh7Ioj4C\adzeΐ._!@3gV֖.?$OO1Ij=u>9Qo|eoJ'ݝ꘴c1^UOk]- R;emNY?C8 헜Au{Ġ|qY5کmbE<ʎAݝ̗쨰Al8ˈ}K_uZNXknu/h*ERJK Uoz[_~y8]ډL`'R[UZ=һr%R)0]N.Pc̾ ۂ Mm@0&^ɢXے)AWˍssx'e۪$W^-' Z}?;L--Lam셨[B'P}NpSogbHbJ]1 0n T#%%N5ҠU{@6 j%`O ư]14Ƞt`-xѣ"x=`f']YY*HUWKC+75\5c ē#H=.CI A^<.8P?`?P.e|?ή~>w=BX$GM+z;`~9`k1"P JD+abH-'6zjƞ9NIG q]VW]E_蹭RU4}hIo,ʢ@{#ǸYžh^?t`e(=gF͖E$FeO\PI xC;( K8bWnj\WnUZ*>OABqad{0؍9=aΫko}#~M ~oky>ًI.}{㏻{rt{z#T2 DKc{dv-YeUoFpaK*"UI;oԝOd.$|k>?G$fFm%"UEK=:4=r۴&tkOaFxGT^ T mFCמ} oH]k8cgO6]4M_?|<#OCi|AHwW̐H7 'We5~(~DSe>!P2#W4CO b/=Ik iB l2cji>0*vPj YD~"Iۤ钇K%AGb킀"mm">a@œ|;xGɄ^y1 rDWdc`4C`1zS ǀT0UTzu6-YBdj=b|ڿ61.|3-"=>CyG}Br1bldj/a<po>"GaWӡb6-.Hih0%]@ӊi|K"N &ʰtawb) UAwP OI[O_h nIa\K4>i(0΍p֏x$Ff$G "mED!*bI$*͘^+aET L&јGKx1ľ3t PU!SYA$fN?&3]UzELCmX 2fB/iRNIDztd^A |O8ĵxMЌHVC\ސnfRw H_;͑ Xvq؋HusHD'90d{|oo퍏B2&$تsV:$#LtY&qRG9W"JVFց"~z=}QGh vb"=ZQG2C#~/LP(Lrtu?J*PiĖ9y(`f+aE}@%z4` a急'dD"X̙aTp6B+@&O X4BBD{[P9-3bmd#cc H$,1{$X2-KP"0A͐(& ?M#U9τm-[E[Ily[R^G6x‰Ln4a{V j#%Y,6m%P` nHJI8>5HJ^Td *hVb1,n/`rR a8XqaZ Y|bPl`&a5&l'0p 2*H$U20CQBGȌAp4hHp; $ aOf5U(=g'>k:16l&Õ`عFs#hlts*hQaT5k[1&"g;؋b%-^?!Ht>8r8a?ۏRA0Nsp䢮os3aS X}2 AA+){t8+ v;xObH lo!}iXEϙ(g줽F?[1$OwgG#M8lPa!7bG㘇T'GL$KA)b.Whc85Of۫>?cnvI' \)=u@z~̶toUmF*vi̿ _2;Ę gV =Q`'s=x$bH *2ɬ$93B!bl*gUa#8aBHH" V,}O("3e0PȽp~~LB U;8ct2;e/J.yv6ikEuS{נ]k"%Πձ*ІG[% UHQ T%0>M᭹phGD8G1 {$g~ $imh$;յpFmS t TZ9;t됲I4pL$l y6~v{o퍾Q:0&Z-{zm =ԊJiI+I|eD6`("OIJTF3$b4ZgVr&S\`a=XoԜ tgzޣD GCeN }RxתVgV{}{㣐khCcФZxI\MPزodL6VI9b[-r8ΰW^syȭ8Ռt%St5zOcB$J} q<R6B/ AEt\LjaU"h/kQ;pmhD%Iƙkga23prDp?eLpVH 8%;e Rh.Hm%H$UP^K@,E壚+Vg6ӜAREʌρFY#vjp:>T%(VNa%b?Dy~E1t Y -C%G$5Z6S$Q 6 c8 bp~I2lOiމ&T3M qtIj*NCmf4'4%FLShPG28 j$Wj < }'}^dߠ"|4 8Z] `MU8Nhp D$ T(t6L-;L Bp6HڤJv2" R1lp3cx)N\ Y v[0{])Y;ov!ǧH{u!ǟ9{N\p]iTC馱Juҁw^C!'y $FRe I@il{fP?| Z" 95!|`v #  'ysɉ_^ =3ɜ;@4s Hct>t|Bn%}h=g.ni%!uuèHa)0v[Yݸ 5"`IHdž$bQCEj@nMnaQO"q!Ifp'QՒWȇ[KeU̒5$pa)9j ԺA臱=J1~KJ[ծ#!\>mInJ7(DBa(Br(E2(be3B{H3ޢRW'/@% 6Џ|(W58Y4'bR!F9w/nc㋛hf2? {oo|RrIBc=1F꺝H6E>g.A0bf0I&lW.Ћ/}~H,݀8RcrG/ʤAq2fe=h TI3;}7!Mp-GBРObD q'L<d2ǥ᥏s$GF)pf/$)e@.|CvzJ0;P֨=8 zer$#ow{/<j\e5-WOuν!ML] ojBO0(IPEa@bb@3"#NŐmwL:?iC̀whGeL LDݓ0dôNUS=;"%tGؔ8ޡZw( (bbvjAG8 tW"C;;Yw&:ogt WA$bl Bô1:.Vj=cn-p$ndyeoB6Ttߦ7ӷ^[,fWz[r:.XңJ~_l^sXnޕ.̻t-_fu[ݟ_S^W+){1^58z)f+_~9]jﶬb;ûtŕS/}]%DBQ#̡I8}]Ogf&zߥOgDz {/u+Ȋ'NA ~"+@57c҆~`X?V2fۡp[ Q bKiIpIa>=x`A@{8x]Ou5ODL.X}ispK˾_/-~Q&;KuXC{dK{HDD -On;TDSM/3@G)ժg}{K뽕H]mBnk`=H?}{oo|2ѐrs54ZK31H[PyE2P9 x!#U(/9]71̈́h$3%t#֐)p6Tm6)GKnt P(Lhi98h˖)_BΚpsPGC˱EfB n搐#lў͒ Ne&8+;:f\p8SJjNӺc,k3 g̰3JLn-wV]]LL ڡi(-\Ê3ٛc '9'2&,": Kݶ\-.}kzרW_ [;Fw<+{b{,/FTOXI$k1P5E6٨7chrņuKB dB{@C]@XQq$u06~} mr} m} m4=sWm\?.{ɣĈ^hڈE:CNvr2 #HR=|Ȅ!ijg)0f@$>b+쾊=!c9V{ze  ^rz ǖ"(.s_m:O+rsC2c%*5% ӂqbOYT &^%θ (MR2֛pjF[1!0bUqT#da2 G ny[dS+= \rў [SPNT6H6Y~"?1׳昊0ϑ*Op>@_ \cd 1wD쾽Cd\^8Ep%64ٔ`=[q={C;x>*Q^p&S_,F{- [Ǒw)t)o[G:%b]uɴ2ԢN%L2)YlleMLEdBXh WZ[EbG̙"Pj t &A@S3";EIlUDeU#…)*'[ H}B@ݢ |fX4 NnFun:9}2hf)9?ζdmYZ)шCi!ŝS6eZ"Fsxp.vrMB?Rg:9 {⯻>R;491>a0{jXVY.[MpE`1V| ;J2FA34b& #ZލbIP{[ACzZk+*-C3kA!Wpj"K &­֝kr6kq="UouAR'QZP_x~^'haKo >{5-gvh=2Pj0^jQ>2|oo|rmժD^s飨a*-)yI2#$8m9ۘɥpI:&6s_y1"7d4gV3Rh4l+y61Jиp~'GZk_`"KtID7rqu(z)AHkZ `IT`r!|(c.D1h BIл#ֆDTJSŨK(y-80giNf(v\(+4kh[:`/b-p|"|T@36BR#.2R7}f !I,R94i5x"HJ{\6D=x Og.oI$%{B [.mvB Q-jI(0R.?074zʰ]/H Nx'% 1+~$g*8z P%8Q 3Ӝ\`[jGGtfcUPּ]')ƁKAkh=\`~ p@?PGA&  ؂@ɞ@[_cT|rTiq:X µ2`j0QIa i)ٱGM!F^;̌6qkqE7Py$أO#:^'Cu!ǟ9}u:/q.Q$֑]Fi(]!$9HJ`!5-eo Eќ= X\D5a0G8dV![WA0x.9 Jg&3p%!$|slсקO"ZH/ Z]#SAarqAj 'HĒ֋dݙH Dir ^ C0m ̃'%[!%kX/10Rsm)]cw{̵c-^]p5F2|p+2+>HRCg'1F+[ړE!:M}y *L(Ķ?~̐ڋ DfIucɨbD *Fqϳq Όe!Z&̃썾qZdZ&3jᨢB9Ι $"/K F13Xai$(:?x$ Cwn@ԛe)N1#KUHeҏ8J4 $ΝPɌԀfJVOchk p^D'E1Gi"P& a2WNg lv3IlTd?rCMT+`y\46zD˞̼Ĝ3.:9XJoЋZ^k!q[lq1'"[_8_blm*lxb}!J@`IHC7l7 fN r. [7׋8 T#lr+*2q9ܹ!b,%Ɗ*7r܊p5HՌOv# IlUq,WV8iFKe;ro=TSJOhdKV(c Q.a%-&H⁺>%_X&!zf\_xJ]fb8tYMgrsy0ZK&1n~]sUMB `(eCD" Q,ӫy^MU2z @+"ʗ^ cWfYon<%-AU*q: hf TbMr{7>v!-VޯׇQ=zZwP:^x!D@}hhv; HFqtA{J{f9I5c"8wstXK8:Wy ->=ʸư)h Hjo=CKBbj^h#eV^N*v~m&$bբ\Vކ6;RYTB.SHvJ5mD|)ɤlhGa%ʻ͝|ꋔaƝ< &1o&x_[,áÏ3#6im?36.e,@O5A?8,L~Kԑg˱C)y':3#ro }WМ/ybD9M7O%CMhN#˭`aRt[r|b&V*MJ`^46_>p 9ҩUj..=[DhnXL5h%6H ^7V)^ΦcK6 rţ ;7Pέ~ߑZaѫOV7nH3[Rni/k@YGPK'$a 'b21/ia7%ΉʠDz4/ 4")(eT3Itʮ7Xƾk0ys2-_;;_\C\N:H?^ˎZTfx$ΜGAe+-ukiiv*8 G{G)2-=>RyP34w٦}nL5]!>!.͂4<fSm"M[JFW^YSTF n(:kyb3"[xբwZsC@yjyՂ?y =] et\(j^ ,廭l±qQmn>̚w 4z/uyꛚ]E*_dus/UQk:)Wz^5$g"Y3IZk}Uc48WEŏ9kn苪$yKuGJ;BRb86C0x=;CwNӂ#CӂopѰ^;FX[# wZ̮vZ[zhR)R׳ٳk8ptդ/z!*]Xl|lG:3&r 3v*+D 0j/~5/o/_8/ oP чvXNNomڒX@n*ViZC<-g?/pXZ1|[7lNvZ!j=8 Aa} ¸kBb]-vK ޷мc]]uqU0 _-TڋޙTG,LLzms:?wٰl7B&-Zf`n ,Di]/máB?%y^bIq3!X&?DZ>xb_2}G*9[Xmjp[3{2,H|u6g,L-Y-'޷ul&8OT`:Jg,`[; 3dWDr` b 68Ya)M^yz*t:58Su;GШ@'H@4JԛA_ܐzv߻L [&~3)w7Kad+(j$B|>yX3H7[^/=~񫡴; @d'fgiC 1A^J!D;zo&58D({'mG=bPµ ޝ|A;ջCn؇\ t(,k}d;»]a;زM6Z؁iӞ16Ր|W XoBN/ v[+|pQe>]}ڠCB299~|F8 8ij=16 ;"IA KoJcWc0%MwΕu*X~4 zSRs"FEgG FAG8⪤6HdT/]q [bué'"L"c0fO̹YjBd7+y\[Y+r|rw\U]kd,sWK*2\۝ӿ/ׯq,NTw41m`v:3^PCűXpd86PkY؏AvA"+)#yr'+ZмVbL;Y$(#bw:N-Pb{Dƒş+/N9vʣ9AI_mQe69rL!ݬI쏁}~k_w|4"ykQ_8I{ PPxqVNW-U-f~ AR҉nn2r|+&[9 Q.Qkd0qPxZt׿C!1o_㎲o@r- P{oyqs,dB;&g ؜e^kmٌ-fbskXǻlZ\#9ajU"]q͒ʵKu'gP2dc)s>m"׶9WH$ԭ-(65CŢ+5GdcvdŮiiq֙ g6us3IIrg(W^7xsxӟft/NIw;di tt7 RUX5Uk[sTgy% sx,=m6f0C nN]viUBK]~4J4ԬrGu79|+i-.4>q9Dq4/ E/=WHcZ)^^W݀N٬\`Z>BXuuHjtW8^ywɳZާգEuS \ ㋉$S?p+uݭZ*/ = Ъ+]~"FAB} 4X@p'`qgOCv;hѠ D_1p $z72h,xxS| ]&oGn4501$-{LktHѹŒѹƎNwվ(K_ |q&.th:X#5jtPL,޿1qS 4}x8׹/7o1H&ēhȄKjܤ;@ cb^6b[£;V&@ $ QBiƄ^y17 1.V6&hC = ӄA.-@΅< R[ni('5#Oqi bsRn!oc( ^'KnCvG:#~xGDAS[l{' #;yRK66_!S%bG7-mnZD{ {% ObDhlѫ)ca/*4T@19om%lRѺ,Aċq"fĕlJ%<^9%nobˁV;}KZ"Ja@2-[؂?_Ŷ?@,>FekLr1/q6%.4zy1b3>wЩY4ZFa1B22N۱ħ[՜HKbt٥Z˒jASŽ wci+>L!~Ρ +6h(8y4}:f>+ࡸ]kkg_`G&>Ia:j$**$z+>L?3.ʶX&$DŽ|hb  && :lᛩв=z3O1|) UTz ?蝛W/Y)t[Л+Y͋xjvr.sdF(Antt&5DC+TB%3#R,"Xa\'~hGO>āyd瀣<GOB#uľ8!caIH8?)'9q1ѓDn;ƖfPnA" 0VOH["װl4ݙ%Zt W% 2JB,vMҧl5L60PBpa`>C2fsgc+J+Yd^45Q@S8ɸ3`x m<[Kpn(LI4-7n洙I}mSĠ$%h GސL=M\Yv `b<SAL#Ǖ@!3q#P" 7%H|N0$Cw; &jZ%04M-aӤ{0%Z=5[I,&-X6n&0?[L` Ka+,<64(#d*!ˤ}Eαk1 ЃHExA=̃[-VKxw"R1CJ08V)[H႔vJ2c&Uq̶0m!/L  bK *L`C1GHvM_sC2Un΍ ^Jed SuV1!Z4}f{GdwaSa,L+1  mmcztyA?lzNϙScKc xjbcɡ^b})fKB} v>B7K:-҅yHkO/C:v2_!O覐{ N٬}}_Sjѭ5cIU?䯿ʧƦjoK| 'n;?]^ՍH٬rByiZJNW8g+I Յ):X"Y46Tan~U@xɢX[n 홞ܿ5"LGj}+ U4G*lH~D 83Rle’E8pfl7x2fePԁ>a `6easƹ|kj>Sק%M'}0uMQJTz4ҰҋP KK6x3\2ÓTZcڎn$YƢZl;|fCqqx EqT̈́ 8ub\0Fxwle<~|(I.wv1$nb?PGo AB0}K9({8: ="Kʳ/kr>C6>P `&!Qplsv AۘskLC$vY,G{rŭd`OVڒݔ(mJP%AXJF.9r\_H"-[z#ICYBX [x65,B#2dGi2t!OFCÿ"A87 W{0ɉs(z<:`7/0:*hpA ҇;"^D l^2iKླ7?|$cxإ %őXd>p,C8)[x4N Hd'`1Kp~1o|ZjI ?}ksȕ_AiMI"|9kOɶ=vƞLfs,% @KT?[uoU~K~J~=FI$fSEݧO>>xU7C Ʉ^p99'пx]d[lwMܷԂg` d[aȵG\҂ro/O``dUwbMQgl a}d{_Ng)[4}w:-OzhWE\N-);m3wjٍnYI{jtkـniwz7m{gn.)ýӶG ?2ܽ_w[[\ˤ Nm-_鮋 .8Ⓥo\pu}Eq=Gvc~{Уܽ?I=vH1B3$s 1 w ^&=Xu6nl7oj:*WتN:BLv\-IܚݩgUftj_zزapX]U]Eo&.^=w %W!V?WWuYAMjf:SbAE)Еv*Rݩu/XE~ *RA~>[cn~AE:lnq-˼39[>~޺\WNtV*&v6]4, c[mb/AguzuU`;DeA,|Ś, =/Gd WE>= kTQOLmGϖr0=?"$7ieVMI9v'Aό?/V/"58tԨΑaM7H5X~nI<*/yp8j 1ƋuD6gfxQCh@h633H7=i_&=`}(tr-|'S3Jv3$h*dYmL$~u$&D}f1F{5D#hԀ<$5ⅴF89$+:$T!gŇD&!h__xuH4jޱF;D%؇D=wC]F?->$5ihd=BK4!mKK:rc-gmls7{;rﷲ4n2BmkPՈ\c=V(EP}rѡuvVhTh}=22(hm^DZNG/3 -/Ϳ>[Nvj*QEsPd+C v/ fAe161&n>ynMݺ%V_o!2#k/60pS`ᮈVƵ٣-K2ޝ=7x9۱|aO6pv%pıieI`9Ŝ+SIkMOR6X37g=Kԣ- }2_l'@n{{x\o`ˌa" ͵4 ?zq'lB:z2(/a,`)(R_Q鷤 B]\n:9bio Y"żjn:GܘSjbH{7q~<6_ln 8F'ż1]/j? ф}d @@#m`= sgaձʿ݄AVE͊f0qM"%=(tqJg` 6I~Zō|O17f#6 :q%ۤmn84 g<=0,T 3W<5-4湰 Bo|8PtP-EX$A;Qw{UY bv&Q1Vɽw (,>)ќPq"i_%„*e 3lh\H+^!˗Ɍ *8_Y3Uoe۶1qG`!&t+3.QmUfuag™z@J7 Z4=o9Xk43ޜt2jf%4cZ+n7#o̡}ӱ 9^s^>7fce&)ƻ`J}6KXl鱎EyJ!#]GDdyIH9+٥ B*/x|Rje-iMcY.U.Jrb_A'zOȻâ/(}3#fҤ7%agjW@F$ȉD@)IE's4I̡gH[f^|Edkd Y]PX>W3頠n@n=;N}JS{YFa>37@qDNsxGM=K1O34[Nkkh"[b#ptq+'/b:V*ysA]`Jp؟c#Y8 y etgN]ܨ=!s*oSqC4jV񦷩 ov:Vq[g ^XQqg+iv\ki ~; GD }{8nk_B&k79򢙗w%1f-P];;G .G&j `vmmww0m7~o%M. v5| x;. xzc_6HYyoSrFua^66xMxha .)+7jY~z{0kf)0%}bL'X9FZO,01'0>tUe/CU(;y:vc["@èkb,mv. Y.C {8;=vWF.:u wEMXŁOݧGE,yGVZ˟UtNX?~J=ںz4[}-ݼշɜGhgB{:]!bIw B{3d)͍पU#Ok fg`;֠gyn7nL6̖,4WA؝o{W直ُK(mwKҚd*R;AuM@?R-5T bKHcM ʟ vs.=羫NBۿfsfz,ăT<Rd:f5dTإTۮp<q zJZ'BGg/gJ YI`N-u^r>v?TP7W::%4__)B w|gs\է|pH6q)~ۇ f\g$JjԳ6Xiw~əV紻=;Ćmva%Cw2&fݒMN3jy :EZVhLs/!d3vJ{R=Bp7y$EVj~*3tv?  0p.e8 (@[A-#d\*( 8o= Jv1/U]$ n8 RW>5M ifE"`x=ݰ LY/ى(d꽊I%A÷xG'^Zqt|Rs"6kKuS G`sv#MV-gpm,\;oYurYToӘ($| 4[|.9/c,,q!~EIugP0#Ӿw`|OgS7s^0K`FT m|Sp<~d Vq^۵/ŗo˩e)F uHg__E)oJW^Zls0f'fN "r& mPㅗ P%oπ}2;^e.5Yc`xy? C&^ne?r'G,,3[vdٷL:?,3gTtr:!eUͨC&ܛ85qBLC&>=ǯ!~8ֺ~HLZɢ͊єU[g\?t&|GTv+8,u!e W'UR6<ɲܗgWdqzur2Y^Ťdqz1VCvߙ,NoCnC.2Y޶iɢDLs3xL}Aә,kp"Ūdqu]),NLdQOLUQ2Y:%4A%Y߿rdy2zC&˿6q)sdyfpda_g2YVelo* \-5b;(=l3r)_5>"(J *ʃGI"b"Ejd>$ v#˹O| nGh45i 85& j%)1xbNaZV*/n|){i!Z29$K-IE0 bZjxZ#[5j׈깛0ݤ <{%99ZnǬQbݫo.o/_xoÇ+##Z""fz=1Qc E5_ > wⅾdv‰N-f 8&p#8F9?鴌D?*e<49jMl݉b1>*?wOn ~ ~CM uv`OH륭)YӍ<"^ eu"ulgqS 5^ V&@dؠ[4-@Y_⨫+7+Yg]]IO8UMT@W ӆ;g')$%H%4E 3e~O&nMϿu,Aagd!6>șmFv^nMUiavb6r"f::I H@4-DezZZVra {pc#3:j":6N .>LH, {IbϙjQf#3 S>›raghZQ]鬹5(J35@vyE_VD\du op2u؄N-9OkNq1$˓l J¾ ' G@[Dtu"'f-ϣTӏ1[c-  3ӗܘtнqY~@Bֈb8ҳic(Vk&:ZQ*ĀdKKHox%F(SaƪN4cv|qvDnMG( όQaj]?n#%=aO\.ڋSlݼj şES"E»v07BOXy_YQ+G~%~@+RR YX֏ h l*miӀ _ ^-_`o+D<1hnh1Y([qyaEuN2{=[v '|cW|OZq#iŇï}} ґ׿w_f(FFv/.W Bo]Axy as\l/?~kk뷯?~:ۋHe~ۯ?н~Hƒ__бۋ/:8r3:77őGvK{ >�м~kڙ~ֳ2墳.A#fecܫ O]bMXgRoyJvtzrB6DW.{}xc>ڋ"EaLC6^AV5XspC@p>e(C~+mZP A.@0;p-320vC$0@7V++,恏SSy$LgH)Jlms7!>gDZmR춱 ѰW,&oG)CmǒdN>z,";(/.ƟxakcGϐpĔ7FcFM.vفnٍ_&qH0.H#Q?N03 ǀɀ5+X\c<!#'zLO#ݥg5%5>y5O-g;0PNe~mol?#*7W<׾.hqmx*xEƋ1Mx*e Q~1p/79ŸH<(RK¤Vflx4hrNWgn1x+RK1I~K+EDbr9j0*0A3?ƭ؇9lk~5ߐ$Yy}'"B$j(clߠ2cfF*uN(CupιjtSmV8˝N9t4 𠈢@)1`6-ЈX!4Zkp3#`XqFT(9ٙEf`7~cW ;lK0%5+{$cVnaT(F`*yX1Fbt i\!q# #Y8AO2c,[ ٣eUhGHX)쫠 PZDm{,@sl8,y95C ֚Cjqv!v'qspU|!n #xhd&"'K*򾒔1HRǣ=}Pt^@^@a{;^Ns/x i4w{;ͽ^Ns/x^ν{8o Ls?x>cZr<E\#=#A4r )ϲ_w,h=?%܄Jx̟])FXS1sO p9ѳ!(^E6˭,S/qW);AєNP4 ~C@n  f 拦]5׻Xp//N ܶng+7a3\ŠT6S2D jc.ĝb,o|546qJ2vc5iqr.jXs0,j7cj12^_/U[E &mu(p6яuaG;, K" TiY (.hŕ6<@]xEϱcjg 8-ԂR/P6L.-} pY>pV\s4_alS15')EVM q}ź7kڙe$5N0ncf*ʖrsbj^5hΠҙܹ<?jPզD}{dy&uA|K8.`j.##4}3=$d(N0-N xjTPTT$<(dlMeM/z Rd#"`hA-'NDa;G(gUoUTcnh5Ten |MNmҎ^Ff k4Zk4k_ JZy\#G/ Ps Bg<߆[{f<(Fd>p+].s yA$e#>fфK s"5&g{ 4{’^0n,mB![EuU7d 2}BCz .{RQ.ϦA31<76Ds5Câ2j̈JQIru}yK}ITpe}֖Z'OBZװa9P%S"-kAtމ^LZu%Fk+ 5C#L)tz?:W!m( z*ZJ8't@h!qSQZ40b3j̈́ =8{,d9a?%ǀ螒^Ᏼ4𰕇mA!X%:o d4#˰Ǥh-'5ǪM`3θ)g[Et.W*Z[,5 \a˧WV(l!z0-GN] /c0JvSjCln`=NYcRpmH43h Uhe# K+%~"@[7.8ȅ+" ?kd$>O2}>Mv 5 HQGE% q)uG߿ɩ|w}7xN|l8N!cއ2lɧa@Ojΐ@+~KTlC4Ry`<K( 4ey5[R2Zu]d++jEfhGˬngJdzMVԣlBZ ffʬY#3ܗi.XXдt ~!;w;u %罈8"/\xs҂7`'t+C,|lYaAC\M%Y6`rȩG+zeLP\ vyز3ܶ5ЉWÜc4UKQş3܀h#UעQ<&"J#pe@]S&† |NeCk/$3J;oKn.0O\q~r t0:5uht8a>[eKo՛oПFWo͊8k.]߽|N\H*̣QmSYyً? [l[}A׸Ÿ'" ,;]uΟh.4K邪5(99ERSY}!ƽ#?N ,(nr6J߲;嵇ӣn,`|zԖ3+&~58K@zzFD~E^7gOY~?~ ]2b%RUf^4~ ^ Wlq0<ݲ0x7 `8W ˋ,X̍_ &H$(LWEi[_$wdgV?Ng'$;Nragyɇw];.L [oUCk#߬VcƁktp&&u!7M6SCzQR0 M?yU+Ce(J8^FI@*JQO,z+/zB"O8F[{.sݥ׳Pajڰtxش𬀥aJΊr1F/(Pi>^ކ`> oJGt|nr\E"e.0D= )y`b }0SW( ]UֻLh>qk4.#*e;%fg՘ӚL22o[aXUJkƽt):Ry'S) \"S*$kQm) {a'EOj"G~xu_'^:>Ir31"hqF;Cj;a 'XgP&hSU9Uy+OYVls?\8P)d*"UF"P&k⸚D/] L~,~AUU!Z@XՅIԽ=ͰC@ QeDKs+UPkj_&)ZGF51o$^\W`ի `vY] p3r~.>\?qr,)&cBSu*U7 \r|j<p&H5Mż4^y;#l*H#xYTLH:+<* Z7_PwUW:ߴj|uĸXo3l݆|>|-|y||w?'Osӱ?e:]wO _pl*ڶJ9s,872El`5nBRFvM۩-(XPsguڰ^TLlVCyPvG}N Ͷѕ\q9{ -`]:?5dzݎiKDi_rUSc 9ʿuyfԶ\ΊYrs[LT<3h1O*8U&t}ę>.Ft$_Li}.Mfim=; M"g?Z$JۘUkX.b^ڨ8Yz -&;Cg.??s%kDgTJUC곴ϒq&MXI_߷DYl_P?߫v_4<,'X-{fHJ7Yvhk )[YZPEA&ӱnvQED 7Cb+,Rp^L'j !fu!)wJٻX7q ODGE괘ΰ4sI#tʱ1e5[7304?L0[cN;72pztPK#D6_<ݫ؝*z~Y9@1{D°_n9G"8RA]z뉓1ogz#O.+ @#OPݖcoW6r{岺[ײwMA.s C玞RNsz鸊`/4נG<6xhy5܄*tq'/Μp?i^ck[MVi=^ȒL9-Y=rnu2wRXL"Fd9L޻i3GQk\vHރ`+(9药)ArvL:ut>iJ{Q{M5th8x䳏*L>m7 ؎; 'ece*qcc 9Ў4iY=HXUrc :x-R%k?`iJ[5TF! MAƩ^z?~ 3X]B6^\gb:almiQ=*zV6*IZο)_NLMeoql3ğr;ӳL;3^ zqJo4^+ɟ%~V.;XqQt68̕qu)B0^R>QQAo3XY8u1(ddr~bرLMFweU%~d "}cjP"URR #5x9YJ=6nt^2E rqR-s+Gq& #$U_(K> IK=_ 2sPd0>7*ekd+Q= WfΫm^xUA Ǽ\L88}9T({H,ց~r+_r`1 W>ݺ kBF`̈́I*^S!핸ta8 x0u#+ac3`yTPD[2=XDn96/?/^4wf\}Cy99Mhs݁zQXo۰w%;_*`Hj<Ǯr$zExN1X(A-f(;5=*AIe%prc,闤(1qDtbQ^Sfg "*~`:_~2MYkI2vjgZNUJh7Q;.mP"I%=Gʋ }@BT F3" l&fMѦA%>iF>L6_cܿ }e7m>-%!l\GPo/ɆL4(+yOV䡦9G k7KK~XHCEjC@bx!M^Ϝ`ޘy9dfD~̺fI+ G"8cw] V݌~B6=PTvU,#d"hJ 0SKf)ik(]^ by!wBRאG0ٓZgǘM聦Y9hAԣ!B{:z:;+nr2S#kQ&r!m v%Qy>,-!դ.` stTTS2a1~ç0 Ύ rMupR0qç@Tڭc` 2?D'y,Y2x1thд_ qV*,zN愙`ŕ2qk宬x?4|,"qαE8mi}oG~vUW؂R=*{Dv]:v6H&+Hwb)<'9Ay^(JaJt՝D- v;-J`D:Kbg~H#%C.ҹ"UZϳL3„L=j+&Q塻G oKP(:DwDSqzTI bĊlj U"foZ7NqHKUq++f7[ZNuR1t0 fVvҊ>7nS"Q\c{R`@桡W[byǟbJz>]t7IxGЛ.ApyO+p$'3{iJ--p(6  !NV?`~$@m8JKDH-$xr0hq#vN|!gROgF >ިF݈_cU<N{Q:#j0)i|@7^%F-JE`rɧ'w3;Gj*qCdK6O4BІ{gptwlt'!T9+y!9*TPm(>0qu埲X>.DW 舺4(B?{_iֵ,&w%Dř+6{J\hc=tF7עEr6IX &X7"̔'2'+UH>$0f(~*SB$W59#ŽsԕZf qc*=HD\$$ҭ_B{UF U׭xrWqANP>2L䎬L1YOW %pz ղ]ZwZPڧ9&.RRl*`%Z^ƟAR!B/bXR`ں%Hɶ")KE0eaJ(>>: 7{B0)h${m|ZQap3᧒ k_ a"n:V^AVRIQ9i}WTTAEB F\d0 0X v=?ù8Dkm\v3zsmwFOc={50)GwhyK6m2Pcp:LyGTS46T6[b=[rFWAh#6$ G[~;ƽ;?IP8jkسQ$BI@}-@UVV++Բ:,̲K&csb^WpG³y`7Ygy\f  %C|; C Dce}ٚS&l.R(" *ю,>>` rcZȣemRl_QʣxGp *)01#Ue͙d1%u/j}+`B FvEO5t[&4X| ^ǃTV֮'c’3V2 uNC=1< IwG?4-1'Q6S@l+V9A./gk!d_8˷>??tv6` A?~Ul>{Pz6+0!Z4W2N|HpArŖֻYKT&yКz.`iVf hUz3(6 &Χ`j1E=˞'׼ゎucR8pV u3mCg*;LM2FDJ̀T9׀|!J~Rp8ue;?)AlV~n.(zXu7ĉ4~,IiT)%ACJ̾.;eg&@A|\0I,_6քZSJ-U ?+ "^;]"-d!3/%`#X k k($pJjdTP3FqR2Zjr]eU ]/M̈CHoOԃԐ!ފ,nz'd-+hG:q,x!R#B (3'7M `=Ge:+7΂'"wa쑊6 0'Ѭrt}A.]sH$r4y/$6ՊkjX<[E.%F;L7Ia9Av45S..&XsK7H08f/LkSL2tjiRhzpeô0#\!E/a_X&7TJ*hGKHgq=LT3 xQ7%ިt" 5w`K]r}J&<qԢ?,J[]OwD Gl'̮>KRUv4s5)bN^N$ԡ[N^av5^>ss(=g?RH;w|G4fEM!^Mx^/"A7Ɣ:G,Y9 ?¥EDD>X(WZj\#EVTaS\tR sHB/|WY }3VH?x:I֥ d+-8{O&B%`rʰ7JGm#`?!]iI*JA4b@Lƻ*md=0Ӌ@Hq\ cBvӐpfc쇎@:ʓb54HdO_qcSJ?LޯOG_Hiə0z"KSeRbb,B+-1`6g۪ K$SVS{`<P'Z">Mo/K7]P_]YY…3 O_X\2[_+U h=qqnW ]f&ZEXy%_>q4$4֗E2\ުqFJ(-K>K >@`)=5ue#)TDdRH3O#+nh`O~|^uvfU6},ǔ)Br0.[!Tm4 nk e` f|wv@р5qG>8 zӚ8nR5%7z_K7*=5'L˖ /Bvcb`OasN-qEێAJ0cowuQ{vw1}1{g>LqQ /Y%UX*"hE)tTchae -x;OEu1QW+-xTPQ,-n@=2C,5E}:gQbWj2xTŘƇWx[h;~/P0wٻ;w+eV޵_W}MV}]W}a~u=[&Tǧ&0[}>Pϴr?&Ż "WGdW&ej̃эX=u S:@iBN}Mq ij}/ S=^e}t9vO-?6>9Rȷ+eNu,dn;2=uqϾCBf{ ?U@, fΘ{>˞[ٳrX$uR$EޕF;Je}<]SUٳz"2U ]o % 6}VgiHk%L=0Ex v!a:Rߘ#c~srN>Tyd7 #iRnzN) >p2u06D}ӗJ(cjDq3!>iGcsn.8 2 qD*RLU̓ZTE-Z5ji=YY% DIUFIk:Np%qsĴK6ʾԪ!5EȲ;k'rXjF`]cp=Ba{\RYCES{RG7~X_[RO vrR#54\lLff!D-DM~$6fUj9ECt^T ll[vո؇1mI+nˊe,bb <(V #na(l:m' $s<0d;Dv@ao^_5t>I}_b+雖ÝЋLg6>V KΌMq/TMyDw6Mdl]e4TZ3Ɣu”z\Ա q~=UEGKZ$|9]s@oTyӛ|3:&'j+Z>w NZ\a[ދPH]{82>(1jcmlVf VMiGVYa!O1{h4wI1M[*w*wFb7dB3߫UMJ4TbBWRWW/DD#>YM@n7s>͜8ׇ~M8l(6`kphWΨu9̝<a w*7(1c]zv.V}r8Oq0Ws3"̙sfy6$K?0:M*^ܜ'3]4D?pJTI?ϬY/C?.R鼝%qz;qzqzUqzG(Wpyߺ;ݯ~u֣oףپ)gM5y+v>HoCk?ٿ9 }6-3QO16S;r &4}o69=ƷH=inwa|AU_pPl~PϏ9<_ۆxS7}*{x`Pr6dRsIG}ޟ? {y4c3SPZo5%OjNqe+{edJk[Fn^*BSKΑX,n*>/݂ťDA-KX .6PU/NG$Hؿ_BAW ˡSY("bٱ5(+BKT++DA%`נ*- \0*!տ忽n?o_O*L풬~ժP KWPb0WaԷd3WRO$ꉴk SaNm\"dΊ814[GNO:T5D$b$ϺWвh39)Ҫi".*Z5F*G1}Xk7ЏO2eR,Xb`g!gƣ ͙δ_OEB E=BeMVEZS Dw78 0o2RˍΔLu"$prX)$"2EMb ^{ł maZAOJ30!d^ѣ[(h3 DWBK@X*,2`x-]pvԿNZa eGhx|}xbYҼtχQ_mo^$v;E~)|E<N۩کX3E17e6  7Yx @)sW <07È2E/Rb a"5vLVK`=1p .0 * :j)p3V"'4XXsl3.L j[ &nAX3舐lg#x='e; da&GHg IO(f6&eh&u' KV 'Gzo;5`Fm-sP)u'@$vHPhM=L.B!B*  Ў2gB ^*zQ+ Y GZ܃i.MF XbXb&+ bgX:G"_1bo~SQi\2QP1ApȀ; Mej4~lx!~E:9eFpAI.0٥c3p I⻄Ήg'iǑW?K`9pm9iFı01ŃX>Kcݳ`ƥC(Fdpq<}L?MfrOPKڞwzVPKiGVMETA-INF/manifest.xmlMn09ϦYT fL,(ܾ@U% jv-KY[t(:x~COT4 $@Z-$Z'JN "CicZْQ$ΔC:ق3@#S^81K_iՏQC)Aa7+45i׽֮*_/Kn^IC}"WxCm:g>ۘ1u;G TZ+ )3k/_T^kaMϟLsGȭm*U LeokgKԴ޹qVy} ?QS,SK-l-kUA))N8S;F9})HJeWO|vӏqe7HYըe]l׹[>\yѱgU+(&Ǎ/<\TZF4n&u?rN%?sr y_lKg$u=}Uk_|>wl_=#*e^9' h9fٙ3R8o;rз*`'lۭWؐ~Z`юiniWϟ\;K=r1 ix M-S|m9%r wڤy"]0{ZX)CKwd0 N5|7E*L;taW~Oź$jBJIqòLq0F1W[#{z7وODsKFVMFV`A*7Q{w %񆴵hӊ}=cYF-YV3|$%5)Ű^~6J !b%5F/(1?)f$M"ayuz>sI0ƶ!rj㐿0ZE|vf8"j&ˏ{28^=p_=Ro47Uw 8!yAAc8CLOS~c&1hoHތiѩx1⢆TmL5`z4A% RZCӊՃQօGe$_&Zaaht8߹I(!OL4K:mM?٥,CK_Hgqrr ,xU~I p뫫wXu+e5XV*@FqGoX2$1@!2U>^b@/.jtXcD9ò &N2)vL>:.J+UI1;c\ehQ`+G8US/ʸl 9a*+@kgSg 75$xm^樂Җ޳gϋR2j'{גnVSQYi_'.,g/ȻK̟fپhj'K M%l-dUPI|RlړfV'ڝf:*L\D) tec&ItF!():=)ٸ$wFc.'IŰw;uF#? K'ZxDӔFg)í>8뫗V#gYFKґ0b)HCJ<.2̯ c|te ?FN! /,_(iOKJKJV,e -%,RS|:&aܫaCWSpB0+}8Iz_n'M4-}qom>_d))g^ѓܿSQJ"?ɓ[0euxy!秎m1QN,/߲qpqA>'XE87~P\słR)7bg8]eNрc(G/Jȼ+dqhyzK\EF!ުjPe[2?G?oXԑ8$$Czv=qHG4W/םE W[̿O?hv<ٶ=g3rں3_/7cҧ:v)钵OLіs-}N>Czzհ~=.6p[ZҜG'~|/qք$262J.5偑f21; +sv^253K~ܱjҌZJ[.gmn~J"aԸ{;Ǻˊc]KKsm=MkjжGH;%]ݳحg:s{{^vK)oX9{~!MZj%+oK7t_-s\8aBŁͫIecg?߯Z9C߮xlWȈyA ըe2{3ˇŞJu r'+ȹ^<厦7GKeASѿiv4?~2:LYۥKcu{6m?()qE|a٭]j۾GmH3K O(wTVM CH˼959q䧵R:>PEQ֯KT+H˨yLǐaAo9x5sXɃߊ_]9i! n^yRX;W.Zѱ\l _{ܺ0G%+$1Gck{}ϊl=+-}OF;RTS]>2k#҄,W |tm޳ ݚWj4 ҅Sf/Cw|Jc-0Kڨ.u @yFut2tl%z{U:)ѭObbe[ V.Q},GJlUE\z!f=?׌ѽK+F~e|[}G0@??C+t7׳LyyO <+&NkYaihh()/k3Z)i9S2ax[]m-SGSz|Gu<|l꣉;dlvtu˽wrv**WґV?4kua?JmL?kfhNoї8~w{e<|^  |~! /ogQ +_Ȭҙt{x:* JS౧Ἧ{ ~Y &5pΣlvy :cj=W:սuˊ? w}X',g uҬ)hNꇆ8. 8L4oW/q!ޑlXHh.ޮu]p/9Rϔ)^ %Mğ}}sنI)Ŧ #'xxRJ^;[S$RL`t66[+^+ơIGSF?5~}+~ˈDc  iqdOB>% (aBF8X5hAAA$Q"|P,~+6/xI&@ajEo03|",.}w@՛6l/aJ1msqS&/:Pνjs8ks^&刽t!wP{}wpbKw2K.ԩҩ{7a`//9`/ݺlEЯ :kԙEм a;"QwpT< : z:JLzb_;knܘ_rk6)^3-5Poܘ7,xqcΊ76k>F{NJ,fDmoL56v #)v !* `^TI156v *y|Lȼ*̋C2/_}c̋} dyTXΤJ[eNvh1 wpmAI'aCs-QAD;;Do1[ \u. ^~OjG{}[?KKٺ^sOjuRytݍ~m Rfm];7KM!̋^J՗lMuVVj!Qq>L"f?M4N?_S/|;M5ݡwTMv7պCqSm~]ז^/q܊^{N3F VR:YCڞ rH{ }SVɾ DSk DMkV = S[V]wA]\{AJU_p͠V_pPyRƊMeHFXiNyNƊMMbF6uW`zZo>SwŚ&zØ+mQ5}0'Y;1ZV[_?`'Om?άx'E@00T^`iЮT@ 6C>{a0&wDbc1Nn_ک" .^bEz=Ƕl\6ma~2nDWWJ λ>Z@%/pΔv4X}J OaMɰ.QGuT"I%=2W$ hT'U4'Mjb"yLzΒuyd}2>O%Qis$JMbPU_o&rO$rO&rO$rO(߄o]oק9K}Oۧ9S}Oا9K}Oۧ9S4UZ$. RWX(9O7sc?xYz'9Ot1gxz'c>Yz'c>z'c>Yz.% R1gxBɞϚm<~v3֖GKLlG221O| s-?+皤2©=)^mXq9\9c[g/wub?1E@wٔ`GEXKn~+|F8 X&,*{?.T'%yyr%~o,ʻuIsbR6m x_l t^֑n]>:{8#,P#}pAzH/H^>˂:GX#4X:oDaqέcEva'ZR4 \/׽mS)K6A8`WyĦ~eS[}:=YeCS/mgBFn?Zbcv@ѠɾF׿b }:mefɜbcɢi-auz)ȑ83?h*-K_S⯳_SP _Xu6=dm8]`\31aQO7<#Y6*gro/.79a/(wc%~ :3 ̷r`nE +q?I^xd)p27'(7R^ n 0 pYb{>"xA{Q^p|*Qm1xLf͹+bN8 (BIz*5j.eyDOJbb?|RE 5D!: bh"6HQq@XZ~ul$H}Z3!ow%VȗB5ARmͩ8]PCDxUo;,6^vQh]Ÿ en:B'n1 *Udt:w0i5<z/Yl rY$Y.f.Iqh~W-a'#Iح1т Һ6ooD+6*e99Yb HǡBia`n#_@:!(ߋܿcQ(X)04kf옃ޠtf}V}Qv`C-ՋT_̔Ѐ[T&* }ȧ4J` PC 9, j'l 1BοĬZBH඄zCi`Frչ7<]¢dE/-xiqVaOrK{hޫa +9AHS?L!d6 cxyHG'"<ʀ[i2Lx-2>~%U0pd?EgW4 Xb7RI%GBZ6UUK'%Y&h1] Ǝk|#?d/6)nFܤd2TEUQ)I(D+U#CJtm{懓 IUozwԃ,) 4Rŕ$t>*r1PyC[8:PzF$]`Q)-;x#{fQw<wa٭f-ݺz\r^,39ފm+U_2Ӈ.9[D |sȳ=eq qtik/pe7(MhJN,voCfi! ٦wT -Y=`VPme:ul,5ڳVNBƻ l*0#jm)F{%3.JYAiz$W[a4aw%^an R&sTU {^R6bnb:L_1//rsh‹àAY@. hmd(([Ƶ=ų&`'b5ϝ'x)jQcXeTrZ\CRre!? 0e*qM L{!CA> .Y"(k'܁M]J!kFV<ͬR ZM!(X.0O-!|wx7*z 0X(Sf1a=7 '+v E\X`p% ӘW,"W 6FU%#]"]wv h&Q #q>0(=vmI{R%?bų:1q0U'șBVQ+N'8Z a/r+bU^οi@ڏMJ`Gm3_\;q4mhT @h xu"Y+O#X=[_lQkva4vag\5U+Ǧ4(7\X-7x5ǃ*-Ձ%7eA2GcӲc?yX=sBMݐGi:hdZZԂt!VА6saZ |i8rn'"-hbx5Oٵ?ov&-%gX0oή7 Qaz4m 7O'S^nBU5zUHm`!m)ܖ썶?.J!٪plopxC>-]*Guߎ)E _1Ep\d^TQ]8^\,,,%.{Ps]~=vl!f5`2C%<1ft-TgeoxtӁ d9E0)j?3Ĩ*[掞;Sy*i@wjEDgM(ZeֳЌlݩ6czF9y*5g/߰=sup1K=V&PۋLQuZXO,ޑbvLmh aGx7Cn$`˲pJNٲALsj&B~ mPT# oǟWKRA/M@XBV/JE5.S96I@/)njb:*QNjo Ys=\QWroӈW,}|\r6nYt/E}L 7fHb8B#@xC;Hؼzѷg1.rr넧)z}&*+ EC MѼYl.an:- +"Qި7 GVꌭ`TVTZq[1fGx+񠳣jJKUjMQ0=5LQȭ!TCZmLXo4ǧ&4 J:C4^v:EWN0J~/N/$qLb̎v\oYI:ZUgb v;ng]Ixvd~7rN7ws yDT0r bJQLe) y!Z&7agh.ң˫heWWTFdieϪe7 ̡0* ky 8ގEHk֣9+>B"o,`LZ*Wj4%X8(|Q֚&heR{o]˭o1t80_evVߵFpFΥ 7ktVroe**`-BW{YyxK[aR"q#fړ!7Q#]' *2j7]b‰A2Q*dqS1>;k$^R|m&.KRI_^yvJzn#U#[74W鴭ŕO;Y=1HO^畆?M%]RY2`\|=zk6uwqpbq Wj\VxƵ7Ed 9TJ;w[r:emC(v)c(#$m- #PV&}GW\fƩ/;ӿK3n\Y7uo@6LܸVαcO?Ey8*#zN?1#QE!zBm(ce\~>2%{}PQFܞKr#"lnu=zWJޔ$륧8Mh}W2'Q`0$rlGU8y5WMUK$y@x+4a(l%Q#::!`$< EX$ _I"xw%R$<:XdB^xc.3h_˜(zŻ$׿͛l9o^l?"{GeL衍Y cOWaDiPfuOKx(Q1=j*7G}sUzd1`Qpnk/[fTˋS*Jƃ\٥vظښu^ʮZ|3Ga2G: 5}MT_Cˣd* M*dS :Q5s9%+6Sڈy?.6_k(mEtG]"~S AV!aw%8Z঑ߚ !C_BhPEk\YDrQ\QSv`o}`Q 6q ?5pz 2n:da?qgWeUնWa؅?ft[#d?/ciybRյ\l`TY{]9ġ]Qf,\Tz()Hny==ұ)HD*+ħi*)ЃBD E@vő9rӧV:yvP%ۆxO2CUI0Fcǐ5 /1lT~0DCz\-{Pw`֎SEՈmcɽ[suqԨ7[iG1^d.JZJB2!J:XSֺE7DZjWXU'FC11L:K"Jl w33zȔvW0֠+tQ) E@4TyRN%iR*3 ~چ)y𪄎 ^3[eBy!X^jt5*rRnCI7zZ)]q-uN찹 S1Dɂ _o ղZ*OU[HxNl1Fd,mǸWRrj{%ԟ )<#:|CKAM^OlKDO=!iӚWqD2Uz#!}E;Q+#˾!oM=~E `pӱUO_"d![Z^}?g0A&>*4ƆOalMSٸ.L< {bru8tQYjg66LVHO?#1Jud8AELt_QmFRp} 0ٜ׭njU¼85 ݢjqZݽ:Ƿ[_wB'>̥}6o bd"}9֬0XTq? &zf$ ~z)(\o/blu7 Ww˯55 N}hG+ar\,o9[n\&6E`6O^ZR'Œj#+v;V M2ɮξC.%BKĂ\ XVuP߬XjWWԜNl"v'sژZVQ_F hJoL|{nT{ {Dg*:);<PKR-PK dE settings.xmlZms8~"$et )-W \ {#d9Wj}Ėw}ywx9R]t2Ĉ}yԾ-}|'B#0AK }AK^ߗR%4 b 60vu)˞,̘Q./wwfir§ǪVU[El3NYRKM1MwkٟKn X?[/+jC>PF6o*7PVnߕ/ sPv4/<2Û֯Ntvx۵m1.QAk1&KFPLGG6.4|OGYreKu87ju;VQs+sc^..6/[WׅE'%A~PVODc0>mHnP@V4}.M2{.mT^}GA@h j+zP`Ek:e5{*fHN}6[>| q#Rqr[HD"9}6& SEs)q`d5y;BP({H?'H!OT Ĭ<(HA*̾15oS5bgR7҃OhM93N/?κƚQï+{cbsF{szN\x>-> 'H3'&" RBX_@ht4 Wس\~~Z.OM"*!S^-UmrG[ D}TyOh(|iߺ'-{PE K(ʃT[(u `r)>f"L#Gl8b}6cKfۗ?SmdeO_j*S&\}Q2i8U5X2jKB l,Rۡ G'0aB:GYN_v39'"DЫmDCrGuGv ,H̽da'2 J2Z[mG1vG SkQtA?Z'| K.n.{?6F'K~cf֤fKU>[P'xR/2lYn{vBl [y?{PK4z'PK dEmeta.xmlK0ƝI,RJҁ]P$֖$_q&)tcн߹TW0Vj Q @(T6m‡]%kB^,[۰5hj%Ljn%dIh*~oãs 뺸KbmJ,CwF9;sMk Jo]FZz| =aS0g4W6E}L_8tE>J:IA6}TPeMfT+?;ov4!NK.̂dk0JS:3ȶ茉9U K>o&S+RJS}KloÙ4NbG~ `K0C++uhq~I uGnۆSj \449fSӆ OevyyW̻]izCP5[~PKs7PK dE styles.xml\6~lK]?.⮇C $)IkAKFZo(嵳  ypPfq&c|8ӿǷyCkeB2ù8sΗ8,YQRKZvZK5nQvWnoogVGVn]Β0ui{k4+ --)??BxۍveqX,ƊZ)W|ERŕcb9ply3,P_$R^f+zC:*M4޾&;Mܾ] ,;m@3RvSJ+UeJh2$dQWctS;%ߏ.(" Љڊ,/I$^VPg:PG0ldX й.S';LR'U9L  IBlHp,i GB#9 ޟ~KZ =$4#Y[),gk q߾ѩjRᯀC`sH;+#Eȼ1OnL򄄞a 0sRk|Bx7f(Tg4DK`s08$ f1'Ĉ3|CD-Q;qxz\iY]׎VQJ6:n*$Z^;(0&8~P&@嚤iEY,xtQ-J9 oӅoQBwh aQC{cB" @1T}2ʔûSOrnrx}R )LgG6е1oH?@rp9`YQZl@bA "Y,H2@bp/pL,Whpgc:zD .[]}4c!ՙ[`%n(Ց/@Bm ki[qyƭ["aXÓn oR$EdRweR if ;wAB rnٌЬ#n]ef1!_tT|b"FI)vGo(!! \:ZCK+8?c\n|v.B)A,vkqAAu,g';ACu6Ueĺ hNJ&ZYPÃJ5aEh2*FwZAUPC~.8YŔ6ܲo':P{3Ẽ<= bKJp^*Mwy1_C=:]yԁz&@}N+`|M=; ހ93==R)G61ܱ7?!3.̦WeSu7)6 Q6Sfq5j|c*ѭ }(y'}FVTB2m8soZ%[R(S$ T?\_aB i[CԽ,߷#E}c#EWDv\^̶~v;Bm 3xx- 9]LgՓikHp{܆C#Qk-Axacp!/SAORa|y{YrAև@naxOGZ4w#WH. J|Ǧkwa>l%p~>YɦW7^ɦV_̢l8;-h/ryx]$1jٵ1qpũ^km*_ MKWk>\WyFGxFǦgZ.F v>} 0ZDQ-Y RAnT 'hyT|V֬ojY4~&gX-U Z<өK<ڗ{cypJPΈ]r_}Z WOnLUQW9ͫ:\ +Ù~ء=Ӕ$,;WRa+Wn_o*_zU4CJiw' ,w]VԲnK1~ՔaQլ]33AWƗ k%tڅdOW]w~^+{?/+?/+xց̏%0~xe;01ƳWK`<U^CKx]:䕁uAIrϩlJ.!0g\kJ3Bh檿UyBi)Fۑ;W>Yc8O$~xHo3s{ R Mkh$ZuGRJZm<$,mmY80Ni̫}jݚ0윜2"?4fL0DMh6m1*(՛KʜnHc}5;yZ-g8Gh298د28T4]=Lh-?~'\Juv ,AXmP7S'|i<TOBx%4JIkWgǗh PKE= BPK dE manifest.rdf͓n0D|e`9\_X/.%}]'*͡]fh8C;4`kg*|>y&ڸ^jj~ *!eI^eYExE%yL,Ƽ FD>} fy%N:90;:PD LچL(-&)}܂Gm-c1su_5R`""?^v}㧓 Fz{ ?VG5'PK=PK dEConfigurations2/toolpanel/PK dEConfigurations2/progressbar/PK dE'Configurations2/accelerator/current.xmlPKPK dEConfigurations2/floater/PK dEConfigurations2/images/Bitmaps/PK dEConfigurations2/menubar/PK dEConfigurations2/toolbar/PK dEConfigurations2/popupmenu/PK dEConfigurations2/statusbar/PK dEMETA-INF/manifest.xmlTKn fU8YT LA!o_j>U*V|{ѻ1@xϢ@2Vc^z@dC[IH6iIѡCz=1+K^:,_\+n\k %VY<r,uJ# 5Gvdʎ՘Kfwng b?qên0QcznB>{dxZ֯uso8AcL2d\#+YPKw,PK dE^2 ''mimetypePK dE!.MThumbnails/thumbnail.pngPK dEQ*fXE layout-cachePK dER- content.xmlPK dE4z' .Jsettings.xmlPK dEs7XPmeta.xmlPK dEE= B |Rstyles.xmlPK dE= H]manifest.rdfPK dE^Configurations2/toolpanel/PK dE^Configurations2/progressbar/PK dE'^Configurations2/accelerator/current.xmlPK dEK_Configurations2/floater/PK dE_Configurations2/images/Bitmaps/PK dE_Configurations2/menubar/PK dE_Configurations2/toolbar/PK dE*`Configurations2/popupmenu/PK dEb`Configurations2/statusbar/PK dEw,`META-INF/manifest.xmlPK bapparmor-5.0.2/documentation/AppArmor_Developer_4-Policy_compilation.odt000066400000000000000000001136411522511161100265250ustar00rootroot00000000000000PKo}=^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKo}= content.xml]n#OQږ{b7:i3Ӄimb3(qJS"@!o'sɺbG2mU<<<(ap;k93 .>ElVa L->> >a\LY"xEp't6ޅssssJfٸi4sOqsNxª҇c$dʬa [j85q,4,ޞW]=%52>\IչplUn$)G0#JvO߅ѳg"0}v|7x8Y49mj)>2"^@T~ m rD(+841h'z(C)>^QٿJyYPrA k*)֯o WA<#9?oa)ǷG<׉SY$H!7Z|LK:@r07yKh*hu:>gimzOf RTOYFfG[> ܼ"_ߞQ"@)3y >|D^G(P ".{wC}w? -?:;7r7ԟWzC_*{^ǀWE^]P9{Mw/]nBZ/۽DΎQT; {"GqfO(/ [xagCw@;[fw_Au܄TyTW&4n @/ݳ_7ߓNnJt-I5l*a˪x [kزBp@k*v n;^j虬a85tg&>UzgC]mKwӲU.ϮX\I~*N [dz[aE"rz4{.;@V*q SoLxB3V%4PKf:a]_JL'4,rK鄆K)1pp~si 2m:a6_g48錆epFܰ ԚhU 2j:a_F0Mg4i˘C?ZiVڢ*!G~ߵ~O9@4 8@]zPMw ָH4vfY)LBf5 CԻa{0IؠUl~ a(#^zj/&,&pV}NZQ)Qu٧zVHZH8*I~7Q[Qe2^ua 0Fu.FrSHWVHZH1umETR{ԍT&(sBʊd3T)et- db4@XFC dd4@XF db4@XFC dd4@Xcbl!% ի'Hb=eX, b2"(e4DTh,QY,!XFCDeFHXƹ2;cbzĞ0,P|b-|-|b-| g2" (-ј2" )-ј2" (-ј2" )-q*"M12.-a{3޴^9dNh dNh dNh dN88dg\Y<oZPh,!Y<9xFCe񌆈M3#*g4ER3-a7-(g4DTh,QYLh)|J8|"9E ^*z^{Ѻg,eB4)=}5ijA6 ʫj;9c@&+%_ʁ0 EΔ% Ƹct:r ,` xN맯c^8n$@=0N8t'`nå d:1w&?r:>H΅17N^+IG,a $cHƓXN-6F=SY-]'0cn>Q6B:?`؄&(,߸w@E ¿I\%&B(@e8|̅bys/˩]Ĩ;IԸ!gI za8Vj6`10>32=ȟbc8N»t;H y53g`f׮E+hKZ% Ҥw|fT ;Ea#y]=d r=2x9q*G=)Z"Fyas4bf22'E |`J\ܨy,|oʁ̉'@ϥhkX]JG]/+MB҆ 1dQKb\b8:3X*z4jU!2<= K}B81D&q:(,P]cp"S_vG1L P*tmPaHD=>N*^'z0.^DH1fN`2 r\4r ^ HsߴO.K@O!q_L[0]?ňt!VX*[= ߇E{2&0 '0^^G%nL1sWj t9*ЉR_AGVSࡷz9+QۢƠkYB' FY&Y$(FrB΀/TM}%18dPU$ⴠCSؽDahBJ&a11exW)Se{ʁimYhWw U+ȃHga@X벮~PR\0K*\8o%@r:'X+qH;֠5bYe1JT%")Z:Mv'T8[O uJ>0ku-aYʤ5;:&;"L &k(ntpS_D%~+"" !z)hkU;M Mq^gG܇S›*$ҹ,fArFf^ * ?m||0HL|bR0@Ufh}|iJ5JXϼ1Td1 U8}} K?gLI%G=;YkYe8ƍ7c| 0wG`D48y÷#z{[!H@4Ԉ{V!`MT 6:BGPWPVԪvQXq&PY)ruXV1~}caz"jZr;ƹd4HLXu l6sUrsY ^Um[Z4΂W=1 O'"G-ÎΧ*Mt_fޡᤌsK#VM!dD<.hAs1?4 zM:Iy=y3+Fy+FD] fcxe%U%j.h؋2dZ#jL@qH-"dpИ3GjSF&D` qī"F'bBrdʛxE(o5Oynl ylW4(`s [vJݟZ.U \h2`TqebF3 @@[ҘXF z%/zZ1\AD{cHq^ 0>k]Ţ%qml!Ϡ(/yB**teaR4e{z[z8O*HyU>H-!Y !~<djE'FfFя ɀ38W1&ڧQ%P/A6T7bIX6` a Ϩ|{x*^`1$Y,ȬcO"(f(H̅æ=Oyɶ@؜K;  b:976R) #m5mӕ`M{|.0@"ؤTˍ0jQnmژWezX%@0Kyk’"YY5:FN QYalN:s;J$#r34TJq lwY_3! bUvXwE"yKg,c)Uxy"p^sPsˌJׇ$12~1ҡcjN=j: -x R,' Jͅ\D_հRյbᴀ}A(Tu~a9{9 oPe漂^Qמ*}0}q>ep =I\:|I TjfD>U7L @eo`qnA$ymN>V k%JMN @8DFӈ9k()9:m߂.G|s/c%S~9#I}Eo^5 zb?L߆{lj W5]/-+r=BF&U:Cm u8NLB݈i!yeb' Ė/vJ"<_Su ʊj{)!KcFI$k;0:efl1Dfd_C@7Bq(|n@cB6U\fMHN7^~1y*u tpӹXӒ7,g[ +LZy=``y<3:}?w2T}0g(L+>WBO^!{:B9RL#`GXOte6XaHҚ҉@-mܑLK?1aA>R;ص ߓUU5?P#;d0ˢRB.MKjUdGOJUqr7X{*Z%릙 " =RW@://K0x|7e"őO!=VՑG꾕roF޺_^a^*oҺ* #~2YC 3zb,/3QMTYF֚m( e|a A?Gg p#oI 2IqhWU!/Lv"?n'|gւ'_OlhoF 5z F{ȱgSj&sM*fAՍ[ft2Nso<+ߢuR;_gKHN|BAn 6:Ӳ. +g+hw҇YA:4fS5]QGl? ٺ3-^^@9"chyDԿStqFёf?#?Uk>!8xo0'o]U喬Ʋ10P<+3捀BM^Qy/k[-b:2ܯc5gywvyսXˑmTYɋ%й酢1_g^Ej9&lھq:mEwsqj]Ulں{aBy-W7 Ε?iCŃd<Σ:4LJ^EU2W"Zf^6tbg (NGsfʊٞ3T ,yS{SKfcJOk/XAUUj6l2PF+J*8 pb<a`x3,P$R^fC:*G~M 686{gpb{nDE߻m,lZb۞O)mTt*u<п-,{ňbβG(i 8|aj4W]pAh/PδIDdizIaݱ8v:R \}UXvХ)Ig¥?IeYį+ ʨ`(2 $BԗZ(Z ԒiuK4NYH6-(_ o馵&D~'yLBon2Ixyن񂘏"iW?i^:|#ђ3Ijy-GADeeQO2Ό/+K.G jzx\%:Y(zNA_㺗2xʴZCEB"ֿA1A d i|&0\A?l>tog0;Km"/rOx]RkR0""u7E)@ )l4IT.ABbL5+JtZ\N*Pr]mQ|N .#_]}bM1K#R+a3R'ApZ5|W \ DCc>0˕Ze>1 >F~N ^~y*Qu)~4v$6Ԅte67Уp%"1tY{9gQ`eR V݀4r3LM-6[Zzvů|rIE PjgǛc;ʰc8C$Y(yayAV*c)G7ɔb'E҅}F0IO;,y,wia{A bbF祈sPҨMSyc[+߭J.3fL'lh|pueA ߫6TyV')&M:Ku*u_)6eNOM\) jq堑M~ Nl[gcș0MsPOmh~A?1 =14MAuTɛMBQT`n&iٳS19Zh2|ORk^3oGn&JY7!]3]Kfm.m5(|MqfoZ Xϕ A}}.lCӸ?g{n=59NwvNLO]CT[nrL$o0;4ۨ4qҹݥ[s,׮檒h?408uQT^{v˵Mu-ܺwK-[ ntD+XY7{lfuw\EukHVa@Cr xރ܍n ]a|߃k`|wc?`Ƌn0^`tcƋ8t" ;v#< @{c ZJ tmM#yԋRY`݋ 'ߓE!G'wEzT$0͹A&mPWKO#o-!CDx?5- iZV ~g%Tqaih&7Plp0$A%:(8מ3o'ϳlJ0ߓՏ6<ԆiՄ FFZ=(%$llŬ]PK Sa/PKo}=T6KKmeta.xml John Johansen2010-11-28T12:43:202010-11-29T10:03:30John JohansenPT12H39M03S5OpenOffice.org/3.2$Unix OpenOffice.org_project/320m19$Build-9505PKo}=Thumbnails/thumbnail.pngS&@%Ӷm۶{ڶm۶mӶmڶvEܻn>UE=ɨSw/  i7ta{: oH s5N@J-woHߐe?躃!kwPNMɡdk[zQ[-^R;XN/kar}-$f!S4v:F4nޒKqn请=O,icY_x\AMǣR=%в˩8ٔ m+}-}&`q7 ݇ة#P_EVǦ(Qa q,dUyTğ hGU{.oM[unf6Hϼ;zg^! ]x)h'a޽ V'O&0?LH2*/sitgnRLF?ެl:ke]Z_Tո/aᩖ6\ѩ/u }-UJu6C*Xvk%TV/9sJk藐[d[#yt] 6BP b97i]#Plezg@x gsi11P ꆩ?x.Rb꤀EX 푲$shѯ C>! @C iϦ'Q?ܤ]jQC? HV:uȣuyPmywk[ ra8>WiLukM7'^=͚P&`ˮ'C2龩}3|!vUWJ TL4-)lsDCkVg'V40bewl>΁)o,&Y3))u|fÏ'TNP2E+ӯItt!Z4xpS L9v!+WW>Igl/h͓(?y~h4"|tIΙ^K9r2W;Tg"5<"2`kgjۡY(e 8؅cO#@@![?yCv"jBF9Gԧ$Q3Uj-25ǥ-0T!GfKO2 薐ǀ|(j@ߒ9slU`j0~2.<,˘M]CکӒpIW!_j9>T+D:d` .2ͪW,;->Y@|_ e4d1 E +`}(dm:=,\!!̅lIkbjpqNXPA~C*xG Mo&-*\5l$) lx=W9'qg@/+'0wnܨzt CߧwݽU 7;~aO3v?ظI ,I[K'D]O']5 |gѥaЀLX{?l| %>Gg}<ެüjでl}Ou$e*GdՓOyT)~`НЊs…{t0v/=wޅXvA⬈܃bȞ?lu/?0]?cP>8#)DxxXLy 72$]XKC7dS=|&zn~B@Ngᗾ@4GuZM~RixojEݳC*d1Bswڲ%p P✭MwZ'\=.A `?P9jd][ :C gɛzeN,)=봈@b[Ͷ"|c쭄_쑙1_~oח6+ryoIw3|bqV_pde}1:R{RJW]\pnךu']40qOKmc q 0PĜJn,bڔA2 ?E4ڷ%#iKOs3Hj]tzKxRs˳l*cC^ S֩9x8J̙i0t#=xqewXX-X5Vj0n-(NTr{M@J@$)i,`Ktu\-¦I| )kcb3D@ʇ㝪dmtWK$ rں%X_!kzmׅ_死"GG +" ,P4eUnR**5pBJǽmSK* { cmqIޙSMbL'# բC}%;}fW.CM4]9F8Ӱ;l\mY!\ KQ1߳S.K>!D#޷ ST0Odgj? M$|1G(MӘ/pr*'za*G@ID`N~,: EI8&᧓u}0Y7X>ZP&N\6|Ŀkng˲ Ҿ~"PYju%׹MZ(!G啉"Y6p4'&K~`?:P{D=ڵ "qX5j }ֿve'~+KeSw;7}sg@{;Nfbe /B>K,udkT?nă4V-jFdKv.+G#ԘHWGs>]g |\2$p3KaGs< O=_'m|Eѻ<֚6ϧrXP.#.Q}˿PVӂ ɍ- κ7A|Ή E>\Y"ZZ_Ψ1`))Z/]{ICO,wGp07$L)b/B3Nܠ./1r@ wb^7#è!D6=0%jP1='HHiv̆x>D䅀;'μiX`ޓ Izd ^]^ ~ױz7yî¼kąB9? ʁ{ղ=&I)a\ȗ|ID *1&cD,%oDX\%s:^1a~)8M+ez1m\4PJ`+gdP3 Yxzz6瓺J(imR_"sƼ9s8?Ir#XMGd~ivC8g# 8a$1JfTHW]t'$&#y򘄲y55mDн棞*仹;*H 0F4qu1QSA #KBťm\}um5i\ Zms'hYT罵ߐ.@ڣ@B zmeq42]#0x O?tb3.P)|}A:z5{%,1H[%O-:3tR#YT6&L儱`RQ28vv!z@ 3]לeMȓLo T4p&_G,™vԕ BYH>e=^AG6ip_VOKx Tw5vIQ`:>GƷ hb*b&7?Hg]g*8NF2\C׼9 QKC!]pY3Q93~[\DiMg}#XE+=Mp:MYU8CoU* \*aأj19B88Q3'F w!)C9 ̏p՞1;invqU9tD|냈۩KxZ?㌟P84ӎ~aX>;:F vH,L6=f~.U3![yC |t)y8Q5t\ /%#DiC/$ATt9d6&I\3nI,o?$W {>Bgr/.qwm0SVH&HWi _Xy#lcOzjaVd~72 cr.bUbJVf<BHb3(ݛodj7.B/{o]J÷hR4Bc\m`Q ہ~>JB p gJZ[B[n]eV?e`tR8 榊_lr]]A&d͉+").[.xGq=waN}Lخ5d?md&1H\_A3 /d"EPbVy`DYQ mQnF%ܞ>]2&BJ' MIRx)1b\ɲwk&%kl]rm¡-xTO&đ]j !)s2hQ-Xȥ0y`=t\K8lD8̽'4Wτ 5`8Q MN Z150=(L!>,Q.,Cg$4Jp!n  *xIC ~ b *Kiw/!,pJ0NŽ,>Q}ggˁ/t}L|})8uovTd\QrZ&2 I>m&  MZ/L7VZ۴%=~=kv6j7^t{nCg-ggo3?"ݎ4YSWn&:UF/Y'y͊ 0#IZ޵^ZLnw.C*1)Ŗչp6=2%_Sʺ9Y//?/*<`}2(ljbg׿x亯=Ba \sTƩt N#'3_rЄ\?ˠ{14Hi8 PHe3H?KZNB: $Pj3t].(0w ~9%LY*\3Okw@fq 4\0풜S7߄e2@7=ES:+@wS(CnHVdn]r8=:}H34ƀS(eׁ2ѼӇ _yBn~?t'ឈh{v>9Y]@%(RѵV\Pլ7ul.6{gxʩqGX躩\|ga [.`\3\\!y 8S ]Hţ_f5A.23۳'idROAqU["D6ƒ;8 Kʄ+8Z#*bJ:yAS(R7Q xz,F z;VṢ=~,Z i`d3c ,jyEOO9ϡ7wI sXyzZ3"Z~=8^6g(SM`RiEW L ֎#A'ހswt9S۩[fN21xHms6#~~~A }ߘ[v#tjD|IgrAvcҽ:wJPe,JYY~>qu2e=?ٜWs{qv y0|ucL:b40T9|)BhՑ7BIQױ,qq;{y7*_޼/qҞ-`.<p TO =j&]Y]1U׌?7t9JHg{$w36t& u3uÜlK -q@⦪}0MтEy0#rj"hRF怙@>SkH\ۀ.yqm)q @,  rz>`jFƒ\ 劒5ؤUـY;1?BX;T-(n#O\*$vV m-brY#DpoPf:d Gxa  ISИ)tauf2YŌ`ؗ/Nꉝ8Bx̨Ƕ0V9PΌ3( =^pfASY#feg~No[' }~CM|kjy(K~m[#V]$tژO)ږRV3ԙu-!%j jc%(aF;tl;D`p>_e Z\޷qy )G$Dfœܭ/w RoFJv 'z2K Ïd!ǵw+G_b4[9< ?u#,h.9`_Qo0s|2x876ۧei[$!VG.2{Z\CP8p)(gB I݈)+ApUKScx$75Oi=!V{u L|rGq7m$bo.>5XSk.xx2<&`y=Àa}w9byr]c)wKS+.Nm^o|=t5& a#m?nV<a"clkrV~&fnN g퇐R kGI* @Q0c[L2T̊վ+Yzp~6w%qx/4URl-":ڎ:N?w푂T%ci Ldqg~/~iնmrvJOY vvrq nivU?ZejJe]8/X[-z$a+P_N8 >\J?^D:Y{,wB/[܋Y nZW]m+ƦSfV M !`xqgcJʅ$J5 Ng6XnI,#T|x]KlCk7a kv YRMss>Aj/Fۏ~׿#G-%8~JcN#cy<aTWعL}8 4u';3lȟbM%26zXx5ىr* qU^6Q͞񨸽&TN䮛?Fn|z9SrK3AD#P|ԸQ` CHccM9IƽJ,Pqcrly RsQl"YΪef?#L- P5CЪ]yo2^5qX,׫cD '-;G몘.}6])ǹO?ҥe?iVbqC$fD5hN qM/]>go6p"i愄F(.%{muxo';f̬>{?r*^7V ?ᚵR* K-fmM[b?0p6zO@x6C, {hVΣ v#$ <3]%/Wi>fAԚw#Nb5<UC Y:"Eҙ[DW6GfR!]%34_قR3YB`|6(؝芕Gszp9J柀BX~&ׁ)ꔌÄ+??0;%F۳4A^Yh0 ch{Y=2O/Puƌ.X,4EbFO. :+q"p3F6JZ 0k?O8Y,G`W?Faa>sk܃3otࣄ ݷ1wGv] r(/%H0Ut=PR2v005D~CֈV2qȨTm'" X>  >2={q&gb 'dWP&,25맶e`0Ofnot蒦!J]V03Mmbb YT}=M47͉^7o^`ƹL8oԷ}=6ja=`$iJJ6i|Fbu!( :@JvYŃ"^3*_Hj|| Ai_z=kQ(ƞy:Fq ?7It: c;-/$l&76&z@U,9G[ B} 4h@ƫ7 V*r$5ㄬ C;e£b%~{/)\&:Dod"4*jiC4P#?XM]Z4rI Hgv ~~529%4`&{qD&[=aT247 [H3FyU0b$+.z^CЅYE'4<3Ū˼I sA@`|ztQ">[n@/k:NOw.ro?_~J'0{MYCSy~Y7'tk\]\bběV/#f`]I4_na"CW1Xݳ4:c)YkɄBrlQxxpdϷwX' @Q@Myw-vGz ooŵ r=OPuW#\O1#k[Rjsilꐉ_-F+@M2puN]J4{|?!FCGeN0m[j)IDwٍ'm!f8?;xFB4b&ǬLbuJ;oEȒ/0ʶ ;]" \z4wzfߑΘQKDuXݍ|NpGXw#Ze/jL ̰HrSp.cnjl6ΈoKHa.Gݞ圈\>E>RWA﷝~ <bH1WUngwkQh![Yus{ Wc5΋_Ž٢>ÊV ()8&|r5ލABV<,0O=%:=fW{69_e'J@":68"]VdًFۏNsN\Vc )5i$oO'^@W@?zֺc^7e3]RgZ7HunY{c MFmnzɚQSI ql啷EyA'͞Y6YiبQ57^t)#@fŖ1%S`##ϢǰQThz<:*b'WlZHnLY[8rqUTIԕva^\C`^jظ\2m!6zy-{zIŇZmN?<&PoJ q Mx6b+ASpm^[(Xo᭐WOפ ÉZ ]% ݇QEW5/Reļ*`Ds@0Q3Da¹k{Ȏ+A-$ oQwL!:tEPB2ވ<֒V! Ջ5 ;ɥC+ORC]ی[lUb4f|NV{N-oOurՍS_b'$Q &BsB\hv!6dFqr)WFı $JItYr|اVAn9RI\u\V~f$=xV_c+cRVS8{bШoc9wɶWxCIDHv',z6x_}3?|֊SIP7#x%@JDa7ܖ} 1nD/IyI-RalG[nC9z3{оjccY?:KIѥ5/64z_yc 17Fm'=-m#\t̵+5K,NEoײ!BHna/*"2ȿa=yXhO]:d]3r-:r5S|Qm3lFqLfR/ ⫚pIJ.ou7c(}_82mѾ{YZJ[B8BUIl{A`7m|PqupT% 1 A"@Q,(,Ž[Pn-%[v\Zךq҉q0/33Q1=`W^)̈t]ɿ#?ve3h :J]H51F}曛L&e+‡=3u;3'Ku9_V~;j#@2Bx FCriʑS:yubo{6NL~r 9% l&ףQ=ѮA0nG*MƀX;v{ï'K c6ގ=a}gog6Q#ʫ0U^(:<8Y8~lbXxjtzQ\3FX3cA"ݙ%i).bXR~ fpӌBN3/nJJ=pCS}uR9dz6q=!pj7}h`& D^tVnI+?-J7/"P)KNa;6h>cs[4Q|(O;<첖2<ո+n׀JX[8Ta2{CPTujg&Tϱ fn) *Ǣ.^T'Պͼ34@NCj RDueOE8%Ī弍ޖ~EJJ`,طP2ҹYšuۚ n!hF$[ܥqKn'ߧꜪpo^ƪF11{idvpD7X{iÓ__"`8*-%슭{sӋK߽vGWc8RSpU/cOh9١R fV-2On$o5IvZ^3 Li84 c~ =9XApVG~lxWCOro}vQS't!|ҹf]c[Indv1C*8/mwV&v^_3i­8&s62|\q' =bPbY[[r6FIΨ[+8^dm8gDx!ݪGi=|EO܏5$'O í0\\e$³US1ƾvls:=u␞ Μ$f;8|Yjs4a Yp$2md Gl Z˲Ey)4 r_> 1/>2S_㒦\z"ڰ 2dA`biy,PAJU6oأ(ū Hm_ys,r;PEwfƈYډDp1X(I$Uw+1פgƲ]@m?@u[hX{ 䄮b2-o\TuBKeڤC"];@Z[SXT Aacn:GD] -(N@a"o$$iLߡw7#BVIIt'SIN#/zAtHU~n!OE\wR++\ȶ\cFdu9kܭ,UI4i pw n]Ob{\ S?\ ֳA֍M|[kgNQ^:ß:_ (E Hbվ̈kZ~13J.vf&o)/h Yű61tv[G!x)*Ax{XisiqE@i*5~O?ϜFv|o7-͌u|JBܡ~j/j76Š yUGwؖeq3ytNe8#bN m?RЎ%i .cӛ.As/rZE74סI3o0ݽm@ 8Oc]k7\VB=u#$Si7UUzNYÑDn$kѨ /bnJ/%WQYɕ.^ }m[?'Q#{-3ⴳ"= IwWDӘ6eV֠Lw> ~j}ЂZMf U=кIu4;Pt\r a*Ga%:`M"ktc&KhggQ]Vs3%0e@ZA^jg{y$DG6qDX4L4f M|GaP(Qӕd !c5z6cZ\ gȁz $Jj37b|f)^fЬ@R;DD Ȃ7$1 uu{ KT adhaK}y p:D1030w;&"L+vsuLBr)i,ШTTa3F^q c(B5Z  = ?KD\wf;+g(-iTϘǘ\>f x?nz>YGW\BvR(50IK."dk%f\LJ$i7Yo9oD^POf12lgLdlX7s<1,ov<~gŧg^կzqĊdK~Г%PƷp*M$GG+5iV =[J$8,F]ԽEƻ>|>=P${ɃH <;\f`/W3Tä:Bh#-_2zuX09ZĨ^O]:bw sv08L:1K(LfO*D<7gk2ޚfn d~gÛR~Ȩ5=kc]?XWOe4Bۛ78o'Lli7ٸr[D]߃da،G˨a~үx_ΏT/"wlax+E}0h߅XK!?}N K'ʒ7|q?|XbHX#BY^}RN Tvl0 |\B66WU4(J6àbTJƳd;@eRhwʜ`%>9x#&6 қ1cS=o͖sxK"*FH.`l ^-,`)0*cxqq/9|kA)/?׷JɧQ9ad}-Vo-ܨV7yMj{ӟ(q<3pdyyAn\Vg ~vY U,==I9\c)w4j˫ߥ>TF`\{coNGr4N7Ex<«GW|ڝtk\/{M} V/Ѳ{gi SJ^ՌeM8,a< މJvX6__sCjWTrBljlNlp]c:N~~^d}y]HRL(]hp+\ ر jx֧fz:j hHUŋ>,?ܸboNjǷd$Ҋ5t:h=beuWQǺ_%Fm)x4{&i,"Ϡp8=gi؈~ >foﱴ+ IN;~'C5أS᳎Q a  ;P !-@n'pӎ`9 0Z9ϩ%u"Ya\CM+େز2*1f<$ے,psݨtvô_DYod$+bSyr9$V"AG=満jŚV6?[wWE]ho3N׬H~JH,Tg(I8ks?p$ˣ0kITCɫ™m/?5z )iH12ߏ:|U|.Pi14{DKD22~<\ . (] e#tjmòYD"@nJ%Ȭ{p\Y~!OԕR3v5H`}^[m#t {SN<-l5lH+1kO|c AI]KZiX++G>KY~1WSuK3tS|mK^JDv)'7FUyxKl2{t9`=wn>"tMt=5O< xlWYge4*(wC:NzهO+{=?$V3%|JP `2(a@X:%kSawadlSOIL.8˩r~(&E`m;&qV'MGniavdiynX>DP|8ߙ#6sRvxg^1h2R-05Mi,ד( ė²z.-T^>͹@Rt!ַ\|.n/k*B'u.zc;L {'P-LEG=;9,|ƐBj7AɺLRň/2DX?Fap׃{ A@ :$bfYVi" /v!,pQmp"()Kq ||@3U0fO8fr-"6xPCgŒ顏[h#3!/YzuD,4(k}Jc$,-\ 9SYzw&҆Ti4HŅ` ;Z;Ȣ>X)랮!t8BAP}&󷯰2j&_40aM&Txu)8 kZNBb{Dm換dlwO(=|Ou~fdDC{ҠRںF J?'hIa|`K mΜX⠘mK"Ԣ+C.?!_/ȏTb;?DX-`!yB&Qdɏ;WJA|" s #|m4B([K6e(kQ_2.G/he0hW!$:$: oKߴOj\VGـW#V kaG1<7dː gEKUK,܈KF!XM coKU,7nhJ㘠Ej(g󞽎[ΨH=lt`8ɨr$|ˎq Ctmҝb37W/\>MTBnt֌Kt[vQ,>ʦ@dY;o_2 !ND~绘]PK_H"PKo}=META-INF/manifest.xmlN }[S]fM|g#@బo/]VuW; ;l>(kJXFJdoWw1Pqjdi nɢ7BaP,CSY[4T|/z.k1O.29,8JyŇ Nx v=j1ֹڕL0qS@BӤ5ԧֶU|wݠr rPOQ.ONc:c[$b5ttjrg^UqSB1u2z?,Em<@8WEޤ8'϶0d Zh0gE-cv 8P~B4Qq9t-if$Ji^oPK'dh(PKo}=^2 ''mimetypePKo}= r! Mcontent.xmlPKo}= 4vs1T V"layout-cachePKn}==<1 "manifest.rdfPKo}= Sa/ #styles.xmlPKo}=T6KKw,meta.xmlPKo}=-%XZ0Thumbnails/thumbnail.pngPKn}='Configurations2/accelerator/current.xmlPKo}=\Configurations2/progressbar/PKo}=Configurations2/floater/PKo}=̊Configurations2/popupmenu/PKo}=Configurations2/menubar/PKo}=:Configurations2/toolbar/PKo}=pConfigurations2/images/Bitmaps/PKo}=Configurations2/statusbar/PKo}=_H" settings.xmlPKo}='dh(~META-INF/manifest.xmlPKb)apparmor-5.0.2/documentation/AppArmor_Developer_5-extending_apparmor_to_userspace.odt000066400000000000000000000732341522511161100313360ustar00rootroot00000000000000PKlKB^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKlKB< Thumbnails/thumbnail.pngPNG  IHDRg? IDATxytwޛq19FiTII(bhj1ĮR'J41$=1Sp ňK] a~"g.fUlXp ~Pƀ崚1+d_zeH-GE=S?+̄>^v/m +γu/jfbv/,:b)_|#tfjLc.5shњ -~5#Gž7~ZrB?KL4ULQ<qw썼wy;N^m|[_tx^H@7~&^4C}F6KiǏW+輗ߤy䐰7*٥^N(c~r9UͱX_ 0-hBj\Pޅ%kiMT_:~Fע}Zބ-?c/o vf?9dXެf &lY̱1dQ9iCfk̤y{WVthv#hlQ' ]ʷO1QyrZa+nw$9G|xh_^rm9rk[};.=צz}FGRzlswaa=Z~;vs?iK=+.ivenߣGngR=j#MUp\@QbrgTV[?wԖ—* ;ݥeW=4Ċ>%05 @) Y (aU$%ՙqB0P& X@!L$L ȏ8\PKLPKlKB content.xml}ْFr~fW֔*p3&9ܡmS=АJ@ @12d"lуOsU1+e]F2loMDr&erhmC.@!܀~C$;tN`["3OCJr]%-ާw#eH 7 6{r=X-] c7{wWjÅo\ifE<w%W< j^vt!ݺsS|3#!08ZaO=`.|t1G%;G7LZpSls2ؠe/ذᩁ|oɣ$ _v6}@zNR ]R~t,sw%7d\,#Cqc.v7+EA~pŕYhĩOm1/06#by3M6Hэ6 NG Pt]~f@kD4d Ek,E>"B(=R}6I3AQ S_A"`UlNw1biδiNKM4Ǻn9?]0 4ϯ𜃡{9'0e, G[מY'FS]Z˹ A?Lݙ%aglTr8;֗ۜ8`p]Nsj}a| 8{ar95^p3O 6cci E{ac8ɰqN]tӮÏj_9u̝xK9u\Kڬs}qRuN]uvR;:.: o6Ω)i㜺h/l,>;6sꢮ3ukq Yd%a~b2~Qk1旄sꢽY9u>0r296Ωg'YuQk//UiK :.6ss˚.9.:qYIqV]t%a㬺;%vΪ:dn]RTs֨й1GerqP׼$s֨rf\5*'$YBĻ$opLrIQpsꢎKEqI퍧%i_tQޤj@~h#:sh5MN7>Y3g{*kf)Jqk7II<ڸw~МqJdᠼ V9{NiəKLv&9n *pSH!f>uDk&8%}1qGsb)UIёRO t]g`.(`'ubPivЩQpo9uZ/}D)fP Fa;IgiO;;M$JVL FUtPQE+}#aB ޾e ;*`__[toU:u,cvv'GFsp N*׷6>YZ.n|燣,:.S.R)lk5hE&4~'F=&(h=( ]6smp/]n%.BR``ܡW)]bS{3Wbv]`9}y b ӎ)4(Dw?'Rtܓ8w3 |XwO˼8[\ cǍ"}3 M(zrRUV8&O5=ԭJ]N|9!3RmqڸmNy9jU=Yїg;5r[ :î +6s3(^/ǚ8}Թ^fβYxśzquG=ŧyStƣƣ_㩇3ס% cfm9v堻NSƮU&>Uly$m+bL,+J ٍ00VY\+g֪+X9lS ?`Kq@~A+zy_Tq_^CFܗb #mZʈQq_~ {*/eFT8%u7Sr"Up #Kl.]dGyU7#H #Xy*a,VaĽUg%ٗ+F[ib #5Mlwj)ݚ/Jq_5 #ΦW2PiTq_clds ,*S-{:sN^--:"Fܗipjt6 9 UvTk1  V]0⾜WqoNŊ+7bebFܛSR #ͩXyT.Վx 9r-9+r_ïRq_ïRq_ï~RQ簧 :뻜OU8 0]_M; %- ZZ:D˸b8?XW ]?j oCUp1ì#@Jj$^#|!Q6M n5BUy KVSa_` k{Xbł$y/V!]wXÂfA8N?/lŸ*Oy:~ňSf>gwR<dLRqd>bAA@Hp @h%>?e:6b3?>Q6rÿJI) jΌp1J6GFh^5,~]&opA9EO`1E%Q^Fq8P/"G7߇}@mXWXs5?}k2Q]IWyU{b(-q?Ge$Vy(OʓgɹTqby2$)c9r.SU''ϑw<<ϐu 0Ez7Od]Wk5y ^ix^Wj5y]Wix^WkEyֿ5Xd/oUc/rUd/oUc/rUR[k09۪ErE>R8`=@`v-{O<1'C0OW[ asZqt]$Q#&#g@ -nSc04_1Wʪ<{04_Wjd~26秀N(p_%f""3ړ#Gͻy&芓->yw<"J>X0I?,#\`K\bżO4ݓOjhz`ӗIu9@2[j>/5}^,Ɓ?r>w9CU{C]vg>72Rv _{P arN.g3[. T@|^iTr /u] gq1]%s TTŽlvW];s}i/uM4}\>}@M/*p遦@{g}q@ODheѼ5Q<|.DĽnGj4VaPfdZL+1`(A0-@xy΁|Ez_~QǢ/{A{KulHxK~T?h/f{ˠX>8 K @1_ (3D5̾сޟ_P3~ ByX:W!J|ф<&'3tT?P~rPZ.Uy~r @=_+!_✾k}}_s#QpeNrIV>(#gUF^N_)-5.|*ȁJҧ?LHz 飒H$=~9d5z.j~9fxtfA}Gr} hmBFILGWfy;"!%4N {zZ&pG6 `vPLf U(I'ww/ޝ|+ tw?wۘ]4ZmoܰqO)p;XRaXJIr 9?@G}EҦw?|ᰠ~7~PBO:9\df=@k#nSRlvMADE=G; Ễwk.69'pW%{o& L}TPvC~ :1& hr}&ZlBpZcLVnZohH.E͎4wsA`j[7CL]7$(L7&т_HSuN7US5YnA%o &l3aQ1Ea,M~rO䘫]㣟p#gyOO7ىT3ӆg5QVL^(; QmTdv@/rDaL[V4\0 i1U (Q 4Y ~ǝGwt܅-`&+\g ֌n6.U!L=o3ZcKXN+k>!& k7k7L}(+jV! ;ъ ܑ QMcQqm_]JT)mZqG3zWބ3Q% K@RFwc|9Zה&Y( (U[ ڢQ5SQzFm`O܈x<.!7ܨwQ`꽛[*P!DK_y (}/T\lI4)<[Vh+zlq.m˜;ZKA|yE@ITcDgLes1)}.AdV-I9ZB&ˡR HP:s ݍ@$7 5G炑UI3`Bg{ BCRي6| \!MQySи[4XZw 9- k9bD[W`YQgoc_jJ9G$WsJyNxF}x Q|Y`C .ߕm( 6`|5kd.fa0Ԗ p" 3htm'l5+׽JP:q+] ԑ`T >\1ewA^Yt09U`SdQ N1/[].mGkƦ-෨gIP9`1qD! Ukd.^^oCϵD>f+JI0Za -0027?E> H-: #EN 7`l{4E0G"a >n:D|O$6 ,J&'jL3 F ڂs.h"7 g$  #Jbۺ$ k /-$E?Z`R{Q-`8ͽAi_t [pcGFk|(b<$Z.(»,v@iC#]?mOޣSm`3A/|,2hO[E_9RIQ%*cޒP\pE#Jݦ4ewjqbgRT*λ*ظKݙĥ P^9=k^ksFdb fPs?@O|#$\TԢ~ç9+@R裏Tsw9cߒΧ1L0~) #D>:Ո!ZϘ7 B(21=_&/Jn Ħsl@DTҡ_RdäM{NJYר֐%$J,f#" PI|4oh?k?pc?x.&\oWoYMzd&sOk; J%PgۨL=&I4z~`釔:aF]c"qH8oZm CگUQb:-! ٠p$ID?]}^>\aA4c5otdu6wT3o<_@ g$_&tsT~yt3&#,%Ȇ7L_L$]^d$TD@/BIMn>h$&=su!٪gQuIrUr~kfI\Y~@X~e <(\ƆnsLA{|tSw]lȚ&E=,R'$ "V#wG\(*z(FB5MAXhHA'셨,B"vQ+34B7@vu0ʦ謋դƗ{(eJ:6C섖l9)YJnPaڠe ;C? CyTi-&e~0SN`1HPsaKIS,QS#`߫¨=8⇒>qlъ-۪FR0LuBS%!s W2} vBPh@A5>K _*Lp1JbHZ8% raGX^zFyRsR|ȔxX_6Kt Ttܹ7i;+1Y- lc`@f3#4 `T]DEh9g) bMF.#\ 7uyq!](l„왓+,!ᶶ+Y|Ȧ;?I䜉%]yN쾌GG{%mHc\*7O6I+Rv۬(6cng>AnM d#yqe4R; M4Oe"#P=< tbhigB:]+miԈ'ddwI1WpB2`C1hLB^S {~Ňgo=gw =m|-=쯉:L.smJh;k`66q0C"h8;i ua*_y.=fiZ] 0_ardj]C^Ntڽ'苞S-II0T=2wy\6n2Vh{+t%"Da[vӦgѳi §aA@l؁# OtKȫ(njC7 1r_ g/o\0~Fhca`Z¶kFEHlOMmgxbޘl,<ÑژR1_ܲ826]%+wS *_y҂74 Dz+ͦ$]7vǂuqǢ;Thp."Nlh! x] iŌq&}*@;+Td1oMᅧEMLcbK O\)O6.5#ЍkU`dҗьL/0iLKFa4ŧOʌƼ83|:WW[Q4iKb`x'i˥}\$4sT&7PObʡ ')#;LI} 1?)*/[iR6pAQ'rFk/˛kL/h,wjkcv2["׿rijh3(u8.nvϦujfFvf4ΚR@xUxl|җϊl|փ-y.j絈i_p4,e@hZ1ײoETگyrà ܠ ܅rޓzl>P9Co~;ۋF#- Ź1Y|y-x4PeipqcՙdLquL HdQCV3#NEld*ov_jgYHb0\)]E-iTȼ#=[l0ʵ1v SϦȘ;W\ ^d>0wߴBH,oW7&˰on! (AO蚬dž16ZGᢉ K-q"O@ƢHbzI=Տިz[d krtaƳ~sG5g61zPz+۱d8"&heF}>vޛp.Xu:Ud՜FF݉.?2_POtFtu|5[Bw)f316Q˜c p5A8+۶IO50}2ʜJq3)0mLM[c#d-ta1d Yf'Țm.YRq?d9,3&lv7̆],%6^o#_0QBtV>vNmmctÝ31І\w{X7u ڸn8Vgm΃=V =۵Iljb%GڙWr{e>(߲Wm]X ..[az.Ubt'Xelϣ'7Mh뗙zDg1zVgG=wv$Jz^ECҲѬ\7 ʈfsjoX{Rb<;+tʛ#l>8|Ck~a@)ʅ!1 H vz ڈ|Jd+7--]wT3f J^= ^3*YŠW\@(neGדddPBOM쎜1-v +Gkebf~c% VU>ZB]Dvm %|s>qC~λl(w$ sdNY59_uhiDnx_P-;R*昰 ߂ TzoݝJh Sg!wߘ5T*,\K.\\s7ёѡ^uvXmuR,ezw}OvyU,J|*nQT[Ҫg\*eՆ[WvW^kd(7X d:]A&^m!*Zp J~ AQ-zաa<|vĊ>+_}&yB] -QR742 725Kk$/'r<?#r+Vyխ+>,R}UlJM Pw#)2e f/Kv,EvҀ`~ju4duTF]Ӗ&Xg5u#Xw!Mfen¦LUA& HeJ- |ayK2,r:%|]Bf*c&]e VS2H~Ʉr#@gLc4E *eC {X=6 =FsuH y} Y=̽fm#C/Tq̔Pfe%%z@&ZUgLn5.zo*l™(Ld #`%!N~7Zf|\#:foڎzmgmyD`Z'? ]5jpٶRƹ;~ɋth-i}eKR0c|xm2d<0 y CȐ"<%gpr%)Y!7e6{a;p؁^*m ,qb\I)hTڗ~=ف|v೗g[! |*V$=eMgdd&;0KerVy/>jV=WqW v}tvݔg\0V'/R]7K饥1|D_Vdr"ldNK7z<0eAVYd=p֚SgRLخ,i)tl: bqj t154Ù+˞Y# '@*~~U$7+H*cf5'cS`)k6C#z9݊Rcx޿~=US k⭥;Z {vW -Ȉ`^) xv-,gq\k S~I\҂<&|X/c]?UBAI7r> JFFnao+Hv_ѻS')!0J 4zKq( = gH=T[q̂`J+T Қq.h.#,"O<-rZI![k InbUZLbZ1"V2m!')dJX>Roڃ=uL(.Ƽ #QM>[ecE4nx»&{KTeqCJ"@G$SFm"; ٘5Q| T]HEͮ,:vJSe6"ۑ(cP{)2H] iҘ2곭ϘɖQAFjvWJ "Bз Z.^6z p*EY,^tIށ >˲YdEY;O͔ES~"gHɺZ_We }l|O&Wˀl8*9IlG+fH!>YE)ݒr9p){g_I|42+pѓPݒ:HŽIQƣV$8X\}(*2M0Lٹ5Ѿ:<2LG("J*6lZsjbubMgW1]/ոw黤qqy,jܕeçqҸ,] *uԤqJmW]p~8~8 7)dXeg8etVd{v1flJgRafvE>*^{Ii4jؑ^)#] DM|\82Z7S{RIo̙aH,KD~|Hp:n1^N=9G̾zD9(ATATsYQt 3nfR Vmvaat"lU3 DXw\Xs(_IYE9V 5p av¬9~Xf̦ljkBUfaw 3h,:eX[e9U/K?A -pK}ͨ?Ⱦ9oߥ%+[A;ʙḟ̇7.BTdB3"mnx~j\үFoh#61J;o‡Y)o7zmKA!"+f5+1ٽ%>;'cL|Qj1bp,DޝQǹukxIu.ZH{!)n:`y %'مCrf(onTED/U!^rPB7Z/;~%%nzX(m۝e"U0.yS@ 읾HP~*.;ׁϨ򙖈E\њrz )>}g̛k~ǫhw1)CL2ĤmKeE ([1K8SD7HH*DaZ~=HA R`UbYNbzP7{+u͋ۼjs]P_抰?2ֺEXW !F/eBRDX^?D93_~.Je-~=7~1xn}:{KjQg/ 0z1q8L|mm{Uo7ʷT" (^r`j6i E x╆ubq yVљ#egll\PyxR4afj|Uޑʭ< OcmbZk0M#c߀&m,tcXN[aU%eJQ7S&%aM@ ]Gᢉ zagڻ51ְ=I I5tJ'85oa >޺fEYEqdV_zq4nGf84#;G>fc|0 &#޷B?=)^JJmhmaFՖQ0$SkmN+Gaj @}3 MwqfjQ L3YӉ݆>x߃ʚҹ 4|.)s6d}C/Ļn WZ TSʘT;m/a+Y{O4_P*fw!VCXsmz5զf1` >nz5%9fe_ HN X[ukiě5%Y6: ]yoVDU36jhVZ5-9 Ԛ"̉ĨŜ915xw"Y]ۛT! scPk ޥpG@@@QDfW ͽa*+ADS (ׁ"q*e۵ 庬N;LUoׁ2j`*'_:x* &,LVXS.o-at :U-S)̩3͕mu3}%u?̙:GL^ͱ[+'WJĴ txl5FvBw N[].ɬ0 s:vث^>X ;mٰ-d7ݰjR/a3SM]54oLm||'Yꚕյ;I(O`=`ϩ>v~ZǪd%v<ţ"3X  թgF3HVwAqrq\1Yn],,7hlTk([Gf jpMVȕ=*WnܣǫBc$Yb9 CWGrLN-jiO%X[k3W QٱZK bPHY;U#Y0$YPɪ 3FhrPpFx(jG /֗9LҡA8"W9)qa9dXCW", u0ȀA\ 9C—k U?[T 5_+)8;T <6>h냶~!vx!(ڇC!Hҫf.S5T&u6/QEGP{%4J>c5j`|dCϤ(jsԛ$?uJ+٣CAjjj5i.˓Wa{U4M( ͥ-k 6mϭ`zX[nhrU|vW5)ACC5T j`j *Vn*6TT鳮4Lt D7չgACU e/3h̠\f{VfOW)yǫ;h;h%(c'ڽj]o˿)0m,[EQB#mGMC~H#_kʅKQxFCm/$&aFZ&$f |mc?}xxt&6])Nq/@4VĻCwEKm(tuȁ ŘbHagڻ$@̉WWprb5BSwKZBfA/b0'ETEv+#m="MXyம=k0-I}R+R^=6\8$n}":G**`dn[;Ptvp|IBҜr*E`QQVt2vލQ.8MfDk z E2l\,#,w}=±O&πu(6zMqvO&XfY# mk7H" LS'bZ "[YQ N'<$ڟJrOxKpGVj RʤtHp~ЀA! nX-UOn@I/gk?p=xGfpya ʄdp8 s\H@'1*c0Q]W|@= ۿq7Q|7>tBow;/AsAHl~Dr=:7m\ D1{=;Ӛw͟?l7ΙzG>܍tVHdPz}_-˕/{##&w["3:z{xqƄ2W >Mq8Y3RtS (^dy6oR;~AQ98MS0^[iQE F'K?Y_%ș{v==~mzSBb(5DZHkܢ$ݳ+G0ڕ~^Ỳc҃ t1VFb !CWn}R{H"괃"D&ѸI]&Ģ; "D*6nǞ߶\躗ڣO5xEa1cFT;faMTУ8BPdj0=8zMв]x5r*ף`ΑysQM ץom'A1(ңEc޿I^;qBъ[yTCMa/{1|Jt\ǍKinka$Z{UѾb?kԒ^e7;A2 5DI(9ߺfNl7ۣp r>M-8"Zȸb >Z.s7FeuJ#t}1banpa\q}Th],Mp9R[!+ ,LcO {lƆ[l2d>fn `g]q;OHpfxyS=n;\#9?\6zJ %Z-ESxbEq~%x@2DM.oZ_roUQ/Ob80'Rx&I@c2O:$ \~!jôhlt8=YOeXujf=R&QX.\e䇮'&V֋=Cywu@c{-;Tz!GvFK` ͣs"Ѩ0}PK*7 M_PKlKB styles.xml\ݓ۶_Q}DRi3vҩf SO.)Ig9<8vXv|''80N#t8wbۿ *Qǩ8%sW8YJV4érWj(ݢEΒ1C%/`vо<6ԋ>CԴ8&$8 0:LFmr+U8|YGc`9`lxXI^W4߯1 ̪i#-D;zbw7$vصd1D/}߱$o45۟RjUt*uCa:#Dqo 1pxIfrҭc3ʄUdtB^;OKR q#+3C{OVVn,kPХ.]:;Xذ*eC&3̈$Du[U$Tbh%͓k3>EzpRR8|kφB٠{1^7l@=?I`8UIrzea q_O& 21b5I0nWI8!ݣ‘j󄠯4?XP;2M7饛dr [T wQWZu-E]߾K$jLFZaV@CB0u;ڼ@BW0";W;?BCM tJA^ G&?[Ym b)F,.x ԃ8*\N<{5ʐu~YNi|jRrU5Yl֎Bȳ/&κ˃%N'Jdv:>N`_V s1ꠘ&EONyZ>Qg΅ -A\Jrly3s6vQp>zaaG+_:;<$a\u[-2M'e[k:Zoy?PKjW EPKlKB settings.xmlZs8 ~.ؖiZv{K t;o&cel$]6:Mbɒ?Id"~ R(nA(7Ӥw|}iuYC3@?@cZ'ꈖ L_xMd*TM"PM71]|u*Kx(^nqV[.Pk}G1 燪J~ fY-;lk^k뇭덂q!29<6[He5׼u_oK` G1Zum_0әK}h_ "{ۍE1.GР`bjG^KK@`C }ge̮\~lUig@7e..'Ј °VCbź p+z)R;5F;?>=/&'b4!Y[^D5&N(\{\^(SopOEK{}EY.pnο0|J~Ļ 3aӶ69u)5%^C,oShwqNd-!Tu1^tK 6i]V]]i3̀\Rw% L!Hd'FJYQwYldCG(Q`&@=30?)`S'wzDE@lA(oSH mq\:2y4FQE4LkP`ԩv`8f>)Mi:#f#]wUsch(2W:-U9&~<1@,6N(i x2 : l s%}*{|mgN(M.S0 @SJȭGжf.d:Wнd;(leZLuEh±2S ٴQ1&ߛsLSr2~i# ^\ ѦBrƮ]@t9*D,eȘwBPp%I|Lݘ?R/d1Ή!&zqoӑsf)M\O}'m21LWϺi? 0l'ӢU;LʏS;o~t>d"(ȁ9R/$䐷Ru3;|ϦVi\N9طy8I5[F%;Q:́P/H៌Օixd ]m7 ǚVE!DRC44.N'e]U)$@0c wh2qZH0o~MW9?CȽ[PKg+1(PKlKBmeta.xml͎0} Dgkl $ajVj*u9q 60}dR$ϱ1K]`j PBRڄL?=IТA9Tぷ*FiF1ͭL,slaK A䥒.<90$ҦvŃ:8sMk* (37R=>Xzx|EukMoGa0mq4kPF*$06'}RpeAe&3:76S'(!{fBah'50=F eoJTfɿ{J?RyEM%^A^[T`$§ hp8HD#z(Ur?ܧ84FpJHM޵*r.9FuuRcHV]oYj^cv=md: / oNgeNb&j^FE80F ITw'6\k6^8-ķPKl/PKlKB manifest.rdf͓n07XvXQ:ȑtd&46XGw%`!VOՅKu]#-KckqF08kKS+Nx`hXL9PCУX!smm-AQ@<45g&"sxmwJl@sbe O8'*lM󜮳h;.3^sQB% ? 5&凌D/w 9z1ՇR~:/jKl'C0)l y8ʆR,˞pLo$}67PU%zMԻzm^fܛ}{mnA2cZ}wXy`F)z|:oR:lVб;/{°v_/~$avOɣic+ oXo2ar7KK_/81kO/i.B76sڞk- >:aXu*56l4&sUO/ϸn.ym,^vn\9¿8'ݫ'ASd0zVh2on?9?\weԚ4 m6Zycm332}kSng}C>Zv L9h:K⼈K #g~Ƭ˰U%VMOK߬+;M?o|hOfn֘e'm~U#7yvR4Ԛ jmk5˵Y֫֏ztg7լ}ȝ[0fSz'vxfj"zh(ڽ|fc hX׈WqꘄC#&?b@Rwh9jcOwr`l5rMIA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)H b$1IA FR#)7UIENDB`PKPiB layout-cache]OLAƿ-ZQQ)$"111φNF"'7rh&M1*'p@ }Fm7ov`jF'`C  ]-e¨? ~ȋ1y]AK!•O&y8ϙ3,yZHZP G{xY ܖ >,ZNS҄E@eb3Mrh]"ub %AXv`[_~G-|7*cG!hWˎ9p4nrf@N'jy_{έc9ńk(gB/.-5ND!nM^7¶yIbY^Uiy iKFF[|mPlVÖez Nqϯue~>'F:!6|=*TUBm"aF_M#Ll [|aPKnMDXPKPiB content.xml}˒ȑ}UGIUmvvzթԆG .`eN1۽\0,}ɆG@ @4Re0?_}^-O(IxFo$ Z?웯aMZgw~Jufno o2MA7oȷH,[?N&0wYke2tmøß]c6n2Z|{Ȳ͛g>Nqȯłbf,ɬ@KK{!Bv}0v塤5j̭P5Ԛ#>=ApּA&W ړW Ϯlq&G_?|d[0wU~mZIgX*<@,We]|rse()MONݥ_`<^! S; ش`|@Dz\LN/M'E4s;$@ IV &l01bml<.k>) کx9},xw"=M~! j27O>0c";%<Pow1D${Þ2u+[ژ͛ L*bXú|/L.Zr#a5[E˗7v7q$:ppe˚w~lx7_O:~Nd^=5VW8@zo&|3 >:N[O.~DGx%A>A-+Vk#(M,m͒^ :k'a5]k$I,}fnok|8kٸ0fGS(a{=+{ O6x#AIM~@{#rA]w=Gkbmlq&|v_RDiMM _Dl/J`ul[!]_x_O;-C%`x)c/W Gqf@[x&NuDaF~16(~%g%O[kTM^e`sPJ+VH^C415!j|vw8n]04]8WHkJH`+b&xsNB`hȇq7lXݧ,SC>S7 vH:iFuQ8$i˖gS]14-ܳwnՇ?Bw>#p!MYFxrl$i͌w&rD!|vdžmƘQ_lYkjX,F7]I,c:k5CTOkb1f@.cZimx##B]w^ڊ&LWv \+< =m=R. m qlǰG{ $RhBlDaRrRa,:xV&J;BtBK!QHOTuu&}/ce2pȳCgyB#n FN>Gt!FQ uT,6?xÓ~!0:6|li7Liٞj |3byc̰:(aCHXK!킍~%eL[ os`Ø-*c!)&ijSa1mQMK9|li堎WRβE[* ⺾؍19h/gٚx /]=3|'ъg]^5Lޛ\9jcN9RԮ_s_cF&8cD&GHsL6N'{L6XA 6ƴ8'wȚzЫD֘R&1,\U"kLrrɬ_$je[cxOZ5$b9`kJdO"PGvȞec0O㲱P]oYelk#6c##sVdcڈ2f1qAzl@Uщ<^#_liWN.3*Gp3*9YgΊ,ccL[ 6jQZ+ cڢm!ee;㞐d*G0cZmk~)tRy\{{51A*DǴH۠ɡyL:T_|a&mUMqcL:qǘVit87cOnhs Pұh3:ƴJ۠a1^6e)(jWIqftqPJSk"ۮ!g9+L_@2  Z-!Vw}:% Y~ :T% H(gС*;FJYnUVi t6Gm[7I QR!E0:FoNئs 7JBXXю&u5Y(]mxVJvQFixsIP,GF;j/ cL2=5| ti)ITĴ9;7##^pbW2&sG :DbQ _r>ItN2.DzdwiCtm{ MV!:{1R5IcL:|9飶"\[ t6G:FBobBǘVfj(#1RL BǨRKP*BǘVjvP>#bCoA*BǨyMBǨyMQR׳[l ti:XK | tiZ&l taFDG5EDA5"yT41mO !QqO7b1a0=0j7#W LAFǘvcscI QjQ3A۠Q3AS*β916| tia4SqcvGa;QB#@1jãʄeL: cL:D^R^$:/R+thǴJ[CQ=Wi^I(4ɝA(͒x!ը}BE:J9vQ)1PD0KyThQ/AG;+,6jVjAخ8cT ::`jPta1jPd|aw F 7jV@ ڇ)G㳌ڈ)/y7Q;1!lQX ׵91U ڋ o4EA*-8vc8ݘڠC ڍ)w'ૣvcʽ qǨV)V'ૣvcj:f7= ٍI5ǰN[A*-B; vc ]KюjA#vc -Cut΢ڍ)tPmp91U'Zm!Va%:f7&EvG:FJB!:f7V~HS1UZtZ:ݘ11aP}j8vBLjViqa2711)iZNA*UuT^;),l ta晌sIQ , w91-Aǘ=0ȔOF,BǨhMcTSLIwR1{.Ncڞ[~uBǘgthBc\&V`; 1/fؘ=ZQ0&t91UnZ1f% 솨XU:fϥv`scLTdKkscL4 T9rNzLU9 M{U5u LtBɣu GϠ @XG- UL#yOQkobvB6=CxTB]oHOBe9'&zFǘV`+t NdM1-Ŝ1I3\Bǘdͻag9gtiqzaBcLMt"1tgti*6scLTQU1UФw1Ek*mZ0:ƴJtQc5Tv,cZ2BwX4:ƴJe6b!c:Fz ۳hKщ͞T 65K~tԗU0:ƴ0!F=!gzaBcSt# | t{jI%Xw{n67BǨqO if1:ta{XP3 'r2{FǘVjаscL4 ˘Vi.BǘVi+tcZXaC3#cZMA* 5! *m|t~t iX/] vt?:[ӽIt?:܆\ *M{Z bAiJp(cZMcL@Lkg*5RcZ) ՀEcLT)WC(:41ݏo-aC}*5M%cX5@;jbz5-ңZM#Z'$cZjwscL Ӳ 5v+}NF-5J6{6Clb6P'l Y\Kk,rY06SȬ%҆ljP#YV#^Ϫ/fȢhuV}A(9˒%MRQNl=~/('QBYvC(a0.9L(Qsb el6M#A(9BE}T oBI_i/~s` 2EB[,㲸.ӊjN5Tòȳ.aׇL<I9< gWx:,}a05z&-^콬:JQ%wKfooGiQ3śgY"o77(~%xߨ`QuR'RDkc8YYPxr',jъ08W@ꭻK7OhͭJkr+|p5g}Uu\9W{l*e9~`Hk4!󺌌C}G))uNrg"5<~{^&?,q qz&Ùi:ܜh<ón)\9D'd|[]MbJ:Lul2*|u a)'Ԭ@%Í@qG&j9fS[aJsf0>AL dwZ4 ,}h3&jXcq1%Ff8{˄xj6:gNS )&Yӑ]v%x66G&j`R ]pE=CHc0@9] #BC AR':@Ob t d7\@1EX] ڱQX WȮaŇ=Ed%:SuX ue )松zkq. sj%gu8s}gj>Ovsx@輱ZGPq}5նO?/ƻwxB'~i <7yE1DВey%&q=,OvAvI>>|-S;0~#&?qYRxaw<`g4 6ӝ< u7 d-EiLabTl=À}^=)wjQl:e3u^BQgy])f < M#p\04N;gFO6gh>?K·uGx@?a \(|BG){;2ƪ,KE&5y0f$kY_Hӂۛu޿,"(]*(S0ւ ~1+YtaNjV0}r2Q&ː+͒?ں"?Pt܄M"EwbYIa}/LӮf ;K~qd 4Zm.%Y ZF(!O:uKOó- = _5 gmr|q3:fsb6'jIՙ > _5 xYRXI=թfrzi[$bX9~~qeK4Q(Zk',"968pb9"t~ab9۶m{XI ^^RJ輵[zI:|jzOGcUSsl_̂{iX9WP43dXs~Cp3xD 9?{ EZ %NJ ;`$Qgb r~6PyaD=99=V oWKqW+sWuWkwW+{yWku{W+a}WLWBW;W4W._]|iws(g'z m zDմ~!cZyh+Zsyh+ZEw/XkZd*ܻŮvl_1svhz"8T=A9 |+ȝD+|9O44-+Z|EF\aeE}U\lLKH:i LO f+uyRr g{]}d$]y]1|Yj>-jEQ:+jzT8nF,Ir3OV~/$l,]՚H (s59 ^FX9 OvVke:|U(P>֐]R}ヲZC*R#Oaq)nG2{`OMլ)̊Bt# 0B{Zy@誽9jDU) BÎlGC|mW|Yݍ'ڎȱ̅yu}>s";oɎ>Y&<}B] l.Cs2{;vD:tg-:KV:':|Pv>9\Jе4}:紇;\u;:'s"(Cd*~9=Ds浗GK9 uMś+`5\񦮉Zev|Wd|.k;-\0]sٰ-?4>ck"۲Wk";u]S:WN5^{\Jk<;\PvMǟ UL`n\5 ^r%h)]EAұ]E *`\jk"ߠp%i)]3C>5^Q\Z (\^dxE 4 (fpݦV8>i2bjU C gR{'5S;\Z$^*蚖?pOvKW4E ]㽮銦kRx]3E<}4׸G(]sTKH&+x|VtEs]nOv>o\W9|Շ  |=ω M<\I߯_61-\/ܣyog:$͝g?SayY8 GT"owf&} B|%u.SrEg7I A] 0D|p`U_1ϋ8I8_3 Drxqѝew -3>'PQ(u*qkjDe¨X _4Gf%~ <1/D?!?))qAwՒGA޲0] ?ԾfcO.Ii/ٛe2{%-oh&aYfߑi??x9;b ovא{"w:K7Vqt=3Zį{n2e`bܔ>Ǜ'$3!nNW}a?#S-S砌z6e]0el2aΦK^教aѿDmk2S<"2Ɨ}FϘ_6g%j3Q3T}F8_6SU>%DuƽѨ}m4 9n0 .2s`ӥ7*eT)3 I9n0]qRfL47As܀7h0G KK(Uhi=wT>)a9208M8E~eF,90[ù/5#sCQP8]Kȫ/&W_0U`z\TRirR*KzM/(?{HUfbIReGUfbIRe'H O*?1z$2{ӣO*?1z$2{$ExdG=fTdG=fT "{gi2{ӣO?Ih2{ӣO?Ir޿3{4Qe'F$Ufz4Qe'F$UfDgvgh2ӣO?Ih2ӣO?Ir24Qev'F$Ufz4Qev'F$UfD _=+%?ii#] ۛ`'+vыؠ/bcDо"sѯ s" ҡ&E@M 8w~97/"qn3@SIy}Z~|>E|>z>OuާGSty$s O*?1z$2ӣO*?1z$2$edvG=fTdvG=fT 2svgi2ӣO?Ih2ӣO?Ir54Qev'F$Ufz4Qev'F$UfD ߋպ.&WwTNr5du꯫t/Xs] V];?HUfbIRevGUfbIRev'HpU_ײLE=b\ocGAuA/RbCܴ |@R]K7 _,D)Ulb)%6MJ+q n*&T* WpӪ.\JdRA/TbC9xCژCWN9q)CA9qACB9q)CA9qACCmsI1撍S W%K6N11l^a.8Ű\qZdCs撍t_ڠoOG\Bw-_`,"_K=-51/βxņ-HM' {??ռYk|E. pM\J"N,ǤAIͯLց.h sێ1I|#s-<8wIA*4Ki^N-rxc-11>s o-[WVP,M@xKaB+h,-F Y~j rJ4'/4—Égi(ma}[ *la7nD'& MK!u?Q ٱr+~_Af0L\A/~?p6D3 ^W[uYl0/qHUGqlZ- K VθN@됡g98!,.v=A{10g1h1-:"kV,Q=YS29o\?0:UeKHv^?,uBqB6 5[(44ܐ_(&b~bw2}`*DǼ9z5ӹkUL\9$Lo^ýb2L^ ߍ;1weUVe/]~R?ZTF%gsVd_u v %_hNd:ݜ]iv1,'9ŵY\;)9[tFUvfa9@^8Z 2!sK#UfSK#{u,2p1 ˀ]eC.pv#20]e_S@[}vRho&h+@||R!EyqU͹jlSjIyǗv= ]9S(msY(HহǸF>Z#h{ %55yz#q{SpW(:U|r7{͞ŗ+Vw6i_Z—f X4gVW'u[Jʟԝ+93*OmŇ`gcqs[{y [̧fC&U(&S\EKׇUtNRdyP Bkʺb>Bq :;)SϾZ'{4um|sNk@(ރCfVJ9_ќ{>¾L̽n2q)ŔLLb9~a'սSr#-`x|Dr{G~HKPPzM]}BwǔQoɌ,LpOX~whw~ɏư2H~,жÅ<X dx)H;Y}7FAjAj (YÆ."~T95CV Z1!pR,4+ hZZDI/2:QͻRRɕ߶H_u^Pn7U ʼtpPUɪ<YuR+*XC+6kZUA1d0X ^YMjEw" Qݼ%H )p3M[?}ic8,FP*,֫I8jR28TV(HnuGl([D bn7}W $,* E/lc*TYZҏ(YTJP-e/JޖH~g㷆tKI?;\أ_o `\"! y#/Zr=U5R|LkH?-2VhM˿3[BE7*kgSoNB&WzE+SE2a{#pL"tj)YA*f6[Z'2gRk:xe `JUW6 IqfVE:8H٦Oa2}]c[?|!oM0([lpKB&"н&u]bfViy%;C%߉`Yh&G?o5&:xmzEW%:8i820!Zo1c)?}0?T'ͻ>oa_uf,?\ v|槣"$9#v˃zaoDijc:% {C^',*)s]M}8`<K ldf] $?%tll)e(YAHD7/;FYpFYăr"±X6X(A DOW~lŖUs@l˳jJUϖWRMES-K>c:q I \l{y!Lx~ notqL* 6[5ĥ2lBW< loc#( ٻi[<ݛ z 0F aG1 $%pzkъ-H}ˆl߈|4͆Q3N[`,{PqV@ GwG|yJL&@$@jUW)'-ˎ@aU5iѿ9ԗPus!Emh=Hm{kܴѲ*B]EnV#);SسoG*=[ѪXtltV@l 5l٩kTQʹ$2HMݪAj$%tC੺l ^Mm˴ ^jc6({$ESGV튜+fȕ8&XkG7~6vffWKTu#$D)L&3 ^]5&UqJ Vmer ^k]1?5UUk#ibT@sTG^YkUj"bX ˳lxؖͪj"Sw;-,- adQ-;fhZO(g8!'n\d,N}>}409Ι; Y̩jU$u)&K\ NH O78Ze~|-jct1k6m28hAjWJ ;VF@e 0uoȷp{aT.2LӐ+Bnc7jIB68u|` z7MӬV.dBMyiFn 6ѷJ:csU6QޒժҧSkU:(F UcZQ,2ڊYNVtȪxnAa^#;9šN^t 8z5} ^ 9P,.R ݮkː CxKQF.eIyɒZ5݋ץ 骡ٕ|]6xm2m8Jlpxj8^xÏ31ήw-9pnCE +)˨]3G2zx,uiZ\PT@%-ʙ IQaR"V +a6dbUҚn6@^t[3gLyěuU9K0wiʕ)lث'2H լ&dkTX7N5(;v;Pi !p:8uY+^t3*e,U@^ >a%w%.+zCY O66&Qgϋknyg51S{7 GYJ4N/bB`jAH)S!Θ ']mfßw:aw؆ZM?Wf[uv9cq.ŎRÖ99QLbɪUA˨8l$j6(ya j ^Y֫V:WZY ǰ uhcha=;~YŖ^iQ5MXؠXSjW)6xe0krUU*Vkr-ܼ5):8mS> &N ;6>>$ޱZV5B?#42d CЫ ^a*5Eп$wG}M'V(>`phPAVz4XgDu';[WudZR1@vTM]A!fYXR*v/Svjq|:(D&Z?_[quTqV(MPittj{ `e5~nuGqΊ`P V` ö);lcx ; 2t>8R-s 5PjZӪYyM . bU2bؠahIa.ʮ+uS3kDuU:(X5}lYV^ڦU +#sr\ ^Y PLSݼꫯAժtUS ^6xudd VX(cO.Ip0>:qpjU Vص_6Cfê!O.6N?knG뇱b(k+N10EV)=n6؃pK}Mҷl @+(c0>NĩP+k0&|'}֎3j&UfĘԳON % TwDY˹~,hrv 5 Z>miy3{7e]n?6ŶuV8rfפt+bp=㉿SEq^qUIn P`0R[ ^jhYƮ.A aݼT>ZxtaV[@l<SFE'.1N7('5Zl}9-a5bp(GZN ^jˢa$Jܕ/vu(B8 Pҿm^*#)orիQbXuӚ|0b)'߹j18qP ,W@J)BX+3uX& LʺC:ݼ冟1}O*M|! u%`?m i WR[A1!|a_ɔL9YdtP Y^pռ0:xmdժ=7]c!Zt8ZsY ^!k5%u%Vvw\Lr{/KqTwK6Rؠ`! K(Z&&y"ikP4&o N]i7~%IMzD{)dZ Qkh\mL,G ^%:d. VeYMk Q`WGV̮uVsmVUmRW0o겇7d\r.5>}y:B-{ヒ0Pĝa(jw'|"8eYotK_y߶hA J7KU}X.)V]oKIgL}MЎpȯ/z:RSqNsǻӟJRh c]퐜33La$qDXfUΡoJ u4&>Wv )@(fR3T]]U yWYJT_yl'/(QAo/xԠ̃'Ǔ :m'w=x#/^6KIn %}w7.޿h=废 Ph߀HWHZҵ#]o@># H7ftn5 ݚ?3GӀtgFzHW&H~GQy ^T=n1x9U/ǽͻ}* pπ"4X@7llͯ~(d?[al(pZ'BQ0MoW6+@tߵtPdg4m]3G5Gcd:n)SS >.Ky%uatTc}"CaH<$ H  )@ҔA@v?af)a4@FiD=*`(&B)n]@FyHcugdC,.B ( ~ 2CY t(CQ$ڠ{JW"]J9Ͼs'YDa@IGLO1 Hg.$v=2t8-)E @TdQ0b޴͂t+8LaL_Jnn#8yȤRZ%e )}I3ꇏDn~wOˆ&J&^m$u.=mnBL#<.r=e@.O3ܠd1 `v/i3g0kc i22 ܌a6ޟHz'/xFKP2Nٌ݉:֜R8Ndf;.yA6P|P= ]+& ZCU:PFAF겪 qww?e@Uq 0"?q }Xٔ*FX #z_:+&6ifӮ0Hp zP0P@QAX 5vc5v|m.:l&@AA BaF^D˂|#A U~% # 0;7a4 EcqDM/ALJ^h{,^ 0IPO+CkO\K(Q P& mq4M(l)il"!SQ yl+z,# a{ڱrfF|箃Ѷ1e $Jx&U+ґS D Y@ !#0dW om!? 0:&Xp WǩO)aIBO!f,ІQ&yXP JJ3#6P IPqwj ,O 8䯁eс&atIvn]8q(Yc5iu+>*)Rx@±`B Dn.r$!FfNboY|M JB |eb, N{"[(t(ap |U &.[(7tH.m:>qm61sm,JЁ{.EN(p`H@ MIH ?5^ȏMD>Vc!bYw %VT kG;~|Ў=ƞM )҇O{8Yx)Zdh;,4+i; Էé+}qut_@Z~_ b$T@' wu>|{ULq6Miϔrn޽Q*D2ފ7'5gRBu3|~` ]1༷O|}/0j#gٱ5*26&>#K96kG !Brw#+xD$%A@0qpiK9[_p&R}.R2iy cYsJ߭צ׍-0.NmTz^ҳ tbi~B+y($=;i>-qUT`JD{Vhtiw ͻ//Hw-+ٴ5k{7L*HHJ$* }ɸ\#֘}̏x-.t/yA\LºYZ1΁5oK huײuWٺ4n8Cw%&޻Xhifu}S+}Wn0փ?8ţ+ʑ]c/37Z-͋RReD:b cªq٦O [#I|j1}@iߧ O,dpCEP ̳3ځp+[bK ARB(2p D39YE؃

K?r+X>OQ=u)r٦!06-gs~ &hJ ډ㈔?V]+MZm *yAH@o_N'hJwݎHNYKn7'0 Yf[?iL7}e xM> Kа!/|+ZZ_yFA$ W3:/`P~}B5r+A7ٌp*B__wD An18^^ qK ,5&#LJQV"<e a"'8$ uO)N|-ep1S/F JqO;60:+RM?NF#8 _Ggsw%"v|G/}qð@b"l~h[Hrs]௼ #LTH)ͰJ579{`co W1@+W=umƇjݔAt+9b.jf IZ}ޤocuL 3Gyh![=CT3R,{W:ɽg 55c= kfq[/6_x.IY+^C/v՘g"en]X1>vYmkvꦥ vN|>ǢGJDa@!9^ J̀V{هVr~#e{/cfna˶{߇>#lӭKu̾dƴg[.6&h`h9<9w>|^ ߕS QЎ/";gyk|.51M`xڒIq3G$F PB f:9U{~MsikA*NX=RN8\6(_ ~~A6b ji7 `6Fn Y3 ҷoaczO(9P{oKqL0h%r^f+RǪuY@ p|35y bm)JTehn'<>=yjaўS.N;{ @:w>eopT^sjW[U*ꑙxl0: -WΞ䜸c/M i -u9XY ̽ڤ_8x@PO+lsE`qkYcȲ(rxdӊa:L^eO)m<<]m~ӛ`/~.9{)lIU5LUW 0A7 ڰ帔M~&gp"܊Yh %:j8')~d.0%C({!WX:(UB UDG<#e[/@j-Vcig 2bD+)80 \+ֈ7@l*Qe\zBP\ m0s6!V-[RZt|Y6\.NxWq2:ݥ/@o-=!f'D &+iT㓛T4Hp!ٛC o]DW;jI&$є*++<.C (TP.<0ErHW`PB}vL-b3Rod,}!-EBzv=[vip:jMb}_jI-=BɃ2R^ {C{>D{X>` Mr .h+:_ûM>Yˈ&IDY)Z4m("4 #S s]0W8=#!|;Qr魸GR&䮼)1o*xLd~RDbT>rɐ-@c~•{~؉>糘MV{'qp ɒ#C}IhpO:jl Y4 "ҧG.맴Z",^!u f!ay* JMb)ݰV]ΈvOps?Aek"vV (/p ( ϷM(!6^ζ){gxCP1?*6Π '(NbC hF{C2D.N9oBXcC< F3uc-K(„l"8͛w%$ծ@-G?Ɛm |8"V V,l@ ]QvLY 9sĖhh䘥MI0%</o`'+e.R3"z3Ug(L]@TBb03+\Kk= -u[YcHӿc[Ě);&W҇fr(@Jt-^؃L2jA5aǒ9Dc励Ma خ3\P|+rmrKii1a^"6eX0' Wa >]T;`!YYYuʒPÐ]Z|0%Ɋ #cЈ"t,ْB)@M܂ۉJb)'ܰr)Pe{mkwԎz|`$y:ά*V%ҙNQt-Mӝ3ӝu4Y`Tc0%.%~+U_h}XCoJ+,d䋘P^xgd>5v؋+QY/$Q׆ GvgwmZd&re}xnx3^wH!P 1g*ZIQJJn[)y^a_?rcNNjzQ IZ2h xqX`ݕby! (H>Ps)vV0"kBɶZP oؙ-odk4_EXf M2< .$`KarP,OC+4>!Ǔ>h@+p.X vJ`iW(ŻT6(K Vf6బZ,di`LtDaR@{u|S*h5 Cd8Y, f :Ɓ,:JK(Hs̰iX;K tg}y_=AxVL ?%@&zPTgHvZ$;oV~r0AjۓW) Z=hA~?ocb䰱4%fP-l$ؤ[iO{;z)G%Ed2] VVQo#bm z#vYpAox|QE%U{QOR>IvAۇuS!K*%:Ãg w4orE! 0hw &dZ۔ц1j !i'yXiV=J@ y4TKhFR%(9`N.&< U%Y)ٽ8rt# CUM5~VeY5M[p- 1M (Bl\)\$ԛw/N:^TuYklU)!I?xWjFw߲ g_?S o\ {u._._1fTşi#nzχ2ZEJPk6yS,80ACO$؊<71[oW%LNi +G(3" gR>I'8U?Ɂ!|Տhy9x@DV;ox#~[㷯U@/1, ]xL,W;/iS7y"oɛe BC}$oxS2!|˛B,wUD v[iVU›Pw L{釂A]uLܔ. 'V@m@J\>U:&AA9Xv4,YEvp&$5bǚL j-ѯFΖtp7Vqs'ք ܩ"E!731U@TQtٛF@' )vAD^c;/Z;9ȹSH)XRY)B_h' 6ҖVO7K(R؇LHRjhRM)OLNz*.J,j[.dAfXQA\Ta|İ~l.]r0V.xܫ\$%E-w]z!fZi$)߇E.x4]r^XRc |·?Q{̏BzZ][ )JWpni%&,=\|Xզ7w[pY88 㕥?)OMnasiUjQ1`ak;Pey:Tu'g,lDʼ%U..Tɬ`9JWEvv!M,.ͅ 'y m6oP %t5OULXz_f7apr7'lpKyg dp8H7n4(Vs|6[ .^コR]ot6QD8pfgY-@T SE''+yi0R3E7n(5=jYWP.گ}3F,U*te4.9Z:'`&j,5l-ǘiv%iUR'wmnD vcB5MQċ*==ݻ_& ElJsF?יK<2$(JU#H 'Oc Re= {!m`EoKMxPKVDX8ԪHeiUԞ4(zSO@̡-(bjcx1t]^GT%dՑ}lim;RAˡŽP`NE. K xi5KrV_Nrŗ/YV߂.>—/K1L5rt]4!ieG#qL>٬T*'Λdꬄ;+xWxY[\;2x"N[0нV|Kj|=\M4^uH=T 8 [j$̳!.E=mGֆ1w <7 Bb%VDHܨj@>9 f\zn[p4xv|p'C>l ^%v18`s9/ti"&}}6Zlb} ^G֦Nf>o^'i|qJ%Y?| ft,~,HL@풾Q4로ņTCUOOgKc1gX,Zq6'@(;]6{B&xܝ.P`8={7Wb7$Ktڍty+(]<~ϔs3Y k A P5oH8mWEoj ғ:ˣ"Lzm(7#%$ bIGPA9J8F.=j' e9bs|V3Xe$zr.3vU#Zq{lĶOì^g!L{t oBZ;;Rbn,CYbP|:m< q ?;.I̛@mǖ%4kI[9>%k-@X*aA$H?~QwRËUWҕ!J' MONuWO6 T%ܨ7lemu;q%hkJ9/m v:? WoX %&=j]vu`LoFd [̫Ϭ.簦HyS2YDohE魋ឡ>S? #*RXm*Rao(8H?z8ϒpYGNa+o6=]d7(:oCQ-~9 -TH s1ĸ` S7dKi3v4N3lBy9"g(Jl@K6FʃVgh;&Gpa?1mwMD8\mM[nó oSȉhaV۴%Гmーp&Zћvvx8׶\]d,-Yf kU=2K-xN8Ԥ[")RV0Mv OeZ'jRǾW݉)ɺa(n+̦\-ReGilL9qY2#.F0^|~% V>3mI|H/ E YoO%f+|j>Z6vlCC[;&w !.} ytmmmWO٠w ˩:1h &JYk5U"EfEi- qZHj,eQ&gi=|ә,pI_9cA6UϨ*!A7~ۡ珋@Ng]WFv B4 ۏ^-)dX"hX~gІ R8EW!FXC'̎a]}a Ӭ/9[U pW36ԏ UpdGȹ臰XgmGVj셺Ƨ^-j/on:_8n;D;P$4 naac ҡJɲA^6lmoB^҄<@k;d l8;tƽ<"2zJkj=.l14ck\v!弯k]EU cÊ8{sTl(1e!Zsi =!La[g NbԊ=]y[!!x ~Q=!Lϛ{iG`y٬i|+Q\TGC!;9ޤpѼ}܅gcnqHpB>/La;@ͨNNՎm-2H?`[p.AnEB.|ZL@=&Ҋ& :D< *kD H}N?LaGܥ ?PJ]UHUrmmGğİYW6҆hrLRUTsi!fn,Dj$({On;| W8oxR%N9-4ׯ+ OgC Zo{]? FYU[yF Cx_<+өu7&Gc _K b2#ȕ>YU0CSLReBQZ]#+ۂe_coQ%,.k9Uu>jr^6>Ts]6kvh]wtTG6|PnX3fNkQ}V\[d"PG UvGEE$onNY+q3w6(VHfcd5B;OƁNlڟNNS#*(;.^=]ô%S^uvW_ws?[-0NwYT롷=ǫ-˘O;En2&i2ꆨ=1bs%;ef? WfXK^IqkTۘCrs"zrXCb=Ļ,>&~s* {IrMeha?+LHxR ιMc=gkJ﵄wꐬӼ U `Bv,V?"WW6luU}jzI*#qha[4;`# ~su:_7&2[poook g) KYfG'eŴ)msn8ul9m7y"e 3OYj.1MYZCmBYtPֶOŗm."j+r7 *wxL›p!;CuwjlV?;9vׅ^pw'B$Dk=Nź""*6 ^c1ˢ,Y@)Y&C7(pkg%\ss.VJRzp5i:,"Q*ܽ'iR٤olJӭ0cm' CH"lo0٭W~ nN%cm[TbI/ {eUu0 Ҹs5_?+U?r۷-L0KoGM:puFQ6H Q1T[ %"?U\0R_ UG;//LeӤ=2˜Qx^gj.?EHg/^] X}y/:&j*3_1: ̟Lmem%.DcU~eyOT.1@A{єAR&;|mE#Y:96ڨmqmjpW*FhP+;In > HlgHG ov * &kCqtoncQM1jea!)˃f3[YT膈QB?w~ui":qEm%fNQy OI,&vDΚ( rfNٔDUOG@j)v٤,1}$9&ٟM鎋m/B@zSM}nugFJ3x,77:*ɓ (tL *Jfؙ_`rۑJ)_$F;dL-Ѿn脗_>^:?e}]YDNzY dP^3A8sRP @/#grQ$tnVaڴ#o;e%@ T+j8cDE2FYQ|Z) Z,{F *TGClJ8"y|?fp,nG]欓*PE3(mvlYaԏjGMu.2)FuOn={z EoV䴤 #H1$FQ5"eAo] 0"i(nBFӘĹvT'ob '`LUPgAz$lkNE:Qf@+شt(.#f1~~#o3,{D1A>bUI \M*Ai~TҠ@Rca̓q2D ˓w.)|H~=0 U;¼p,g MMKW>mzZ8]k+eǿ4RE EVG׏QQ"N:&XVR0fTE˂.Irw9҉@犺IxQ3VЭA7Pf.PaWb9e\"'8tŭaBкLD_\.7f][J[{ șjO^gM A:u!(60#3t R>ba,ܰOiNcS ;>x>֪7}2X7nKpEށ~KykF&akkU6ZZ Q>77;%75KIX&ZVY&?1j KR誝^'7 B:t'jA7[TJ!TI\I%@"# Rą6ڶyH8U[`k-X%)f]վYcrSJZjF,A*ѷLdry t % E6VN] ujꗒ5YuUW৤Q+|#FU8́g`4_G\|MMz8,ҟNLD"IUL] Iڦ4԰Bhox\ T)B\*Q }$Eo_t#t皈/Fr߈ z$ .E}] 6@$H-8m97Q@!K@DVv3A%T:lqh*Z}#j3΢6`>cÀa+lci ꈷdn+0f,\cQФ?hS;Dؙ0&PýoMÎ=r!3VR=)' ~胖%^0H@/<@[/^[No4jW{ϘpK_ז°HZ:Y4Ph) J5 w01ޭLzi[`H8Cc#,PMҿ +7f6F$<Na媾y(K.= 1IIn Tf'aBՕB4("bi k P>eWƫD6@k&N0Zޕ.%yս>_#=p¤sD1 ?4΋<M*+|z8KOs4dť ӂ 2x$-DFK*8 >EUE,cLs-~[^}]48!Z$*l(LƞVIzi05i/o371(M? !vͼHЉMTFUUߚ6P4c95,.mE/4׍0XH&U+frnITLY;nNW8Xe|DB6u6 [ t2V :5.^N7%LjiVwf')-@vhPDˍmɑa>nRk[ u05\z5BJ~'oDA^Q.lT&*AhK*9IM35vH8: \$"H `s޸׈bJ::wefym$D*ց"z2cVe|2?QjLkTa6pV;r}%/Yt$&Q,cfcY*ya;#z 1#aDՎ\wj4QmZpk pZ֒d\04|!de$Nd$\p^l}ʙu'^#*4u09\Wa?Bϕ Q%"Mm}&l[OAQaV&D?<. Pf77Cs/z3\"zL 6h +NGmckecZ*NQ b6_=o-j| GRIIU}HA&(qz#58(RsW4X%YA]e{̓OԮj{)(j^I74FDQdVʙh^+u)=I\xWu5 mtZ` 4[J=Aj)qQRLf ܬR65)*mi$k,fTHh%aL}jڂ!?VFFzܓӋlvaI,ũpŕ kL}+*CrcoJ>d-׫5&5n|qASdN8|نR2G#zH"ӅU(.,V\M#>հ#);Q%پ J3B]6$Dvғt& se &K0QK]B|Y ˑў2$ #vJow =h"10,xLHU$_%ƱWHa.>R)m4hzt095/BJ#& ʘ K CA2uCs̃ʳ%@ʙ'.FMdnu6Ub/S|Y.o/_ g}w7_|$I}  -'e= ?7. (J2|yBs[]@[|o<N#cӠߏ~j830($FhjѴFY/%'P̨Bf|&ßITݣtņ1Ot~g]3g'!⟑8Y Kly?Is$&؟z\ ,4!1/0/8Fk(u'Ta*?k%>%(Z;1H]~v )1,o|~kv,f-7sچfs`fJ X捽;m lVlX,?2c tpޯv9לɝ & 8G6@6@Swtƽ&m炓Juj?;ZJuBOPo>u>p!|TUN &|Ӊl@[ox^|x3A= pDY_MlE |=@Mhp'` sFK8C`{" ^N@$Age…_gco;mS#j]Uxv`B :#6d><~Ȏ{ ü8EY'ɍ{"l;MMXe\];TV}gqi*ܝMy"N"31n4=]ó7Ȍsj -ˆyK᦮2Kic>%T`5t:+4-128;$kg!YYU@9X6,wж7mSηp'Ӊc†*z88b:vOiu ?P3T{0=a`̲kLA^n-ΰՃg?3h͠eCm`Բk.-\ul,n' ،l!Ix86~Q%"pr6|X_8 G8 vBmdcOP{vYL:09(ζ:Reͼ`PS<*(̆XR<kMqy@L ƹ8 ϿCSZlL? +T{=}V!jǼA@&Gˠ h9shfW= ÉlL6Hܙ;EBN߼oMDS?e,^'-^p7e- d߉3<'?4m6d_@o`bC)MV60 ?-Ea켚-Èv5z2}4|OS5-=6ܡPzZ=/O)mgŶIMB Q_w]~ wj:;"&nOi OaCM&VM,/4tn+W-|cז^xz֚ú7pxhۮa'rxPH}GuL\g?Šd V-6F\x&pرO6Æx'F~'_c#h- \4JqkŊQu_[꿪q^97MlJ;Ó;OgLH=O}s<~\.rb~U?O}N~m/DWl OW}/1} {qGR 3u[u)#5.TiEbZzbnG+zI{Dڵ'쒚ix) -C0(ZG [m2n@ j h%)}\<Q@rU\.DgD#qQFfF?)G"Ftܼx\Em2la$Y uϖsQ/d uicIe/"4{bG6{nx㙈ըާ)+7E$Ty6A̱_EBp BO.:;"׆"EJxD _Ǎ}̱٫7`jMKmq/D27:Ϣ&0MLn?!CsgYByKx$𝌄QX2dGmAc;]qEĔPDkP4V$˒ 37}ǐct%ۉq}]B2)m:ū,cֵ;هPp@$Vw:l (\QxcAȳ$ZRCWD脀^ϸ̳)I*| WJ:l?[? guNI'lag"$D8Ud=Uf$3ЧhY6{N wrmdt}%H Q+Q\GMYTk9#¬Bq̓:= GqVc^{e憎-lrGJ~xZ!S@w R) @6Lom$v)'|&YZBhr0paNŋW$zK2(ڸkRp]BDߡT 7VrڕTܓL;eOͨMb b7'| d1UZ|›.^fgd{MDDss+dCz &S;R1&] (,þv,U>' ^SN F[XDo }R܂m8װԓ+26m`6[}=GG s-t'>d [9{RHV?J )(CH;qRr 6ݷ=+RDݣ>ߑ6}L~,p}7 U8 b,b;x662]oA{xy|I6⡀/nlG2{@sohvACHxSfad0R;,D]e!$,j >VZuT>5[&'2 2ϓ B-daRWQf%{`|BC0쿿^fgii])$TRvEV}mI ]hG0SMJ%.Au"z]!N3; /e¥DGh΂i90 $FE'<y~I ti(㫆IA"ƫ$%uழAlItvF@羉v;WdK %&FڄmҪ"X/_ 5| zo ֡pv>Ǜ \$0sH 2f:RM XΛ}W8 | bF aU[afA.]21'Db vD78`BRaC%pSo-nzS:"bqUܣX gzP=uf=c}O4f&\мI+a&paL`yz)+ /$Ҙm m0f3Pv!`H=@`ݧ|nGy60D/yha?qľOs|ud/\S .&.0$wNYktp W 8'_`' ,%>ѽq~0cxrʼn#l M R#P2_O_e<.ѷ p,E$ ͐%m- T:x(W/j5HnP(rG8 8Q*c%P_kD۔DT C=TAAbB|{'K'fO^ {M@چcAӸ)[a2Λ J3$hJd!,≥O} 76#>c*j{J)L+K =ꩢ hnIC +B@XK6T{}y5Pp @©;sÙ?[}?O_(%ł#fJ%"ApɮzT.hMJY+YZ4И]f+rgS _ШJ#2WPHmwTƮSIQOEtšPjmH7W M#wΜPu"FgJ[D(9F[Ci\LO }G248Z'F31 1(M%ek%JÁNǜ ܤhˤ\ _Xv!udJ ̯0TONq8:GdNt=`%%nOB!kiH2{[ EE Mloa  {Rz]O{>d#15C0_Ss][n'E;Њ`֚ _LQ/Wk@1+_"Vр\7=Ň=tv9VZCU#(Z,5%)mnktV7yTY@. >ɉZ?4 D:Z+򠶘,-u"[%,sl,B÷/E91ØEE5=iWB-e}z zJPN|$ L.;BF4{O4L0'QJ1ly88.":nG$(ϊz-_G_!c@?g(ԃw@s--c쪥< SHHNL4@XBi ,_L f}YG'=P {>Gn۝ e44bIe$X*F̣i$m;qews*E#ie%ȳ-zcl Ta}uIVW*!:VX'.,1iU8~ET sq0B[Xu- m psVr ϊ4V3_CUUMR(/qP6eBAQ=CwY.N4IېPb:ijUQ23a+NAiKzTG $=p MUլPqSUJf#nigkFq(bg9H7'~1H uVY`^0XVe$ $Iz_ʘ>Zɔʾ\r`JUzυ޼\O qF2}(!C9Vc uViJأb8wFUp܀8Ѧ@Xw9pQ{aFbQOk‡YrK`U܄N])vlLV(6 h#q=*z}.@)}h\ nMX6YRk֎wL8[Qy;ҢCUW)N|Ic&sQb@Buh ϙĚ@ xw/Tte 3'U0p8@szaԚc0h_Z$WU%H6hm .hwJ-X EO̷)]> %Pq#G*YU0 lq@ ZxlDP^ ,K%GfMsNCYJ~GS2FlyJ (wبy {*S+!;+p"\xp-D-220L4JuE!Ϊ:s mCHD%}I2rʇ]|%';VE$>[Nm뿌 n\WEiNFrpΉz*j(}A|O ,BXWT'% kf-QjaKUJiԀf3.njD oF7,K'.Q)hЛ~~5?|lM@-b7ƤAjwp>uuK^uFɱVLMW}@ߡ[aRFW$o4 ueD0\ncFO& pޠ;6NW_9@%{GK-LDKKKryK@/?dm?*|z;)D;jkoEZ a,/]8>M}rCߩ􂋀4rEZ˅.y_Ax_.vH/_v:Sj6/7t+ka5juuVusֺCtk 80dBR$!́0-^+ҿQ5p$-G{W1gn~ GM;H[5&B}'Z+h/R+Hx;NYS?t8Qd삪wF ii&7 ~ xMḩSƳU_]Ee+qj哭VO#O-߸,ߜ図|so>oY|twx.g ߸?|pp,%K8~'x"pqPgO% $=dS J85}N[J 3sY_<.tU^būwmW^Zv ݖ{AJQ9n r)#CJ Z e1S8Mtnech,e/-ڴRm(nZ̞[Å1_ 0@` 3@4f}8,*@C +<Bn>Wt ܤKҹ !c ß$S$>?ZjtuC7dW/rOXℾ#sNqz2K8fv}$}ME4Io] \z4BS oUA\O 褐q^}Bhөӧ+=zkp{ |y"&wa㉠q깱ë3'ÎqiZ< 9J #ߋGZEnz|E>2jA9L,Mt•?6nXë[ o?28fm~SBo0!Q8U7n F'vn:2Vn4D$ZJ9UK.?V@1^ Q抢JRYC \Z_w~D: &t :޼s.7us޽ZL}cTy]I6B>+u^ET.\`<]e#[.$Ήػ82~q&M%TCj sWs^*K@;`NG3P9\&+ދZcTBomU-yc8{@%<6Xc+2bC6YKm6nء}XLw^G8~S8 @F\>${)܋;V x4A3ր:lv6ćM]1=ΡE~ɢ_ڨ!AU0C$ADDcBBf+f5iRxS)^V\-e->v$-]]BJ&+P:dB툁+vbX PlW2)HKֲ6(4`T3D $`Ik%m8Z=ĚՃ ,5ui~)T-q)&X,!gJɉ|R-ED7 YX"untM* /yظx9`W\Is?&ML(xS`[QQ^C:,,٘xBY?!JT\Z[{ >fNTr.uE:/Ͱa8^]ؔR@bZآq60p?!>Ѱ$$MGNwun. hz˦"^08}g}~0rXVtAP aq#|q1XXnM^l[䘨dyկ˲ HS~& @q*K-Lũ]u Aj֮6]-as՜E[Dg+U6P9}{CUa )H VsmXRTrlY*qyH2esnP=`߅s'r4$Q>V4QD Bn{HxF4Ļ xXG> XmJ`'uQW喖EK%YUmd`Wd)ZˊRbΨ׆ D,$o$4c{z0'SU# a$KbP8J%A[%cMbE!)T 4ȊFJkG+ #QsNg.^U_ĖPS[ȭVÞ 7B%WRH݆5VfXzIj0k{`r*xo*9"*,L6)Jg-l E]VXٜ]so8. VS̙ٟ_DҧWomA64 (+3eC=|PE*L]X.D%ϖ̐AF蠐 b4!6ءtMi@o^dIw4k!Wu 8Cé&/3,?#$)[m yI۵f~ D60sZk3<klj`Tz`"\ʷ[}Jn65,Il@-a̹xؿZX5d=9'RKrKصC:|cyQ6$P*$J4NF\T*ȅY \2/iFAzoFhY$@JrAذ*cB9!qy.}Bmޥ,OC.f! t R-qGJj%j~G]%OKD|3#9v.Đ UVz ] *%>G0b̨&)J'+ \ZOL^T9ZdXd_Նrp!6RQ4 IB7I3ԓE I a<=bLP$)0_DzUOs#!P83n4$Y f"ص\+7qrAd $5&*IFZWe0,HЙ ЅFU\\\\Բr)rݱ6 & 拧H:Ȓ+9:]yJu 3Ec=1ۛg: V5}byzwjgipfg~fg{`֚1?g}4[x,V)oų Z:(kb4x.-[X@  ,+LzGY|. na[IӁYR!A1 KN=8Umtulߙ=ԫn>nLnϩaL+I,KL|5T39ueºܘ`]PpbИKz}JaNŁ> K 0"@OWh'"l1O' 􃼿KbЮ~;^,* - y/v/ x].1M=S^Yk'c揃ځ6V{biNT^5}&ע'"b9-n"IM1rve-㗜KSPVQ&K,qJ/rhi$TU艨5 ?Vw3A g(&ꎈ\2*8&SEUuS8JtU:\Z bdrJʏdk3^D4] OuƩ84;jĤ/ 7Z-F,kdgh16]Gq:pa&R9*/+^wƸ$XfmR%Wl)丫a/7014ʳwè_ y>ӕejl40(ubk.*RbjO1˻8/(h1chR~ kL-kNcvSaxJd*Gk%eƋV6<^L-KÓU'Xi[*۴"]ZA0V%$mE&!.ot&9@ "fHR l`ʀYtAQZ5NBЪU梷t0w#N7Z9L-_I89w2ʽ9 A,[C*x'6ۧl]'T9;_b% 9$ bqc˄mhR$S?GcNd.K87B6x;R"RUH]NVVjQr(nSS Gv)4q\YT:tDH-ق+sI/fypOZR|&At1?crϔ ڢ>uЙ{<'84$ $Oz(D pBѣŲU˽uy0s(s҅Ѣs:ҝCuZsuZ YW:Jg]+}ƺU.Y ]Ո|iL璝8+L^ϘdIoI29 f3->`< !xHenU]lK2tu0^~;t39!a֓AqX#U򑧤TY]晵ՋGNz( a'-X3:;_DgZL pUFŰ2a5þC=}d7,oR"-dR테i~REFHz|4i*GW#OS98 h၅= j&Q<5G Xs,>}O Xs,9s9cyαXzgF, {byNxk?xj kB( %կa{y yP TКQbť?v7\xCOm[{>]$uHh?_>i wRi4WW/<%hIxe0ӵX|n֛~+kr=R"h뛭a!^"|jЪF6}c( G/Mu4LS4LS*r\4tphz]f#EuW>h};T֬x-ծ,M<{zIb*c K z=g`p 7yZ¦? ):E%kr[Ѱ]UuoPpx.< O.,Åkv /k*|~igD I]!μ.GF]zui cp1hqc 1OcӖPZ`jɓ>ZDvH=]3fbE3;w =m{(I6Rsi4)$wN?'+۟Y< g)5= 8[<}6$9pIxO|O!xzUwևY:C|H33?6tN 4yk<俧ĵnjftNW;>ã;Ω%Ԓsj9䜮v)gr)OMS+MS3X "emiMy^JPw)ȧ줶"•} қU?EfԳ@5^O:j%;޼6\O'/^ lm4S:.8u|P&.N֟A;E*D6v@ūu_eGUyd8-DLͮ/˱mZ6` eOKJUE],<_nww32W1D޳s*~9rn1(D%ʑQ[@*W{{z-:SM9JMZ&cܚ=ܺO-?`O2,Rl hk s9b93j-8{pg~g-8&OG{?u<{?Fliw@EKSt!:1fG)+ga~ݴ X(-k{=]]iK dT:ԓ9&ŜY?Ty~Nr zrY=h龥K]f4֖s K>JLj8Yıq{FE-SDu'֙5uvO%d |Y'he,)qU&nl#d$\>M7Yp˻h@m=yfu Dn{j&pɲ@(/`$A8GC^zK^ y\yU!Q>6*q]V@98ѡK TP(HSdsʨx_0=:A'NW)'9"!'U-<^b:>p1Ek%%"}"3VuV7/ _*:x閙7,+ ,oC/,#(T/hUJL.oa+jWۮj~\*18M{lw&)14iCFS߇(~:i|_@ G2%nܶJ4پDw H /^3  啹= J.L"o.[$DEM6Qp]Ñ夙CWmo3a0eR>ɤSf$A-9upcA`q/JxɃIo ?dl "hy,w[&'  m7DQA׽M Fեm6 >x+ľLkw>5@G@cLK9Z&@1w8_^/ ~O!!Cl;̶ ;:cK\\s ]_~Uy$/5U$G-78)d-oLLµL_A_lxK92 /(\-p{H}OHD<lPNF>y]3<. q%*\,]/G@u;<.B*DZ4XhkB?6+Y a9C}~0Akċ-_{f{oAܑ%cv)Rsgn7 +0w8%=o S<e mEen` M$-Zij/5T~Y X%@`@"b-Ui H3P= jyAk?#@w#480m=WV%8案|UFJ;N79X C=[f^@i6ɞaDdw:ͳ Sւn6ݯfI1a4pWAV:aOqnPU oFLICtnTB!FRw?Xʷ iue9&7XEd.7YF~'4D8NWbzH@/C#.aYP%>V f`O{:'(LpOYB?LˇV*R(}62G@*ۏ!t6^ 8XӚV6&$&5k)̱BQq/`\. b9#JMzJMg[atx>l]"b-Y}b]e8|qDE 8.SkBWT{:[=tĸX(#"4Nq!P_m9yn۾U&;?Ut= V[g-_ĭsy?`0S7cOmrtݟs@rM5'ʁe}.#V^ŢQ%V/&Nl5r "b9X}0f+ L&@;/QApDhzAD LI:jAD]e`ǙRbD."eJ=6$oH@p;2/qYUVY}x2ڠ[Z K@eȠvM&1CKiVC"Iek5Gs5:oˢf aT{a ǎ)2,5"IQS%i^̇,3p !!E#1,@ TJ++>,$AKuʸ 19Ņb H0u)4xX!^Q+ {&Zrr/]$P;rRJ(43f%K,n1O85NS+Z5$_"91T8gYq6\q@rZ8b[6 7.D?vgQP"_nFF 'r0z&\M2  DasݧC b&eb^y!";FxF 7o 4飒J5>i"=ķ4dzH.i4چEHibcEx0ቌ wq_(|Xdf*ŋHB؋L)QD2+=gIsFPHeW tVDĉsFȺ;(->Fi_DkdZC?~xzN0swkqz!CK% hx9Ɠt]b35@` o0}Bcl5 4A#@!4h0t7 箼0zk`zM98^?ꍋNLpVscWe,aǹ.r<6-Qmm\3@.LQ\3Uh|\@í0WkjWQcb2ν]4nXëZ /_Qcޔ7|7hvqGula6O-ZYcbEMDy5])+쨌uQ 'bڵI3 16*EMbVGmǿW:6ջT{ 䛘qF $yIm 1|U*mxE"tS$ DUq򊈲zJVgjVkzYDos.& 9%c/zptCJik+J੬q + 9qAԑkyBШ^NBh692RU-Ķ{b!;9`&EufxV-5]C ANŘѵY ܄l%plo7nFl(ޮ*Ki(}ܳQ'+-`];ޑ8vZը0W}jՌͦn8b(S`qfR@ر=Qmwxgޙ=\{ w˲:աG?\:=6k!ՇO?rĿ?}~hZ_%*EA&ar`qV4,&UH71+B2ﱸi]l,I+BYm3W]̩C" L9})P2K%KKw9.G{"sGuJmP/KBb yQy p p1CBͨ*0OTO{uTtFQFG<]2J=E}׽Sp|jƽsUij ƧH\ F)& ݾ{ڃd'}vb_ 5d ^I >i٫s_,:9lŽ+0LƓ~C[% t;WVJ%+ʝ3NϿΝHܽK\WNbx|tR}'OLW pX @5ͥXG0AmcB4č a4zF)9A.l%>ݬFh%vm,$Ē`&O/ ϦȔ˱ X<ʹrd)aH}Y%n8^9\Spu9K2"wh+]dGiKϖ'Y ֆ[AD}Hb֨.mtntVB0GUK_@:O/ /XǦԗ ʬi'q'vAh~,[! '%dQON\r3U,,nx4hYӦl,V2{VEE)۫vVe&9؟Yz̼ilU̚# 1752Zm BuEϱrU)?S[=UR$S|TM-ed|(DW$2 ݩ=;Gd F`94 n סQ6 ?'"&| T1/ƨY3wGBЬU ߷wd\bX/1K [MFfzuįd:?W_Y43Tg뒻wܽj YA=H?9dȘedVFϔz ;s76c@-/א?6DaA`E~Rb~Ҙa? &c "cZftf Q`qaF7!ºsAjm#ɸjMv^F]>@`){K$h;QkҥLRݣGĒ]dMUa/xF%^x7|Uc]V⧧îĪu~FSFTW/(GHy0 0Ax[ћw {pqlFW'N=`{atJsdwyoQ1j=S4?‡4Ĭf:]ግ`'L'@r'] 1]=f {"JoBXSD`ַٌ$UG]MpvTMc7ĸY!\-%06@+ kz:bwu$pj3?"-քGB ^y*kC'ɽZ-?w:)%pIUo;KtCV<%tC1CY{Bro7_esZ:֫z3$Yý~W0k2ZZ],E&Gkj>y`0S+PE ۥl`zgΓL?hbx"ȇ.ۍ UTFcܟЂH:6mHWS!=G.E9pt"ų5缊$p"]v=l׵b=XεiX>yՕ{ynFtUwC-4?Kh`4Y+2^=;pJbp{{dGf,>b c:R-WOP "dkf.Lg!c@z'L+p-hʫįv.XObK"y 8 B'Ut05@{,plDxnu\^zBQxJTPHH&u?V!6vEqulKY` _K":#)̕͡$³a64}C2M(O("5^brS(ue>}Bxvf{A!MH=`Gb)=m|ax⇈ağ[iI ,L|aҘ`sU:Fx,SAεIxڔ#jE ϶Z5u蠌.tTS'"^;=ѕ15>@ؑ~htv"+#G+57P%&mX-7b-69`#f8uu%̒u;,QIdUL^@=$!`7ߴNhXO?bc=K2Au2ےcF! Ǿp Ǿpl9q&c߽Ǯ؞qlw09nݾ(Ǟtͱ}} NX{r?c/cGyw-NHV; kO >a?_API T2=]<aMTn[z?`7>;ޮV@+Uz T~ Bz ZFsp ^U& ȫ♦ptHca:qf2/ΰDgTci &^w_88;+1w^3o0&{2}m$L7d8at6 ;B^}]arze_3$Ix6/H4dž#Ǧȅ^x3B l<~D7S>$6x~-Nb=Xw:ұc2%"`"i1X||/}*ٴ1CYJei+Dtuϥ.+ͼwwWK| Jz+nZ1 ~>*yҫ *,ەGTk:Ze+3y :bbUπ_ix;OVV/[qJ*]ZV5M 7()8fԽln"-rb~\1VMޝ^y9/D!AQdI;ZlV,Er?ܣ]|}i9`jf8%GOi1x{(o*\Z[q^ݸ!|o2 kEwБMT;BS{%<ڠ~a8n+?WnK/VtlT}9M{'r4:@>x<^`6~W7ph\^O u}CWzQoN_z zfZ+*{x'#⿈9.a#8=硎^nWxKkNrK{um&#? ͣ!pJns+5Q8Nq>>՟Jy(?xܓ!QDgsZHp z{!8 j+Q@Adz&1n-*z\C2 n\g6/`8< ܓ!,gDt6V yR@QmQ0"Ujϼ;Z %MHZpj9>p2;'̣ޣǘ\0AIFc(1'$6voknXFA/^M?m0Gf:0z5aZKGc~= {}YR_ar+]K]м˹,+_ *ЪNiܹ#y^5't_n^Q=6eqW ᕯG2RVr_ە JaX=˕ m@UMIf yE~%6u!Gܶ#-Wp 1= k%Ϫ&(N*tԾ]?wC(|I;H!]Iut*D: p|Al}Cm ¦[,3ۋcCA0Z, # G^5].r[/G.r[/GƣQ`RT|h&eA}99B`EFSwue;M9"8)]X4ՐqmX4HsA~~L}qd_j(iӛh8ÛcocǍb⎱bK:([uʹyfV=-R1 3y79s?e֧Z(dLp'{sUhH7omgJѝ '"G#x8zUۊ5uS7ۯ~p$#}x1XIn0t>GTrۗG['1ؙxBlЊލOx.8zQ?E6؇7̤ DHJ`)E }xmGDZq8uW.OM;RЎ dE jZƢLcXVdWs< y쀜B\c9Pp0BT)>"qo{o 04jT~b|P_ȷN  {)O "wzYd[')]Sg:b:Z3>;<<ϖkԴ;[(ɧum9ՖyN%u&"ll(?͖<-~) a3d¾YKyڈ],pEDg}}ާ xxv1!^sɷ {="eB<2 te\Leِ/tk <ߤ2(Hۦf|`)KW4WW3륂J<=O$!DCMn'^]mv `_bQp;i:OrF y SqU6tMLPG\_\lAqx.>/ 3:7I<+'wGAyћw $_ppxVsY@j?!T@=1%> v>G4ȕ]IdV`;t*F9Il)vFex:d0Jܪ5@}"GFxΖˑ~s+R%&yJ]xj\Msꁺv-0HWM^xM J 8+X3OdcO);S(sh"&6ȒX\}/9SOfwi^'[7ea)ppZ|@*lU=7mg tSbj6Ey.L\k3`ǺJqMztH ʪw,*^źƲZrC$PьYҧfzbc'7_3~wg[Щsmce$+MrtΗ"X[w/7[ݕ;}L/KEHMrBz o[\/W߾=Vۿ}'w\ "7{rʷL^N&5=MŽOIi  B|/+`.8\-klo,/R  흊耊4_S!K&KsiIq_?OdX!j)}އB{iˡ(n2+eYgT-cբxh@k䎂K~q_) ֬>j}ĻowQGZ5~-F4ӏӟk1i9ڗ3j9cpN 6# 3U8M?5SD=^XA휐I5{&dQ0򑦀݋`# ;(w1q_DEigAxE*Tf.~a,Yz'h"ݾ̬7ض{aݲjֺrv'坠 =䘼Pn*TP:KH萜YPX7"'Q  E 5 o.\"XZ)̹3@r`-FQErð)5r|Spv|f ң5Xst6*^Ys@X(:4+x/J{f͙ 0Q/x֜٪)p,\0Ry`FUsN4({Usx)d90L3x rWk{ D%W`h];ܣ6{tܯ3Z'>rt^ӫ rOtϩ;L4ՋvxQigCd/BRcoi.YO&\W01ԋ.K-j )N1&Fz*I%:RᬁtGYm\T_5?:ֿIW9yݎ$PG" uR 8D=E +'^Nf.wrW/'RwYߧ] ]t}ڏB?VGiӞmSH6JTZƯay5A}옻8_+6F#t/GBz#|=xP |Bz3-P89YWϻPϯzz@=+Gx:gM)Q`_J}M`PR*r/Bff{ZX?e@Ea?cmpFc<rځ=] ^Ţ`8 TK u+;lhԢ`cU FaF?ĸED6- ER7U_+"n"巉RWei*utGTxh_8~5g8Kk>#xhXIAqF*+ &Topo7eͫ+#VXnw\8G 758J30 "aՃq0j cם87nwG ,]L/*VX}y Vcbe8Fʠ;ϙ i`yX`:<}ʽ)Dqh@&C' ߤ| O(=Q,{/E~kaV Jia|mq%1U6Kx H .Wk*U̫|43&;(? nW=Br}ؗz#l(R{mCA]1NJZxUQܽ\z{UQܽ\Ǡ;)jqz939wp==.}E-Q_c'YqJ1Bj'Mk2{L[k#=Uh,;#=NxS=͒ȵ:{᪹X}%?%ЗbEhMQxӛh:Û Ӎuye맞g C61g;ºq́mgF>lMS=IE 'TR #W::#8^$A[h ާ!YţhWX PZ{e]: :JTwjc` ޴g!]@ItaXI&~4Gx2)ޒ_bY.0@0=+i\ZM ! ҜbbR]Ry>_˗ Ĉkmµ *|24]Vja (gڎmDO APî.p{ -B#@w 3$s{x ػ}ԈZ:PW`!{`떠`Wm6XWYF| \Л C :Hn0C7KMOT箷o> Gl@~3ޟ_xMvul۬[A%h:xr×6T0 {d3I g<90m<:8céM2qlMm8MVj??^r4̙9xv@EvUx.߅vvnm z0FO7ډ{EqJ&LK~FxXdYƟ'E,4Ǡ~sy6ܬ.OϷǴl[zT{pOsbx_hY^0|/Wo^Y%ɬ>eg%־ 8 l`<߂APl61JBY< JQ(zZk'aZx/V_3H) XtɵXj3>` B.X0۠0JH+> {I0KOg[M2}Ba n"!"]E9V̘tZݖs?LHDIҕ3!Ďa K#p'$+WӪAIy (G7`;A+mb{'dGGk?"<)Nn u!:ɮU/["FNWPyZ _$"];SX= ,h# 6 vzKO e%zwz6_hꍃ:m1oK' k|,|s -|`p=P5.!}q@ܾCUk^^*w2+(sXڔr |w|b,q5~?"|чd^҅-+7짎l@Κ7P+4%e5"wX\ 3ܩb~Qax4_ʈW?tl;闁o#/ll<_VV ;6!4OSCw|PU޷.q 3eb2cXzwF[;X(O $^QPSh8'ڀt2AvUK@|V4r5sqU滌zW ZlA`&lAqɄbPRm$uUXeߣ832P\i}C2M`A>y~8 q0 a^dE{c9ɗEe+'vph7hn`a @޼pɕNMm7ٹuM8>[cb_D Glਸ਼ײ$DYD[hEKd5QfLưWg yn+uR,;Dl/bq Y?0;U·|73 Yc1=S) ̏v~4˱-| P' ЁV[PgTK2"K!X|sSe9 so-tw:u9oJDuB(ŝf"*7ߨ|-DT6tnkR"+w_TpR^흂22մ4,4 U2I:1%Є%4˵DƏ=EFxXH.+x#v0_:}p(ȳUup*я'sX{2(OHXrXK̖ Fi qMбOKǤQ\ի9k;1)4|ߋv`1.J !`̒. l"= QL8GPcjZ h4ÏHVAvJvœBG5fG_4mZ`'6LQf5f  0ͱcxT |Lg\=eZaCEB~0bBSzGVGZ1 H@m_+?R!ҍ.w6 'q+4ěl?SJmj< _K^.kӇxu|zA[aB4[k<1z{-*$eȞIu+Q8g"gc:fëTݻ($9FDU"_98N8GЊ@+c 1pdq+;oqXV*N8FD-h*f;Ϫ&C*ae|Q6QkYDJb3<E[^6gÊqy9s֟QR< }T=ѭ\}UIMV)x=ah*,xe.a[=uui⎚͹-IB%~ׄ#9mJVt5G%$>./ǥڶw?x3vPkkdeJRneܡޠ|.ʧ5]}eTrU;t0ix}㢔VyokANr7 9U MX<̀SWq""6꣩wW`7Bq2\HyI9HIft@r!9ԑ6IJ'Ӣ 6#\V '2`{e֪G}I{`c":,mx6?HtHҳ,os!5ݩd lv,,E^W@m] 15/,%Lk]5"̗C"zwhq/B^&;z^0Cs6@Y5ʍM1ҢCg{8bSkr+ 5> /jOE]YQBM&bfe^E+[ݠh8p ?jPY&Z~]`9dJ]͔a8$U Jw͉ DɜFjSd6NϰtZ{_, +]WR *S.JGHF]K O2nGN4>4B0cKOՄzәI |yW'ІlEY4@evLvyʥK[`=n H+ֺ9u:,E̡0t#ZC8N|?9leZ@/zSR[ j+0HP -$F VxVVG*d5#otF=S)]5[ru򳪉T;*-VZ η) Y55pz{2i<'E (,>e]>!УWIm=?T?]E2~n)9D*&@t1mxs(¦t)pm˗J;0$UB%O"G4<1FhdUp%зzazV;B^ZqsM@DQb0SȊ)%="FY\~µ64_@=1j1fpѴ@P ou/Z'Uת2 5xu`, |FDI`FG`4mg4+/vnVRǬao:b>e*ך7ԡ?;WE{QEfB">ҕMW(c΁ F7k<<8k1ăi!e1רBXZ=iּU  r쪃v;St~5>ZÆn-N5wu)ť%5RTuwdT ҝBU?ֆsK(VǠ7,DZCFժYL#EL@u"#CVlDiզX C1Hd =hEj碘`@guXA:-?M6Bڗ@SXNPs[ ,]}g+7ƟW!qQ%)2Q\$"'cKـI22F,B ڙ ^gM܉22PW̱l1O\kee1V)%ղug~ϲJYoE14-;x1Yр{+l1temʺ'}p-E/:_)JЏ̷507^xnkӷ_8& `"cvn1ʌRD{R#SdHP*BrMnx{tm̲?$8s.i5&m?,h)=".Zf9gYYN&c!sA QϪXZ EBC]%si8r8#zѪau|X c~Ta5w lRSlyS:S>#V5C~l26Qw~Gi8$=,hև;Hgƛ) Dny3+4^X na '|np g$/,3iAJSd,)kR7Ul=PןrĠn[r/ʎTaTƝ"/2ITJ2 Sa-RZQDp] ;=MYrKt+yK1ʞxwdy^%*0D ٲGF+Jpv8;Y]Ys5h "8`2)z4j>XOU nIhh'٥Q9ɊX p!xxL] LJ>bA;d 5a\}iǑ2k܂w$I-D)i #n% '("Y~6cMil&K[zjLRG#zYUG-j!2y +/kKo\ ]Oz`_ C4ϩz6{[qy T'$4ce DRhEyz#rT9H}mHl@3p~͟MWh‡,S+9 Y?"rEwlDVZ Os ~ $ޒxX{xvDP2d?%<1k _f PYYlW+n&Q&%}6~=XI^/byDѮ`0XG=QѮ]BR% "lJSw6][Xd*TVb@ ~VbbQ~)dV-̦;O7?P":fs *dA0^sJ#Y F.7q6/-K5_Hy/k*7te)1!iO iW&ٚd}FlC[HD.06(NpDE.=`/n(Kgp! ΌJg|H_V9etV=*$Mg[Q 1R~YHoA⩒"嚔~Dl; pwJm"}:O%=Ď;>v6J jifocMB5ɽl]v1G1DzӘR*mo42W"'%vOb(wk4g|o!ɎDRoTVICK)Ԣj%4xN존w0 =Ya8 s$:۱߮b70?Nvݪy_R%w[J2H蠋 N4$T8?|4-'# ?Wbݧ{Rqu!HR $*k%!K)V tCe K}. ,ƘkeC1Κ ,]]̯ٚGڮ9N`4Ҋ5++ƳD{;4Dz.|zĒאk?$Wg]2\YjL ɲ6."" tԩi˖RQF`o?% *:-R~7@%a ŶS^dSQ6%֗ϻ,Tߊ ʣ/qy,*k*VbA8W}#lbMY5nw` ()y{OPJbx"`NVX?42{2y1B!9_{L.]B٣u2;Б|T!%xy mm(T+֝)x[0Ux-Ttxuv-K9tG063Gs o)ܺxҖ6nBp糅h7&+|QԢڙH؎<πyƍ+J[DE%_HOS0׋RFrjE°GTs~ݨ$#?v% l"IVw >E7,%t5VF`d hBqJ\rx tSڰe*k@+E):hڲh3cPl6P+b'eNzD%S?rՒ0$}Kt|8& f3"]>ԥA6)r-*(' mQ4BPtRزU?&9X !ME*H];`Z)p瘤@SrkBPYB+[!¨7== aszDmS5bQavuK_$d~m,JNT`ńZv[Pژ_ |1 rtMݴA{ v2$‰5VNt$X+V~!u^2#6ϨxKBZ(K˯RU(sy*c5e2@Tb誛qRuWfAe5)ktAc2RgUyͱ{A{$L qne(갛!^ H)TF7%8E~eZ~-Y'"]e9A0{I݇DiiyBeSX6&"eO).ji j8c=%I?g:(Bڢʘ5(ywuYܙe.a#%;Aehflt魶,YC_bĐw8PREsO׶d7Վqg O5P_tڸV#Ӳ屮HY 2BMba(IHy;Š=Q!-lGƛ2T-A.ۇdlDC>[L΍MUMWHO u< kk'P/RwXF }m/X+TOL}U [F`g8V+->!N_W+zKy%e]5o0 $vR/D(  ;R~oZoQl2C@=չVfmMXT;N7t(:bl U 1n\JЮU,EŴږWB֖J<88{<ɛwfj36M]iJ$, 93d\4chK(}X̸0MH.x#[^vƒxKj/XS`,^WJH@.ԐpY2pgr@НU-qQEe)c MBС0D3HS0+0}Ԣ2 @$%hZqKcc%ObQ_I 1~nk9Jn8f&B͞߼O6DmFɄ$ѺMܱ= b(a%y /\,p\uHfkP k8aԌ,y(#C68{DuzбPE] sϻv渵h}duqAz%y!ݤ(' .1I>>y> ݮ)[ |>ВK,XQ81e+Wg)nu ёVUgxZYDla]HAڢ_V6]5Q)ҒUA:`7@ְw;Fr4=DqXJnLB<.~fxpJY^2@:A^|݋CE'wj]D:E q 8N6*Ut$ U3A(jje$pc3D"=h))VgI!,Qqe#s{oO$s J( fӐdCoNWfAcM x̏[=J2M&bS#;+264,$! -GX;#49n 2/C%7 A(7 H.;rv$ˮaM3.FޅCU⪼yyNE󎄌:?a;oܛ8ڢ]_u#;8Oucs<''ۡmxF3FDZ'$Gh&A ũeazhDJTk|,Fc$v$׵9]{1sE͕ P`^L>ŤJ30fll3 bHr!),rlF4fpi.bhM8 !s9}9}CO'uIP%B xii)E^A?G~"_xmP,kҤY~W t]<@Ք@hCƉHn"\p͘lH~GKIo%A0K7HI*Sr4aLV;¶4ʢi~|LeG9MU^Z\Vqi oI\},18BJٺrեb([PƝr07kvyQZn kҶ߃SEE=kPʋQ{Ow Hsl+9h(7Y]3+ w]4iASq *9: TdD yηg7j&[7^QCThDQ]#s}u4Vi9Z;,.iǜdcBs0t[+]EnB7CDN<[&nV <()jܢn \taAn{K*[X`8%ldNT t W6~[=aw֐#46/h@K}+[.DK4lW4BI#=2uSR8OI+Nܾ]پ4ƕ,/Tҏz%oTe-ZDǪ&hNFI&cv<i1VޢdX-̾#FZFGK,3R)4*y笎GVpy̪F{]9U _5E*hn ѹboѵ{]]x37*LZ,{Sn YZ˙U匥 ,Rt6qQY[]a < N jR4#pyE9?DQm#M[Ğڀɣ##Uj+QK^VH~:=R}<~QJ=gwy0|Ci_[_ ǣ sϒjsk]}by^,. Q (gZyXߴT6|~ʅYiǍv3f~x0j=n3F}_夷[XTT:fKl<{%(L|֊?J0:ig}򆬶쪄ޒED_/8?S"V9 r`hPʉ?6_&h:umE+d)HOM(,GG$WqWƃ?3q~_ФBAl y2Ȃ#B\4cT7<Ӽp֯]C&zw+mbykJncSy4,aP+y[#Ỳ:-.r)h DMjg8o$Q$߮,zثB}8aUЛ}s*4#vI1H Ɂ)ܒ ?[V&S R'qhz`JB>B/揠620Es/'iH6V"UylJH(]{p dWxv/] {v#p-bJ& h\8;P Ң ^~GSZ*ٜAtjdG/L#Gf! "Y{xdB jr?˖+saزK\J2\c~;֋X\ ,%BK pA6(ְTGK);t{j%X0: haeԈ2Gb,b~0,.ul tQ[`:*~Inj$=퍶fZ@~$xU= 3mRiQPJ?yqեP]΁9?_c|Т4#CUg*n=j/n#*.A=E%А_d~`IBB9m胂\}1q ݡ8]PIQ±Z%eRle8p?ߡa@nWҏ V $uk"oI$">~̐l}hN3z{o8{s;"[!wix<~ ~g\^g}cMkN"8v N̜I˿˭&v|n 7UU)|Ρ5\MrjdYYY/ ^r&fY 9kԈu\Mm7+Ԩkʻ1kW5?6dtɫ7כ{cEuv0=afo.BSitrH$ԾL=>*~9{ެIT&#%+U F1xow*$~8$>;ńP`@0A?(TZJ*ٲi?4wW|h/z[,xQgw(zk00F:o3<L6WFIHԯ U/=iShV肋[IgRcgg7+%!])&#g'A<>C n-7qQoYTVjy-Dka`AVktKWH۹caBJgN l (_>]3Ӑrc*paxXd GG*0boIsan=/\A[kXey ]80d+NS!=k9^`1,Fh᧥ sETd',r:s3:9Z,:kk(D z2cGlX:tŰ%{3pQ!r J&VMa s)ug~,gu,G76$nuD֩@j;O,`y)غ//':cIC޲l~QgyL(EaXAI o.?/Z$XQDHTjs] !h[ xO1[&h'"^Gf3̜EsrNRIr՝LO+-"Nex'Myg7納@Jv'ߢ39I0Eg:W!K%/SF9"_*S+PkrYXeTj8IMalG۷X8 ,k8?.o*sZ`4ehC&O'zZcmM*8)o~4l^fdNvKںW,&3FLjV .jS3ggb?q5?'_ Aڧ8N&D)[e_u'k SƮ9nt2wU`kbށq >s#\~ʶt= ֎$bxK3J/u YxTudy  J(6¼qrI 9Sw# M^1kuva]*6M MAM$sh{u?XʃtFH/QywA.Frx˸y}h8<˨!I~.5QF>8lE?b|Fh7<,&)`:DLxgs D`]K,NueMlQ1WW?Z:z({MXU5C}ݵ;%쎨{{V쬶NGZcKf"*#!_[Ւfԍ&8 `{IzY͖1:|%Z7rԢ e?ȓ~ h]!V3-Fn嵷CUY'ߗ8gf͓-"o^ j7al:LTUzaCXB6QMY,5OVba4m`5Tـtjy-/%cZG y g&F.eK⒈9dSxkͳ <Ԫ9]1x:z֌C  I9klskz:} E)c%@g8(umN5dJcN-ZDIM*vE8G8s~kdulDO4v[ gEt92  /VstV+!5ЪC~'۬xz}J)B܊h~DR>-T" YB/(4NIx )$>&Ԩ1b8?z|#4Tkh脙ei^ؒzh%~-\s0NF%̠ HF⧝s!Trtˌ!M :Ҿr}bƦ Y2Kmєӯs{ITv2O>w|UI6?\9zL v6\ קL8,{8=+3$9덕'BpVjᬫ0#U`~S\OGo5hʎAʙbRŽcIi:w,tEDshqؐ_3 'Js:BxbbJԳZO򦁭6Җ!?;TĤ|G~0 `;Йy-f ?qX+a wzNh0[Oppk>&Λ|[ MթC3>լPtmhai| 3QV`Gmk[r[B'غWPԲ P9B]ĴnP'V$|[2/#^uDNDWr"Ru+8>Ǧ8*:5+ld;CH9}0NK)_}zkrCxMǪmaMBz@l`1*gG˹22աt|r3asoe^%P :K$녘w'=e$8u`*i\86AmTVi,<Cr8Uhaۅd.2*7Xd%@XsR'T9P1Os&-X 9@U3f@~l~SџiU0uq'eM3#z%hI ȗQ(ji!//8-d ];.BeOYbj^ʒhaLT)0}:Y^vb ʘFNfm_>2$RP(*h%:h k]h=N@'4E+r^uYE'$^Oit`{|_[cB 8k(o&M5G@pB^o}-i?pӓ{#L#gpϡV8tb=qP:f?䢠k:{ڜ7K+d1P1( JϠmH:o+$d-iZ*y"c6 %*wu[I"JeV ,+t1K.ZcJhg6Mk蓶"-/iZDK(' [B4~:3)}͏گτeő>7jkOnW0 K`\HT߁p,CfK6)$HmTGh,v VbpB!CXj $5M4$`%ɠt a=11@E\&;@\ENPeY_Ur,iNҵ&틏=iEջt=/lWUd||4cG5nu#;o1 P~*M 0UkPW0z?Xb{Enu'7MSRq@#n;oh3FI@n, wz|M1.>`q;-9W…9mMӟM|}>|} 皯ç/'4wީq~ӊ7Ÿ}p]K*`VwrQQ]95)'Y8Vo ÷z_Pͯ=G1aQC饾Dv72|+tsO)L'n;ԳX 鮽a[EWѓ#;}rQ7z^i=%zt7T|w~MGċo˧k4TZ 5#lO"ွA8SSnT+9P%|XX"IcO_4<2;. [(/EģݚegKkcBUQM#ӎD'7~]ܲxufɤ.3Vv"c_!{ X)r!S05lv5V!aq⻋Q#ճy<"6}l"$,ZU"OwGWeH5>ZPřO,# ; onŮxYWuz1j^Qd?b=첌|l%4LF%JPYN.i2vp9{Vc5qq[5h H2ya[h?B(6>x.1IcοӰ>n>TF}d= RR"jIS\JK&{ C/h:㝐u\y.%yJrEzs;R2U`{Oor l øbYR?"g#pga YK >:$0=sK*,jMRrYrWC .3fLYHo`~Yޢ(/]zʄF*,ڨ\wW]Rg[s&lluzbu :~ke,R9p]⽊|TE9s]{*1[KJGH1AT+*zY4i n+gt_%zFS ҷ{y:+NՄV5ynR6/oSܻp9-Dzƅ,Ю:A QL~ '"L<1HH';/<}s61Q 8i:J?UjwS͵ 8sGdѯiUIm)9᮹ R3CO[a1;GZ6zXK[!#@ Foc{?A+@06g1f0 N?\Z }N_iX8-;~yq9}>t/W4TMCAJr.TֿO?S@x[_ !RQM_*C$g)ת-s5$L9dzmJFĢx}Nﱂlν K-wиKx?DyoFuBJH.ꇒbu;Ztv#b* `#.yAڊN@g#;W&<)搹Jd_~K$?[ٝjؤ 74x2Gn ߗQT{g9x삤rp+mvtg4r>;oӡZpƿSMg~x 5x( 1Ƭ퇥86-VﻸGU *ΏwK.p=rؿ.~t6`:U孆U^7[ 8m["N7Wc8ܺnMTV!.@~grM+uu5,d)pRjI QuzƈD91g=kZey!,-fYT!nAǏ;i$IH`*"H1Ƒ㴹b{jnF~!̛jԩj=NK 1@@սĄKffV~95E_* -Dؠ;k~kR-u؆gf6zB ·>"{ĕK뀘d\R-1ݾ;ލR$u9sath?SƥH.`xck wG5aH{5.>i美Ҥ'1`Qi,m<jW#r1XI$ KURûdet% cwXŧg^a$DlPmQdNHWQqѐ5sFNe+]ںczJdotwmU.qoVsZ+2}7 {roNrƔpTs`ceg7i4F=taf%azy~lDzq|+xTeVwSm,Da ջ'ĹFhc:I/,l#0#A;H*8~0!x"Ue:n|ysO;59twAئJSJGATAH~6ԄMyH^6ˡs:8<֛;<詋j φ@W]KDhJ'K^-oY'&O Ӻ,mD9/Jc-7hz]E=ZQ[85MyTmm wDel $ZDQBj߅Y;7s(X#HgݹTǧ󗶩W88RBϱ*'D'}+Ei9VӲTKúYLj x{ō3-) (ss3KH- NdeXzU[BȀU'CjWIV!fr䞛h0|gG ()<ޤ BV;%bA!SPp,i׸|c[7k/ 4РHThpI;_#P;he[Pe^CnvQIz}8 {Vh4sV˦& ~cLE$ _/ENvC|H7y,[:Bi9Ge'JXqjp=:=|u¿m$Y<8V .xwPR7]wlj5$XGwy7=BiM}ܺEJۏQZr+PG|(y/xFjPuֻ.xE.;[tSHCHl[BwGn8J.BL\HDawVhW$5 a֜kӅ[Ԧߋ|W(wmO`>$c>~#Ai^]ݻdL[ [Q[5 O+tQ37_| N.-+N?=>5b+i.=`gl!l-M+ƆQTx=Φp]'vo& vEcaq+~1={vp"߾PJċEԝ$^p]Cv=P_+f; (T^2.b}U?͓:wzE<Ӑf1Cd |0%|@f|{ӿ~t?=ߐ$9!x\yPFfi1/eZ&#Z/ h^+6AHO8,b3Yg €}Ǐ#b*uZ8} I>ZkK}rnu\" Jӄ"W\ JQVe ]JQ}dpL x!tnT6FF2"Wu2BõaON,_hA4ᦡB`2)nM([_W{E.D9`֙dI< =tWńbW9|^^dLN8m^J"{Ψ=xZ::>Kz' Mk_/9 Q[}3#Amd[cmL!&r /!{m14 z/En"Y7k_y?M>u%\Z`B ."P#a}H9;rGqd@X<yT q8e{EG Q]9"=f#aasX;|W @߿@aDm8vg>`eן'0?N051<`#f`8-h?Tl~*?\L=P1wP0dhATp 4xnmHPpLQaF|\1=| &F$4-p(dj,/&j"~|0ꅆ"@hoty8BƋ{%PȬaJmۨ =jFm #:mLȔayz5iM0j={pT Csdۘ2^ߓdDhOD? v=" Y+GoC}~?*lGS5^'k{MY7Q0Mz 6S62IjU˝RÍ,J,uTe%'}bI/!  atIw]8q(c5iu+)RX T0ĺ~&@tG7Ǚ@9xw~to'S7PJ,&A)P1"|eզf* Nې=*t(1Ы@m,o},1(;8ϴp#զrS̵(AQ,N@?#ɳ Fy Pj=ol5Q"gzNZTX5M6^co΍ )җ=4ֿ>'&! N.aib꺮U`!aXZ~_b$TA GwtEz7[[M` i oW[w/MK>#ҫ{(o*rŹNNSgOaJE$/^7C3rE۽{?{yO;n7<1gaowɻ=RbIj縷3d~ D%vbP|z)%N _5P@[S# {tb] J@PN_]\lpRqz3 F~yIj-z #ck_5勹; f;>L}qð@b"~h퓎[Jrs/ LTH)NJ=7Ou=u7GdQ " yΑ΋6"-/ĐYMDȾ +fzႩf ЂĬmJ,\rTCTzw@3-S?("΀Zpcg@m O,YɓF99#5O\hpҫ膬hn7YEWf\GүGd,C,Ł,[S, r!n&$¢GRxV\[/kYckf`@NMBW-laFm>znqkL+LP/ĻGzхF9Yt{+~&W48uV~&vp!sP,eejn`5[´n)L҇<[r*oC.m|2oO?X? =dnFj DfK+O3ҥ@K n~MmL66?Pdł}innaGhFAHA7\t3h^XhU"DʓW*o&M9:fq_#Fܗ̘iZUd0S14]+9M>|+Еۣ7Oh'?S0 <;a>s& iUIl0~\LQCm^*X=B 4 ~/r\ʭu>/@VoVeSr - 1ԀS+0wY/)Zl`)@lo!` ѷoaczKz׽56mcvVmbhϱ.A-zZ:6m2a p|;y bm mhY3#AH1=Ol4Uqzߖtkљϋ)=؉}x ;2ε太ݫeY3تa9glu0KI;$8?YSwE90i{V [6->]/gv%U.n}o_zp1MCq0KŻ-A W6-@MB?F6LC|#?X -I@/<,'bY\y(R'Oض,3.ss2(vM8=`*6}~-}O})r ~D@muA\NCˋ+r [ҫ`(:6I.Px>ÌS,f\}ڵUZi;J@j޺o0Њ䔭yaQYFSWY<{@|G3i #F:gz51Rcx'3kvtee0Hb +K/ Y- Cq(Cz @TEG y7دb0'nl+uZsxc c8! .ILeT~h~Uim֛5g>YhO|`k}_[ϡJ\ I1d^*R=,]-\pUt- ΕVUI2uiNO96QQQsdGirTZ9'UHpƳE}DqdrTԭ*zvA*ⷥ_Ţl;,;7yO?OZŻhmgVK #G֖ V`KE9pHLDLOepƚ>ʿh۞v8?Ќy]޵Զ4.,?F k#u }A<=9>=}.NJ jArKe|x'N8ɄLzʚQAay^=i] 5Q'HV$t= '^k, i0 \A}V rXAjS6vvLŶF4ixdf94A![n9?/(S8 &s\qѥO.QHm9ӒBĩ#FuF[=3.tۑ9 N絨vQZ+vҳmꁱ $c flV>=yol$?mYxCLi 0 Iiqij,r"!BW\iH/[YB-2 K1VOIJJح明H2O^K_˖ñBm5 aCW /j"|Wb))a oȷ qSqQ9VDDPܨ>7ŵ6,S-jl<-~ Z얊6 d\ 틴 \*z4Ok2xcsC 4z7DŽM'7J(4!>%9M`JfLɷAzdE>t&/jL@8gqƅ Hk9îޜFKw-,]r~\-?'P!9 ܢ K~ ׅXI  :dtVo^0ck[BEt6 Jbɍ*Uu+$XQU OIX"-fpPDORxgkd8.(gD - ':*ka*fZN͓YXy6mr*<ߴLtLRP*+< ^Qz E&^,*N`>}l̼EHu֨ITd= @*@,R[Q}·^&Y{)5Ҫĉzp'gYD$Jx8ol^sGˢ\q,Ք PR19MBnRasل&q\J[|o7_\$%s cێ~;V%зY+meRH* *Rģ.6#^nų&xDa矢FrM^r$\`aҚ[C$MK|3)uaSG.LyVz#z8y[ݲeC9gQ ŧoJ)D˦{Үw=,=Rjt=ҚvK RIcc-?-s_+OU+h /8w;x \EQ"p[vtid#-n.>:}q,|ɆP6SC['Q/m4Uo\S*{OaZbh"[/t0mNC@3nY*VZ^3 3XMZHj9ULqnhq4?T@/9]/eц6XISb-iU ɷmes[#6Sf9\bk)6t7t\ ."-?"c!!7S`sq>yJA%Z @MnO ikMS NfҀ*R:Bi>=z)+do}z2 ߮]5xx-d;&cE_Wkk&|s3{uK){yN cg liOrBVO+RE'w9ڬsYKeu[jCUe>(׼֦ˠ&~feeqgo^@x≥.5 #mYy =*8 WfLZ s/FJ~,b{c?Y54:;iop/\Wn " @okr~a:>q ;M|V9?A#\ao 7$B/ Iw/i+i 5iX`%υ;rIjQJ%o '盲 |K -o~0,۲ۋ>{4o=!jzofw+>I[E1 (WqZ^K\v{z {gjE"al4\ea[%`^SzYvEG8u`7Ջn\eɦT5A`g"d׌.ՅY9VyiPkXr9$ _,LҰGWH6fi\_d;+z;b]3Yw^ZUJ;KTYM2iڜpZ;ܽ ~Cs8Ug~NmwcҪHBe/$[wqYբj7lI<[Z[o`7ݩ0pfM&t_΂Q_R@ 82j8_oҐ+*Eo wM ZȔR\%E[yx@SnOۺ,8ԃDJi9w1!W<-Dh1M!xkMc~ 'w{$F{(,#lȗ+8$>,pd[Z2psWN=<8xOЮA.I/7C>ۣ'(ɉ07ʧWҟ`هR.HNwm;L -&7KoѷiH,+* H)<%0ruX9ǹ/ƗunSRuqA[OlY(B&z[r .-E!w;8 .-ǴQFy9oA*nTUQ{b xd_+t %CvZxE/$[*nӮKîk u];Ns!_#n(V;.oй?mp9.9 K[zI ;ˁ^qުA蘭hY.8[kvPաH/ &i'sMI.rT ĥc h3+|ʂd{ؗ+$/&gxYk$tŮmCFs1Q2W,$mvT]27c-=GcRxhbp3@ܿPZJ,mr&/>6{b568cQƑwl-D\'bXlpp<i=<YvYY\2V3hA\1@H3!J -wٜ[lx*ZN/rA+s?7ޡH >oK0&=`l$9(&P}uOq&m{O OK̪ݕ(3#n1>C#/$a ,#V%:']2C$,9$h?g(X-tLUZHHGϓ7b"HFaL(K,ca<+j{eUj˺3yeE NWUiGځE1|@;Р u̲Ze #Kc sw҂0^4pT83!]c~ = t zK2\&/|^eO<COp,?) mHm& z}>B`Hmx774'6jd:+;$3tR)Ƃaa+ ,`%FA{GU nbu䫿\6"c\'Vhs;n 2 4% uW5 ,jՇ%6|>*VF  f pcԍ_^w) D2!dW՞Msh2O!kTI;^I'@# Bĥ6u[=MZIU4\TQe]J8JR}["'C zO.ĻjwEy6OdW\{Jؒ/ o$xqIBuGÈes^ԩ1RWwED) [-ЛZ$'NuTM%umvݷMr]`w IpqD}f|zHs.L=3(! Q!.KذhrF)1vkO8~"2EavկiwԿjtL:}HjҊ.|kco1<)өM&$ۑ`"JJ@~',:WX&2cjw 5Bz $eDQJ")CRyb&cMsm,avW Aq Ge'J^|Tg~ Pjj<)DMe_jq$RZΧJ:!Eh|+SF~RY숂r)8vކ ȬˮFHc#EBei=!cN&tgz#E-rS:0l7BL!U.)r5 ~|K; /Q래b+˴=͐!+=w-A ^㊔+Fb"KV_򟜰\#J8o)|]~ Fb5*U(jm530\V ?F/*-kRʅ2W$5'v adRLSnDY)fHй5 r`cDva\>b}$(w^y6NXl]#x PV%[ ;LN@el~dEo+o?$ *,Iv'<$*Rx++P@IX%ٻQHMEgՓR 'ծJg-|`@GTe1-'YeX W(^t{Z*L"]q\Ĕ~9{əɦ k0(#QtZ--J,G8)t3a6Y#DnUi3#t殮Dmx!͐F*w TYO`PPᾱܡYU?G2^d<9]q&bSMI|q[ Q UFe)z|5J={J+AU3e.ɒ9уݺ m KQ(1ʂW#݊zr1ap{H3_q5TX6W9HgWE !w%Q+$ޜDPn#6ljz.d&Kj8q@2@fc1SVi +zTKtot .Չ$d`/`cZeR8W|wS#mᦞM[S!rC~e*T,p2D)d>w(p'߼`zTE> w@A"j|۫x ^=ȲްOcLՓޠ Ǻcw_~ꍙf'}ff(>!~{'4_ I"6G4>A ʃ0 /XQ/U[U뮰Xh>޴ꮰr-h}c|^(5)]8/ (,#O@Jl>LW| Kgg+vGjQ#/9&sڷ>pN`O2P.G"110 M/?.&1D M|4o_%@cxsKmxs b^/yd]-Wo~fV$Hi)"U/(+FqKAqwg9[0(4u'Dn[An{vfwX s`*Ei`NQ rNsB + -B= ݑdP~4BL7J!: |AJ x+3(igd%=2iuؐ5™H芿~$F!H~;q^yj1F13Ƿq.V>5Ή~1\CD1ge#*seʣ@AFRgf..~1|3\Og3̸gZlfP_dQ?nKsD_[V`FF`;e\QOӰVlF#̙e0[؀rl690 `v̫a9ŵ{^2i@D3kf*KA+ahfE3Ǵ+Ĉ64 LZ̞ahe^ȼ5(Xv όf̶\_ .Tpqnz!f{gl302d& m홃Dv÷ɶl %Fm ,Am8"78 £u*8)qTG3Juüq=S;yNsVհL L V >t@41/ߺt< >gD:`Eg2]B৉觉O@-η^.JBezv Cp]9D' =ý+= ʮ]_ a{_cjѷ{m~J:7D75N @(Ǖ!Yg^<p?<ŁxSwͼ)hPffC=wpBUtKV׵񱰓;QT L#t]UD9*hS<_;륻J/oճo3f 7Hqd`EĴc/ =̝ Vge2h#x!NfCVVW8+S~fe43B>$/92}pȁa2Ckw'nAv]) ĄT->$BZO*)O'#juTx#.YcηIt9j5N? ``Cl8eHCs0NctG LO3%r cTvՀ#L *VhQ0euhCmkZe+F46n *f-eP7i6 IVC:?ʴH$Ԍb=`K00fLA^+B'Ph͠JgoeoزEfpW# ;#K' ،l!I5ԎuaQ%3DFg޻XCxNPe-*i, 5=CE`O:8 Z~ T*_ @Ffc cE$u\'uڍ~a̙ K󢗆cH2ofKj.iUNq5J,8L| f{c4ųP%0@kۭI1|d>Q4X'Svω׶zJB? kʻ>[!jǼ]٠M i-V!B 283~b݉llȰ 7ThUtYhf#q$@G4ێl7ڎז~iH?3MS-W701ג.BɱM2AbU&/B m:Դ=ȽW9IWبmYCaM÷?ִ|]i ˷FK|7sJam`R%%S(i=`kGK۽4s˧fVY?/DlOpf?=a ;tlr^<wᅤXܕOC]ﱭ2U$K>Cݭy%B㚿vk"Ī4~:n Fߪ9ۑuaװ{ua!Ff=6Y%wԶTPE [ny|x'z.{՞xU%Z_)=wyM\fqZfFX>*5s}MY}`W9Wz&C՝ -o\Rs;l _?.\rڂhLo']Ayan+yGX O݃&w3jv :GN n|^ðO+¡,u壕IYRldɣ"]m ޚKS*s9^E[ ֘b\77sc׻#Ŏ~3L Nn)پm:+5nwFTkGxJP hP}6Xq526*LU7oa%FbK&-b\΢;JV&p Ub޸5[~Jn `Q$3-o1d7(gWK@gY nN ϱJgC-{ݪ",1> f.)"P}KʶUFm;@d>ufb'^w);%1 ve0;Rp DV_iw^ y͎|״_Aj(yJ sրgP%aBt[6% hkV!-$'Vi(Q0x˄-2wY;@Q7,oZ+t!kmHTbb l)ԙ%h('Y/xRajmdÊ H2EЄmrP7-d44fᵎs 34||ZJnUTnHfkQY"EW\?KЙ^i"N3ޯޠrB ?/t[CcZ]zH] :ŀ#p=GnE{wFMۇCm[fhc{~&2IX:omE%|?sAϵpb]X8 M7C8A[}Ѱ#AInhÛ Hq EImϋ:2Yx>eG%S:񫆮ħв~)hwxX(s/,C_ +xKc濢2Rnߍ9V&o6{PXƘ/%6K7C2Q 1mX* (F&c4ZW"S`J)hORЅ BMJ~'j0g01/9>kUf7ʝ<Cm8/* a&*N҇U}B*COI'ɏr^BUM| @ii) mRna;?'0/U&^+\ J)ag#T=?W2K~RH*j BVGv9Q: #=JQw(&[mc>etn"i]֟*Iȷӷ58U}qn:,1Hl) F']EhTCE 1(d6;@ kS>(x4]JdO[eDjAH$D.֘- x<(Jr0pe? LD+^S.x-Q.*?Xj|WUO>4@KCiGm;8[xGM ^gQ֊޼cH *x, -9ﯦ:"Q,UBT)*+w>$zL #w1$RDE@LE8c}kWdwh7wx(Kg܏ :|G4 `eHnd<3$<,܄Do^T*R=/0K*le.,G!l]A"m=/ Y64()yBY!vUT%lj** H_x L@å FfXc {7=h-zH)3Z@a(VJ%^A<x<#GYe`ܢ$,Wub_WKT*HW;I WMǼRǁu'To8 i'Rdؽ4U67 \[h1<"p=V?x4e?/}lf{@RpɇF8*GG~u't& VT3'WpM3Q 3*par2O>|xǣRU cdLI"g*pu^r fPGw:~\"hind-Է(eIqna:Y}g&ÈgǦo=Οe#ؓjTUQˋkCґ߁bR$@/^gx?lŸGLxe|}e|ݑd8^D/_su3~KvrU93pP:kU] OQbYN6𱊫x5x?`{;=;F51z _=0ɚY sEu_z•o*ACܫ?]PD.$NV 16 +V6+6 6_F7Oz FS7]T;t%1%e%=x/@m҂M/_" Xblhq՛KtqS5r`˫G= R p_ `X32}6nP  '"h)Ru9q77^i|QK&Bݻ Z w/;˫dK7=^ר*}|A_ 6xc_ #Ƚ"0VFf ',W{sֻ5{v{oDcݨmIt/V!yVݗ҂לilR$bwS`=f'' :5jF D7zV]xN߫}p4hx8:}K–fmֽ7o⁢Uqox>ي;Fl&[fE7"߼n>rkJ7,pPdTw! gO$bH8 "v 9dqg "\O%| $gfL™L^j*_ Vuٲ>kξ.o1TNZ.~[>r@F-gKQ/uǡ:*2pceƷ)z2׳wVgTƣFZ *-1WlIMx*vzfyCL< ;x,yHu1e1e\]|stoNbul1x+œ>M2h&n0;Y-1J&oF廻{.,g(Z?¼pV9UsQ7̿?Gi(K^(-`ٵ,ݾJn0=?-p ;#{lb%H,Y"TջFJl`Q;Ic3bf|+nWnD='uSa:cZ_P0ޚ S"4d*Ŷ[j^xʛV̓]& 2˼;F63i]G•7d 2469y3 s1Wv1ەyu. z!Xy`Td~ WWC/%x# )Gw3QNfqɀ [ #uWZ$TF[9tR%IqNTT WoLa0[>m@g8C6p9uMTN WꈨnûU !UQt+`e.!#@.z 0PWX7d*U~n0(B!uD="D,Ӟ&BDE>U0,=8܊Kn%KnxLgqڤQ!Q1A=҅{\Dž{\kӨU#0ݨ*T?cKϼ -UX:Ȩgy ^+)"˵[ƍ5A30d}aW{|zbc'>N| ksCڏO/:&B 8g1{c={Wnt{55%}ا`oտտ4jEa4}hsK0IluuL)A$6KNo5/^r׎>P0iw|''n~{&*- j.G%t L&F阦{XrX"(eȅµö$J=\;tKxKq^H w%gݧ얧I%|әc{.)OO x񓜞}?c+>m?*PЩc-&ZW, )nR֌qRBewuN<U J]`bڇj^aeIR+Ŀ'-rP.㈤ZB m1hmŶZ'zbbє~M3!gF] 7!T&wiua^8cM0sՍ%a}Qݟ{"ٚ[K+eX&aQX*pW!Ƭ͊~XK\b$R>+M'$R~'V7!O=B"o?ǖ/>8iݽ7H#$ ]5ʮ{r12_^Qry5>g`&JxQ2A@adtpA>.Uh PrX;!cfZ-)֋g[$dD̷i\6nLoL߼Xu_4Dtwwg/(<Wߋ v̔Tw*Pny|h@RJ)3r,`WzbR9=;O6rZs;qFiOD-8_,/ƖP!]h=WkL̩5/3 !lb@z#΍vDhʖ_ .?IbWSt7lpZ  ajS-ޠ[ח:.?DpU|7ފrdI 3osVk~;n~ok> P(6V(f+7C>EUy=߷|o(h[^!]aO|}$S+VH*1 "DQK$0YT:H`JCKвX$lڔ$ weݱY dTNJ]^`U@c9P|Ar1>0d+6`U bě{~ڕۂxQG:ArWʱmҮ|K%_/3_v (8j,),c_|űN1rP k=Ic%/a5t0é=$V{ Ǿp Ǿp_%f#zi886DZg,:>È=9sѱ/±/W±[Az*{~>ұ=VaisȆӦ:ӭfxz' ≛{g{T# ou W|ZT}+HƾV;BCG֘ݭ\;װg+/sF4In ͜BQo=r+rw[nnۯUOyO*Oy= ]g\dH;>圢(E%(h<5؜?AR2XO^}.* Sadյ4Uyfp)3]A[5^~h;<˒<@HdUu'Qrz/#.l U`קD.[T~g-;p +{ zǡa7 d͜ "AhuЩA脖]0la`]еu6" gf`*.f(AIpmnk˴ȴ_ A %XT&n_-$̂[j3}HZJ!ǚ^)d"]5YeRqbZ4"LµysZOK~jr~ǝZd"I#i aȬnAb.!+/ @&?a4WܴctYj.}qL>̭6by,yYC,vUwW΁w vd۷/[†`|qwi-HRQ*IY@1eui%F|\hL+ W^ [RvBf'8YB5ig:EG d$ˌEFu_&z-e<ٝdSDC; E' V:Sqj a7l:fڴԽyN紲?s `h1[9\dzOxvOOU*r-KavգuG9K78eZZ Is6yG p Il<1t#p,k pϋ},PϪ ]}BLb.!ߗ}=|a8+Qm^ ъĉ ؊vfk;%+Kwu? h>/ p5,'|פ(r7O 8ʿufN$'=yCzdPCQDA6'ao(- ' ]ܻJgph~?pN3=_WsUoI Сypu>rQt|h'nQdΛ4Q{-uR.ӛ]Ϗx-r}WxK[NrK'u}&k6i<$;\7#Zqc +[;]ףX}#¦d)k #0OfwB넠 ARZx =}C&D5jECÐt]72et`{VlgymG#/> CXGh36j|V@ cl(xѷVol#VBIUl+8VpGah`CGM c/86h" U0ܭ ` 0ZD0^ۦ#$ ΃X1+Dt +ZN4tF-P;s3Ahg3B$3hgi <#q|[=|x4'5KR s =fcַƿ*c}1Lh,^a{*K+l_+ 4/W_artփcƝ kay9—0V|z9AA }dPñ+V?r_ڕo JҎ׏r_#zr[/rg"U<{)BtQu:pݫl$pp'M cBLIUkzm|UsHݤMSıB30N;=GG #NjbR?Un.ohu1z#f_.eLQ"TW;B_?Br/Axtz9bv`k2BlaO^m6E~¹;3=R1 ͇Lsw^f{k"634m uƼL8&h O76Ώ3ъ̈PsЈ Rσ?_${qa:vG#u a?nX`Or|ҁ'v֕G&1` ء!6hc!`mc;Q+ Ίגgp#%;3Hɮ#@K%`ۆXMD#t} m~4CGviD|$@,,2L"7=xo:~3h!zkau,Vwd+N; 88am j~,`.Q+Pn|[O3Ԡ{>=?5xϚz_ձ[%{L9MUl} !Ygeb~ml"@9*uԖm}F^ڳ╏bښ%´/L´/L2 *L̴oiWjٸIhro]xۚEǧ>'(\T p upȲ܎Bs{ϧ|;o|wWٷ3;~NRn3[\uA"ѻ6ˋk )oT`XZpU< |pFuʟYG#ʴ)VuKVH1Z~fݚḑaT :;߶'ə)&ECiDˎ"vržhW:;&5 H9ubh;X"9dSQ,Ф('^)QFlo&;/^5UO=t6b|_5iJAtVL+UD&*Ys"ώ˴j(W+pt±s~Psܙ Tslݜ vsNI Cy>lL#\͌._=Aphpx!O1ۻp`i<;W.Ўz>Ⱦy JNo&f~Q/:-ϋ/2^UHʿ~Hm:׿w&21y:İ]ti(wuXC] kuz9#͠t-R~,'p9CS4Yz>>v}?6uJKتWtzǎ)Qx/1ԏк.r[/Gr!)pq0:ꅴg^C(d/_=B=_zjGx:'M)}U &I.čPGP9:.Bjf{d﫱=}DՑ%:zгs3sMOpЙ$vtp{:]jR mqr6~Ok{>}±,X`?{#^|B R๖Sr,@ӿB?wtӀsͪp/"/EUn)oRN㧳4?' <_|%pah)*ڬX'n2 1EIqd~Qi L;ڪǞF;x pAwt^+8wla|[!qib ғ]WJScUYY0F{| oWVF()Q%oXpHoZlqZ=eb~0*fE*g/n8~+0J eEcc25s>pXiʣَ]yꦆNXp?RA=gʙ9CXihtXi7Dw³;nI  _3%τY)'S }1Ԓ=T78.-ùb8Tj Hu/W_erx*L?i-I#z /hoz~'/r[/G8$S: }Ac1QjZغ(n]K.[+(U-.r[/G8}*g#1mǖSJjqP:P ˱98F>:ܙ0oxD %8ene O;>P;r2>tK;YYS?~'X=3OJT/=cVyAdxkͯ2k4(+{+=?MfQح1á}Bc0hn =E95OƧ`?ijv0s4FV47"7mr쓆A(YqQ^{aF:HUTӇo 񛧡ciAaytљ'JF\߭TO;d!_?k!? ]^ }b7ourcWcyjY Y$7&x|{bG^h7 p|x^O~>X!|JŶ  la< @#I'm6Ex2@BF,fXIEދ,-,&\,x$VΆ Lۢ0%$~ :E-w̒NAQ؂R*lH_P`362~wI(2@;*H`aͰX=~D)6)`ހ2w03irw'$ãaGK}"duZo R"ݖJ'Rl uu]-_8P P:?%RGk>|jR{5ЩtGauwװko4ި}ll vfkO u%zGXB/4 }1upY>&hcWm3,tY<jmٺۗC‡XUދgJػLmuNʘӿb$*~+WnI׏r~H:]atsE\uɜ&E{V \3_Xh U7O1CօaV"scW~9gHӪMMe%\/dWF*is9{e)ɲ߰"۽lkپo0]~;m vEKK'\!wiaYg f:*u%c`*,3CaЉKtSZnkt_ \aОPduM_!n Mi{]4]`C;o4C-w.^ȳĸ}B.^ޣ12cer=:|7E_e+GoemV eLڔ6(,lJrM[ #혍/꧟RJ@\cN4/7q]g߱5r0]l1dDpfDJ POD? B}z*N6dxomKOS2Ic4!XaC$x<3$`QYvLǀ?:4ɟj{8I*f0UcLͧ'}u 4>Y=čŊ8 Xb=jT+Jr,'*$\IqvΐbpMY(^aYf\K}W\cW@.Pw-EmGj@5 qX{c??0ixcG=cNYYtbA0uU_'~NA@{Q^OQ%!nbB!-R^=֤s9—y^EdFV=Yb~/TL}ɗ\$R%DϜ.G zJmk 5dIGZWT55l11]{A- TE4@t&pQ0:Y,~P Eh{HRbꡀ X<'? ӆNgu$]p#83wT$InG9BSIRPi\o'A)Ph&Wp"H*矶.D sκg+G N6%l[>F4aH<_>qӈNtW^iaD}^Qk (zMu 9DU~٧#K b"}%Lq2ϕ=eO7'z"'{9>fmgU{G+IT;>W<ֈDQGј1'Ec; .; PNZNs qA[5K9@+n\KjTXW 6B*KHK zO*ɇ (MwakvH"RXYՒ[B1{?>  l|)q`o,yJ!x)S~oaS:of“Ci >a厱k }; u\|K!c/>Y6_~?6MC"se \NXڀ ?aԱs73\rw–c6.?"'%ˆϼE}{ÿUW P0.\0ؕM$T?*zO)A  f[r Z3-.EyhPd5t+o90%fH:OVzLW= 8Lʘͪ (W.F(eFMbX*ud}GiC-^{ *19P0*ol Cl;8ΑzEM\ v * yEikCkb #AGQ)aiFT`N)#j%+/9a㍒Ӄ2Pl~0#YnyPY(*4UP"yHB.US/+6,\{$3ncO'/Xj,@uӟ^~`PZN}s R.$DئDi5C؋S7رM,[\•Aɶ[+cx_ %HRZdKT5Y݋= 01ZAH=7JDA4)el`e .-QYpͻGID#DAE^:qiv ߖЄД$x!]!ȠeD#D G** /S̖'-TbxYDO4 .^zQ QHp2jTl-QP4,kgp $]n>pnf|Y ʗqs3OW,¸{W8yO7.;mCtH0P>d-&aw+X #"qr:%_WfVS" iҴIᧈx10,o>ͩSQw# ʱW(30/I@fxd ("`mOEqMM~c<ΓN~ 0jW9d4EN0ZWKXF費R5PMf*:%\W& h:QW!D/!q|Z~bnQÞWGdTFu=KQU<-BjRAަl[GHqY7V{hbYßq*h Y#0DHwHBj`֒9zw%T1* Ȁ,]`ou=BX5Jh2;|~ō?rퟎdOn-&QUQ_%{T J#[+wwĪv6ܠ ݒpzҤُ1lj?'"MCccdMi7 .d}^Vܮ56MVަ\'H64)*ND.$hJAO+WX=P4޶ 0!#0s oPA'Sxc=3aq|bnO!\?% EJb_oC^5JHO:鉗nMKA&=Ԃqk[\ nPX@e95I(GzR2|:J&& 7t5p$'ߡ%ZN!$%0W -Q$&}g1 2Y;LyWRxE~h#OȵY'O{Iפ/^$ :8(5z&zy T-(I(ߜ01;{W'ryej Ñ,5*dH"mx9K"Oᗻ[Hr n դ壱8Yr& x s'#|:! J>/bWĀ+81AK'v3T i2ւ&9C;]~>&7KQY! #GuʦaŠCG{.aVV0M Zlz^7I Hmr(~M:w.Κ:3 vB~ .jaSRLaEnG,ث VClQ);Q I@ʞ={ww/ wW,> %FO)syl/K2,$U V)z#*ݘ@*]}=.jgvz Zp @Sg~t-x%<$Fb &{NHayldH*C4י]085D=(&HR佈bYfL@\ ` MŽ]% mKFNO,&oy[f%oN'oq`Z#r7С.j #ԾE:1ɩgeF]jcQS2UC,šˑ,>Av-YW|btII1pLb7Ȕ)i8@ ZzbS4L|,_OOO)URWO2ul DžOlDP##?t1ٕފQ)!-SX;z,Y^Q3(Ƈcf,V*?~f 2 B.qײƑeėa)ẍh#JQda^χK&}d*@V7ݦPUyR[@J5m`nZp7{=wn|~ Rg|ϐP\+I˰CO[J\Nx!`N#b̩XLdX;f{ssgO^/ h)jQGP@=0ϧs" @S5{\g҄П-$eqOZJ'Em_w0,*va5=sHa@ֳ*NE`ٜ~g}o:}/_>0G=gFK;@Vzjv!vz.e%KkjыtP/fd+䝒&'X>ujD{TߵKwqzr|Ͻ"_L<^dV*J+/cȇx]؊1n]|ϧ^`Rvpo 8|C|-&h}جJ)Ӏ&5%3j]eC˷gng7mwкvtg{Sgr+LZYO1kd k@Ե?侀ǟe)8}% nſhWszjkp+jRWxNX{;k0{Nbϋj$.ْ?m`\ )_+Ih*[qfp OQ\,y>k5286/ENnMnRv;~Y'\זsK.|" m%\X"x h5D9hMRWy/YEzZ;U:kE`; :.l0G mZy#0P*LAb |e2d}uMdP;olAr,G.K:(k7`p `') Z[ 9!$lU~@+EHLۗ_17f]%)Xz@U۬?7zBJA=} H7 L+ O "->zXLܥ#ƣC|Ǥ,[Fcvmwfcl!P10|3P1a |+&؆K@NwSy"H%/E~p斱H~_x˖ @2&_SG&x&wVuJ7 r%b k'6 h[fD) 90nYX>dF(NtpE:+R?$?~]rT`hB)f ֏]֦I/7[p4%bɖZr/kxN([̪m*WU¾S1 6X6!E&P@Bj֩@Mc+_Xr2/HV^>~:{[r׀Rx6;RԥP5reGڡ;x:x841ηwoMT/yD2:+/ՀQ|pG 'ԤE=jiQB*9pU-%R9[d،z=:ߍk׬-.LL9)?)S:Kv-ݕ\BDj+a uRzou牯; o)nIsDϩO[}#z>,ɧ{Hy4"wA49ȵFLFտ/9j1vgiՙwU`MNDe>r9:]^Qtxmq.7AϒJњFa я6g+D'IQ;ǻϙS~tV2PaFw2a`xZt5{P F\)Eء?Sca>c9Qu(6S0Fk4O8z70H>fPg܌ǾEwtZ/xGn Kv:#dN% pvR瘂lX:lI>ˋUb*||HcnsD7.j@j+n}cG(>Oi!#i-'{LO -<\1wO'etbAU__%R,Azj0:'tzIȉՠ׍ׂZ; ՠ*(ׇ)G;tP%Z xwox[dOBPICu{S% fWq[L8VLj-%)Wbtڕ05V"|),QqAZYUd׈(8}\˔W @ҮFw # -6cj T1۷e#B B::[nыcX+5Ii a'ph? Q%ɜfD*-[}+1+*5_j]\`x(~(h5]jYﰊ7_\/S)u& q_޶?8CoBLYNj_W'ųӓ㋗'g'Ni5w-7tR?R]wnޚq#q<7KGknJ'7 *PTZ㚩ڐ%I>/G=\fu_JfYR{Qdtw}oM?p_PniN 585~1| W 2Ԡ+YI#,M(/d3gIA 6,ʼI|M]=/5&J g8g}\q25!gK[}\-=6߶M?4z)v%zim/<8ěR2.5#_80=FlȗߦW;D^JjX&vPDBTol #'g)*Nx#;i\[7-Dmm\'!P##gM<݂n5UJ(3">B˫SSm%sD3z0%xE?+ fOL$/:&.X/l\K& 3/@%xۦI1}`#Nو”RJ mqKd-*An/8 iGyL&&j 6 -|0ZpE$E뢺ɯ!ۄTp%|lKivp5gL7#U)v`xveĉs\d<.}uS?wkdd2j pzLD(M)v*CǁH0O1;ɗn9sACX)0L qCi ɱ+caMI,j4)hM]'։ͪCEk'~qhR ۪\‹ `zukQÛ6_)"bO'"^zbN-0a$qCpxMId+=XNgՍ]C&w"EKdg) ~"VǤZM#E=nV2= nDžSdbWI",ւUh= [b5HRGs{ҨFgru\#N1Y91&Z.%kAoQ:/*b}dDuIZ D7yr 3샻ө*SKN\L;6mQrEy^Lѽ^ }tpE J Be˯~k cou6Fk CO4?jx&<-1&Kהۨd>(֩.uY`-rdj3qa "|;Ы.K[ ^X^9R| \؊I0W 6zNUidg^E9b71 1VD3pcd&Xh/_l8\]Ba ȇuwexT͗#&{shtx HoX&8fZ2S?8)ezdky}:0̳9 8Õ&SV[]%פ !p%J;u=PRJ0X7-r\Jd*&xrQ6p!?isl~1M2SI=i Ryʞ  R+,rATX>-G j3nҷ̵e546޻Ck_b"l^>"MwΟ6k`5|ZYȒSA<+:{g* ]i\FKk(JI{V[v0 ̿;,% FQ::/njcI%N[ fAʄ# gLe `g !(_{Qm|AZ9;`loTuEgł@(JBF&H_>rH.xCP}ZYM`[@b!lE~#fnxe2yD~2GfCVG;3)2e,  ]S'C怣y _rjj>8A Û@|aG=Q P'+SOo܌BTQ490A31FfM].}L߄1졉/%-\madS#ooWȋ5`壍JJذh.LU~൦UҮ%D)W~H \_ŃZIcl+<f jPK[j@N PKCB styles.xmlۮ6}Bp}-9))H&>DjdQc;_CR(K|@syӫy4I^iH8]=~֝{WdCp)ssvHp@4ޖq X@2N:Jb] d7{ֵ3ǭE# dwDѮkg 2ջ/Iƕl<;~3Т_W2K˦ Q3^x3*cqN +Ew? +juht6!fܼ!{G߉Fc6 M c%#Icpr- . {UĮGc3wH8i zH[t}{}ki?GV>(Ab]v0jS|-}W\gw})p^wg(0>nP7z3%``2?uYynG$***Q`Tzfr7uL2yBV2(Nh 7[b}u~[Z gC6`y|w92M &'yUծbْNe E`А9 `1e[IV,mӉ"{"Xk[;h6)$i'o`y4LVE$a6m!]zXUt^6M6a2~{|%?eԧoE[F uƫՐ-91 DskH7g&X w \%a{r&ۄ c0h?CAͽl1y!+P2,C4UG~ꄐn; O~Hù {?Iy"4hD 7=0M1C0Ns:PAlPEH/z4MCP.m 4a׹ur5dv[e~M,PL+n5aPSxN7>]H`5:_Z:oy|mۏ::^%DO')}Ӓ0 RcEk|tRGA= Ģ vUD[8\J+ܾ֮d(@YjZ@a֜pl[,jNUMn('O[|Yj MĖbP.uc9!GF(]=(/5KP^ӟDBNwL}WSĂ70ӯ,1$lIoysd'{-P{ o~b^m&/NJz샩jӓ')3>|`6!xXʿ]HP`9mG _V4z[ڜNkP@:':;IRw>Zbw-k2ݑZH7ޯ'糽<E,R6+j-}z%& h 7,36K4Shk={*<-˜_fzv,>Ye gSWʰ I.-V{l'`t(;+>1senȤ'̋5{;!3>q/lQ6se-%?DVzOD>")] 螁&ˇE#wle4󱺈oF^;9<JqRazjN_5Dxssg>2KlE6dc >YV +yat[T%v7#30˭Qlh~\10NEqdZJ'XbM]]-]^v1kj5_&%:61Kȸ C]%yq0ؕ)!@k%M1)#Ȟs/Asl ռ1AQ${5BRFwddJIrFˁcwHՋUoW'7K5LnxF R R$9"k-eKd6Ol/>JsZӢ˦T(+sk>kW+=Reн ms*8rwbC9jNwe;/6D3#*)D./# qO|b*VذY3Ńa|퇍\K\'3+G_jњ /b6:MH<%PWr`Q| Gw(zjrȾxW)Ϥ2V aAƠpEMGc2r ʊWZ0$m{xP,}4I{OwM O"T6 tp t|)r$RQSE =X7KIc{$tShA-;˫ /uh5" =|f/ykKc&Jx&ݍӭTtZڗ'znckb#C=M.b@IaW&@v2]}.# C?̉vӮwVEcfQW1O {#!PLw)ٚH {LcJuV78P WU eo*d=s& >zWhOqmXyTl:m?TK*=& H&T[f’k׊\A=u!( FIEˡ]XqZȈl4{mF߻E`'{1$켇[Xl$(lk?5ݔv{aC3N׀}%{ps!8k+4Z>R !Zڣ<$=xu 68Xv9H0$#I_BD! xH+A}I @_}F gѼݒk2n6_xh2ﻫxjI3 }shq DRtx)ei,)߰Cp:(c.U [`Z}޲ ӃuyY[)8"-w/}r_S/ w *çV nTWU}3#*;z{oRX?na]SuDb?%>e/u}}UJ>O)*72\iXA <)Ohvm[4NR Y}41xd:/6Nd~>ƓO/8i7o#΢[$#3 Yۣxƒg&\J9cXRqxmS}"E/*ZiQLLjExӁu"MZqE`Z?aJKc^ b2X=,IR5ɥa?ƂSM{Mb)5~=i;Lv%8E<tAכ&a,šQިBȲ4iY%[f|)cNihOo?./d\+[!6pJ Kk8Eeh6ՕsWCNP>7܏~OB=QAZ#i1ًs&O(r̷c4;kFPkUq65*u+0ºxFi.J` AHj$@(nwzjYbxNs<oM>Cq3/q^DSf}1 bgͲ/\vjj'Gkba5iRDEԄdCמkVlN5G v7/_vSxT$Bm#{vbI3%Um(sqg?/q,Hrms {5?I,CX(phr201o(aNI}mn$n[rxHgjrkMgt؞6Αw l Z?^V~F_z>]Ee{wQKt_Jf*gq Ex \$xx9K ,lx2;NIvB#{`߂G7XuQ 7z>׻[;bBȍ#ruɒ<3EC#|?6_GtD!f`' bpN@A!k;plLmQf ~" e;:-Eߐ@998BX"܏ w9L7 lf.M^\MYZA~EwpܒFhЪQ:,ZČ$3z8d*JW&("ܗ# ƛ\f.ÃpzţXے`ޣbk2a=Q$©D2a ]T+oo,P”iNIRtY['y:2u7*Y†JJoHEܳZ:[ 5$;L} H#g N>$i N%g=qiu?y|KNႈb+MŸSx4T(o|+x?bŎA t3#Q!ܟ}B _ȟy>ܼAl\.!?} +aPR >] JlnGOyTug S/:*SLU:Sߨi^ߨjf?TcyI3#1_gy"u/[#VEB'?{l잇PK3PQ#PKPiBVersions/Version4tP\].  ݃'ww@pwwwwww 6{VR5޽zz~Rh xVjRD)e&l)o_S-78pz>`Qj8Al_==:}o'F wlzr1/NL)^ ݈xV(K\h ۖ;w}pKt z IRGuäR&+Gq?u)`~AB@0 S 2uHEzAK-sjʪ)NgAUSBY{!K^q -  nra'q~.cHT}&w^VR?ΖWݠ{r/! `UOGqn'kiy/xuZs3%A>gr3|y=Ws6q8QdĻ %9adL dN__Iwp%0bV ^v}o)6xlSz^ܤbGDC1:fwNsymN))̋N|l"̜?8{)%_}=ЎKn9l&oeudSd~X#:}{>iYaU`k YU͝ ,w; mW;v"V!Z^R ~8Xz=߆t" wDWAa\ P 4"i&m/ lWGLw~?D (l?ز(5J{⾱/ ՛?XUue3b¦l6o~٫sxeO rͲ:xMvA,kW'e'Nm E&pߵ}-]V\gf@SC9p;vl_:J\w3C8P^soqJFtrz^ȏQ6oCNpwJqɖxds՟xa9Ȝ@+F|@$SH%ϟshߐ/A볛/7y/(ӕGV|iZ=ףILoЫO+sI6hhџ~70DQgm-;רkJwn '϶޶u'ȑR״aw͌l0);[~SZ- i@ˈMOvzY;2֭0oy> ^ףո^t1<!_fp59Ёh!M1 @u"x\Bfb`$STc#@Nܭ1$3b4R~@F*%ŒK93]wk^ )ѧ2^jWݚIZɾR+<;YXmg9i)AqV̹1eTK:2ױT1'JhS>><^}221jHq͎̾t3HxV~!hŏ:= μ-FPMØK]'rhPީs s(u?KA]qߑgOU?q< YiŒ VbuCc>=@?=ۈǷhM]72rH,3׺μG6a+_2? ~zF6R, )$ QPZ,&l w wk\ei篃5b] UVT__IAĂ7`%K=`QXө|3 Jk*ȽZCƤ]뽈+WI瓿#~}F*bZޮ=0[j+C ]JKĊ!(Z"2znTcBU÷mn"zpORu6ŕR4Hi; c|Ѭ\2a-/[k7F~d|u3#앥ʋ|4&6yÆ?5?QޔHSM;N|z`j4.,^ZPWuƢ(-ۊ&.,"Vx?+OZ1{W"g-W$'7!>0 tLQ#lNgG?o0Ճ[%yq`K e6qvQwߧa(ޕbm87?b1- 5\wJ,->(q/<\q3OoTUKJ0z-3t2gUS:B7Ɏpjml K6- JFW\<NlHLe;m,sYc덱T#o9A~A2.؇%)"KBIx6}i|Xnznw}( w+6rVHs8'+y(w୷I !|ʮsrO]V+Q8פ3j`]ޕ\ffxh`!mZA̷E-[86xTl$1)HNaÀX_Qk#^75'ibY!8Z(:Qk#??H_O/O-}Dkt<̼IbBtJdb /Y g5嶭Ꮷ7o L5P~9[gbg#fLN;jCL;FC-< 3nߴW^.͟>Gp!|6١N߶%D#Gi'VPt ~HfwQ!o\7@o :;%vZ׷nPt>OO]?O]X}wu[{BxK1~veuUqEFpkVܳV6 y,䥉V0*:e2ת 0:6_zG)o;<0:G*;G ;mht;Csu<ⳕ-H%MJOxYh/})O=FșcZ EH n,G3};hOuEچ)X:FFD J뎂O[EyN\?ȥOajރ!;DyDi]{@k45& 9cq\C׳! ޢK¶Eb˽X]:I-8iHgJAo:=2x\:j ħ$}OYrjg:Sěe`ke iD*]mہxbk]DlPs!%pfe\vR34|]l1׆uà qspߛϐ(js+qΤ\v*BK5ׅ2س~FPU*}Вwkl2O1…MZfa o!wuB"RȷEVc|)\z^},3~cy]+#8sȀ}Y2LW^iC "+(%y}KWUh E9N7d0}wa[AX5,1OEʴXPVō^3 Ol2m|4fSҶêk6k`~qVpM=ٛ d_fh\<>OdZ:8jo*k1@yK<.ȭuPvvlԧ֑cG#lҏɵ?,b$.-wo#VU, t\-1Vf~^`K}c '$|F tID0ED6ѦyᦷGj4!r1Tjp$8hv/P,iݘPfPUx,H88U>,DUʈ]='#:Y?W1ą?f0ok|c0P?g=~VBlH{Yʳv=ތh8rbDR*YQ@(mH4/rk"]%*Q_ st>O$'9s<EU]&*E^ȸfvbr593d_9+c{I:K?-{W1Ǹ(Á~E=9U3MVK$k鉙u폽ņ `"e+&qȔc/c]5!-bi2;մ= Ua]hYz9"]]Nw*_Qz2^[}T4YoݘX!/Ί-8Ut:WD,T0r%zϏX ^qk}Y5}RD\S D;Bmp jcNF֏뿮ېYCY#5ԭ3W2,'hQn6rv6D0RGE۹z źRbKgxSNX K% AY a$)3>F+M6 ?qrrwBT-Y[v#KkӾZVhAe(T*=K4EJ{'JQ^tp_iK7vofW7В_`D%˅csw( OG|CޛJ"8,s)Uc<^5%' MxiE"/شP_$YI%nkЪ I7!jPmJ"K9Gm8ljA9YD*+eO~6T 硑-TT,sTgt]xJV9-Fӻ(sޓb,qt.Eu"'k8:.1ER:.(VQ(/&5~,NDzг!X\VUͫ灝acB"IHvqSq*=/Q ҷ:hBNDj^nZ&j%Vq@cƦ&iqY t9DK ګ(}jMi_vUݗ®icߩ? @o ;O ȶD){Ҥo'r_s<m=8:Lq =oNqrF xݤ_h)oP1;< GaN} mln'I+h4#-F:(Rns&s㒚}0I K _kujJ6P[r]%~Y0PnVdO\7>wCƁRйyT7{q{.uы*>gVrJ:mbm Fbi"0ֵ?ߦ4xB3G(qAJ"rl:9>2? @\o!xWݍ-H\7> No|-i[w4׫dd? U]rHz2+6uj? t }Y|۱P 1\H/gG(Ήq?@ˬZ2}zWvqp1'~=7/(-R}[A 7׻rilTeύ-D΄i/\2~#C zbq6zlEIY7=Z!f ;.BC6G0*'TOB_-s{EX>PsA7ꔗiN=/zX$Cq:[_5߄JvF:g,|~PLpE"tutM| TGX`pw *MRsXONn[N[62li׃vwx'Q,YY \gTܺr*{]L8:6.Ge<z2M,D_"&g:K$+.Yn(׃Þ* `-',ҿ5W&\ `%0+P K~GN5> +#_;\$FTo)# ,' ll|YB$1XkdD-BaMoHL99r 4`Ӌ޵Wʣwh#843onоޚɽԨOkʛnzP_ߠ{- YbH^9%iөeLLx"j [MȷDZٕV̶-^ 0Zׂ6_"a:lSZ&N7ϼR<?mLiwG\l3ۯq}X)яfQ}g+g8<6xHܖH$kٔgNU^҂6s714wVK@cgM-I) 6!k{C`g8S9(v pw]oԙP£\ 0XXpw#B,'1/ӯ3^>>1ZomG\)Aae+?&twyX3=cCJZa[l: R$:c=h,Mf"T. XjJJkVG,>X]!pH$;"dzTyS2;#B;REI/Ax s[l}"? O)"UO_^ ß4MJyMǽܱޅ+"HCr7#7i̮XkkO;>Q7UnOz#T23l:-7o:SS7YFyke K,`D$^pI(t~\Vt, bY@Y$&ETkm=ĵNQe# {wƻ}Gj|dەYg qe'4R/$Yw҂'dkk\-,6)é=~ff"1Sئm,B(ݎ֍[S, ̤ 4n22#b3 %_mW6 [-ozOf)_Cf)( fW;4qCPF[' si=uK%Ea mR[t*^W5"(j"+?;İ1&I)'cVP F$nb(WĂ~-D^烜ν|gf(5|Z`oTZ:Z?ůA._E^~6w ٮSNg K ꉙĬLQN*B &MB\4~ܨ ɀoq`4|Z2͚tot[FX"ٕ|fe*GGU>z:./~KBPg !I>uSH4a59HB Ǚ6V99v50}] )-NUbJEmL@7K5q z\NNɺ?0S,ҍ =dc?*WsMp߆/4'F3ajʵ zIM< WnE9u*5.OUB]bX&ilN^Ս(fLb:{u@*efdTbг胆"j$3,rKQ)zKhl/0RM Uq||X#t_lhmi_}Q r-YZ+"C}kĎi7UF;Zp&&hK@qX@!ݍ}-뾡V׸Fc+ ]7_0XlLmvII{eHÐsad_v 3FmV Նܳv4ŜD|a1O 82߯r6Zg9n g&ϚL?"dE8_+1&Y82N&a0[7ZCX;;א0:Fr 1o"YUaWpW&1٪xC~Y椀C#+x|JvT^;cYPGGQi4?Jz6߉yidLKʿa1Y1L׬G#~Q~gb$bua.;kD]5=ĘP),(31UA&:֝2> ^EjFDzqAҍ>EAZfGTUCσ=~H2Fe?Υ&(%2n6 ]g Lp4;@vs<Ug&AgJ% UCg4eyӹ[4GB6aA4AN#pU QέW̼E۴6um-y$ !߬)a$\2kn)Ruԫ1 &ՇA].[(U G-bźBtDݸ1uB$.JRM_F=!皵8p2-1eZ;Y;H'~$keiZ dJ]w[Jt~f73,Xv&kƃKwAB&xŌt{$5l#Jpo`ZwWdN$a=bGCج:>hqF\ii jڛ!ZGS❬8DNX v]@<~ɿ34c\+M\.?~0?*3Rl:DA1`gBxi::PU\{[Hc$.$c14*08-\)] M6GװY'o qnUJәG6[|"1z"VnLEVZΚ{cZJ@c ʕclL$4)zhqӐm=ɘv 2yFSi`^ei>1qe&EC锱+jn8:Zz36963;GL ?"x?wm.|Wj&EL$F@dL]G=obOS@(ʴ~y>/f n_uQߟDo7<~q>^mTjz xe]D+ׄm#.Q3.l2tw~P+NLjeYR[Y5lSǥVCO<{iŤMIaFiSq! όpS)xsLAQ38JܚF3ʿ,2 ȯ{%xo{WMPRG=>(!cclocYϯx%d%X$.ֺ3eHk5 $zpm]+ a:h&E!GiQxw[J]_Z(T*J2mh.Jr}iȘ<]oA =g4}`ڨyҥ4vCoh4MbXUz^iɚd9[Ş JVPiK4= pUCJcmHk@r'BGңG:Ei{m5p[J&Z X}eV5 \hmQmл, a|" ͺ;$07Wv? "WiY**Ubñuj |FH퐟j/.Yë>soj &hjb6_ GY- nLUfxE]gУT(DZyl; 5A_oI c?n_B~ dܨ仃!zfƭ 7f4bň.`X^dZmʘ*<[1_.j4篥{kDmGnW]Cwj7P:[[33:@2aGu oY=4EHܴOTHayց#R j~-V*ek!_d' MA;l鞢d\vY愤,bMHۿ1V9C4=ܳuh7u {0l@Zej8,;a](s[p YTm\QܵuCȎ }1<>vz>^:73 `TVQL]6*Ũ;-LJ BuEz[Lԩv@HxA7D^Yx $5P^5ǧӮ}{?9윋h|89%ZB ȕ3~>Nd,\/dF5}kT ?<Ԍ)] 1F ӱfZ7kX0]c#pssYbYa8dk3w[@UDX7 Zڣhx;2Ǡ ^^AZӸf| m#[ Y_ZQ<3q_Ł=1$G-XYegs3̢GO/~&CfR͎ݎ'O_iH\ ƢC~Fx_`= EoX _.ǟg1~[$1d`Y1?>PA`ЅMU Z?P-s%PXc&8F'Yg9d8Ȭy I+Xtor/͏egR|8 WNFR,Pv-}ܢxpi~ťbӷRN0gi]@RJ+PXQ7Ϛ 8 ]: ]SD7:_OZ4VߓnQB6San2L{V%(\¯!0OM &ڥJkEZi7?JVYp;ΘIj"x8_oWi9wx9)+OA$_ہ޿L , |/;;RCȲG\i)F'8WqFk(%5*5s<$\m~>5]aǟ >]BNK3_JEjӷQTvc' (0%Ҏ"5t~v`Qjms۶ʅ>X)c`'Ŕ@k-GB f`x*UhAR+F![4Dħ~|ϓYE")hPSoAΕ 7gV: 0+G]vܜPG-pL91J!+*P`ɖVZ^%V +YaiOjE`Zޕ]/_tN@QlTH5jb)6f7 KܔirNNgA,f9P$:_z}I*(NƬ%1{+e@SƇ}>KʁF,0VOE^n6@ɿd ʺ9>\<p1 TOƑ'2:퐠'+/M^KQIh"諻\ݪtQhg߂H-H үtڂ#]oA H7Zn,H> үtւ#nA HwZ,H>ҁ k^3,uϾ 9ԃTDz yׇwe7) ag|&(bȏt`//0_ jPUaxJ ׏(߄qF#|<w"q+BVgVH.'5Cc05 [qO("M|W~ +d{[>4hBﱆW`HcY0dxIz%9,/_$ ok흖f~4 ,3 Gi14Q?e 5- VE-J!?2Izb+*y:F㐋z%-CWRwp}͋x=b{P&E  93=Č+f`eןpIZQ?uG$_؀&N揵~6{#׃eu~`u4tn_mqe* ~#76#Ht*?- #~wn2,_6U4f4Qs!c^X$)@פioj?zqh {JY3Q(IlQ6z2R؆\G)Ƞc-=BBZc5P+7ٌ~twm=Y A=ZAI>؏+=oR;1gβ\d?:,H+]^? DŽykGе)cuW=A7RkGzL2mZZ?ʄ'~TI.wfB( Lﴪ2ؒS>)H$(8oC]SN=GGg床MO+ʖ7]{Ri6bcg m")_oYdOwt8Nd'ю~ΏnmI)%',H RbUz(S:mCGT* ܜ={կț:{Nj㯲S̵VSR]\oA(2tEM3Q(hHy#/_-4G@j='|M(`V-txTX1-k]z= Ï;7:+>XFﱱ]R%^жaSq5 !@@]׵?d3F^rF5GPTQD:Q(?-һveU[kytml&޽J6AHʊpWx@E8 佲SgaJE$/7C zE۽_wj' b`vΏh޽w%Ě<I ѲH,~zSWTKKm#J<&N^o*¼Kꃐ+X:Nc XyބʳKWY&9)p}rS.Y9 6Zi3Ks4=g_3R{`Iz6ݻکWO궅jMcX):j$iv۪$)>(8: 62=tHT=x*./mCD:Ҵܴ -C-l-}B2J߇8:g%֍c Ät7}ǫCs#sJ t820c?dNx{?&nˋebOǭBabmŪ ɱ!lLh 8Q}ڒq}Q.&<;:ɖ_{R%H $+ &Z%>⽝M[R-rO 'JW$ُl$Dj;S|IҒ{i9کu]xQ"ta [?WH9IXd:9V~i[Y1U'phI?u!@ 鸞Pj'#E9Uqr\e"4,( lop{w6?>ϴ٧e si\kX5aMΛh'1P~jaְ#+ݤZ i 7T#i4q52~'bpp9[xK$g7ӆ␇߀#kndkwW4+K?&;#||`V^3BlD~z'̶N S~@!aGm!){t㏏(>P]uA*'tX_#S(B!d$bSJD*[6=jDO0"~RM5 Y m`L #SQ* ^z҃;A/h7Xoza0F?WMdb1YF/ox9˵_N}T 5o?i铎[*l#9韅Agwi0`bD*qtݳ}SIg~z6=CBxA$Av,p^9-ҊMj45RW8bNϜ3\0ZMYEK@ӆ ǩr @}Pi|7I-}*^4e-bb~4멁quD Hg?wIYFEY, &rzP"]u-}FhZckf 9H,LP\JM;knA93]ٸ i^ny 2y97&|FPׅC6XϓK43`essv4iD0?>()I$'y- 裴UI?l,HbkRg*o&fzogQ+@XM]̨B;*T0ɑ0ch]+9M>A_9Z_υ !俒*)P>g>Ĉa ;: G#O4,2}=q+H?@!eЀ۾?S3Brs獪Aɘn^Ua}a Ij ^$5_hXN i#aרZ˄jihPES7޳$%Kj+9dql/9)O*53ӎ m%x1w:[-[H$wوu]⸟PQ7hRIs obͦGgX]ײu+B6gGNV# [SoWvd7`g+N/U&r 4&!Bv ]?urT$|t3x!aMi-KX\&y47^LΞIRPIЮȜr A}ŽǯRCK-6?))q[ !Fq:c}ľnGZ=u=H`6JC0Xv*iJWI]ûnv#kU{xb|j羬\H݇7s[o-aIU=_ּ(N T=9z͘<:ڔrM3w _cMSk(>UKv*靶 =jܾ6W}eЌ(Yr"0 XU:"f?)ߥǙ0~e㜒ߖ)~SIɲGxIb [[XD4pVi9x.ՅiQTm<^Nzd@y2|v!3 hƋ>`U}h?=yZ-)\dED{VUd:/6gNnnGX%Mu:|+'do#^@:-2MviIH$ZCF?Se##_XhoQ 6ŒZ_McΫ>~~0Vx:(l@ZakMۣ,n;+'̑KR<ҍOd0;/Ķfalm,䚂I0iՓKBWF NeQEFY$*?|WgiO%GPsyɭzkGߤ"Mt蔬 B&KgFuPY!;3 8e+~H7wEonZ= kODkg0b^r2!T ^ij9!K/rL B҃ "RJaÄ^JQ"L%]M78 L?+?}_N$O``nV̋z\K6- eJ4Bk\Vw~o.)Kԥly,4A~z(O8q"u-"[*̭\Q'HϔqyR8DHD2"|YACUfY,TAr})Mn)pP¬lv6r7 UK}[1`r_?{8H ; gLD "ς[0%G@rIJ"TZ\RFZac/݆݈]?4WRmmƅH$WLŲ*hQ*H3 do7'(`xeU-\y\FHV_u$j1b,r/ݞ$O͊_k?5Zlhߤ)b%[_O.R70 .b%Q>) [b(EU*}ta\P,OK`b_[J. 1-h96첢?dDFU΅mΰt]kry_յ1t /h^qT>┾$yU!dF;&kFjD6'M=v7HضS3̤%Ѩ+M#eUOTIͻO{Vt칈6A;['W9zII./I#y1mIgq^?fMܶIv*Z:Bl[j^膎Q%p Ђo1h>"4{T5=|H\F͇X:aYkY}xC-!eI %Q;e,ܚϲ>iJf+DIB $6Ty좴R*k o73 &ZZzu6#qgN&pjFHi*U]TՌ ZIXAm(*Z(e-ZödU:K5oȺus΄dwړ3 d7AnϴausizgR(.X>%M}:"';J>.?â]@CpiѪ)rtLүBxa9V$h,R`wmUƗAivvTؾz,FVP!}OL7su$@UWDH#)Rt?HKl Z+vήNfo84jbr<˥8de2Kޥ9/ߏS>H$ k?/n}OKEYIoLV*\,C&(m g]Sgam!I Nj 'LS@xEc1"; e>WuN%* MYrcMf5>CP*wU [yʤ0\r3 (g52a t/ץ<JUW[Wq6+-^"9 3N%Ɋ-Bhn4* TP㉍/lNI2S $N$d@Dr*hq"^e"|fj1e*mҕ+%.**}u1ZO0{;ǡ@Tb@zHr&VOXIi{qӦe{zt]mC#hgh[y6#J4F}VJJ]ӀFo-ę>7lٮq>oq'۠wҗW;ϫ"%S(4HpQgM( -o)K0cTuQSRr,yJZ@ ^~,"-эC`{W3RwZ (ZntEHCV(wCIS Vf}ei$`f۶APj6эM{Oݒ.qV3Qׁ)fh4V䠵d,̫eugΨy1%VrhOqFL.U.NU0*D+V6mK5% TE3kF2MhPV =U)Pt$]* EG7M#MGgoޑ* Vnh"OJ_t$*G ͢xc1+Οu)i K3[5t/i[ Zuhh:P-vZŻE پ#LVQTI QQ(TyzX ΗWT!Xvk/Tn*,vdՒ* q/V'fnNbr b^/. c_3zQԖp9llIY[)R''6lrې0H%2Vt`$Oٜ8{KhZ^-zǍwuI hUlt9Ìi@D>)٣*сLGnY7ޒ3vUK:Vu (rtMDqOiCMJ:$\9Z,`cr )&Id+:mK9Wc?њL#jgc6ZIAob |ʹL/&ZyxےNiX|Kv9Z)"8[a(&5df˩<1S&9n O9e{1ݮJt?»u .%QEr SzqT(hJFGoY囔3q 5IP3,o_i!/qOsfC-XǛϮ{)aNM& VȂ{Õ"<\-C͊׏C $}1-[m R[T /.^wO1TMb%&k3V=w+xc;YBz/x-o"zQBxS,,hꔤQݻ}.o77'pEX9 ,t@"eNɳm»)cÄF뢴h+Ozu^\ 1^~`FcnaYۻ‡ޞibXoi{ז1 >\d*3' Y$y/J cQd޹aʇR0 ;m{BYĨY43:Z&Db_$C;ƺHwy]}g d#i+KDr4`(+r2,8ˌd$L4yU ؽVcB{=dq`RhB7_i?"Ht׌V>- ]>fnvP:qɉs[3a+5yfsآγP~q`H歰.DՄZ 쯱.Hs{*cCN(U;t% FMs9s{t=ġ.P8ka]fI g]ȾsS{2$hFZ{6րk:$ZiL*'W_k_3. 8c~Ҩݖ?q;w3zFS&{FJcH^H>BYƊn8&p'W(-EMw^ǯk4P o>Rni%~%@bnP#Lx?F7Ct|̶#t`BbR7@aD21Kxe3ЇľLut$E$S bS2<^'z|j),wtƷŝr=Sh1G(G4HR4ݣDCP~3co)rK7 {WWsdЈa};Tw6sI6xr inG*ums-b/R\&w<#E(ZP| fOq7\ܾF{Oc JV_7yiiOևs k |a5P =! T s"̹1c߬Tdue&|2362rjxM-^&hެN[^cs8$x7R}7Z>2o$de{rn -,~+T Nb6Y5iنbGF~Ҁ;]|ij.QKO4Wj1|ScQCxCeJQf-PeX<4`!.[%*T)p^ hnûrkjRȅq򶸬=O)Ks3{1˪f`m&{O OHJ3({PF9b|Oȝ<8\k&Hf"Vt#,UpIʋё3Ktb;@% /24V%)r_?)';/r7E@h)9^ U:L,cXXU Atw󽲃jir3$Or#]K+eQrTu^tgia #t%"?$ZQiE @UD:((]8%|#?M7  ^M,zM\(f8`W|܂;*qeUCOp,tW?YAVlU>B`xm|js~7YMQ 4%cdQ$vD$5(f%O dH' Ƴ 4)=OGPZ K`ݸ9jY>~/x҈Ī1Y&ņ2P&<~H^ć15w OlĿ?/%S^iu ;jKf](qcԀ,V:$bd"oɞ$neR54Cx 'ny.%cWp!!Z2CCtEۜLRuOVxA3R`< Ҳ>03*8B(PHB$]2w*xGFP+bWIUńz+|귂v0Ϗ7|>0ߎ ߈Pg,?l e7e "S_I5ωV!R{FxNQUXIWTDjx֮;s.o24󖥳 uB_?5m!"PP]RiC H%Yr.׉ABJ_/.-3(j .-ذHn^F M1uOYx8~".=aziH؎֜έMǴ-C >L1Y$^&\z3-$n'%c|5T ǖU- &B+"9*A"XUA;S;a(T&x`GPa=w)Cf;; H9I+؎Db`'# + (Ԡav!;-MZm"!E! 0ORڼI-x>960Es%Cz \2Zjp^5PpY%Mw0Rp0|n*kͪulR"Yϕ^% bl]WnfpM8%A $U Y(F!^ۈhpݘVIѮTb,r+GyM!3o(|uVxL;$ce6ђD&TD9}?yH^3vKd]wQoӚio[|;•'#~~.eb0)ylqHoCp1(25?xe*.!Œg,F4?^,aLHeS&q][VX`-_WR3u W&ыs<@šb٥081 #Wr9/$#ѧ)Hlյ[&bJŢ؟,*+̺ӆȆ[mźi9+ OBs5 'h2vIꊻmkt:j F6'ن/vp2C/4Ag͓"3^~=[yc9}p:z^VwDM u Ծ O7#_icXHκ hw XT`ݻImd/lE^PphJS6m(M9۵Tƾ[V7<ݴ5@uϖ!]pIg}y֏4_iݻ/x~/>~HuzgvkAgg,m>\|^< bUK1wfne D8;,&Q~~n-"Zda3>^C5pu/_"F%01k'3~Ok~yaeiV6x"gn {b؋U[ٿ- X3>g~7C􂁀4AQbztVb!=C0RQ~t P|X/ovo߶bj_`)eUVu߳]לk)EmWCtk~; QE >Q9+[揎)C~<2o ߇Yn/d:Z綐123'd:DS< |%V5Bϱ,fça۶ mmқ]5 ,> 4MgA(A1l ( pX [{߄xS l #bn$[1E7"߼nY|cν,TW@1 8\'DIEH8u\$sp^cuYpYpp]fSI8_ ǚ'p&J8&˜EъO OX"|0@ϿI<nsJbK| i7yXL7Lbq['!|E'6-X7`gHOXq+B9nobtnJI,8 3NNWż,ףR_д@Jٔ  r?f饮cv0X5؄$Qˆ}#X})2p2XykcjԬ4rMm[!ibS)ԴX %>ק aaͽPˡġBQSǟ.PÜ;z5.g\Pd0|ol$ ?o'R]z]1DuˢzubʿL)7-Jʈf=T܈Foc4.cG$5FS} &8b <0̓V4N ӯqKa=ÐaNca[z`Nl7nZWgWP1KaQx{̘=fVcj㼚j%olcuZ* P`R~.(L;flgƷ--bKQֹ>z.].Y{Stg%onԚ9yAAȊ"u+%w?yhe#Vc}mT[#TGxnYaQB\bUMR1*d@$˞N[/W3ݙ8U&?f6gTsj4< -X 2M*l_+=A<4l'0{J>usRl6N±tokEpD!usk[-v nbv( %]2XRj{$U &]!_+%E2CT< bi~$lژ&L嫖 =l4*@lwJsZX-HEn!O8cW%Ǯw|T5ězvs$,^$$_B/!ؗ=/ ܪ^q|E_|~ű1 F H;,,`y9W8%-±/±;qltPkLDZoDZrl ySOıN\ؗ4 Ǿp plGUSm:8Ƕtl|:vsPGU1Q8[AQkۏyYw^sgYn\? |$`$})=k}e]gm@uzdZQwJsz4,Ի"77Љ>`y ymU!mo#EVo}?Ǫ=~~}ϜY@p)_Q,FKnfM4b5KEL\wm]:>oԎ=^Zj*Ih URم=]K}Y sh;d{j*-|C8s a/#Ll U`D-\ ˦gkF`Z}ͬ*49&aɴ B?sBfB~J.C,^b3j,f(AHptMg-,Ml[Yݐ=kms IYݦba4Μ-Rȱzજ۝rvtCSY1TfǾb1/b$\pzVYӛ.F)D3$aAS,H Bp-c 1r|}~x33'|hIl:*Z<߀mM`.[U߿X"}=e `j.lJ}oGʿ$ṶԺ9u~X9]OӂRkgLvjaқ(w٬14p|ap1 ' 8̖:pDW6"]~]eq=$8(SQpUځ4\8t7$zkкoNhS}X !M9DXz=zǐZꬋeǐNo~1??dʆ5N喾[Zsz[:-im}&QxH*?V,\\W=jQp s]8mۡ!äԎ)甚 .Tt!Bmz`^qBodp|H I۶ i b9d4U0 †,#Q+f"l,AYEn3 (2ۡ"EjSX%UH9l2kw |_ 7^qQ`4e0z4(QHu1:aq^pvbctചR - +ZN+ഔ F-m_= 15jΨg|s"|$ݫI<#s|[97ܒճ'hNjjpki+Q<Լ/ sԼa&4WT9% +y®z,~WX\3]\=|AA mew0\^V}#| chŗz#GUAH \KAI#tGx/A=xz93D*|!s(WP8868mHyECvJhjR8D7|ӔP j;QB(ƈO  e-yb{q}zLu+2纨r^[:B|_::r[/rxtz93KQz.*KAU̹nzJZ{@>s[~s7}`FY^6w9A=8dS::^Ѕ54LSuaFhmGɌ4e:=}.fck5}23} dc ~b':(j2-+͙Yp Yh>s59yi=@Ӕ&8eYdoV\tf @ExpnCEܯ1۪~k7X{hM $u2[hJnMyuƀkbV 06gYkwV?E6؇)IGJm!Z*Rl̙͈t&6AGDNZQ-ٕ yy|NZXd@Enz "y ˊT1cZXZ~yyIҩR:|fzd'~3Jyʧ0O`#P[s')O7'hrYnc[L{ectjŢo"|ibGUl^c=Siٯ iVΊU""8c݆KkCF[v{DJÂ_WODa~ב‰xO&RKK-^OZzXc+Dl) feVC1pv,Z4!a =* < Ү=%{>dY| [Xn˔fL{ 9xApDD?AfRxuJɿ`-7K /^h*ƣ1dw5G~AStlxP@'=gk85w$ OrV?.Z& p[c{]ݼ*/tYQSD OYf;oS d ,K j@h")+ƓF2~DwT:bPy5McPx;)x @Λr'٨-^@\r1xrE~I-+|l#ldJ.?ˋ $* MpG +]aXv}o*%)P9ADPҾs+`pN"M8- ,}Je|3$_AFpeچۢI1m *oZ P_O(wnV02,e IKy6hsy3W,ꁶ\zyDI%FW۩Xҧjpx-1ѧ KL➴go|I}@vXYڇ ;𿫨iru:6DJɿ$@(E|0ak@m{;} le# Wb!m;W7m/A׾ ~yo _}<VCnxKurg~~f߳ۛ*t%hߓ ]~ g{4ϛ/BK|P^ ^b"9t9CUcl|2!YgUNj"rTm g*ŴKF Ӿ0 Ӿ0ʴ}k4O jڋۚ{?'0g0 wP@@ࠫ|Z`p% .!\#hߏy*g{b*;AtEwc~"q&#rgfQ!ڇ2sYs=ЋYd.+P&'pYI.B^=KV[u0"̡j=x[W_P3UaDSfh ñ/S%8ZSu@rHk{O>JS~EICNS+eaavmjj'Lv-e8C؜g fαXO픐xfY}S# |Fs$0b&ac17Q_y=O(`TLTt.$j f HvrBKوzA) Yu-[ fe XFV9u>hL;X"9SQ+IQN"ES 1%L.v^:k}"m(}qR ye]Bgjhs&j70Qɚ8fLԴ_5Vøj Υ^hαW6xAe974ad1 9kn`b97fs,G !/Ġ㨜9s; :Tȼ?TW::s7 !G%9ș[xQ6q&>Ib˫UΑxQ%&KsG1#P6171UJobz~̞;A̞W6 )R\OG݇԰;|+m$c (+-FNb]ώwGJiwCe*ou. SQwV |fQPz<]rW/wrG?ULGeJK#knX!f H}_UZ(-ud |!Եyc>Qr[/r/Gb[!R/9830|i}D!5:!I1z3/QOg/sY$!$e))L z!aжn^9_r::RB[܎;Ԝ۞l=)c*X=IK 0JŘ;b8goa ېHN10YT^ҔspNinf-E-0^Tv 9p聕G+RVZb vǃb}ݔ.'1R{XiWҬ`%>*mgsY NIWs-/qӆ/хgwB ؊{Y>j{c]@a]1Nt*_e|/"վ\~U>Uԫ콨LSBJd.@^t#TľmLkVA*vbУԴ0ˢq/7$ʛeQܸ\ǠGjqz9S9oZ2]VRSUSbbϩ1RLm`1քykD^j(A@ѓ>m2@b4bF.YoL#f*fMFK# c/yZTj ~akbllc1CgbUޙˋjEy͞}&v5Ҩ И6`7ov4z4UXд3#biU'\!MFb|V,tsLjaq@0!EVl^Xϴ":у7}hf0Xy:៛G纥<:}P~DɈnz8E6͖cوE.tوgg~@Q\W$9]5j]Q77n3qN0ԚbZEuB ME ր b:1*7]+ZkȒ br!i\6= eͲv+G.㵰.U?~-w}.[+!h/ض\>aO] ,vzX!ve-49]Yۮl]]L,7YYYkxZ:ͲQfA 4B#EY-Zf|ar w4Sj>D7؆onozN *pLVە& h.si\C. %c\ @sQ]yt؅ƕ]xn(t{XN[.vi,Zj8YO(Z OYi;+ze`.E7: 4\k`_f^KV0J30/늇E OM)=LQ,pMs?9 &/!}:*ュ*#jn oӣǺ Oo]~/V?wumhZB|<2F@WJ S ;L{luvu\w6}:[$fci*&eBX{ZŋCѢln^`HSZWr P3^,57&@g=g5 ,&x|{&7<](/h}ͣۆ^e{(4ü"VVyD2K(j)~ϮL))R+X+%F◼rynK@I,QϽS+σ-ӄ58|ꠎcn | c7M 8K@fǜo hq#L75nϑ]D۲xEd8~k;ZMR azf}oS2]W;a GCAz8eV#@/--߰ Yt}0NŒҘۦo`,{,=Wa) @Ftt$X{Ұ=k@ggnhz h1XkB *ixd~ $2YL*X+^YJ4La')ydFNR ܠ k{BT>Ղ(J;VD~qRB}>L5 k#+[QK>Z?c@ue!z,ە aNqloSr/ݣ-diOȳ{4s[?Gۭ.^Az:ӱ"0ښݦczMi:.ZUp;zgƣ"ER{x{}ySQLҝaDHXPJcH<]SB &ljc@|џ@q,8Xy3%IR&KQ5 lr.xAV\{zA1P@XI]+~'/a( qrM,LJ`p&JP>P *P(i=WX?j[YyW\cWKZ{K\/[կ:M;1%~l=`aSE2}ep-[)Y$0o2Kol'8!%R+C7qC7Ϛ^m(ݥoCv}EԙW)”;Ez=RV)ZʿߔŦXq?h`ĕ&0/x1Z#|91Nz`Ñ.,v|Yۗ|Ub_ X?sh_AざXGQ)͗w4$iQkٚZ:Mj/(̯d,N\=^Qfc(e' 񧕀?-GMY3 ϧ?엖?kuDN)I,GG5u[zCkjx܎r$LI̹ AG;(osy5W׫@}jYPlX\=}g#Ig>jR1DzFvZkӧl926&S5o}h/?ϗ5\5y\cDF4rcjPXj (zAZrOF t'" $|7)K:٣2ϕ=efצc=쓣iٖ3Q63(${ItskGcˣ^bNV%z\$w {D;i(G̞]7ĉj3xQA==[/5*Ra\B*^6X 0.!/)o8!ND>$R&wQw[$[oD0L*$7 Մ$%[s?>w wЫ߬BX$xk%]$6l(E7*!PoW+6rm|ڄ|?h(\Œ?lZ>~y^ZPio>o:SgJ;}4YňG@R-K#xpZHaE%حežLgC '\L[>+m<x೰ݫ 䑵}}Vxc5'8>KʼnD;fPxc͖ۚ.le /xהlO2^  f"'*/NarN=Q5~2M$b4vɖUaǒO2ΒiR^=~Y-Xy<{ۦj`,E ~JWN1~$ G9V!IV+$W$\!>tx1q'ptӨ85q #AqW1OIf]tXZ^2Ն͋v6,ד'ʙqS0}7o%TnU N(O d)C*`Y<]5FkRp[A~U;4ˋίxx6#enƯ’ $ lcpXi\I?:JM:\.^~ߧz̀ΊѴa# F+ !_d^ЉueQd|k~ۤ˦t|x:r0S²B!ބ[KuAeg2F+I-$FRR:nz!AD/B l epqίAC"EeyB!Pq'p񎺚Н7lb|-QKbAbBsO^4:s8+~\{;Z)gp7H %\FзD+r*Nsz\60:xOeOnKYdgY.rމQpQ5>KF;@#A./ttj2 fF[f`@]tG yl"\M>PRA *z?;*_!z_u^:ImoDSݵ$4_K`8D d~Cvhyw$  $&bkYku9tURU#Pey2VpdW$"9#h㚃g?V@N}*XpkI/ IV˜퓔h^V^rQ:eJF8hXb{Hp7MyT=p8S&0`!OÖ")("U-&!S_>9K=k@7` D,jcZ"M*LEkJu7ű+hNNٺVuޟ{-aM맦3p`ZĂjPg`L3 f;|Wf|NU9Q^b.:6|I)UBV ޗghgU|E%~RZ١}1\mrJŅA_0 Cz{=7Jb#8 Wb&t rMQۚ[HcM{;#/ UICUtŊuF:_EW#Z@\?8"W-L/B]Yj17F1{s5q.aO>52N~1\EDh`>2<8dd$:.e~IXy-́Ղ]VQzP1$5?/kPzƔmYCTKZ>晖`WSk=Jۍ3k! nafʅ3g`f5̫3&z L0zAm_v̘2F3喂fn/7N3Ub2sn99u s@IFc W[hfMV*X2kQŽ5*l?XuGc=}&Y\Ou ?PP<A񌲲JiZg^fu;cͭ+j钊mX״8G-@sN^yxV=1cFCsH7ȟ[H?XEdIL;PYiˬZFsTvY; yʌ>|Gj܏Q 9B 9RhGڋη0t;CglO,9lR T%ԇ?eQ :on:Dۧ DQcUqUܹ{#bԤ!/-En *9TtG3%Kb{AƢӁÚjҷ8S"+DMbYmݴ*QEޖZnئjN^4qP *RQ9n<%mR~Y]:;7ʴH$TbTz0>a`̲#h )Vrв7-OUsjaF=\ulЃ{o,84D`5j&@`slR=YKf tG=α5 {9}6/CTyCgA@=8[6%DC@eqff@-Ě=2 f#!o[鲖_FIM^L30WjZN]Ⱦ'"u\-G7::701א6B2u2AbUP&'@ me.i-֫lF̤Il4 ؋۝ EZ;lCyiSzZ,՜Ҫ[z[liHߔ>EZc=Rw/uKslG"*, 7"uG'Xsǝƞ0ɺ*<\owP{죌ъOE@9-˫$I~n{XW`lsXjs/lBu=M,D76@leX uaFf6B&ZGwĴ4RWC`pCEoWWI ȉZJfQU;{Pyv Ȱa.Zī'j;f oWXHPy&݅oNnYu>\Gs*|olF1X?&Y~3<Ás{=&)'׏ W?ġSus5'&:Yr'(J8e 7mCnNa^"~h7-miE|[8ef~2G|&ڄm)ڿiw˹n7F4Gl(x@)Ѡli;|dDʯ40&_^)6^p\Tք!6HA`-pxD F)}̱Y,W`:?rW@p2H,y[Ƽ jl͡δ▰D8 mΒ;jL7*9 &5~ūƈ6Qw}R+$_(FLQ#lEwA;,kLm#Έ8mU޺:uhI One.Kcfؼҗ iD.vnz2?OƎ0&yMۧQt{7BlVJ z`aS)R g򏱝nUVy3i>c;^q)!oc}I%$Y'wb[Mc35 zLÒ( e˶m۶m۶m>e۶m۶]}{ި'+d/wYwVs").n/tכڲMǏRc%d5wen ƽM!Ћ*H~Y!+X&07@jq <2kUc -3f ֻ4uG1KL"Ls Mj2Ns҇ZLP/sh=5#+dgDы2!.@]~UǤ TQ#0GPcU{70]Jx Eg}iW8WF2wR2t(;@6="p aS+k@3B # L[2iN|׎xG :ep1ո qʼnf-ͷ"~J%j~[)hq 0;BR0@<Gq(VEa3")#Kc 8,ig *ml5c!c \Ux bU9Q!H2gܟ#b;$! HY:pHmQ!fhcN1u|zg}K$6=:e6KCg 001uf*>(,)Q&ˎXB=P )X{ܑQJwZE*SNNkV'ɤ !q"-f2]] j<VN(GrП'6 9l:2F5I|ldPU 7-~JV~g# /\H^=]&S(ICAF0`YW-X靆`Ϫ9lk>3E,)Z#h8JLJ'߰fݿmr 8U}lAӶGm{ !S0'q[9?ȮvXae  K[ A,$ոkpZI{N,I,H&99da}A.SS|9Z*0x%번^|~N9F("B0PA,X{$2ȓ8I !$!8[+(D6+zgЍj%jCzc{ 4~͌o4%՘U Ie-X^'K,Nʬ3/f v3iXS/6;>S6k\)[ -Lf8fƊ8=" wGpM ? rTn,2t-5dt&:|4"I0oBuȋ g xtߦ7%5mU)ܿwFOi59_0P=OW,TD/7ɛ} W dRJLCyv+ri }tE8Ɲ<T3Ҍ¬^5bmٵVpP`͢Z-0CP*vc6̊0l M5 2&T7nS{z`иl  >dR1j.#O;'l''[ lCEo158* 8ݔa筣.E;1d0KRL*}lL[g950N57?AC nc1n~c$#bhդ '?<jo nBr(D=Ӭ/[xIWL7k\D9qwطr7,&1ӑiNw׉$řx{rP$F ?f~L [='~5i󾉬uΪmLD5:F1ͪrIxbaz(0'iM8Sa\)Bɾb(l;GvjB%xh"*8R PpPP3gFNX%I\% +A D:!rJ gj ՗)#j{]ote7HߡCe׷M]ՠ#&=p̏݅̃r}EfESLҷ<$1D0}p1`y=uԦKӅ chM;d2(chs mOC:eփ0Q0ϫ.u_aH tتK+vigᇘDNl*1_;ȟ'MI m*l~&}xF3w,J,&tvMJ0Me^w( VVX[)^eoPX8"?4vYх/7$k`l9noc^quy}\LE +Wmp2/ \UYO# mWx/P`Љƿ]A "4ԽiYWk, ç\HpiR cr-m02̩p e0: ZZk{pCh|;tXۥk~ ͮ 6NLVU ŽrG>k򻾮$]{nQ$/1Ӻwl9j7ɋhf897X OnO'iSČ7j.y@Dc@FStbOԬ?4UʗC,gD||x!7{V/?ÇH"Ր62(eh8^L&W+VG!32؆?hagڐ0ug<0!g%L&FUd{bPx!) yp{<)tEc0 *;ֿL^u/{v^^f*?@|u4)uG+tX/q3pX<(-1thє 3؎r! Fug@PL 8mV"p.W3טeex^.L'и#Q ,\c!ڑ0Bg3*r +̩KEM[;E?x,#2j+|(F(pt,Б(nx:SM&+섦h{G>8VS {YfS"ZU,d{G6 eěd~I+ `asI(xd l%oyK9`mYʁGMt.C7N@ i9+0ɺ㏇J\8iOI zf_3_Qlod  q-bEݷNiN]xs) ]/ u*`-ڧg:/e3dF>B3Jj;;[fI"㧾'~(- 7/⥾H:3JN yPI: 4Y?$7 F!M b^LlN o0dX ~#[q+GʔNpF-Y_C7Y#"2GJ&+xBҐtڜRDN7bb6 xMfyI@%22g6!SC8 2Jxb'T&(!li*r5jj:94];Zmujتg5q\{׏zt^ׯj^)*a;#K`H =H9)nYGIi5_1d4zqvj#ZkK$Q ʑO3j֞ &3!DT٤|ݢ "EFx|[6iҤ~OF}6ffkkB \Mh0,`0 zzgs߳y?gB-HX۶p^Y:$Y޻Rp'|d7q< ne.Kv.6&҉`ke{w}Ip%AfL F,Mr\఺4.mlC F^ LR?jA>L&>_4~d{ }}k,ABֺ⟖hw#Nϳxj뛲NeL.a"ZHgF0ZIu!K] $)]_{jIvL𦠼&ŪLy$5H' "o@▙y(DIq݉O]ōt-aC?=% 0aF$)h*ALz[zQZf[t2vn^׼޲V_#G[hJHU\[>߼9 '¶0@wKIl=E_C|]uX%{XV*]&Hb,b!W?rP%j6? LU[zmDnbE6 LEqw")N IbL ՀuLp򸟗9?gnASf= W0m5 ޠR(pgJ{[GJA~,_LնKy19T{BTu DaRq3xr{n{0f(PZMa<3`T߬)},{Bw8jiP4-chaB F*e5b].jܨwYX}a <OhJG 8L g/M>&#/[] *XTJR1\L,@]_UguL/?1 6bUpmW8یm,ʢrmBL߶fŁEݍ"C A|'㚯vb|(3*C 1i!`] Jt B[MÚbiHM9]sE>%Et.bgW*C){ !X  \zp[. 'H*yVWjf(-F$]mWlߪI?u-hZ^.ܒU.U֕vn|S o)};e۵&Ƕl7}ֻ^&rr\<]Hd8:u "aR4ᄌG$ؤQ!$WU0YM`d̷T4{PZQPf?{ WeAKeh.Ydcfb1OΗ1a|DZ^R/9wf8!O/j5!=HʬG4T=PO5_ VyI.! Pvh/]S: 4DD(M{K6UaQrHX'~ 4Tx1xmsh8+cwNL<~\5J҆f1NuGS蠹0WvhHԍk94Hit]gu5\jT=TRiD/aIuڠMQwjMA@~ʝ5D$pmH ;?XΜgb|Yv_V[QA,hw1y/3Ϧ\(anvI(̥U5QEwS0.Hc_.ţݎz㖭rR (pNCR^eCBKxu "PZ4͇PqKr廁)-G͟(%`zig_ amh}}Gޣm2Þx ih)h&q\_NNI›W^kj#Fr{^ؐ+pP/UnΛ^3iw'i!)eh;莬/8Ś]tT}SX%WT.m5j풅R NP/jLψ, 5Ҋ})>[M iu-{^tP0ׇk0zkv}&>,[1lb@Λ_**7밳;a$< H ՆרT=d6 jȻ#61Pt7IR14GQȋO4/ag YDofԽ〜~Sko2=YOdn`u2r[Cl6*oEӏ]EiEDqIcCDg+V"^DŲd:YtOd*Oz|8.LNl& :V}U#]l/mb8g_sҾ r$Eq2&HPYT(;339JD^=^X2/YRc(c]5U}ZKI3޸y ,V0甐J5d+n} L}tCåuA;3ٖHV`{V{hπ0oء<P.Rf¥ ОBu\b`[Ft ! [9ԐԟVK򭮝s/9S&;']O~5,H3rVp~W$g 0m/)z+Siq ]˚M;s$CosN>(۱hurR4F4=^?EhJ˄YL;(3A OQ69nVm dY E)w&/ʦ)K?s/3$Av1Գ`FH\DX_%곀;_'^2GF Yљ֗1VV࡙"t'w{pIdD6`|>n-28p Mt16QZ03$ C7w"!c/eb/E5I$RKY̷3$|SRL"I(Lqdu5*莀^ф,0ň/,9U';)=Y"8p%0|$> JlyvP~ϱ>֎3r)D85W׼f^nU)b̏Px9JPp)U-BȪi}(vN`iSVH;DkL5qD!$e2F[Ɠ'^d4dC Բj+@Տ?]nOYvDz~MV%'x5%;<_-YcY ;w2^dOȡ3)hagf?I\0Y'R*9n_[K(wqS˰'fs6eI[ovz=7k(-9G'v+<]OہW}8sEnvM:sO١t$?‘u/[ =R;v UΚ W̠ `8 յLuUTlewcIoc| s[}7,*Πv:U`l8%Yϕ.dP}BxT',3˜ǧlX{j"}u=t;PNG51he_@0 `l$90[99;yx9Ѳ Ġu]85BLEֲ.#On^ub]\&o޻z.E{K*JjWVM$,GaH{IxOĽOGW-4tG8=JC(|^4,? 4i1ZIcbC$9Fg=c6iq!W 3g ( n%Wj A(2Ss!6Ba-Op\=Sg>tDve%8HgTM;|rg$b/DP$>F2zg+ ԂP0ĶR'fJK߈`@,Fv"3Nx}!W3I5Ξ,p4E=s,ָGO'@-8w" (P&O>04Xe^^.L%+dLLo+:srߢ2씯U ~GM$9:6' S~@Gma{g.8w o`IEMQDp~IďbR4QiƙS"&T g#(څeEa`mCJQPE Wg\ SgvU`?v,?Gbˆn=_i1Pb8#KNb,%'@eh*ew奁vQu`Y3Ljdye%ukT̚ tfs1myBGL _+i`\xedl1 Zgie8HLS>Y T- ™sYQ]bq\]xћ5կyO<,.0 / {&.ݚUJd'}(j̭CY(ӍWYqdFƏLMgjdȶmD#4Sȩ݋2( W%0z9LCiGڌ+J2*%_Xeُgs~AuoIGmJn斴PvM1=W:erRW=׀ +O>UtJ\YU-b}X3$L-ItpQ3G6Tj_nհ ?% x iX~wqzcJ,~ SGS@PT^riITI݀e!h !T Yv9ߓC)\vk@ClJh[WqoVDz 消{Oˢh8#ȓD${qK}r+f%61͓fYo:FLdw~(b U|!4S/g(GƨNHnZ` :DwcJLP|ɦ6 =(Գ,hoMAѥ)5j atrJ{Pz6>N$d5nO`C_Sߛ=|ln8 xW-/{ŀF-G.@zN08J ًpAnJ^z="Vc &X}qUk(`#*M9J&( 7n %s+PȾj1O[֟ 2"b*XVPY14Wv49z c ױuauZE;˧;xd$mxPa ;BHIT:yאָ(o?2< WĆkb [ +mqfBa8I" R~!|^j.c1.Kfm%85.ݭ$ɵLFCn 2dG#ҭ[W%Xs8&=>qn=KMDD>S;g'=ӷ2Q?\pHs]@\_*`Cf \Xbk >G7B6gke2p6ƾBԹ!{1Z#mhshIz6/*i涽; ;ҽG}5Ze[<KsN:?!zzA àLBɿ_EH ioV=;-H"t. -#}mb6ƏxNJo\#>ݛd;5a nvW:A)ȼ}-F>4)Jb2K4ntx.Yiε, " -HΥě]tot]xȗWmIÔ3T6I(gxjS.%.ii^dǃv e p^|>Tn$4M_GY'J$3b>t5GqШ3,V< >zX43` ~"sa4 cWB?0H=zaxM&P%_Q+h0A,,gؠ,7M;1(!/*w/:NJ۱/Uol4(@ Y(١/~"g+MX\:*y&^p'ɠc'e܃?9HfRBg:A(Ff߳f:;6Nj㵙r~qeV#NpuE}w@t,}KEu)6:],̠`ui9h49(5hO> b%P9%Y0q k}73Q$ih'L02YPy7{(`p,k%V{zJʝ!խ/Yt8t̰T/D%Ek6}&îbE>U2^e%d]8Oik=)e7{^T6uNjQFmMGQsZsۊYC nXUъB &E-U6;Afm!uxW{ ⨡ZU]m+ŅhFjmDBaْR%l91; Tn?\'IU= z=GL1(iSp<-q.IryY:zz{[:z::9:&~٪ۀX5gW<&ȼcTiɆC>aÕʀrGR487c$[Ēї P )swucߚzD!߾^6VGD[,6S<,~k_:}saUgU:\Ic Uz0XKi)+ <Hjhmg#I4AY&)b-AW ci5Jn=CދY ʃlU<+ L+Zq'h݃;yqwww;Bpw$_2̝3g^k?OWWu=ݵ" *^%qhMJ#%*rn0y)~2n_A@F[* &~}tyQ|g3gKWj$4}qנoиZvWͰcIΩ]Wi+ x2KN&jxBecj;A9=#- 1 ѭC{"RwI02]cM q^~9o bdd"cKc?vm<1hxIg9w;GVwiE<i곻3S[~} +'&8]1IŊ!qU2d>/W}{ԋ;$ƓŊv\,UF{j{'79 oEaKWQ9bdx$]ȆZn 520_~x 3 x[y uNJ>Nʧ^?:rcl\ YM@b~BjLRb6$OF&lꋖ~Ap+>._N*jG|m V#vDFj֕*d/[y{+ &Lw!3$!󥀪ÿTPL0sXLA kQ_%[͔,4B_3wW )|ic:;a?axL܊gM/\K.R6\ϟ'QKj7x9x91$H:3@nc~Yx '376^+ p h Dbf`햀M6y^a JqBRI0>"WKTȪ.UF r]0{>[0)w'DJ=!gב)-X[D^9'ƅa>^)՘Z"0˗]1sQsDxzMK8Ig>:|=ʁA A~T쥀~6Α`Y^.:Ǟ+4B *6u6}8ygܗpٻ6 ftj$ZTDAMK{?!zAL7/vx|f5LSyĹjI! Md|1;;ætIO!#R8;ƪ>w`)kQdrh&0Bcsٿ#y!V7|V-єGXV{žb8BMտ 5n_zEM d:VO0}M<<dz9fF5"g=>TQR ?V Q!ui╋cB;WiMvq"p!A8MiRE[%~E>)DMjP9ɱ?D,2;/UP=|' sL%JPǏSq7_4#_Eθ2!߬]| +'g> {J9BsEu(oxEprl0"zDWȮjhŒ 8n).{ MW~ V`+ݟULp&`ay>jF(;aW5|-.AOJr&W1NV=ug`{{E@=w|u=F8/V˭F/~nea/,XuAnΊ +@d!d&J5…鼭m.J\Q"S,)AK6AA}b}glTOX6= VeÃ;rT>@lYj_t Yp3}\^\-U[s{]Jaoٙ\ !P:KnTrJb5;0Z8n'&N{=o 8i+A5W 1|&,Jh8&+yE.D@{8Z-ʂQ͖.gXfThOlx$#dq "\)_3ĒMiNe-;Ox*ɶٲE5Bb#76aj4[}-?]ԑ*HC(N hQ@ ÀiN@SG:7kJ ۵wݴw"C'UU9Z?F+CcnNa$OI{^؎![}=,o&-%9ty}eux6D ۵vS9:H؝ u #Aٿ#Nh< 0e!q`t[M{=}f<'*2]<i^ra>q\]=ԥf‰I@@:~eF@t " )饃_ ΘG׵ gnJT#ݴ4EЪQ΀8`-s@a%vvnFjzyV U[P\ We۴۷ZתCnlf"R@!p_϶Yy-UNc򵛀% aVS öB|EHp;VV[CK~ YqP%N8 Ue Q@C,)QЭ:P'}8o*^H5Y8Q17<8cP^{0x; c~8RZg nW,F"1@{tOR@3Mf}v\~_!kIƦS$ksbUWn Nf^@) 4m0: )vstRmVB(^WRcb⾽yJbnLDGNwXsmtPj3) #ZYd܄RJʏe`D>.FL7PZ5%`1 0a#s 0)*Qx[δ1 zBM-pc< Tx,3ӳPnY2cԫ<ڥ߻D}^ i"զ8)5m F[ྑ ZdÓl}Z 0/Bw|mS3…taԳ_ZO4~tG6>|էޮ W]B6"w5 -Ե 0#H&DVSBt4@qr" 5 oɛcjn&1zrۙcѾTYB@B`F [(mWzȼU/[$R ) . 1'9t^ep7DwBTNUםe.ģvsVM+ Wb !ߑ7 1;SPw0 4uXC$BNbA+QQ}Q¬ IU]!sB+< rĢVcRXS}!5eE q'ysG@P_aJ٪ja͏y D: kDf|-q%%g0!X < U TbU.i2jppu6m?^۷ǕCW*"q: %k=iTg&.q[*bu3[pqq}v‡,;VoKhM.B;?龍VZ??ǨMeOD7$ʼI1Emn6yĸ"L*2Vț鯠ع~$}* K*K2I{&\v9&HlQฝn/@T/op󢚛 >Xc݊p_د/5θhª nq"JwBNS %7Zޞb0;%Em'4%jHɭ[4n-tPd04 YP*u;]6î>^Dz<&[9&ңde:/4 xѪ 4?Ş/`y}xoHalc8D(8\NH)!QOO]!Jho/ŽY-<#Zi>)R@WZ/ʥN%z1Pu=Zghcv%jN:rg3x$j4 ! ۨV^ZS&NrCTCOY؇0oC0Q7$I( DWO ׷•ftL6mDDfDp-f&:ui埾A|oos 0Ul=WYڻDd^*>MhAsc"Ccx%5QYN|f@n;^؂A>'OsTy"T,0;;_ں 嗜G#6 )' =, PR?WE?, \dE,h&{V68tj-_h)@1NV2 a22dg7BT)d%Z̩Ĭ CL)m[)@w|"^{>K^k#F0R0wa>?Q'8Vn8fe*;`KBfgމMd*SSM]L%Ia$v"L"k:wo})Nk | -VƁh 'w}0dS(zyv;@VhZ?YHSalz{4Lz @iG}cAwAcK4צryw%FE't}~la#sJ:;1I^Q;+ǖ3g~DS(_cRPhyag,>t9$ _r#~PB>bM}mg'Ȣk-OwqToSC ϧPgăit{M>C=G*@># b=tzQiGb^=5N;dlh<G>!wx@o$OkeykD|04$AKUDIąyᐤt+>I4kTlإ!_# R~p8?0'?sQP*BG@`ͣPkC9^ ۬bk; qHrq7%7pn$ o JuwdU7L#CJ\91@OşxYx'ą)56Xkc}<>6x ^=Щ8%ӟԐ3M[ufA T,ⵯ2 U{f\f{{7Wb22,6Zִ&#lat,x*N b0hhČ y4H UɇFuQ:CP;< ԈZvВ."-%h߾T(MtgC," R y:$h2ժMp~܋Ǣm9fm^ E-<5eB &0{܌>;r2h؟14yN_R7 :C^mQ߃]YwO9$:aY0H}]c>S18I(ĵ6*8 =y~)\AG=!;I"ͪ */YuBEϜYƻ)vfM'ta3]]1ZSJu@CBZ$MI Sh޺uG]= j~c%!?V2otu)C^-D9٪pZpu"K(Xz8hQy.@8eRRF ܙ,NcCy N!qʳNAtYs'|er[PcK g(+ C]/o?wrr[!S*@ 2A"QE>XYU@-`$21|+ /$Ju'Rwn5)މ[Sb,fAƆþO\e8Aٜ>Q{8 ;7XyIS_.e* dzW[D]TGؽ*wȱ%fב"#r,b9b yPۃ-:M|b20Kaj1k.ܢ,FB:Vm(AnĄϸ|a>ckJ*`Q]i882ty`{%pcL@,|p٩&w-_5h%t-i֦NI 4} i;JRT%STCrZ;nUYB]7]1ĥ9u u>K4l'f(YAKilj;B[)Z*Ki*fk&O+KX5?3\iRl'K?sT5FryڇBe) fm%ȈG&ܺi+2l mᬗ*rSx?i+TrIt/cim*<]crڱ}4W fi rQK2ǏJDΗ5oHֵ y"g|k-s6Nt`ŕ]2eFm%BB?;Ɇnu ~SCF8HZ1a@Q( }A.$Z4ܻ "F= Une*[A`U'w^turk)6Q-6IkRUuFM$k&Óܖ8gV) Հᴢ/)6Вf]<Hv643uts01O d@ 6Xtib рNG]ITS,,?Ι۩cވi83/Va1"|DJRµDCweXCՆS?%8ِO~#F=a/0-dy9 kt@U[gؤD/ +3UN9F;l{Р}60&frq= xOo\'p+yj$ \FDkU_k| G~۟Gӄmm,͝ ,mm- M, =/vvv֦6>g3466|u7vvp0q wwq5w0ut42t Gm L_sUyhghc ? J&+"H+)'F?3!UI6!a' 3gM}]"gU,Ru?.0w时cY=c+A:ʗW>>ńc_?3TXJ̸E/ &hҋЦc-6VU)M[7g}$?V$ÛqbRC>ZMz;0 ˫Xl }< uMGbǴ}ggxqz4³tinWp"ECyHB%o?47d846xɏw|\7CgF(a@<-v@#P?s4ſ ~?pVAwϸ?ʅ[gU(Ϩ?%Pv974. 7wJGi}9Oo[AD$ OHg@ͭ mm\lmmml m蝌] ' +}7[g':C}C3cX.ipE):5bXRDYFL=}j0(eTq4ƹXXx~Cmc\X&=2v8B00QL=z ǦJ`!0+]ױܺ@U)j0)5eeqam](w?&&pԾ!ұ|MM3rabk'٦:!W;w&$7b 7T Wĺ1/:y)]1.GέA|aDӔ%J7iJ% OQRW+d6:ZSQ+ Cl( R8ahk9ZVgkqñTOå{Ғƅ.S^JSPA })g}nS' +W; ((M ɔ@Ex}Գ NT+ sG^>tޮ_|^)Q1g\d*޺p=2:;WMNUS(˜HmӺK_1c^휬Ah7@R݊3v$kxyzvlO->-U#oPԡ8qsxueQCܠeߴb"}gwywR%)޼C{B""OiB`α$SbooFӭ傆W9L z- T<ß+0 ZK6Dϗ4H3~em,bSeV&CQSވwJWLB-wF)ݫ > Cn})ÄGr@爿cqޮi*naۈ~v_ JxZ"YegU *upFDy(TV MK2slu=5cwE(Pttձ{xNh7^!s H(B/E{QFw4eKN 5.(VvQZKaţk4lۻ@v$ɚfxv-h.мڢJNKmR䵅ZxíK!cU˲Ic/W _R_\ej%BdkL79t&}ynAIcѸWHoJEf <q?Af!U*ܴf˧Bj5ϓ|^vH9S;B/8M_0яS \o^dͽ9x}̬2J^ii o,٧ U#\PΩgrՁ}˾[=x`uHߥFE z lÙA¾0ʓ0b=|636Ot{Og!c< P#5j'dI(ED«{p={*xe,ALq'IɘCAdv/9˗{>61$rǮLԽxo8lؽT5X)7^p M[)Ϻma;R_Ӝ5N8N*[{C k.ۙք+ڔT0X$UF^X οKq(Db^Vh-^kA[S̴m1S#$s~ٿ=?ƀ78q|ԁ3?ajIcM2?гp>XVn3 yEEsYvqѻOY#>θbPzt؄V <@6 G<DZ[Oz\ oq/_IF&˼VG_17}Z]_ZqQ_Wꑎ2yc|_rt(ƶy5$c'!a8G"w۬SV4g5fy˩9FIz `"*s:)AOC_' D kȩXo<Sg3݆8/9P{Ё9h h!pI]&ANJ6 wLuJX^QT5>Mf7T OI?n٦H E(26'RXilqIvFDI[7nF⯎&%EX'w8J$bVDkK5:g9?(M+bKv'?Yܴ-3mV(A*dXP%>]0cf0K1O`:$cZ?T$Gt{m#  !Y9z$>_]ǜF o]לa|c nQ6k8hCη NTI_NLj\gt,kxP*? t^%s@Rh7P)M;+5/%(exb`MvW:Hإeojn4*9q@O Bo1Y([WqJ W0.J ss8%.32|>I2$X S% 7%`)'ccX1c*:9g&hɢq_ս ߕ8hUmTYJ.l̽;-خ8*62]ĬsC% spZܳd19즻߁S%Y<"&^osEF1#bdTn - )2[抟#gJVTA5qiZϢx˵`RI=w1HSuɣdoh_xat0|659J~V@_ A~&!5O[$=4fRDh :gL Ϣ0F yEcJS\ZᝡtVvg.Iq{^!R?PguY=ޓ qj΋_#m6BB*4wұx~y$gy/D!dR?'|=(}A:MژE:n2}MRߌhƨ%,7T[%MD`gkU'~=89}@<Ŏf6ZiBqpt*ͿcM.L9HD7mGYi[ i\WٽL 4#әe^_SSr`2P;0&J]G t? Gr m[CB`] -^SJ^ =,5J݄L[߃rBΐ8LьE n4-MAja/ԗ+pNe1h!5 ȧmܠ m# ) ") )'ņ"~dgr ܹo '0^?r@UWz*EyoAY'I*\'gʍu zH3-$Ncf$OɓռI WSύֲȓpQȞe T34={HNHb2bDгsՌ)dXާ I&dSB.Z-崁O?Թ Bt/q_{R&g;eEtB}5U]kʒUfF3QnqHl0ȆXi&#8>Z+mq-i$-i]1;:ڈ xf=RGzɅr&ڙȹ&%Z&rjmjS\癖dz|via6\p1jxe uWӎ D*0ەQmL2͓3a/gF\hyǟQZ}/Q{be^h- 1s3SJud&AI0\}]uXqun=v =xT玝V\&D!ON}"32)qFgEQ `Lf4zg} ,@"7B"MJ ) /sݖ/&E}e/`{!o(+f}gNFy\S| ҵl2:)C-挝lɁv ƩEB-["_32e{Oޭ莚W#ۏ}C8j*c} e4YW>Z!yTȢ5V1UMP@mHy2p汳=?zʸg3%\l&L<{ *uN=}2?|G ]O2S =qgbhQ KcCǚB 158Fפd}D+OQFB5VkЕntL\Wg|0;tuyבd\0ߏPҖܔG|R,[&h#/PF@)x.]+} }vȧ|vqCs? ̙{R2'B !o1|5Oy%᣸IPJm\" 꾤+b]hdPb4 P";P"՟j |4c5@)H htPa%(w_1+GLnOo1J (#Xx4>1*C{RZ%.Hj6-:XI:i!--;(Z:or~--yǻͫAnw(I7^xN;4 @Dq붭X +sÇ{FAJ>fSZ,_P, }wӟu|/p\2WtkpcY;>cc~BJ~/|Uqj3g33%h}i,RCm}/] lAihW/':C/_MkT$w%li1 47|杋LoUWEz AK[L!_>\t7%Ե}MlF]?WL,X444y21G75Ӻ|CJ_kSn`v(\޹` spo|x|^{+́nwImpsMPZYaL1(>$Pm0)d=ˆzK4Py<1(?')]eɠMP8_@R ~2"3p0RN& ?'D "3JF=۬`{ uj2Xɕ eƞI +S$+\7*6{O{U(aY]GЩ7]$ڪx|JG8rpT+#D9N,l?6x?JG+d?p,Qh@qe/oz _h{D 3(Gʪ! +3$%'1sIrߐp:eʓuj+dz9.m?0QRt~\{z$eȆI/[oYһC7H 4:!$._k~ ^+6߀[< < ;~pͭ3QE#fAvq;4+;N"Á.#N?wVDNix5wHE׺n1ojMl^.4QA@iqlBi ʛ_zRl{oڶ.9%?.Ch5GQsݤ N5W>AOLςtR[,0[$ӂ 3I%ƴ)4 I0x.!^;O; g% U$](s!Aph)8BHO ?n 8Ő?ӸNNm޶O6 P95{u$7@lt0,JM k,bLlUU1y@+[il̙960ZհtE=@|u ۠>࿴q0C%\y/~J~lACi$atA(¯xҌZ2LKl~ h|D&Oߋ$H(gxztJq^MgIK6%Vd&,/9C\ZP:֨Ӟv[br@zLg/}~76?hRu-7kc6"vT{į+'٩NPL1W]m2!/pteP ՜@ece׻ Cad8G 2?FjRQ) ܲf "n)Yg6<%?C6s4ëM:)H~'1z|Ieа%ݏ*{6w:LIęsn<ƶ΀{s o]@^x:RhJ5zEbfd c,%!TAbCD?p6MiWS㬪duqQd(2X,|[0ܜ槽:km𰷺dvǭ8rtIL/F^S"G{ `e߻03Xݱ:0Ych`V{J9@y WWӏdPc/, #C"oj" ̸!6#5RurgS} lM}qڳ(R{3׷wwC])sh#-ۋUo;@:qmڧxoēomd81ӏ'}xwj'o!1c3,;s92 ȘOCd48= xQ Ƒ#<_($80(WׯV*RX M80zS3FT!;lZ}?)9uN+3m%Q (S',9Ήg. 8UHG֓rp[k&[9j=snP0 J4?ة _z#rIpR@/ltF!.9`y[m8>h\xM2G7q"hs:3zڅ,N /b*SVC>å77*ozLT++9NsP00 o{L:ƇO;\2ԶZd3~Sa}G"p6pW;{{>LIS3`6L3=n3Ӥ5jJ9q'hmXCh>6KE@oiXl O?%or!ݫUKSe%ߴ EBurk.b)xv'tҌj{OOVQnp*Ufd^PEg%. Rd'(r1&LiQ(Pt Rb%x 'řd@Bp~)d kYXaGBPX#ޙ"FאЁRǴ^ _ףnTa-u4 \Ug@IeXIQgDZ>5YNSac"y +6a0q<܉[՟9Oyjs0P\h̟G_f,+aj_R<ɕN[ܧe`ԌhӋ4F? Nxg>$8AO~s>X`.I{βsghc;uU.6UWV"?S~ѹ7 Nȼ3nњAjؒXFE݆_!ae>}M^~qRŲ>j3KnmM)t9 {y;*i.Bzph=* %mf'Yee|͹IR=`6Q0aF֎{tV0pwYSoFڧ giMtYE4n;' I'!.d慺rlC'Z ŝaUGJD֢EبH \U }OdPİYB=wiM4uSIjQRchA^bڮU`SC\PP"Qs&,U+A#;UݳKbBDomnuEE0ߨ1 8E}<^:ի1~$R -.U,}J8 :.L:w06LЧt+A7:%quyzb5!o qk"bwy5x6c7Wk6)D/]:p̻".ǡkA#f*yܲ>ҷ7 `jd Z5DAA!7:uXG|AdX-}I[~PpfNDxJUf_#cDapEӅu/nh2}顳E|mY[ECe @/@*7 v|5s=I[);dd< x`UFd:# WV >-8{Ip9ͥm/wmg 2׀ƠvATD2M ݚ⿷=}'c=̂jHk&ZE qj@ ̤+-o;B=3 ʠ`CN3!QҨ<# D[5c@y{: #U;#! : <2lYS>_+H8R_a=d|D߰uMI#,tܔuX>VtEfS>\LB\1ȿ*8MUL]nG {G 9PzAQ]>n ޽QZC 8{ZIE\&_A{ sjv;'{Cxl4^a;;}~M-9GԶwς.Ĺ:qJ{ LGf8~ZrQg!mPʭ:JJ0AuBۢuK+ۍ~"kH]!fZo!ЍA}'“i/\q:xS;YSñMhx݁Jae+k`:*j?mK`kZUey:c-3M iJqF;FVw` m!کx2nl{+'#w?wĂOOZiYz"Wv>Ht[{>9^3ć;_nsNuѢ$qB,9I114>^N8Xe׽o^GrDmC˦y:rU]2_0DogBoG ,*u<]sOs"- q h-.F*LE^tqu)qu$U7[:}_  qe8d--cjm DQӆD}>7#nSd%4."3F ߔU8rt"DJ[L>rF"|^L DMF9d.1B2J26q4W~7x  vL2W9RAgnptX֋<:,$sO妭Ec/G5;R] ~_DW?zu QB= B* <Պ!ߖMߺVRtZ˖|o<¸Uaˉ*jxa+e=,Y`iF` H G]Ҏp ' h5xa /Frhbͤq3|~}.1@~F]ov <1_;Q"9ygѫI䏘E*Δ"ƬVww^sH;*2Y\Շ>g$1G/ )}V6.Ua5zPUGOj zA=1gA7{BeEWz/{ QmCgPpAJ3Vd*fvX޹xzA'^| k[>qCPUH -UC9Adpr];*(ڻq=Pm_Hf5K?i;Ir;j-< GnT` վj7iEگ@Ϊ :wYT&gT"@jCh(l"FFV3FγNB^ŤfzaYͯ-50͵vbu᪚&{  1 Uo Pyz$ILm:>G>fz5o=ozUQ:L:X\Pȡ>џUh[5־=[OcAmFPN& b4%.7»l.ukC-u2.~O24q+H?Sy!c5V~MW~.7TvX~r্#-tjc߯zN?L!+HT{XH& {{[ Ŷ[ pT^c-<$rc>rKly{\_ҋ84a'Y"+yɕK5Ўȟfȯ[lAz[tvsk+S{`/7z,)Й=*>Ϊm7DʘـdjSw^ -ߌ4QЍ&D3oMV<4A^E(c?1*-CmyPI#Hv6R-n˾S>V:nsE(7Q!_;о M^%Liqyьyf;=qrkdH _1C _pFwq5}e2F:?[;*L;^IjrAѸ%TnGc8OݣU|6 #{Xo6oI*]-9YPE <{j'w$d42bj!76r `)jA=1q گpg{b(q4jޤU`uqWV07 ̫_K~z]wRmtvM4Bלc1qH q7b4*/I˥5O3qq7?Qk݃ʬo5QA:,,X`\樏"n}fZ쑚"ۓ惇уĉe~_94NbwG4bf N g뿀6dj 4؇cYYc7K6.[+ Q0efdPcU'B ( ce.@ѿk̂P 06]өLrBtv–.&xPPb;Cp9XTܶ%)h=!BfǕn@ %]y(bfp__/N̊@j(!tC}x ./%w* FE:NamٜiBv@xo/hJ>]6j2LZ^/̒;mMOn7c ~%k8_rj']k .>~u).^4j ]5iľ. #1tle}l9c'>U Y]Y'"yWX zn\NL82o*-=`u?os,~'M aW΄ {)A,k`8xCǺ^mƦ\"D26#Xz)rۋžۡjtKT'~t_- MBN3|tE S;0𢀝yhvx'8aA{,ȳm8^P?9=\k+8r8tepCvʢ/ ^tp=wV'GvLҶAnF`@tCkX@U[ Qߛ--{ h@bZ[.u !@\GРigI*F Hb3x!X1D[*a;֫ʀ1/N@=dq+ (~`upBrnqWe!<3nnM]<__Ͻqp9iXBYZeo'P2: }ym,̢>d`.w&۝k}F:LM6,3?` ?C[#3m'1S-i;%"up\+@rb);cN4Vf'&0fawp);N4x ć㻂X[a &jݗ2m| )K{LfJK 邱:&GlVdu[kD 7 pѠ2M?jx$)~1N&#nchI)R@)woK6$Xˬ_Q/(7.)zwz/oqN$ұp l2!"to, ^`nw}1ty^jy:+EӼRڟH1ggzݭ½m+:ꗬe$}GVA Ax%fW%f[Ug_kP_BJ /7x`"o0_V??rV!$X0>o*пKKXXvΕ0N/`&=.&’A]W{@9}KM=a"9!',ZQX%1aZ|Z 1ĮπT4bfDBZ% @ʫqWIaD+ eZf1rIDk+ѱ_/<$"K Wck<"[/|`vQ )EZ{31m`gYcWH5zaӱ]:PPlAY% 6 "=NuvXHr^7pvW.m7t{eF^ 9\~~YvBKzo|QSB^DNN4+(xuExrڇz,Ori  4dEНDiEf5Z2K\/LgT= Y{2'G FUf%r(+Ep*5~^ &-bGfE`sDNו:6LY#TMZ.dbTC.2Ҿ(c)b6NBNtdY.GJpIⲵu߀dj6n31j^|rں.zAW  LNJ7INqX$&DE31_PV=eagLIˡ!=aoT.,F_z F(ƐR*l҄:iĠ b/R |UA(XK KĠB nr(/T[}L\:f#81yK4#(d9x"'8]U_&&[hM'$Zd)HᥜixV,:|,AbD@:KX L^ v⭸Rȅe\؝XSbSɵW]A ړo]aTά~5Ez=]*^y٤\s^LSft2s`V8F`6K ד,aFJӜ(MDQlꮟxɵ1)_p`W d6(sjz~hC;YRR}[yUZu^ׁjTpej`x#4߲W࿢^ۖStEOfdIoXp1H%wŽ`/X'ꠦg9^ щx\W(N͌ Sqax1'ApI)DCP=)two 'hjҭ=) iXB8IuQnd>bPf׺8"5ͬ~aJ,3roZJe1,᛾֡=-_^6d1yз<OFF/j2pc91o;#ѳ]ӻXti8%׈Ab GgsBcG_ |Sקh1_$N>?]- c`cǂg@U.m}}9R6C]ecXSOp`Z." WrlK$2yON;ܣ5vRY=ÿ j#,^GHIX0JCZ&Qowq>9kT}N_l)~}>&Ikv !ͼI:CҚDpC(_j_>u?}wׄ+C/ȎBIߝ_n rO _X,g{ng@y=hoD7j{\'H>Ȟqӝ !`;eKAvȭBl{X,s];b! f+Zi2qq:BcC7  ɤoB1_k#vɻV~\[$V|Nݒ>@Ve0!d(m!j0S~aD8[q͋rɩͣmJLz1;tTܰD213TwڷG᭲'y,*v(m8"ېk6L! 5M%|cA.:83#=˄s|JH^JdE3kdk Iο/ɑt /t:hrWX^$2-oTEɐk9'X+{+Be-9۔_ '*ӾauA-~/DA 1-[B'>jpZ eAvj+SvZEh)Tѥnz`he;KtYf`x%/\~)^Ee=|;W'{pXhH $}*E2:(Tֿ`;"0V {%Ƕ팍˟޹Zv~k!c@ [XU y(0ph\#ߗ:$M^_f=Lx}ǿ0рEpeb+LekzDH_ȋC8.S-CxRyd5N2g%\ʢ$@>tIy.~sa+dmZ{8I GcVlӸ!!O%1Y]#ox8پvhަg9$1P8e[P`'绖ۦ\#\%/>xo~nl%hȒ#Y(Ԩ~fbOzg&oBY뺁+#ƛy!r"l"A#J2 :nQ(v[c{u4j$[9\)S&Gihz٪vyY;nM\r;V}+$"1fPJ1f,ؒ :dV h.eUIϤQA#(C4ޱ#_|)v昈, 趉*P]^ó6kz"I d((|ߵ8i$ElF2FHgG S.ˑ-Y,Ap |q:;=4eRH(kkUiO-nb/x+|0'۷;N'8SO+g-҇mFP.yY63@QU9 d1[` jPe`Q2Ua{ nQ0Vi ە'%XdX3= `"oaﯷ{']>B5`^ν6)[l#9"#%λЧH ?:3e*\lc򣶵=lj H{'[K;ezjZMJ451=C] ci`H=.| W;j;X8kN tW樺Whe' ]ٔێR-[rdD&I`tO ֆ\J&;(eWJtp\_pfʷXAAkQ9YRjQ≴kiWVF^28Y{C.]J&;/hTA{w|F1uE,phu 'xխY}\h1cP/پaf@s yr pɢ5;ʺ#X *L᳖#΂Mh  ōO*gecj:J40 [IRҖ~:#Y3DFH2oKͦ.cq]3Q|Mw"rƝE$tBfTVsXCoh3q(]k/Y",lF\l'g^ deI-6'9XhIZZ+MLp' @}4>%ݖp0  w0pyDb*߁~Tylr#kg]<?-a*&ҵlI3W͐ bJqt¢x4ki$\&a.T|kwu q:By!xE`AP^f-z`Ɗ,[SѢ S@R8|_XN׻pl̡E}%X#{<p=+OKbŻ hcIqcEu25NoʈaFrAjw w Pϻ;ZmȎ ʾ{S"`ޞIG[Q~\8 F+ 'hݟlq]C0I2[D`=JxRݷd]\z̳^nuv}gE37cE 5QN! ϱ氰e~Lۺ`7ĊZ,P+ nEaƩާ+}8+Ԗ ͢S_%+$_cͰn] ywۃ܌],sV5E0Y1 wJX*st;-ނY3-ށ!$ /.Î_$հs[fTJ>G~} OzUIZJ vqkH?iFfxw0u[ p:'|R04_$L,U\\}bW)C)t5pG_#:hIb 0 $ǂF+ Auw OqןwP7FX^cŻ)yX tD[U,>8doMĎ7s>] UmmU?T5`jAf)5 u^m(ztWt;[|'YGmsLF^9^0)8&;i%R%pZBu;Ǟǘuz>d yvh=46,b]~SJ5nX]{l ό"iG X;蚥'SJ$Z:qlxYF /wŻLEL%? XEn+~_H7_{,g|Q6@˔NZ#uY]5Yr!TU"QKͤbhR 1%k$p^Z%XQ櫛Dq`E̍KXhs~hfK5ׂͯTϓJ^hsCdʲj@ ϑpA4 c ;IPRI7:;R$lxL^p 1<({%2n.}#3} b{~Vn9/#9DoO~$Lͷ2@ -[=O=B؎_o +ҡ15llj1ҥP(w ݝMrqCULUE|JQ9JO QK?d%B wC9q&.i=HPcmMLM0C0tũE0Fc"Q6;7&{wٮi#B恅9ˁ'@ Ň-QA0jҩe1myx~[1\m82.:OL Hik61E/GL LِY^׀T(c{wZ%E͹( 7+-tXoMa|7`}V,ѫUY hox-i*a]5 IDokvQ<qFbgȻ|mn07-dO: =:P(ƈ 0zr ܱe5f7(]!ÙRkIDzPD rv,E Chm?AM+ow|l~ߠ`[evh4xb">w Wx+=]BxS*--{ƍ,Q`k7nTy$ GMeWmGnĎ"! S$H~zX= (Ry$ɓ tڭw|~b߳ۋR;_t/V!yc'd$dI +Pݕ\~~ =2O&!OHþ*9kmѡm cOF`@#p| 8  pX[E^Jۡqx>ي;Fދۑxlƙg,ߜ図|y7D7.pȗ+8Tן|"/5p?>8O&L5pp%l9K8g ,|wȂ?'JC7M>g x3ddTT G:Dp2oӢsÎ}!)_Qo $mzR@,;Gl/[./ ,{AI 3JG <[>bׇ$l>\,Ԓʜmr+Qp yȣq1/@>UZ:@sA063's|*coʏ3q֛ LexAta0~7  ͢Xf,<ƛ8@:#yY`遁ɟB]PRTqCc[3ÉA'/eK-zb=-fev}/bƿ̐iwW{]==t#͈VoOctcG]=VWӍӧ6Js w8I|AÍ[4Y8 0ܩq6"0|0Ht>rp>Qd'14s;\Lqm!l=qh1qb(~XnB#BрmUw|RQ*S$;N ܸn_?306jޓI9c9'Qt86K^.4q]U`689̱̱/;%Thcϟc{tlti/6+swt0}Lԓg"6G't~{Yx~/W,BFVm|[@.%ҾY;k5UߋQb{pk,h~cc.Y=h[K*+ ,oXW>`}&n;En|"Įn6{zL)2|,/_'m'jN>>I cdol&nK\3ڏ a8IWܿ |Z-/lJl.)4j54ц=3צK}/9|юwzͷ(35uHTTqX]kܲujT}^pY/!o帑o99n?>-HM 6 B?%DzWЋW0nad]ywM7JݓlGgЅGܾtl7LlWb-Mlϊ"0dO(n:ĝBRy|cHgDk7MgN8V|ȱv;ȰvtPDWUqtfHgܳt2 uzzqz32o)HniLMis;.H8݂ y̮ <6 eXl_&N\?ѥ9ӒxKl:Wx}ۛm]߽X#-%(`nM6KaZJM#_6`(_5R$#UVRی<]E7j̀L=9i V`pͬKj<* ʖYeKZXn$bڦձԹ^}Upr)0*)_+;Վչ[]Æ`|iwyfGK*s 0ȴo"ް̲2vKuJ+N+ʲɽ# ڼ#,ۈ񱎼=R/׵0^2 Y<0H5!? v41W6Ezfjzh|k^/[K'hd 44P*o/VG(pў0}'z)s#rlNy/zѫ<[s)z 8pc a'Ϙ>q[&ۿ_#= S '%ܠK$E-ko:?P:A|fÂd[k7~p>0Bq 9= pGyz0Tgx0й+Re8=M\{6>8}-hn}|/]zFI̢`OE!iӴgz:ν5Ʌ&>hm wtaO#)G Oy/Mp:l{FsLٖ営Tt8斶N|Kl{vI~4꩏s{y4_\uTy|>׎s( ȱp)b N#n'N ; xW<Ѓ`OA7`2Lc8VT|:$q1: I _:V6ϊ̶ Hv$a a&FO ( yl@Y/VmjI!)o[嶂3> 0c ;\D{9ı F{NU `1cPHk!b^+,mJyy+/tוidX J.pw NNOKv`ҏup3ω;$vpv?+1=!AIVpƘJR#Ƿ3'4l?GsRSf_1qY)f# 3T®~ݩ,{ŸvWp_\ W>_gž^Ds^RPQJtw M^57<KNC+>m= >2vX zoG(Ls/ʷ%GGx/A=xz>g"UiBtQANt8Hn@+DŽ> w۫͢&EۉA'IЛ:d3-g`TwY{EB1FH'#h?u.ohܞ1fP-elQ,T8xtz>g0~C4Y] Tޗj3ۍ%r0}҉;}>0Y*#kM[O0prNNo$pjٔta TCZѲj2#2nL{ɾԒQ[e=;ĉ?G{s ̬v8H,h><frf72\{(t9ihُendo6A[xaqwp~\fNb'"GGFf8Vj"݋ Pיs?u#ktڧ $$ {hJn]ymŀ[bV 0v/=k .)uYZQ`Sdb-%~-Z`)v'Mw@g .vAGDNZю=Օ i?y|YȀ2AC(Dic".b]-JgIJJ)œUV\v՚|}:P~$Zyo$ ߏM4ؽ{k}Ϛ box׷ԖNXMּRlNyS+Ku,/Sݧ]1Æf<+23B7OkMW~-S[a#l ۜöY`KeM@Z,-:*9+ۼ͗|0 HxM\6%Vv &{ t|.6zH١FtF/ Жt2ڎ1L/v[4e_.񷮋*-?W˿Sv,]7WoNفEn%|w~ϊR'u|ʘEv`7LuջoKѹa] PH.Y@:NT$x-{2\SU)` dsf=ԉp~6*>e(7\<D\^_ j 댨`rڟnE\pTʕpe7&U!XچL%iW7뙧x l{S)8]h(L4AoK+`pn3A&i^t6Kmg-sL+T88>~xPIVY_t]7)Ɩ[6s3u9Zɦp{RX U2X IKy>hs^ ed9R@_-`=AKYPO٧( p;KT{ο%&aeߑ{;xP]_xW7>)%nUʳ 0X&9w#v+}'Nz䲑7b!RWW^p/xnכʉ7o⚃j/0T* p˞2W7=|o[lz_ -^oF;@-ne|i"`bi"́ҾS,ew% .yKurg~~b߳+M 'h?R ]SDZi̟W_|~U^k@*zyV3_7vGW<аJtVff/,JXiG q[XOHI{RQL0biiL;=#Iȓ50mT?<v Ma /{;T,ا.Ž}g}޿8aӈ{r)!>;JZڋۛ{?%0`0V(A_ pHtGbyn.}DJ?H  `!3W(?!¬UaW_=o97Į#XWulGˌ7E3Ǥ | P}AOR Z8Y Wgb[V9xGzCw)fڃȯ>Q3|-Z$ }x]b*yNw9ah0 ٖ!L8:C؝q虡L[X,vJ4r9|Ԯ!cG`#CӚى`FLr|̏tlLB}ՖmPGv el1nHeDgWG/11Dv$2 _ö3mTՍL;Ag$91da0 (Q9$(''=D(,a (=!Ѯtc%3&n1+`"l[`h-+s^)u>TMrxAtYdHQNN"ES +r70sk`5]LNi)YsFLaϴ2Z4L`5' Lkk1 G/k;F4/М4DIPȴs0L18NGދa`3mftIds< oXb t(>҇sg :1<;_.2z>Ⱦy HNo& fyQ/*/ˢ+oMHʿm>7w&21:İ]t,!(-x$&0&F MLO '΋y2A*5 (ГzFj;vScF 4C ׻I2$?(&y3V?8:KI/OE z z :|bQPz|:c2-JO 7sc MgyБtkv>JK+-(-|^wdL|!Եy??Q|[Ϸ|/GRB C_HwpfOaBzd uB|&YW=Sόz@=#@\@ 8^,0º(]$';l,, yH_k9 8'HtnG& -E-q^T 9+Kp{i> wtBW#qi9®f 2}_I\c9Ž#,fc<Ê9vg7[,PJߣK r7^(zT`̊TJ P%qV0z`TRcI,ϵK'= ijib+g;VveVzb2LH,:+Nc%ao`J|&Բ=g Dap:@'C+_>|O0>fqOVL2 ЋSe:b|n9AU֫zE_e|*GU^TI#zR g4y7]?B}突)e%uU8U(F{F(9Ij#&~XGomMv1i7hn =eyUOƧa?ijv4 'N2(p"7m6sO쓆GQYQ^{aF:HUTӇo 񛧡ciAqytahٳ'JF|^Sd#:|?X"NfәʼnhBړ V#=S𓡆&|` [Pq;17*-V먒 br-Y\#eKݲ~X#*[amT< x `{:5=.h\m瀠p`&X3;v:Su?0DV%V`~b7 -)+tnmݲ+w2aPTܝrV#۬ڋA%hZN쁦È@Q}Ot_ء(i8NkGw7>a 5$y4ཌྷ:Mڭ{;аS\&B. %c\ @ds(Q[q}؏/w5:vUy --Вt.w]{uF30"Qmee *Zō &K~źC|@me[ k~̬| WyV',]q}EymbWf`ʇ̺VY[lh.'. +Ͽ-eY^fo_uxnL7ګb[;' xkx0FȖ=ڋ|O3TmK@W{+y2SC"ޕ\P.6ruW+ tSVSlOZ"Z`B䶫 x`{jʡpS?;8P[ZťZ7pf#?p>} ˳vc̳5[ZO )[Kb^ kL7|pNnO>$|[ 7~Aֱ@Fx%lx}JBPd)JCEh$\>[jq(~Z['aZx/_3I A86+sijl >aD#j\1l:#ٮkX"h-Թ+a .vtC`aC0E,1ܖ߿IPDY ZaAd@B+a]CfMx(F7`2Tsǁmƃa+}"xZo P"VZsJl tMM%5oEc8Pp]"vhK#e;&|*fCKQlGauK^cdl vf+Nu[ﻴ%zGI*P񍃗*{~M)V?cyWm׳,Y<P6vl-Х!X[:˴4VQ2l+Oll0~ſ_ -t xVmæ㈅|Mi:Vմp;v'ƣbY|.1gӛ5-}0CbٙDDfy(9?]}=A*/{=$^_\󂥧i`K1:ˆ82cH<<3$"4Q9nMǀc \AS2P@X,Im+Ђ(1aľ85 VIc>0[Tw ǵqRkZgZk0qe+|lJnݰغʲ(Z?ؕc]lvEJp(["{X&0+7V#=ʪ"ow@G:rCGdЛQkHH/ESTQw^ UQg򌨩|~SVc"M>[ࣃW=gu5G蝏@AH؉~GVe {mxa_Ve ?d 1z3/:h~1zZdF}5_]#VN[nvEp" κ9^wŒ8(E1G{QmC*{\?a_܌CmP= 1?׉b ~?ݱM#|Q~`$!DQ2訿nGGzZ'$ SR!w$Hk%O8n.﷓j{O-4/ SF6 ڇOWO"x~g#V'GI[@Tce#XnѢKcDF|cj`Vz4%^S}SkBΠP}_ڈңDDC&e &{SeDك͉yUlO(Wx3©3Iޏt;>W<ϭ"/3'kc0'`N^%v]4w V{ D;i@ ̟]? n7yQ==_ϗp!6p!/)oXb!ND>$Ң&2DvرI"!(Mbb gUK"oj IĒ%+|A2Ac7>ܤ=0/V:ZRH$2@t_O|*jjAO}?l/OHuQ|\KmOY7׶0};}fE!c N ջ?Ņ,ޚSg3L }뇟gY0+F+-3xpZJ܈ aYe'{@}o="=F?oG\xW9x"v=,gvL⁵HmZ} V& ׏p m%xSq wv **-5 -E(挃ZQ"&,ۆ eۗTO<,{K~ xdxFeVm5+e&EAeX(! Lu){|Wki>.u%2·IwPp#`|=CnJpFks(1t}YR'3nۆTT^({E -Ci$x|cA2w _ w=\2;Pz|zE&a/|j "X+4P.D;Tu@*}ypeԑ4_J.c=P]!QGJR]gۉзDV}y9c1L L|*vs֋i5iUͬA+;!JC+ ^nܷS:dD 4 rL@(۸!DnfV&E[O[>gzJC,JxԡzCߜس?BvS~WtQ@肣6T߽ջt]-.SݑR$ mVzA׷ʤV'Qe*U?Uo)92U惧)LJe"ʂD@rYΖoON?.8юss dLѧ*5{ذPݞ-*R(ն((\)S5ASÊg.g]o<7h{,pqT;pLv`Ef5iEx6k6Mv\fãn7-: d>j@~W"C Gބ+aGɩԠ~`c;fLh*Qb"=7e\DlH% "JEG$0 ҮJf5U+Lk*ɲƚٳK[RٲT~>ܵ RHJmoǁ4:2C^eLbgZ*YdqXCɘ*JC E]5R5"XI |5X4_O kDܹVfU #Cfⷁ%ظP|Z1ttST/CŌ+ԕTs-QrCTal8SpYl*:9gt/n4jtYMڡu+ _]”60^5 4 ŚL}9Pdn~ۆBE7 !5B\Lnc00G}I ȫ9O,[S'#–%F¦c"Lg.4|e⺦Rphi^Kui2# [>"YJZc7qAƖ[]+`&&JXKU&v\;7&#mXӠv7~Y$R*c e&T;78)z{}P,d/4XQz[@a] t"{MJԤDZjc/M({תO~ȼ(Ԓ\>#+qU]\GF,n ~[qLlz?}9h|(F=8Py}%ㄮWir{yEVmAٿZ&דcOb h tŅF1;;Ǡ?sRFb#8ʚ W^&t3r?'rr 7ܑ2Wy;^hzi^6u|EoY#c4#K=PqӍRNiA'D#nY64QXtRb+ώ>C1f|F_#*:G Y#ٌtx]۳GrBY 8"ӟ0=Os?$t33fQ)EsKZ8f/LZ c C3/y;?lYfЊe̍cw , @t,R$ DzK^kU7s_+Qm¯@^)k}]pn6#(Io?69Hަ mrK@J\(^sptG 6@נCϵЉ8wWv}o%&O_6% /~ي~z|"48v& /8fx %><'8D$ e==K S)$z|Ə;n@u#P7vt`B)Eug==>𢰭ő_UC1Uw= B5$ͺɍ{9 ףǝt u>d3DcUqUYG#bԤ!/-w=LLcl7htư$J 2,;H=i3%:I$$ ]V9J B]}dlFSn¯xH?*g!ԍg:1BzR/NΜ2ΒcjFs1 8 Yvz-SQ\p ZfiVfвۡ60j$U; iCCT6.vFlR 6v&cw~u w~9؂N[", Y{/ =# \[y4!>(9&@Fǐ@\RdP`f/8ƿ0nK]OBQ? \|^v_lG8Y$/ ;ƐfeUԞ]V?Ӫtn tvR2s#"g3A&0@ﺭI1|d>Q4X'S.K׶zJb?|.w3}V!jǼ]٠M ƑᲺ^Ж~mH]8m/-W701Q>Bsm2AbU A m&j[ޫmF̔IlY؋;EkZpҮ;%X9MQx60)Q)}0w;^ڞ^ET83'2, ^7"@'x ƞ0z:6( ux\i=OƿOE@9~wWEQ<v[]n#V|hs/ln ;v {oبc- ?d얚#j n~7Eq }͢Ju,{Pyr^9;c(uGj;& oWX@P{$ݤoJYgu>̒R9j>WԷUvcmnbU,`?Tf?f0!FrEɺ)µyReXˉ&}@ G}Yծ}ca(<]~ΨE8 K<J/&nԖPiHEƺʔ&l,m)كG"z &7J;US6݈bw{ȷNC=4(J˹ [tnּXvk@~;FO0*pnHíl .ֺʶ+шqj٢A,DR4Ab;u>rd]0{"ee]$4hoX?f%6k^pTTք!VHA`-pr<∯ƌt$ A0[+9M˛}q8c믳\@:E5&0O%v?#CoO3ci 9-a+ p4kɒjL79շ &5|ūƈ6M/>j5Ls?Y#t6 eۜ/0h3"N`ucj;[Y0p](}`^5FwL.3.Ĉ}rk֋!}>%vj1GS7k[f,H{nNzV(u9βMHݜLc;-;]"*!&}~ TSC޾B#x^l9&'REw717zl?j>=UCZFD I*ܯ>&byeEHTdYn݇lpgZ:G^9wRw.mgzEXzi].X.n+ D2euW %5-Tn"%oTkPA2t:w\3.eBCn-7VrڕܓLiOEF(9<(?IJ׌:Xpސ&$K%^لu-<u~YvT!zon+7d*!Ǒ%6Ah;Nynqx>_9|*<y$ 0Eq3_xWrw[n1+xZ2HFJ|(<]ݻ .h"`i#Q97 $ }=]m$4K>UZ,~ر&'2 2Γ CUsdĴ =/CRN n$z^6M >׀ %HX؂-rsIV\ancMJ.A}RzMU"ƙ /E@記̙d]Päz/_@oor\UfW )yMݏz5˗ i%#\clU Rl_h#vl5r4cpJX.(x!-O.-8P3Y;t%An. bY[`xw9G>4x\RϱP_8i;dAZoo{ޑoծU5Aq><\ot`X%sF&iL<ɽ~(0xRQuR6G(___f2ۢAn0ƎG'ú8CN^%cb8D<#WȪ%$ NA&ix T$jbQ6)Ð0pku`_;|Fa>J%a& fG!_ !{@ֆc@ (e=2E d'Rc_n*G!|SzvjI TK)M! ͪ- 1s+ƩLWcc%dJR|?t篙&-KWsߊLIīR,(\.T0MMDqaƁk}0jR#Q6K,VύyUvY\#h3]_ܛy‹adt98И]d/rпP.]"4" G6;@ RXcTyU"P(JmNX+E#nw=!t3E. v+MQ>b ZGj`یc~p *APf. Ġ@7#l)X!Iܠ`T&%*-Q%aoe2v@s '=aڛco߈WSjӦ}O%dW|KM3Aac[R`YƀߓMe@Z<83.(5D|evqO '2#pk =ȷ WKdE ( ܶ0#5*l 8J]?Z}ixaج0t``:}of|9/Uz"wi-eVWmuheY)vjk]~!\#;l!Rl= N\/p='B-6,I:-k`p)3kBg6OR,mHUmEUUv}=)F,D#o +}Hyre`K(.␑$肫"EyEUL_ !q:Hv=N=ݝVNؽPw`N9lj<׍#'jPB]FrLl3|[iDrNw-zb^.T%l1a*giv~QnX|Sk<$bu֏[dJ?a†k"jqP+LW:-QD:L6~myI11j]˺tv1OvzӘK9@v7)((V3r"dMhFW4rcj>+ŰDᱱsh]9Oٓ=ܟ}Դ {@E\,5/XP=ESy DF]O[A۱AơQpY{¸:CŬ23oa0^ň6[ge>˕d p )%CELqPɭzi@GX36 fx2eg'-i/(x ?;ݵ8o(usa_]|;Wf`|g_' ]k{U#ԤcWi%u!gB+%+3s6ίx؉8QJ8B_+|C( n۶m۶m۶mۼm۶m۶[M*IU'Xc+ 8 nT\W})w"]xՠn@,]yxW) })lQ/V]‚\')zp*!۔$ic("MbR,1?R/}^2Wx%L1mN+p7#@kZ5Q9P?&_@^2VZpBNLIAvo*"Lڪ]sunyz3_G_2f–OSppTS!vP"vf_r2eo}?B0¨[1> a&_`Ms7Zbj"Cr^XEa[xZ7܏U"rY ft$T&f|=c|?j=/5g_WB%bGMǎHlͭ),ѤPjpbze 5fșR2;strMU$D s܊l3+X+zA -J=!I΄Lja7‰, JMrMH p"VX]Y>4KD)~IțهJZ Q qs[]ҹ&/M!2ۆK|\GMynӁEU>X{Ye^R+{,1=A7h8GЌ3>ڳ̀wmM||5fqnb|ضs#1w pd~f-paB폺^|6)s|Iu*Tyqrx񥥅9qDwC|oITfJDQ_֖u"BBY:Y&CjnՉ( UkkYV.7 t@D `ЉqM?V[Y-١LPvR(Ɂ"Jըydj68Y87G;t+ͺv9`ϐ\J5Ikز֍@đTB7ړZ <uBH5$za_V^BeNaˉUc$1K+1WJܷphNW#8CVuzapQR~xZpk)Jݨ(W_ɥ𥣣Akz]HJ2ghBMOh$ N .6,4L"T1sYt'Um.u]PMY2ƶ⫬PZ`#"umioV$߫lpBe-xEd nkn}-3rk(? dDUGt{퍰M ~﵎GjWR߉<@b컼 )FOK#w4يIgd?H܁ ~,;Y(Iyan^Є6\\Wf |XL{0^-U;VcEԚS,$82U\QbDZJÁe=A٢u#f)=FuA@3#je޷% *DI AW!Tܱkb& 5Ur;9`"k;gR\/Sm]7*g@&%t5+w=c5I>Tn\S7gcD YA3KcE%L#Y ai2_)Rjs% PgN&!HS53 bo-j3*~IcAdT#lҎ83WRE386:A0H(PE=&)ş^3J`z%F*Dʫ'˓<'Uȱ^A;H׉R=(/uA#.-l3V wFc2[ bq5St{9]xAbI]^Y: #ŎaѮN8R,3c#Y:UAWLO \8 3=I-(`ay3#BNGFd fgMhƧլ:)6~g><:&Wq%^{X5i؜L83`G(:'X1m8ul%G%Jx "Éie|b535mئ84*鈲%/qHٚxu”XflҠеy'63oO;.UqaE"Z+Ⴠ[)idb%=B" "Wߦa,g$UFE14Djzkk.)W =(U l4QPHlT:t\,SaSfŶnq_t-Nj<8lx&{ڽ~v:HƆ-^F /f"A;R51 q&E}Bb31ZMi"?]o9OTR?SL 3̖xZׇ[y-"ahɚEM )$nڴ?!fp-h;@)WTSdCʺ:f iKf"5  .N )tq=gv#3HM! Gzv>jsDzW(fxe4f}Ieup>vT.yo 3O$a0W*rz`xBq0ho+gec\ *M^֫ 6iщkZPB=%.}cGFpMhdVxcK|W`1!)Ȧh<ƥ ['8ESΜi D/VIy!G/$Qh||٠NE.sBFFם/+2KNgmzKAcd1=ǎxYW(2F.cixW]kmJ? "Cf3rXZxbf$а8`B KX Ө dx ֆijG1q\lLP>+1k4@-_Sk@ cW%+`Ls1fcw"*jNy*Or[&HW=c~:WO&6~~Y¤ ԯOD\koܺ(= P.muxWamѴv̎U[יg\(+@nEzZcmoU튏߽}jPke=["]𲶕9Aѝz)S]O~l3N2ZKFt=6U{G.nYwaKEЧ>_VL.#&~2Tnʬ펙DD} .ÃjC 7"0@w2|<@)A (<=27*ĜsmҎW,l2E=Zu:F+NՇ$0HX{ՐʼAƟsǐ65{*kwzFJ84CpDIM2h` Xɛ17_uߴQS TéeB Q]:\Y%n}94 L),CM w C1n(:+a0EE*е ˔N 0L;,^\w4mq>1nks+>7[V8ȉ;w]N? hX $;%3F=~JN_Vf 0hZZ=z Cӗ SI*Z]5E\=/AޗM Rޟ;8G2 5q e(ڤ*w'F)=ɠ`$ y:t }I}`^ʵw:c9*5`^sQ h8噎TM+֚8-@d%nsKzV9ӭO46J5u tw'uws)jIe$-GJѦj(sݲ?tiqjZB =΢YBhҸgԆ*couRL05\@un0W؍f(jFwu2 HflWujATuD%k0DCЈ̥a^V}Lt՟ռLD"{N];7RhnDjmo*3=- Q BDD̫`5Hp瀥7☁A ꠮Y5Qe٩O9]#h kUnR_򈱩ZMaG O[.M9R?2 6~AV*!E[Ѭ"2z dx'%3BYtxŸw0B&3RQy eh2s͔i'tkanbۍ6I2 T$8_@%-{ȑ2c9ݠp%)Ǚ AN2JvuBxwxq$ĶZ9n=WצQC"}͒BH5+8)l&x<*nuH,,NI7@G(=NHO}:jH8oI`uЄnЃW6` ýfYǒu v,$@?{ \_ɦBM|7E6jݑ*<Ҫ(O'v}u KqoM}Ns޽xo*?u׿ьuao8lF6Z,ރW{tUo“XLm5#qňz#\u-iDS:7D-u]aOzpR8(ƴ8 %ݲgVӍ U̫c[xP풱XEHӔ vtM?g$]b}>ш]Ps=O_r)A=+uZw"wdy3Ut=6՚xp ?#JHu >;UQ*:\"0Gs{m8¢S ) soNq?x=@߯0sl YxX#hRwø (uoXE'^_UnX}GNGa6rb<<0{[~`Od:Qb'ujمq ˱,/L_M^w۶"6aTds^+`uD^ӲߣI?~[_aHZl06!J%_1Sd ssIj1h +O> U_#o`qt Vp`|h V#6g7f"H=Ҙp+yi=_g۶,B n(x7 ~Jek6$DsfޞpEHC;ZC w.Q)ҢZx@neZ?S uңh`c ۧNۡu5xATF ԉ';PI}Ag p:eYxǸߜ)8}vkmXt$poe[ QdZ魠#pAB[*uL!m3p>iV Wq*ʼnnʜW㢸OlLBH;S^0Г.ZA9b%q~`}^q@Cˣ%UV*^-+YY{8_SbړzG;~M5>iFg={l ۔U$!ɏ5j-Ή0.\eWe!ˑs k>",cNȵ} X|VW(ڣ0VFʶ,@+~9gIy y ҅{ #FP?T#=xv&m#JA+J4M&c Qf{"1D#dݽ~ HbR0|dᵔw 3*YD.|eTϋjI=xiwv) >/L[*Ôȵ A`ZqI(D@i+$)JTu4뉆6 :UOkڅ٣k%pKぬB/y).u$/;$FEzBi!iBG*a{A/ڐ}+{Hb^$uݴPe >/ƐܢX5uT$L̯2Oy PC/rTQKOk7vJTi4O9] h&֔)rЭ \$0u]vuKoR襹PUK!^ ++_p[V&U8ʕ鱷9G:Rs-I r ?+P3?b~%Y‰s֬Nl;c #X"E~̏< ..Mc[p#%RrB[ywLqe#nr̬:rRw Ҳ4&d):(-Ĩh"J8p&<׵t.6!="&em4&AehE0x3y܇dsYYWj絯"yeryww[joW57b9E2957<*ThSisA+tlW(?5,4pGVCr_%+Кᬔ8DREKY簂jI(K#y 8,{KËV-{Ƀ$:h6Aׇ+WFh6eGtZ :ǫ۟8^UY6` R\oһ~92'ǁ4ZR(؊MIV#@l+e7E[ypK^.uVE`3,'AVCv^I;歫)k9昡Xx= pkh4yhoDs13 j&-p04_IǤ4 N)yPFEv v [3t-8|9hb^,$AcFbELeZ`F<:>A(Nݸ#)$ RXtEH[S ^_a,~4zt-_VNx۫n%H:v~-h:V(y3kAdp٣{/񣍙ba.܆E`+mrqv/" ;A&Lc dl?ʀqkNz! 6D(b z@˯Ў=Q;ԹCpC59Y껉$;b.dGj4q#9,#֨L E#7)OX8 G{%O>Tu_X X22uhDx_S KM #4!o\ë<ұJ8-rP6<_X7!L0PF${{vo7* ѱ1}-ɋHXԒg:7aL73xv u.clAps 9YŭwgH. *6N^[ 48&iHJk!G_OʈgZ~vGCu>I%a<ׯJC69ʼnt>tlj2o47T)cu}ڱ!x DD8=k,qF74=fk4)&aa-dlBkh>$.ztS ;@Rvu ey sz"u/rgb+hu¤B.|;` 5.35.2=`apd#,'A΄H"T=A fLVq/Lzʱuj<ǭx|0O,[;X2zPr"HkK4F fE 7p}:^8$$֪$8;M4 8P*P& E(;DD\++..tLb!@Rq}h!+ȇv ڦ$zyjPDVsJ=dH+JgIGi=J"D[tH^r 3sƛ g NZ&cˈGÌfB';j5n{Kx80J|953d2aǔ3GÜf[un9XgZgX w92|#:Ft2ќJt}άL7涤駘j}Kflu/I;~|BhOÝӬg>NCؘǝq2r'H#"_I&IrHAi䄩GF<y*i/<+OEQc7y޴}uU鬵cQ&4O$kڠzL U BfKc[[\a&egb6x FAt}v\E:uqDLZzwjZˈ!QϯWN396-xX"ZPdŃM-_Xs.mJh+hIDYR`I5̙%(IR*:WEu nO~cM{YQ`'N,DTWNeri7솻-H-\]&P]K'84Vq'A`Bk f-C޽2 ǮeLpf، u4\?0s lle떾&'RZLcC+|?+ tuS2B cBI-Kn?jQRGU4/WV6O&@4B"+JK/g%W%7cJ i+f/j1 bNCb1KvRڄ}JC{m0l# V_Qi+KUp7_wlbh| EOʚ}=8]r@TY~x8?CFwwe4-qa q11 yEJoe{1 1쫇ndvDbAJ'uzKԟ/FOHpvDFC j^IVk>bk4dĒnG_K0uhnj MoD.Y9x82׶]=Q&N~L &EZ_r01Нg:eTȈ|tt;BlTG ČheKₛ8KҜS $,:2ʀ-LE^p$M ~R##m) ݥ,zR mdB P\JT&6HC6 |c,EwާL. {cF86=Qž,Ӵ}lTT)vEvEDύ4?xNxW_zx^Yt؃7Zg/u16=:}[M[Ȉ;p?1Ƹ \66]Ēkg.?۷]/yRA- (wgNb!oԜwz*$(2 /u^%P60[Q͘2 MQ W(8U8PBo$P PӼV,ܼĔ=d_"ɮ*1MY *#F#mPmB3afmWãs~NhzvD}1A "<+6 4ƟaLdy5I1_<4 ac7dxIOFR/ .cBA$ǎffWfH );x Z U 0zX$lXY#V%xt{{n!)cwi4jٮtH@1mpjXt2 Z0 &!ǃ4Dcšqa 2jݭ +0SPAwx4Ϩ /$L @s:IeYmu_ZмJ4x^Kchp?=1kW5@4NASbEgXaˉ _Hߘ,6{b9'wI\A2/$HS OD_EUU\ /h5{`}kʌhն9؎Y;U6 s\M)٣CaP^:8dX8?蒧 !r_{/^үmvkG  (s e@L;Hy:9=v,ljLS;EIOHlyRqyfjQ&e|6@MkEvf?`iн}}z5un~(s{ӊ!&ɚ<2Զڭsm[7iϒռt֮<-i<97/Ҩ<Ƕqp:e~}R#eygZx`*+vߵZ`Wⶠ&6:rj)i}x@˃ >:r"}oY4 bLͨz\UzSۺzK>Z%_o$|[g\bed`hf(<;e%dg&;e&,EsA~AiAnj^>8SǢ/1995'/O.-*J+ 41A5v 6'%%9JO,I%U(ۅ-A.,HK!Q_qIbI)R`"Q u qsӇ/`r0Oڮ"7f\S{˹=H. u_Iז;6/ζ՛Maϰb߷k}*2KL{׉ E.O.]*o~qҟH}K$ol>nFڍ]𿚻ij:}2{U^)k',#cڢ&mÍǚCd-=wb@kS1vG[n ޶NEe"V|1+k';-'}'' l%LQ:V0]Ps hR&H1@X_Zy:EEyy%셮υ-'>թgu"As6@ׅ\'FУE-86FE!näP 0; MŜXQTs ZXRP6IŤ,&! NbMDAJ)(X86>Ěc5 (Zxa P9N~PKq_HPKPiBVersions/Version2cpfM5LlLlN0m[ۘض1m۶m:w}Oթ:KծWk9IP H lX2 YZ9X9Z[:YXٛXYZ8ZY988#G\z֎z&F1?Am12l@v叻㳙Zç%[༉=i>?{ RnF3Q۳Q{( le*/::﹝a^4\B'nkT0/8*FkǛX[ <#{vzQD!i 4e\5hKz|.WCr!ASKy6IID/@sª$ dGȤ%?K 1^@kZcú!!e&b+񌫣㍋clG X4+)TD{;Y?hMV$dOÅLʧ^5WIBUJ|S~8kGQ9 u@W[zsmO_8o]*5LiJLgf%KY~*:P3m&02lqcIUCx~n#Fɚ'|z*3 dmh\^<õ~}^Q2Q@=|^5ZF.%USG}bA(!Ez8>6vՕ3LG;p. ^Go:7TdX$ 9d1S@lK5ih#:a"-HO' ntgs&&ZSbXPU{6"=LLA@_؎KM=,Sׇ}ܜc;c^ZH?.#j:z4{" IVGKƍ߈ZYZk5-09J M4w6\Z"uH dp ċYjGޝW#ѓLNSpx;IT !и]FέW9.(+>mَBYz4=iqS.<}N$lVJcoY׎k6!gr#;-ae\ы$=OƯ{L[cC.YgQsw,Q".wO,?dE t?xbw،:+\cymMCk_$>o^_iY*uǪެ?aFt J#8q D湩WI3J׍hXRSjK紾hY-k]s@'lF݂צAWhB&/3]_{OnF%]r jO-htg?7ŵ~ vY~<0t 3:e| t|6'gz8.HtBA%`Q ňCNMa֮5!sgAseSJG[^zD%WpKw+ w ~2v閻ћj4J"_2gѯY 6#!qS^$B\TZDCf:ʰk"rb/|\cn^(@;:Z(rVoQE0K9n{ka{8B0ӍL6wiWiF6ޑy7"/bg,ɲJ"-Bؐݬy1O6nDW{I7XI,wZ)m]3#`'Kfu]:LbkhC{!hȳ,he{FkUIpo%ce'X^L3u2(^N㻬OTS2֝7v=QBH&$ȸO3x|c a@89K!~9w3i=Fl0 FGczӑ HKU]#! q.ˬlUUa8HYLϦ yZtEϭsi-OF2r:(n!0!Ro^TYm&bihX^bE` EWa{0}/#ݔ/󨀺d('/C;)Ѻn^o4#'[ TvJj3o34$P tL+s8݀,cP>Mp0~h ZdL+EٞL~rV4tD"ApdXA9fp^ Ȯssv+G0ױdff~M2i8?t!k]9:`-8{ي( !bӰ >YD&Pt}'K|>~XG;&53S;ͪ[9%QRoFHH0jF7EsdR=xWrrBVɜ',n`"u;M){.uH)cnY {W<n`i6\%$l-ܥhlrT^¹[Hqcʖ*:jzp&F]idDNݫߍ6?hOOzL ڃçTˇf7mx*,IdI9xۭc;t__369V%Rtv7%X>^.w X0 `>!$of/YļȗcC/nSeM^7>6 (*% RN BzdxǏ)xuWǶڳNuzy&ˊ+:. 3G} e@,qQylb/^"ܪP 0?!b ^fKʡ&i9Ŧ<m$U dqXB[O+f&zJQd~ozb==ޣ$n|e޿hl!{qc UĪF@3Sk9@l1PTkW9~ϛPأ+5LM@U딴[ToJJ]ֶS)yO!gu ^߂פ1VsHRѯoUD%#i eYyƈLMnFkxْ[{Mn:kγZ8bڤf 7mR;Rѵ lyyVǟךtBm.4sL:%c_pSٓjC&C"]Jw0JȿdJI+4AP XF$\ 7eq7̛2i|ein ]<} b fP}/@)gPږL29I}49E'$y^k:O,NVrOs֗${"7Fk:[6~8l0V+%ΦSh"4>bAܲuЇZ P $*'WZ. ʀ;V^K%V_ :4@(aRIegn;ۀIL+:,Ɨ1t3hyuN@_i>w:'c1@>^Hj#\f\Ad)'ͶfwELKf|Ȥor33 F&"˃O|~.{}w[$)f0;HGkV'XS}\)`Q( X.2 }ޙ;L$t,D oG>#2J"/VUPR1,^ƹx9=Cp-=%|<9x -^x#{hμm(ι2(Yrm0j[s 1R AީFw1Ί4aaib5ٌ .jI=k&S&ʷA`䶜7Ɏmzy['wP#xc:Ҭ$/<=JJ{۟?ց͞nR_L2)GC& 5Rio V5:WluҭlT%Y=ex2)ۏPw޸_=zbu ~ sOz|ح~gs{72u& [h)hN]S/]oٚ,Ϻ]!r(x"Od;An7kU +<} كYڀE\_Y;}䙟+v56NB%A-ܚ+6WMVυnb_a5B^+z  jejHJT??YR+Й_-n-Or&5HM"s6ob:VwZ.~FL² ڈ{z' u㴤Df=7,L9MC-;h%N]SK1 0^xn'1]. A= }J3ukux:PzI@)l5]8QV^ sqExR LD۳Zxhbͧk9hk**[5D8:[a4A ٵ`taoU*ɉ@`]j]oؕhMEX4oܯ]\YPQcPꞨ V^[ŠFT([ZP2h`go*L3kijJ K +4vqgf#r8gNyXܙ~#EO)NzNz*nNz0z[0uݱb9raC"+sٰYZLZiO s0M L扖NטNzŗ>V", ڥKaVS@Kw=X?}&=61jJ4`rsb`. LITG&fuoܴ׊Gu>+ܙITd70:x NE-S ;U=m@ltH*-d8|VъQ؏8biɖOLV.7HjU RТ ظ\d>?KȹQњo1uH `WAC,x`[mh]++l}f1O7ӆ&WPyo #te!'pC=ՠBr=P׀R`Xtl֡il0+m}⤤[hPjr6 n6sOyGe$ Ӆg9u!P"=crv:cTd۾Q肓,ms:4irWhU(F9QN9bM~u،=*J0>sot4w܅; ?*s XFcIԧ\Ъ"~D]+7QW8mxqEO,EZaIvBÜzchbvHw@,/Ҁ:5\gNghrF-[؉oգ:fVO o yT\/ͩ`\/3X\%GT(Q*[u17*/}o۹ |+m:KbFN8;rWh^$G-TQYajd4h'iFE*/S#Jܹ=] f<̺g#:ȷZF vv;u )7Vo&{֤<hJf~lp3]LBcH;*3Z C"&.^֌!\+QB0@Fɩ]SI[7HU!Q6 ym0n)R2wP_,K6˓! UΪpFN(}߃S9P],98ql߃Y4DAQ sk[Lšd G.ǧN8jZ4<<|E gXW]@ĻF@\x/=ɠ_`= b(%y ˓EbSH+s Y2 >Oz_Pl \Q綖Heʍ__E Toj?)Ÿ %C PfsuO0+r6~Iy>Ao[ )ɦm#&?W΃? *$a= Ozirֱ}['g  ;V ŻMT_B\ :/l`Awv$>+:(tBFz*/vS)8J 404*vb񇶔'rW"Я8(MPمh&t~W yߣ|Tz" QO]=Dڼj( ʜ?{TFcϪDuͿšO̔I?ߠ{֟=`]B4z0ov oQQ ~=ۖ'=;шы8}`|l)Aum֬%E$/n3vOw΂sEEof~qHH_}uso 0 ?ɽn0?X=cKw(ӗ*rbi?ܐ錺{.hXS>Jgy%yX 2ԭ2ruFcj2VNd65Dȿ[9ϫ3/+ Mo(xNCbw5w[JB)]FAOV6z، 4?!F8WL+yՖV`d'h.k֫WksgQul@U$k~xn^q*+B/ ܎g 4b4vjlTNbQK\)t_W= =Cḥ(8zqj/vS37`*'գFe $X~QC!u!|,0RlP2Ip'||pc!BȢX>r*C@jӪ[FHQ) EO W);Nb4*QqBo`\xjXǙ.D-bKANjbU8yF( u@֜ѬYEv;Ftt9O p:aksHFՑk=f>UNXlI8?Dn/َ>:]ɺoؼk伹{`ԠTw5k;]).^&4oNV;?Ͻz "P߰4 o@ eUэVΎ2a 񵄚L;>!,U-'T.QzFy.o040jN{8!$䩒@9ݳ,%Seg/̵Qi]T $iQ@H̳LmC0m󂂇27:'= r.m&_xP//- T,'  u<% iOUa ?Зc:^/W.,~6|" i @eU!17PUKvȀ|Wp2#F!fodG2GV[~ۓb7c59!<_.Bz"x.xi5/咚+9I<nfB眨<%=X郟GvXӄNz-=EI=-dIZz h1/\~P@e-8)*p5aC뉿,L}-m y ]sؗh[3fg>S!ЩSm%KW^< vl4k5 ;{/RW~^/홟wT"nσ.//}Iꊠ0|YoWH(TX.XCv@O )nZ5 LpY(-Khcmٲ*-cHV`*]ҟAueAO.-D5L_ioM0>?UHY2_YNÕ%Uz"hǯso ]T,Qr_)q! WMK`;J0 =7tz|Z}o3PPxmv;kI%[G!T3uQTc0E~J٢eT}0f -t_xeLjtylm˙*8{q6ߌ gVCL!_A%Jw<=Q5y ~ y2?]N"46R:Ch{G9e Ev+M[9w5JM=GRs+8CJf,V"oIHuT-$)!brytJԜ' whǥZZpNY x U=".%*v\EZJd`n@%5"A\݂eQz<e3mWl73Q Bb˭Qd/eQ\F0`FloG~_;u=_e:~[0/dX-?o _ FtwY<ţZ+k,H_7)2ŲJCn:5 ˍg8eJ:Ddb7Wt6 ㋌&7E8J$'k461(7ƐISҺ{@ҧN0tRMpT&6]צGUBppvY?RdcE%|a*_gSDزdmgMJ.:˅Jk4դ+WP Zǭ!8+L=$FLBBdP!b]#f+r_^m@>&Y<Ǘ,R6ݽ)LK cu1S)UZk՜.3?/b!"l~eMĠC .ݯ~x^P} j.[:$ tjF&$M)RqGK31݊s"g]N<:70R}Ml**UY*J;ocΦ)%Pݢ Ht ZouKxSL.LAHVixzRJ,a)噍/4Lm(1~ۥA1"I85H\ 8<]6fO{/7ݎǟze T1 yqΫ@yx$%M@}VH a%X^];0~O$kV5_DjN"0_rM7ʏ\H9\Ҷ$Nfɢ܂8 Ht3|YMPβ2,{ZyPvJ>?O]><ܾCR$蒠1I0M0a5O$ /b?b]F8.ǞѲCo\u ՞UUܒ25P-q clϭbQcT0nhEWq |4ѥ JOpiN[JۥӃXٺXmD]TIaYDJcLTIiH7+􀒒P٭}:IjjA|02K ט%q r RƧfŧWlZ# VŢp:DIREWޔ#O-8ĉG'#~y| Y^齩 +Ia@#t:4htGHĺe<4o,hbGmBx5[s"uB۷H4#udʝ)XQ)8n6CmE1IGbT:Pj>m&Ri/jno|!ܱ@vVah~[7}t;Z",?-/nLT Lb>EybU^Д5I,b " y ^mH{8pKo hO fb6/-dB iJɉ|hr*nba$%-PA~d  Ka!:TmH/Ssu칚@Ӕ6u=ʌ;gAAY|hERe@>k6[RYMG?ވ(-! S"z}{Pp"t"-$*F뵰1BaVO{\ժ{Bk)<_3$wY&Pí#obD@q]KJن!PJHL!GR |rwGּ_0!M: = LA܌`iAue7I!:K5+ TA_aŻG?a'!DQcp2}t>9 v[k)Yȵv”VN*L.XH⚬2l$6 [r?νo p}rF!um:E h#YH캩pA<4rϪ\' @l1v/߀/xߴӰdM*׹ԟ((-Щ׼b8X$DQ!EG\M݅2|}Һ2aq2l*v;,]4ۯV͍in3,nPf!s"S|cWqe *hٖo )rSUu":RŤ2Iga#HG8$YRZ$tXpb*C|6.M召̤uNW3n`y3GBI> .w!%4͈̾^j]yꮘ#"Aکgu J 3#`>ʂ&jco~#DktWdm/Tm+!wi+O+'T1`fZup|Be/67iA7R9i ZY%ᶰ\G13 \ x׎߂ëtեRlq]%VCA EٺNkzt3OidwUFԽgedQ rLkdM\/dUbJL9äPJr"B2T6iÂBU Fmh>! W&÷-e%[蓿ޓC < G̥=[/@L~>mcfBBPґM2lyP A;p'E¢얅EM6^ldQWG0KOIP7*c o J4h`B&_[y=yۦ늨ϰ jLT_uGԹ%`1d9Z^KRJ$"%))͙-@3,-\XѼqgmgLx6ik&6,kX:2002@/_*1X'B5 q`z92TߋU +5 4j. xtK'*7u\ >ڗ,綠5}QH0 S^=|rSe6VcǜoH͑dVbx{88v;VXaeJpL>G1U2caM*!In]V%. 5x/:0X"CલPCEg>"aA \"YanI2:jX4NMCz {zRIa)|ʏh0J"R,N6-5bۿs)ϼc_:; 7s/xYmo{8O7OejMmbT-檟ZLz \a&'uѓdo}'}鱑-k^f)JI_O<"7EK z':-ڻ4#NsVmeI|c%ؿ\y2qRMBǝlVh6m-}D GA 5mY`]s4P8or"6@Di2WCIi w;|I0f[k*vȷ` 4NN](+E> ţhTzƿ*i'C~,Q&I%!HyEiIQ'HlC*DL[vUV=ީیJ E9S]?A2[L1pg}4 (S$#kc D$~W@VT r>ZGYxyw7`߉.?WJCl&/Val Q~" <[(QpEB{N VٖK}@pŊ1W O.$_*k搁Z@D5db 2К%E?L:,* o1T%AeVleW>@s!7ŲHa47'e` daR?xO%PsI$Op>6Z#u ]It Td"' 5ZoEVu!q| &֓9.`GdU$g.seZZ"͹7Ϯ{RmRwV](Ot z-gEJ;#-GYbu~ʰPCхhD;r @I)=ϫ(.s1Z}i%oaRO #˅|hyt[yNȑA⦠M7H_|y|1AaDB&utmJ?KHSiÜu 4`~C*.~FYcbdzB<4m˚n>n3M "> әFXO 'sf)wߜ JboyM,|hkrЏdQ0=C-BEcHjSm[~LdTJL=[ONB,໧KF0#`,X*sX"8)%Bw)G5w…%5,  ZJ{X!>i!t#I<@@N0R\зR`j$5bܸrs ź5W> 4-OVjl 0)U4\if*_[(sƢvvnײUjb al? .u!K(?3+7 ZjSI Ia˹#dxMbXkxD T\2 0OHJyJȭ$#fx_ IAyj"mGwdⱳ|yRHЏo:#[2Jo( %Ty#BK WHd9nUZԢBcb-!#Ey ]uh׌4oBjiBA35ʻJth+Au6L,/+c3Yu)gܗWH:9ZZabZ%* +xFD@eL欁QIT8bkk囗{#oaiE+:jU1.N!Y*ߔZJu%/G:g|*aT"J./цi@H{ናJ+z=<q2wROw__і+Ft.uBgies.g̍^mAO2 V^!{Lӳ,O¸@ 5z}th=\ B}:ĢlDD-X.m&&śLI@XLDGr|1 /0zr ܲi=w( ӂu PTv.MՀ,'g < @P{ە] o'I\:u {PMra0Oc߮\=S\@?kcC]ߔqjb 酭~ CَU_/^]^[U_<<[+C[ǦkJWNgVkV5Ҵp]}qgt!Ĉ>`[ޑiLEu\y3*ݥ%,٣XMG#>ݽC5 ~A]ldy 2>1F猌\rFSXx@m>%sbV^7m9UFNikN'ygT{.*p2, K3:2\J*J7ui\SoFٟ]ݠUAh^ZnYEYE-FvJ8]Fm~v[†m} !=} 1mffnf 7FnxVpcܘ'' 7PM_BOߝp(zx4ed*o ZqOŧyPș^>" DҮh$67n4$E/ zD+!xO5Z5ťA[{]g%[|J* $L y"Jܱ=KH|B>GqZC\eT0u,b JVo oe;N`CW<'𷊥ۂ>bww߯k dot7N߬2ѳ NWĔ|6zVG߇wރyN?(/U/v~]!+./5肇^/ihv)jǨiS zA @iw&ʆ ZZDwߍ[zLƳx((D4DyPGPڙ&OӇ1 mpJT8V.gąi"-;iOF^еIeJ5F^F26AF'SkC/'}-Q1du`>hNx(>0_3b0 D j$f6r#7ʩ@w 3L=0\~&Y!O_MrsRFZ9IjXæ kY5ȳ}LJe^ؕo"4 εϪث)ydsdK3g]̕ xk6.2,7;%?yOб¼ul df炓JlXu'#k~˨7{2PEL,61sL: =Ǯ+5S+՗HoVp67X7NLSKo=Av^ y=y%0Vv^w^o?y^Ctk=v^9y=y\kW fhyw^m9~-:,=ztIo ABxH}X ['ΡEbYYR)QX"X4q⓿w\xhmVP itw ן\Y1hG;ʕ0R; Ri`wy~|,9PNm]"#^CpBeE>Tg xk <z<w- "5ⲧ]{zCx\=t+l뷽靧XYQk_Z0cBpD髫%5\Lˑ6nQHTka ]s%7F=heŁfγH^Z|ܦCŷtT4O75fK_v(M* uqFv9 F:( ɧM:W^a3xԤUo:F.4Uw?(jb=Ph쀂 ^Z Yk( 3z4rjq!zvܶuv8V(he2 Tr 4x3gTwS 3.R 1BŊYlzaO6Kq)BJSBmXT KØu{a OsS0 &q N:)Wo6z]W o^)4<z}BIF l)<#Ch5Y*bP 2Bf*_vB$bn#.Z7(#-134@̻Lä&-hv4}ҢZVx6m!\Vqx[N"@:Gˊf:0h͜9A6K  ^n) aJIDmLJ$4\"J_t5'3S%EwχY|XMJboqCDϱ#zP1#CVPQ4Ko ^y@ RoYҜp-e3^%2d0shmiӑ| dT pVV 4r_e(1h`!p54ND8rȆָdZt1i`t M9QW~-Q/# $8ӱjT.j`!8"i!eһZ/ZhV$˘Smy<KoDێNs]b>9Itcgknzwp$#h*иUՆ׈KJm\tJ˰i3J褫c3j F)rK-Ӽ14hFq<Ξ:B6kcIuBzaHU슥1pdjhh5œ* |S>+S1t0GSiq -eGm-<[a[ oau#ma\2ЦPVuvrμL+${@++Km(lWt,lc k :4#\gu]409R!B/"% J*AXxB RsjRmֱx-DFэe{ gXDy 6偈8D1c'sEgn缪}U0eiKKuMRk+]Դ W5oؖGQN9COj7h@蟗+I4\%&Zt%FKt2*[;X~G}Aj<}A-Md fD)}nRAgb>C|$*AیBg|e 3'HQh&<*T棒ZCf){$m}( >ACƿ ?QdbPk !+i-1thK.6<ɥeTy{9_` p@h %mVoP%Hܡ ZEti>x| WJFKgET%}xx{D\jZǍ|,X/x_+>_5P-qthuoߡdaHSmt(wYxj+:aU»rxW+@*&">k0KxTBG@B$*]+VYWTrq%v$;o˖Gq ~ڛ%@>|9G ֫zE;\S0wq-F+4+N-U̢/\D%ٖs^0*%DϏLKab^֔!3r 6>m~8c?x}q|QRXrv~Z` )|_~@%DUڻC[ #[e*oL7`>A,5p>)ĭn`αP|u,ԘgbUΰr 9oت%,m7DXI/x ʂ{.>3oWve-]xu>pJvoc~\YU?9xr_Owrq v  maQaqDr۴nOU_/>^]^[U_<<[+Rul>Q}yV?]{Yxͪ]hW_\l7F1O6wd/jC1S|pxyEVUnOx%kN\Y>ݽ7N]Fw -OiWUi>o+%j)'@,Siy_6tM:f5C*Ac+K!ԪAX (dҰ^ERg\J9kmi Ycry}٘aʀ aּUGMǓrcM,TP3Sj\1:GdӘztY4BfBBS~q}޾;GRoL:I3Afr -6wK6I\\ p{Vz-x}%a;A4d1Y&Yfi ߼E8+YzGDΥQ+[b.hV[mQ[0si<R pz$D=]IF_T:ٌ{})mign\42Jo2ev76uϳ=Jm jC74|p5 '2B:R4_AِڭY}=;\g[׳zV_wCh ^Ҋȿr0ZqK}>GmLmLdiVc ctz2_lm\۱~}i}=[rT!#Wq*:WcS^\"dQl4d5Imi7 Na.zG;I5P#sJ.7gIѓ)ˍkglO<R4u]Uҙwno ÷M( ZLw\أKQ)Kwr#*7M2z52zɸfHF Nzt; },<9\ʃ̕*ч't)\l38>q2qWJت4^i5s.ɱ3_|Sw5Yxg)|Q.6i6&Op:Cb[i_[څvqS(0Oף(?Ql7?{\I`Z_t{ [xʓ~V-Rxl}׺=«G~KIWU#.^+^Oi}“msoa/{fNjW+ У8w4i&KǵQ;CN0D7GC["cu`|^ /v7pԷ֋Is=NT/W/~2~`i 75]oi/Q8uFa.Ñ /w/ :;wmNDzQ؍I1?C3$`N젚:Suc.Z>4shLcJ=U2S#Þ=rMt6t‘GᐹzgMÉzҵW-wMl*}@esqlnx5jӔ>wKSp</5\D`$>7~`DӦҝLF7 dpln!l]7wv Rp^j֯gցw nes,=] /iQ& E! E!jT ;W P4sz[\&q Lܙ'̲xrtvUxʓ܁e:84_@'<"=ptߧ?%"~(;p]"~Gug1g8 7˨OG/gb\mՇ\{m,}/;&,~gY曝"6i,G-{䶝+IT^t_;٠/Ǭ&;#َZ<~^m`ǗA3R.8rr َZM֕n_v>}V-}mh5?5vOul'| i٬ߎykBGӥIa1sC T<*F\"ZNp/f̝~ (xU8ح#J-WwPHˉ6PPI'T-Uo+XS L Ec{j!Gqq*)b=1ןsܜڪ `”t냢P{㜵UUL[ǰC#T^Mtcj9 ^HPYHfr.>۞4ATȟTum "bXI_~Dy>J E&*&jzX9? |DΆh+ /zKI[sZpӹj3]. "}a$@tua-ߐTy I-/AFiR2ge_}7{RY8{<.Zl Il}dXom緑]q$`K>͔=~Mu- [yCe_AӉ4|Qn;ѹˢUҬ_(^2&} t:t;86]򿁗Vez4͓Mng T" :7&Z,(2OWsƑ\/UZ5*Bwy#!|w2ma,US  D5zq%''vX?&[IlVj GwX%f FiZ|AveZk;q̌J$xL+`B2Pޛ >kZŏNG3s%&*LWJ,WhUY`WL mm|߼];Oj'O7J jdeC6*D$= W"2zM T_9}{nO["Iؘ>3fSOv/Rjc)RdZZ_O_ɐ)q uϖb^:p5U—88&é|)p$.2և$xZ ~v3jk׭ tŽ,@r=cYձdKb6T;/p?ܾ񶴴@B[CJ-(.@cdK43fMI,ɮm7Ƙ, @? '绒?,?;De:RQ p=aj8md2Nj;(pJ]lL+ۢ7˟hz ֙?q 3uc I={ӳˀH`+J˳:8[px"}lTf͡DzL+ gğ*_̯&[؇FU J%K_b0}]e'TPo"ŖNz#vL걇2Ǫmi^=٦$˰}3+4%釻gn_I~h;A:ܶA8M*T.ab,?v4*sԐ`E 1E [U!4uhh`^ >FT)0K7*RV*xy5'?Q=4$!*"P(}oF%v&M?/%q4GpǓ`8ݗ"%Cʀ1ŶN^~'}@\>M5!xgf5V 0Yg7Ko%0e1|qŭ"L&FZqNg^Ww#}F?3.ҙ?#Ɍ_ʼnZyGUR0\ $ jg^ ?ppa }hhSܧYO"څ-89cJN,b4JS&6}QXޜ eRZ_7~ndhucIe-_38Ԋ̐iđEӐv)2y,90O2N U4kԵ!!QM`5u/3O;^O$`*>|JR<,)B\Hu@c!z汶4w&w]q<%G :^ 1bf,:I$E9V+c g_xӳOߵOM;Zzur0"XqG5᥼U[Qd1U'7 Abo\~qDK6-PwwxY_͊"LYk;>-3+f"*v g{c4`xp%"@󋻂R WH/ob~e]s *&(BMPTM1kY\2[VI- s嬩= m K跮4O}2*v^k[H~*0O7s ?X C۽G2Zk oP.D;Q= rDzll|!OyET-,x,wsccV+uO` zw5 'I'ؚyBK ̤XLdVCF2_vQ&Tdx/OIH׶ ݃Ú1|W_m*>aQbza<`;^}dDzF))؉;F`[*CTpHyն2܇06if̍e9)F؛ I'kL'7*> &mţB[ZfOVaKbGèrmyw"&M;r7emE$Aٽz^7S^D'*5)p5yeLe-}D7TLbjnbO+n=t.;p1G.7UOD7糓r}a,2fFR P: oKUXSC^x|nT;2҅z` IܯyF; Ӆ;Yη } od5ҐgC|lݗc ;aa!b_ԘHk5/>]Twd;<>Gڧ$.t>EiGihObP/# ^0 Q.PM}\&Eܵ0x.MZ\Ϝˀ9H.w@%3Vw8zP_1vP)h]L$]s 6&.tadJÅey d2 p>߸AMmu̔Dx/EJT߬1#pέA>EBuޠ'Sk[$DtD_$Kl@ ^^EZuM>k)^S,YcHXEzyTgkէznPH9^j;AK!T(Vu[;ID.Wz>zh: |M1nqyOҦЂ6"V"Vjpj+<Lˤ=Ʌo> ,*@pxPQy‡@RG'u¡݌)2H-5M8 /k{Pbܥ;qʮ ̍$2+R<_=ddtє)qiEY,s!&AsNQ2:p=H.ʕR᝻l|n uÔ ~`蹿ܡ8ukQ`.~쟓X:Z ސ% $K@5J1ؽSx CQUzU /gnx?tsQpÏ7+gğzw2Eu3Ym7olBxL/Fx$ >k]1vrNts{G{8ƛS4߃ OBȂj%D\]5i*9O&( y:KHpI'p 4+y $f.` KSv@KY&+`d}-6EnVn1k)&tkRzO0G)[gqTWD?~>h7Ž˜5U({J[';<^M0rGkv1I$.\. `+at8/~:;"hEV+IX#w1F36D>[V- o'?zP8@v}{ 8BIH21]L-;~o,$գhÂi0vp4 {fuKq6*. pL[ ǘ2Z`Ồ\FLcKpS-Ս!j1p2G$ nvLmFERdLDU(5>;v3Os+KF,lURl fƈL9KؚsܔXQT P|KS%6sNBw䅈ςЙ?͇`,$lV|ڈp,M 7\YOjsI}m7t 4 'i#6b2||ky^N6yF7o6X z` ^tص{ytturEDNo sJci7[+zγ-ӀPgw1hA?Gd?ȾΤY\`IfVH%9ΐKV Q&hel{RoYB}Z.)M)G QZ}%n)M\%*cղT:oNj@eq*ߓBJĚ'JNf=L⼐Ԅ ra|+Oc~-gFN ~Mu')*<:﹕RigySBR Wy!mLU tUWiKl"WE|h5Y]:A+O?Ϣ^}.ve`UJUz xa8IaI{i.~.X ( (G_ܔry>$ynޠhrv]_ᛔxeȮpt~iu~7,6Ab䦉Fg]_"6*Ҡ I #Y@ {qr盾l 婖o2 V knX$x gIb.pY;]1;o7?"-bȄ)o6X&rQ281!`hNAnƍPP!읖1eKHhpoo< iX'N{{h*H]j},bb%J~?j|>$,;n-5m5KM83TLgX3i]8S+`);Gp4d? QWV3 0U tmL/?b(mu嚬Hjc!hVsm`4'h躾xAsۭf2Y{}Fl̔ܭ3*-ҹ6џZېM۠e6 'L'EFNwAȻԉ'IVC %*9eA1U db0y( \#=ʯg8g?լ~{g~n^B2_lyZʟLS70lLIs%OB ԫ-&/IAt HEw]k Ck(8nHd ?jjY|E2I)`TP'JǞl MQK,XV:I)&r~ m yƬxT]꺴wT3*;{XU v'o#0ುX\Hy  >1աYnEʼnݖ~0luNj5H$@%qDX#8s (ՃwDsmUĈf„,ݬ DzSJaKz8M|s}MxCaG ay>ރj0tL _\r& f7hs1 z '-ޠ:B 8,X +Wڅr#t>Q9:Jgx)hLjiv,R_  Xghie +,TD/-ԛw$@D!i]rkJ*x31E[Pe)HlVG=9=)CemWGbS8QY5b$#*6)obL3ayjMjl`)`#QBð)h]ߛDbێu7C 7Fe62LHpHV*3 ~H{K&Kdo:9@)*EtK20m+FCĩr%|)KRq!'h<pAm8c6rQ{TGj֠2M.slW Ǣ#kHG2)4L0cڧlLL.̗ff ^fGV8@1be3XV0Q\'r~ѕD ]~x~Q'Ad5[l+$0!6l]_Km,ϭk0!U/Qqg(;SETE1 uFmwT>D^ܠ~;3KK1Ìx2^T"Td 7yu3B5*ïJJ(\tSCFW\ysn~kfahu;bN5_5g[f7`mig;*}86ڋlD{GRPoL!{Je8+\TGesŸ%INJ`NEE03uEYZ#"4ʮ32MR!,a .K;5q*WGK\CVM˯t'!*",|CI0QS/7fsntj]wmxvܱ׹1]5/H'/z!Cț>-?> WfuȭP4KULF+DO7cZQ ZZXui9,D3F22L0'&|ɔEX;6CcnA獮͔Hq#u^$V񴎯K,-Lm /8*OV,!-,)8qTH޷2dVޔ,>|2={[*#$qϓd]vB{ oyQjh'*=2zώzdO.pQ7a)s 6{ZzpD-bCMV(k\)IYt5̲4GZ f:r`ىPJ57i "|U;,L>jalYSvt%oE+{ BկLijw%ٌ҃-lUOn%7]/pv@ɫcBGrTb3n62Gwû0:֤#*K}JąC 54`bDBYέF)_AHEqER Ho' nrJ3Yb~Mawhwaۏ`ʚs ]7eVdǮţ= X_4tqqum bo|g ~[߶{^%67hg|iv,?04@sC[xu[ w}s]~ Jg!߲k{/j^eȳړ[Q\?BGRrW;d_=˄4LEs4-1hH]+t (ɄOY*ؖeSkM5$ltG:T^ V]XՑpnyMI%˜p`]4Unㆇ]AF}Wo?J.L4QR.?'FjMt @>VW#` q r=Yµi c<)~˸J vl| s,%tϫkM$j6}Dx-X}Z.Š!!epx^$K+$0lQ .lAtCVQa(͊SѠd-Sm?X.>H7(HU+zrX},Cy=j VK DyɬxM&sB1ht U$T^JݮkڏZJ)?'Ac )Qp2 {2?(q@2Ml[҅Z-ۘn2:)y`MD7Q~3"E;%WUT?ѕqmw;5n) ш-F<j;r#Mڄ'>WW1]\GgBnxOe13 ?Yy3NY/]~6ԫ`b24H JEZ(QCv#;W̔_@vzG^> }2Y'OFH#IeXE5$yUhEb0)X|W֜cJ;xT/bgfoUŅFV#B `  ,5Q~== r+PuQuo;ꈜ8r-e'ȱ[ /l\>Va =ב;({IRz8@Fk ٪[@017mR}JE"9 _dz3}U+HݷU^hcH ]ijZ% ij;-Sn~lbQQ>ns/]e2fpT&Do(Utfl}jq ;Pm}LpiC@B4(`eb_`+b>ZHQȘ<{TżbV* 򥐪ۏMV s.Lj8LDQ6y/|ɜwX^؍ՑZ0>fci]t>+/eܵwj f"|Iܢ, D3C܆\UYơ~)^&XڭҞYxFdwW.Ԥ0סG ' HZ~/y329e*PWuqrs{̒tl=8rP ε KpC,JA%Q E}QQt" t*Qs'.@} ҇)SjM cQZ|c(g)<`qn0f@)2~'eU_č U;o9=,<ݝ::IZPm;,q']);T~1Ue)ҥ>&SR(9rL@xO[a&eRFx^|DuY2F%`PsVkTT;Rq'p=3 ܛA)456&JZswv  st{AjE"b7:NڑdHR'e*p]Ynh˪pz`f;Fd6U҂焥ֺ\i-mQ ׯWiS 1iVgBr=DjQ%N`&t,՛dU ]X.W:*B롰Jf(TT.N']dQIy\d 2PI=c4+/vwZ5@sgʈa AQcB1㩐%hiiFOw 6APZ3) BEPqA%=yy<7nM촅r_=Qϒow#2d#edĘ(Ҭa KWc GUIsԊ˴6&qTʤy%W+s\P0~{u햷!@!qh%6PԜb`74LId$~?dz 9 >E9HydPUnMǔV0hԊ֪(}=9%,5:KJj)ĭTN -dMaLdcyjFE1*p@d Ԟ`wn k;Z֡Tldl 0#WXvz;8^:8o6t[9S WDfM5Z<[n4)7-@wCWaLͣ ,0?ưwducAY5Tí>n| ~m ^OnSN}wLr !LMe)ޑnCU<`.m}BsLjx%3ela#f^|0=rtYL]J!Kb wR>I _JbYljZpc1Ƞ=(Z䲽8NuMԺV 9McccG Η6JTsۇy\”Qȉ{" wZB׏΅ :pE!VJR0\$OƈTE Au$50*n8. %,qKnR_)qNV#lju,=Mn^{pXqQJa4K#Gu}r-jڙ@eb! Cb A@]{Jt,`t1_$|%Ao/\rFH K?`# ?dEϚv'f*x ৖rw ciu+ <7b=v]cf;{e5'ks$9닗'pf.|\Ϸ HK8,NQ 33墹~CS mLXS\ Й>DM$-noT́2tpCyVF!&w̆Vsr'ؔ-4\krB(t*u"Q926VnyJZ}ۋY۠DMU-b,fpvhf{M"T<<;2X*_bIc,]Y6rbe7Y{g4&$0r=X/};24vXiaotIkgd(\0|JB] 4SaT,dٳia8G1aJ{7t6/qF<Wmh_CI9`}car28s.U?c7.B㹜WL#tIYOd`ʕfޑ2?֒~*مc4ڿ [, 볝[cu[iG'0,]YNEd3%"6,G+*9x@21-Y&flV@lbUd)]_梘+Fj)irIֳxJ:i3 y)X̏Cp'otڬsrb[Z5<*7Piy-31mFj"?Vs}{yeX]K-N%;;`wwwwwww9۷}k9g՘5גW}Fh:-kYx>rF\&%g[CA(3N:xmM6^aĦiKɑ:dy-V nG[/Hk8PM4Nh3Ǣ<9VywL^BڊnΊdQ Cf\! u] UЦ%gAl%q>\ 8B"kP!GV‹OVA,-Χ;h=gASڱUf&8#-ܘZx _sFXYHpjN^(Nc{F9"vaQJDd0iw` "u7$ g˞WiF>By_J0N  ٶ3RGB hzxtdTR~w,߉E8d b#@5UAO<vՃuNUt2qmzb4-lTX Km5O4,#'^3kmJj`:B-WZ_Fݒ!9&3C1s+qgX>3z_6*N= 7̽A R(+{*9,JYWcZY^rȁP 'ZVpvG5W~F4S$$ bbuZjT[c(|("=J-S|{ؙ$:*@Ke_bca*5rȢ HK9/vI`ŶÙ73!!u<džuDd;ThǬr #3-|ujR=; T]%@>-b J X>PdnveXJFs-r ʖ9C€1_xjBpǍ|&NNx" ӱB*kn"ۃf;<-RA6mυm+%؟آI]= 1٨4,Ei zm[ha8 d,< KS+Ƨi2Gn."Ͷ]6ש[UGPiʆL k< dl6F̺j@o8,#?6 ({y>:pa5!QBd(**UY38Os}1WOmdW@.]Akm8H(p߂*޻vTL `:9 Q @rvngn>˹+dquz}g(Ŧ{4|vbe+Z. HUl"d-2|54bЁaΛW験"Kl"xT6BMx&—Z0PS /2"TUja5$I[޳-cS" ?E*XӚ8z%0qmuYU ypyyZ^(zYy] \f}1]llyhz Rj} qS 06Z)bRp'_BT\D3\Gt0EP˯dfR#v! 叝BʛGü| \}6]h:'k !bE_N eCtN@ \{#kkSe)=K6чp3d'{3:z3ԛ}@ hpaI{V[SvBKTi(@,q`%3Ndbm_gQ6PՌrLޑ^8 4J*H`K5Cn$+v =Xx,+,'}_2M!uܟ!;E9L'ADlo RZїzū+ nSd.C9c`vG3L:Rk K "]6r1ϕn*0?r[0ny$[s5)QdUNV>\I8u$(r(p`$'n^Ftm+ŏ :=}e][.js*ba[jH&q?'u.ݍFYY hNo!m ]L z/Ǚ=)!z* } `#HJİM KD_3pHAub5y~-쀕I'H r 6}3Oh*o1W pXSA%(6@7haLDut-G?4E= SG>'L>|CS `sd藮8 iuZ{7e(5W{",?Dx£嬢$^%L Ao_.qJX)=& ?3lMmE)*B(ӵs:}~aP|;[6+Bi|+ ًRx] ~QʒQ黎#'TrdǀYsgnfS3Ya|37:DuxS5p*F8*C y&rBNOYPҹLlхWB3? 摾\ F@MoIw߹_$0(I> 4@15OL:sL@8 1T@u8fo`Ͼ"b{:g ՏXE~Hd?J1z}{롯& dejv&JF|KyG|_Yy KBȷ3%Hby:YTgi T5fCmM _N#.F{ yCw><2: :͕ʎ^rqmf@mnީ!n +u _*l$*AL>B ~eIT,Mݶ2JhU:$RwXEMnLK>Nb񼿑Or1ixZDSqcSekVIqbU uUD8%\Y3>@R1eYLuLre(L2c9S(9G@^d@mfRg|!n-iHp4*ӛ+FcvwZS4بQLq)9#Â%>f3SJOcQ9;L` rkPXs64Ppz: iф}**CܭܬpV+_;ij8(ėxQkW, t;(~$Iy-c9lϔ^ܧwDKFQ9wvB  VdMi02  ?#~"`e=5du ]A0_M)eQDYu.Su}ҵ YGXBM2JZgۤ26ڍ^S^ #۩ua[.9WH%Ԏ遲֣np .LqLs>S-'tT"UU) x;|Tq Ţ=@i}t 3W66V$3&M?z)1{A[;{S܇P\\>|, &!DXbAW"8W#uzSn#FB({Y,]o=/Do!ҍ"ν(mآl4o^Y@ v8wamH{w!#3\1HVc~xj#wԙ9YνʷFվQ`̷F\JZY 0Y ι(hnm.Pܓ5sYݪʲٍJ )E>ص N^P~@RIe#QquK~3*EŘ2iWn(E-kqݔ&OQz; )}Lh}mTdB$i=E϶UnVyZN'Fa JΆ#Av1{pn.&1ߦc)*ᨵȏddZ ?ȍA}j\?c AiI!p"5^W!Z o%Tʀ"`\r.*˖K8#ɀsylQ\iё T݀:%@Gj>tlRI|bU}'mY~bZ]vc$뽤/t++uI 1Jf,=)e.vkEtg^SFn,SK0j=Xa5ʶg>Qg;?Wn~\?F]'RMuȳpl0ZV.mgfxdjbَܷv97](*cY+7,% );'hEaFLM骍rd.VhG񉓠ZhmH, b,Dkt,HD`Di2t՟fKkX}[PK@K_,rR#jpӒfq3'.Qɲ3RעE!+x:|\,q'˂U AZ}P Y(?SWg+Ӵ!s 4ќԒ~X ՚ [}[bjDfU lӋY 4+.v%aF%q׷S06 qSntگ3v4C|Ho/UG_߬&ȫHq >`y ЗPkglTZ=u[TծφMcztmAm9Ɩ:\3AȞ9 Xтy MovIA45$Oېڱԍr'`HQgE! r=EK z1}&TC|UJCK DfJ LruSرdt)x/ݪ]#)4c%:Vd'jɿ},vkkSy#7#Y(:)bhѣLxkB=d;U"51WCnW4;CLFܬ#n𘲿`hL`3`M"5`"CllC ߜ襚ý^L.vK`~ bN҈,'[&V$4O!G] 60Qce\IoQ^7Aq,&-tngɃ[b}NM0tUI  U '& $-?ϻs.>r-ٻIF "˝5X)vo׭3GLg_ňZ5TMf Q whn,iLqdq3u0ζ4j85ƁBrg,GNt{40 Ka홉)1%0M^m 7E:9kt3*f[9:K&: 2s퇆S7 j9 )mv qaAC֡${?-NF(M?HkI觻!LHg5::&͵NUCCР1DQ\g::H齺Nn{ Eu^Q$Lv E=$)Ծ8QeaMM Y*Y*lYY{0 |kΘ5bJ}:2d**,+lC{Ŵ["=Ϧ{ Y<۫5&ŋ\4<`rnCG/R_܉COY(ZZ-ҏ0733tܫKx`X zŽqbQvH}$C5_-<" M3q9&.Jt94uFƴɜ`|̸ⷪtZ$\Cob) 9#9vsg$c) e$BV,ҳJN{ 2vzu"{"Qo %,Qcbl^*uIh5(R$nȓv/RW:ѐDdmws>*;529e 1Ꮳ2J;5F-R׳[%=S8(v :LZM>L/bRǡ υJ,#Z"8JIJ<,ʚ ƓyvtXG0"<,p9}2LxQs-ɠ%2{AjyuB'F8p2?sbYI~y94&E:M!T#cż@q2K |]\i&fe;R[vG'{U+LL8?]`o-LR2QSF5Y$CA˯+P !]L 3]\s} Kf&FI?C812ghy[=vlsA.L|j-Pdk[|}V)D8fX,|YX {geޭ"@9L0I{#}^-_x a|1Z%jcN~pWw,q̅'Ӻw)JmiƄ@(Q0P%uDt}/lINZrEJ.r6&ƎNtF&cc6 AMY!jשHQ*%Y;Q捨ڴ3}]J,fuGԜ-tM+ˋWYTͮ6TIlV5n7ܲD]sQ"v8ƷLTNcx8ھGBI94H1'stmr]RGM,lUknx%yz8]m'3pM|`nWԺ[>^+Uv_480P ?}###c ƙ_njopVƿ6N-A Z'[[+}c`k`7_~som8G'}'?w!Bտ( JȈ+ ]6=!n'(rxdgPz汝z%gbC,ΒU%;sK XѬe@n%K/+OQߴPRш.Db_ֻS Kr$]"^ŦiS1kz)nUrs(da搑wtVf)ԌUmJ~/r>]Z\A]GǴgg1:B3t)1TEny)`V|AaF21q7102ps53pp753r1uu6qw ?2Yxع8$H ȣ km0+PPC Gx- C'@1oFbKurvN`.iif ;['@~^-\@OF#;S5+b$lc-f893.vtO0 _۶hRn?C@!TH=@Hi@GEv*f[؅/ZbB<٭3nO-gՊ)3XZH1Xi< F`I)th Ep567;74vL8@`"3 @ ?_^m/67_'xH¥72S+*T+\b@J4Kq~6y6{26"<@n *};s@ݢpzwmqzy>~~xo>b|KOAқ塀69p<|1y ~bDB"ZI {mAV2f}ag7lXqQY{5?e hŋ Ow:ljtO$gsd ~.3&.hy\M.w-bxˠp9%x@.y{=FkdzzYf/8oYц 7 8 }yHMA/<ɒz_9_#bAޛNl`wZmad_b%kb,teOU=/jvWv[Ȯ!2 ;x 5iX68lpnJx'L qp nKقViXq_P^qsFћG8Au`J@'Wtz$ƸzΙX=ӵ^|sT0 b_N<>½ݟvW2ibo5Zk&1X'aQsЩ"@wH%U}z=vIRzEԱva+ޡư҄L;jJqK7 Ī6G<+ν{ZcAn_[b{Ԋ&QfpL8(Gb{;Rٶ# :'b ܲvhSBDhSUi)"TdYst%BF3NR"AWS|uز ' g &̿Ԇ#4o/ a^y{3d̳.ǠVZ9*|?eA5K-cWuSaz[5K>"(x>\궎vDf5o@`;56Kd 8>ŷ_r"}!\=nhyѲiS_)Q(Cgԁ5F!ZH殝hNǠ:5RHH(n즟dqBl/QPL·zw[Y>8>_<8QOj^۞N3S7;ܺk+|weg9kμf?s$ٸ8#8gztZZC8iI /~a OITÓW l0.HXq'?4\|"Nf6 38HsA]ѳׄ>lf)&埜Չ=e"V Q1hjb2$es g 'g?$˜R\B'(f+g<$3,sjCt}kPX=mq޺mI lG_Iu!yfXc<ąDR1!"ucvg@bL YOѰ YFt~כU ?U¬ΥAb PDP40<4\Pxq昕zZI+>97_jeG.C) n[-TNj' 3WsjjXa3*4qO-VoP܋\>:O*Oj#[EHe,,_{[^-. XON Ս$O@79LƢ#5ɨ^ܲ kn\\5?$qU8lQS v*StGB@cD7i$L*2+jWP9ձz+@ǘчi%MQ!/nhXx:Hj5'a# 䈔 zLN->)L[9Ȥ7JdN{İD^KKnLx^'!þwHaQ21Oԥ\XdICG|{6ك@rvcV/tV/] rh-{4|QKθ'w ;SH32#|2!1TЧ8{m$>#S?XO:C%w鬣F\+'K_eq-bygQUWvBLH_==kC܎+fّ~K04|3dg7tqHKpؤ. .-dWfB$]#+>>xxٿ#\D/D@cVNd=R K%C0^e#F,p/q_; 6OLT!ѮSq42ꊻЦ9þs= 'ᮢOD'j.`'PK"]zXybw h$RA<b˚~rn!b7[#5 =9-:NSۍs5'Y{jtӝ応A\r2)Iq_LcM'AE&eۋ3bDAO1)/z#eLYd#ZFZo?VJɎac!ćAӗ&j> p8S720;zؒL'܆C? #evj w%WǸS9{@й9:e OǞҠpEřYρ'oa@FDmA!Tw҆:Rm'\?򤘙]bin< /G`TvF)pLSCу;(s; W{;!""=MHȕk\ߴ<˃*ڙ|YNմ, qxY@SycN(w KY/yŷ8Hֱ F稙1{JH2N\2"ڝb&21n11sV^XJ5:eT7Bmɻ"hS8Cnq0pCSh,NkWSBxt#{fl5oz^= k#.BgEBΟ'؟;E_SG1Js"pDJp mז9$~EyWm :W۷iwV ݆CkZ8'ZR15~>* Jvף=,~AQ\|>, u"١p"GAvZeRxDoMqIDFIHKK_=2qtH4nh5x4R _| *rp>SNQT[hwNt*g'Mjgg457мRb8gtU6ЏB I6, #Y~,1h2bf+ ֡l^WHģ#-hQœHg0:uU&:W'tVU1%ŹaPdS v.1U7u `#*/etE.[ Iqh@/)r?+H݃X|*\:zPL(%-(["﹝߭2ǂG-+  O0>>:~_#IXAp=nש 7_X1X~JkDb1F[ ^rM8]PD3y4,#,sFY/ȉIe^ ^9ʪ}cZW/|6ږLtU{K56ځW왛G|3'l9mG g #3Uz KK~rujEǤ9 C0I=3DsRHP4$UJQ?ԃϴw~3g+I;Mgc&靓4K&^E+@WjV cf`Vк{QMz^ޝq3LWB0[4\^1U?N-YᘳdW~_vdIO|Uߣ ߅Eq@3a$񎇧Jzg`h25bY_S﹍Xc~(-Hx\{Snvw7wzF"6y]}9/nny_@G&' & o‹g*NzbXg?q}+Wv \5&f5T=[i**=o+ (Y\N?;B"1CF iΏ?+mtF)(y_yz[\Ceղ,֒.|-=py۶G[**BUf]#qt'I%wWO2\A5H r̀Cٝ[sjp;aha\,;=HAp3qLq~ :A^@ׁ9amǚ~F1}(^!:w(,W=#n%LqWu0-P+dI2G\Z=2tꝟ/x/)ۤ#1䅡[}}`\m,KfZ#--քild0vHU!C`G8<{0WWJA6=niϽ[Ene #- 5[&//綰,}*|})m}rtmh_dMn|؀`ꪡh!n\$<5/Nm2]ٚ.Ƹ饘z]qP[b'Lnc΢ +%9oOt = `YБ 3́̈́q<' z2֗1_0%c/$ "&-&\p{Y=/-H]z= ^~ye~3?c=[&5bL :^Uÿ{6TbKC;Z荄 oS`~?}fٽX[]Ź:f]Mxpj$;]ÇܽeO~by|Rg{sTc=$~s4 p|CEEb ٖlŁHX).S{PFoe_NPmbbfImR;fx30[៨qшn-{˰p=|~{Nc~wxpXlrqs%RlOiΫ1xi) ;Sgo. ;b7XOW[SJ؇ԦM ˍV MnϤ5y<&^fI&w6Ʒ{qu(d) Mُ̯T|x٬vicl`wްj+NHԙ: 2^@] wA+"~ )_ ~%6bo񄫲%Cيv?)SXZ#ւk XY*/Y듧*2hHrg룼M+c%T>u٢OЃ}[#tnߣe hExe|oߣf~ni!ݣ߶Ufُ 6YD6y܆jh<^u"E߬ \cxbId`G,4`eo~*,@Ҳ*JՎ :c Pi]J}XVuiKj[K i?T[ Eri1Ft_Pla=Vn`U*oadEyΜќAav%MMs6BNk7d8:CTS:3įPPcB]jMR!\*X30> D];( C<ӳ̈́2Ȳw)g_Iʲ[Azf{}Zh%vu__P.;^:YA=8,ߒy 6#:1Q9EpK9* 5ĿMmpv2MM5j0?"ǧF6zL }u 6|п4(66i "}E=p|^9Kۇv/jx$c*oDqlڱe`$EF ggT%( %$Z) )z.§+uzKHӐǰ*޻Ϧ00{)ˠw3nb}&s,ٚS`0R8 Tb;J%=iZ8s̗9J+~|0\?ʐ/T1Dq?3TO(Q}&A(@{frI 8bF.kQ)#:9]<r=NHlu"@9Is/U& 8aPg<ϿKcTn{傍ij}`gؾ ydS{Bф/AٿraS,9 YoBYiNߟjQCN8AI+[GGY&1q< g0VL'F] ]z.' B ,FZ76o|eN|@CJ ]Кm/BV/+:AW0EMj=++qSHN+G/֪HF:S=֨2oQrvk*WewT䙔7%rFALmS܂:ju6t <.^‡EI ʀ^pGנy6~_gsȈŚ(m70 VMcMh*cDI8Ta~X4f1.k4:RJ0tE}$vD\K/fIA=]H-h`lF{IFH_#04 ЫsAq9@L1{GL/=y6>Uȃ<} : DQa2`"}tZcleBzDφ3ȶ$}b -B Fz8/$RHʳ|y֭#xkz"&ZWDxj͵xh8hZ- 8*&SJK9&r6Qd9w x)LCǫܠ9op-Lr]bbbMs.hŢZ<1͢wEt?/@5 ?c߻WȾTX`c\ޝc2\'&`${H)ԐH]C3DI'"Xr,Jk̭$])lz2w 'SoFS&`BgX|#`MVV{ĀʤG8!*|S|f B(bl?( ,QDbc!,A죳--g22SuL5]RoO@](MPS& ٲQMq݈O%}bZ٣ҳb "G D0\p& ~Pbd4Q/YE ˓V[ո yiA4++s,+pu9~p"3;fn9`Y{R& 3< !/<yhufh7s&w!x,%߰$ gz0EâYUMhfs<m@PTnC $2@`øedɴoF!;p̦eӺN460e,#Uɡ4{ *+>N:*.'%Ez!>vMfǣ_O^$%:,d)0f[B^i-aDNNM%\- #VjH| oϤG[e]Ў$Z4sr#W8#]_j?KY(a~a1}_;b}dx^h弾 E#:h؞>\խ/40C*NR#VwT6ިȰUǁCW=2[U8>8DxCkAȩ]F]KW_)k0W %rᄉ vrLņfVie{&HYh BH"eiz)3v {HDD"7MFn(6B^eǫMj<>>U GR2v\E%TkM{:ǽ+6 ,TճTIda+R U q'[. ٿnC>Ao"ຸqɒM?^s"hUf §OXB]\&?uV#:b5`@ \?}Q!>{oPy7ThDyA1 13w0 ƜpLӮ0-iAYd/eY1BLw\kԉiC`\zFS4QqjF&1s:*~Mxda7 $k= W4tABf`<̝~t{E:Ý/ 8{U+Uq׺l4L zf^w4n|!~33-%w#GWT&ed>>H ~\ tAp_k@g80#DHy񰍫`﹵HKb󫞮L[iҒJvڣG`REsE( tۡxN3ъnWl5KDvl^:d`iXjtE6`lx6zX0JnEl}")NU6/8*bGm..YAj *RsгC XxPmC4ay<}oJYxE9?0;;xS*Ȱt^[9/kTM۪9}#ѫͮAvR3|k~QOm8kZxkTH(ÑLb kߖa9ɪ 0DTxJ6.g},̆Dw |sf}jptT:ΐT"= hFQLM 1 u*B۪rL6Kfm69rM2O 9h}"tEJgLc9[E:qYhΚ!Aܡs/b=)+yfl<6c V~'sQ#l%-e]`/üBy4=hP-9Ͳ3_k^%} zF{̇Nb/:@$z ۘZ+VBo4W`j,%&6^}{Щ7* QRgjYACRlU5J61mڷl~mz~43vX8,"ΩM#`ٴUK^$h š[l&\G8t6ZD#%t`N Y^ ]"RUŠRW(6O,ʓ]ȁq'9 JM["S^|qX Yu 7-t DE>67't{(`Zd#/uq#E}Sn[8VrrёXTRxZch-$e0=,a⨋]V=&ĆyUm}E;C= C]Jb Zicd:iYJs[pņTp|\-, ]LUͼ:}2\(2@C2f $DAp[Z s|SG( q-s  )zGhT"W鰈8[c(f ~#dcVOqd}ԬY*܉902b+H B8kT'VxlG ./@WK(LUj\)tdanW,+uh+u"xV–g^\䞐(q5`(k ^OGDQ[bq^֢pAvv s8c>@z)8{z#$C 4&Qt8ΥD1i@C#m>T,nBax`v(9gP1D}WL};F Ul|,xq*8&SC f9N躴 |9,kbӧqaZA=.;]%5V"ը Ej PBæ2& (<4=>D)tΥGrD)ex#hYLje/r,W)+IӂgG?yڲ-> (GӼh<М4i ğGiOm4l3e;PP>˲èYs"[WlT=V`'GRE,y'O=DY@f 7tB|!bD狎"ygL%ޡrn 3uAiα1f QZHB"d.D'3|C@ehePȋ<5)i+q` 2#L1V4{'g> oVS .IEqM4W, YZ6ɤv2,k,gc!QQRRnN'ݪ?u}wD(LEE~a:IT8e$ OzѹLǟ@C_ޕ[9fv]xh`EJ_ۤ%XzGHUgTRypRrN<^E >~m찒G2dXm١8@'ڌ@~l8kzLo$;D ZM90PjRgʋ^8'fGzn=*dplۛVdYTf2Gf/ñ<)rqXb,7|:/fQUlsx5R4ᝑp1mЋr:}G(u?tmhl'3m۶m۶m>m'3֌m۶{urUWuݣOF;ё{C`Abq&dsLu).OƲ4R1 0YZF\\'hgܴ0[Cc:ˡ?_鞪&눒aW}AH9qRNmX ^g2=E4gˣV~%=98Cqx2VSP32c-/ĎY +e[FӲ8M-͙u"]Tb9MPV5yah0Q>fQs`ЖLSd?"o.p4I4թA߃PuX`F^ws'!к:]cjMdWo:+I >C0GO r! O!beލJohSn&b"_ 5`JN&`)%i`y3&+\9 #xb9vmS;<7ycL2L)ٓDԴ <| V>N<xoR{Al\e4bogب{H0" SSz>- 6+OxFnrUGlp䞣ֆp[pFC˽З|tgAp_>%$$尠@0MRƍ̺N'W D{g`zNc89Sax|: JfNWJU0}{5 }2/.]%gS6tZ*uB&vN6<ԑf1PH's]lr1 ^f0zݍQ$e^~ު\WJ8 kp[e(p.m]Ru&*8)W8\dE81i۔څ8BX$DH:u!\t%< "=o(C3We9'쯓?mw=Xy;|@u)uHCT=]𪳁_:^m?[mR\s˕BU _?qN1AÝ;G`BcJM?7hO2@Ñw# h9 fPw?] h~i4EW!Xb= }m)61Qgo)¦7:3'sHzV\P>dSQVt¯#ڦ&XܐEFSo70aVvh((;wnGD#F`ٶccxCYi/:7BSFǡ:nr&f.#hqo}%d zD'fۢ%0 S斌5K4qKzlP;Ɗ d { ҵI@|`ef4wO"b.Ol6%tV_Jm=Mr#}5}ÐLeF T?$UMΔԧ ٦&~xɐF-b^ß}s9eJ-v;wph1Sya?R Ұh #7ɹ36w:˯iqB0*:g˲.Uq Ms-7*?!~nuqJ3J7AI*esm3.ҧϩ^| ^q]ɾ^Ÿ>&&)3%^v푪K>C)g86»?:/l02%K2G>g(ݯ̓uV|jFIN,u "ձ&t ff"4FuFkicm@/] n5 K!2S-3Z,Z9y,G6ğƚglsFWo|uqBtiH%l.Oޗ:kk%"Ҁw d>=^o4ܖk1Vcv9r}up4͵W"-q פrڟ-끘|U9#UsAI$`W9co*m{pi$҆2uN1_t,asL.F?yKU"2xI]U*]lm#ƶ8VΖg0Q V%X \VhNc !܍E#)GGݙ~xY%Oa#Wٿ*>SOdS-mS4Uñ "fǙ*;QMQܠTᚫwͫ:wwAQgx}oI`tfbyfE*CKVbۚ4RߝT2˞yeI, ݩ{<ح}ߥE06r 4 {K0m RdC 3̲(MΨUp/Uy [rgxv!9Y1o>_G0GWBwm,#LD]hqui? 8Ն\|vFN.r+|^؀M0 r⥡K^֌^ѐ8"UF WY% 0ZzkXE3saaG]['BecYDkuEHK Sؚ2/ێncֆT;y }a: {̋p^\+Qlhu&;YmHٜ)3-`T,ic`T6ڂhohԴ4uCʖ.4LՉ2WKA<ģHi_YS]6?Og=ȞF!2.8p "|^mXb`s2il($@-aB?'ak-:VgQe0DF#ϐc)`P9nN(3^ȵ"#8C 19[5DQ̪Ut:lE]5S|N~ȳtFq2Cvd&D%t|%pqal>YMz Ω~Sp^h^ޒs'X΅;~·h-Gv"|h&C6H5J;p0uͅ.`ze̋T%s*SjU :V%屔tJFڟ֬P1؈EaFڋx?IjNfXΥ2U˭ ?${3WUE A?V&kFl߇6+K*\'I]0G[H;SV*F%lmʡrmٍšy5`a.'|0Y0nbݘSF-`t֤b8sU٭W} TYg$K"zCu [ \ ~ג~rgЕ 4&IsFyۍZ\O`1h,>ͅWEGqu ?\xG-:R.Gg/Ю-ffq0UZ怇<彥:]R"Njb#*%WJx)E]Y]tɚU TCFpze&&Y6k$h8ƒw ս{bd3t́L'CEJfen~n6FYUXu1gʠf-{t ;-49ECI@; jF}Ev( R#k)+rn~ӡ,UX&rmk4h$; p6 GFOsHG~6+0Lv5;ͧz |tfY ?m88M +\4mp _3kIodh/j7ϿtO "]2] GHN԰h~yDbxn |-OdOE1;ĕg픐߿aAqsBͺ F~\geCDXm…n:o|nP hY) zaSFlvWڏn#CuDFIm0#v 73B؂m] A9f';T+Q0qog2L  / B`]_F68 jy5CO(H;WS2J=j_YcO[,)/yU+"xt9ífm!WP#x$C4'w8[ ?kE=&ӻtP<@|Y~Q _75'1 N~?[yӒxc2={iWDB:+Qx gsI'Miv;Y%1hlC$t aضY zS.v@,mր {+R_~n>΁=8›'t>e2}]_,,kܸ-v<?SY\Tr" qֽw6wy Qߏ?[E`;FԷ )[:<^)~EfYz4X*LcK"j m3Xw- H>Ť_D[_ IRZ앛|ۋ^9E՛>_=x+4?ΑMgjSDu?)w%$qH%$s.u{w (T)_D0x ^x%ڐ*B̏T-5 ?;ǴK K}CiʼߋF/c+ C'ɰm"'zyQFɕg'Jr=AU*#:юGpjqFTfgJ=MvlX@~hiZ8S{N,bpZUM fJ`uYSge 9< e8U”ڐ@J,G㾟9̥#Uʋċ:Ar#b콅۶6--L{)F 0b n\j)(}ٔjsIifY@bM=Tvӑ,90,LЧ/6jnxE0DQ;|m1!"aFaZ2W\JVJT^b6om@%gg}3#tӺZWȭk[/,upQvIm~}ꓞP[u'#6Wd;M)XϚ,Qh??J3UÎb!+4()~Psab3:{q&M\: QVΰrt+[5BXV-{cpHqEQ{k&S`cYf;@yשUlڡ&a_HEϠu{jVV UmetZH/Nc ~*R_f6l:˜N>`UvmM9#~0G-)) Ą"@Y焌0ɚr7(g,&c_):)9ߪKeAl\!p;#܏) ?7C7W/,^aYwRXȦBhpSr\ GɄa9U{C ~ʶbMZBFvGcCK*9 T?îJSCHa^(L K٥o 2nmY? r|M ]ZA۝AP a[ 4'G6,i)hFXB9:Rdih i|nc:8b [ ?dͧʂ<_ #c` /#[ܩך-ٗX3$ #UL&8; L߳(+1=c0؋?%v|Yܟ^S(q$n Mc7EK%ixyGiĿ ҐpGۈWi1_?Վ]Ѓeb k%{]!7"(S7kH` tLh0IUSސ&S21fG" G]4ܚ'_If9 aSz=Mwρ"3ZmAKr*vX b!"nv4z(Z2cs=D[ir015=Sw?kz5f25cCH2ITBKR9+ؼxIVԈj6umW EW)w"qAwl-?$DQEC*:? /yD̚}$$ #<qDG_5#= 5(@Z%O5-Q?w첁P02ɧDJܼ($ƂÃ8_xZHAOW;}H? x}kg‘NTI% oB8_䫁܍? | ) ?U}5 !A|v) nTS)"9W3M  jXϧCb@ޠ0ݐ[T@0R6/k`#,&X!mGW13,Eؾ:7{AI$0<}tW ~%B*=Hq @mە߁Քl}Cc u'n=Y> iV'䪧m4gIӭ:πN^P^hȨ1S+.[-(ʵ2Gg7RyFޱ!?!WoQ:ɫ2Ɍa NBlHU5Og$D9cv N*E2M; &D;Y2:fo#SBb*ħK Nlk _W(+D 1{F Np2r3.wTY&ipV.V<@VQ=cE'o~,;.j#/6_["hNSeVYD3k1L4^[V㧟5TlC@ʨ ̭]eج#*cjWAvٞdp۵AȽ"L0 ݔf>^rKTM7;%k0}֡闝1%YCMdܙ:iuf-.;k-Vbfּ+XػVDvRYԸT `.36On$($x: jNQY? @-W$ 7I8Αe}c4}Ih䓧[Su`0'[Cz4u<V{C=yƕyOh(.yv}@ZUw;^[ \_ͺy(@;>ٯt-ZՑ˹=ܫОxȺTJa0lgIy ffen53s 5Ч`VOVP*[mt.쫾 >Dشtki y:((\3P 4τ U\I: <811j|ӢJaRQ/KH9趱 1 -X0F7Rp ]a`+ e*5)T9370c#jLti~ٽP o Rl oǏ䄇U  N{zWj'.U(p^@ @3w@w+~ %%L9_:6sMW-sXՋ'mjXV=pʣVh.)f8%ꑑM_nZƀOI*Oq⪇ H1G?Jr Fv5HWˮz`ir Ͻ ñ>@E jcHseZY>~-؝U_vqh~wM~=e99U~r#Kj #}yF@`c/JDh4tq,(S׺9OXfG^Aj>|b% %6UONN5_nӵa./!ej]GR/C?x+W[V})yɂ1gm}ͨEhOK$_Q`yn: R.}ӓj? *Up," HF%O@˜6YY~$c(n[k[UĵSnЧ&B:x$:!?UuTr$Fo,a10#Rubޟ /4k8[eNa Agy$2mJjQパ;o%c#PU6d h79 pa$UǧnKo|0z#YN,uefߧm DLBDP7!6i ,U>8DxA}:v s7&LG+ LIH ya>K8p!2 ʞas\td(qhRfT e  g\nJǰݮU9/Dc39@јj ,^rQUjzxrzzUȇH6 3ɬ A04O6ᩇIE*,pz<0s>eH {H;ּV/TP.&1 @I=Oql_Vo;E@Pb 0Q"O>ZE#ʶĻV(kWG?$@KQZJԴVMzF[wԚ(`vQmMN >P&j,E7CP;xsEk%}PiHP}"8E_p0+ !3}fM͒Ypɥx #–cr h㘾 ,O)L>$# fC!oeGd fxX1 -/0A a7bcAții.GbSҰ+|,جqShLUaug忠e،0d:;g˛ glzaιfq M75-_߂꣸WJ~"S3JyH,m -_񯀞wsBT ^,%2tYf<@cҀ5W *^ނ7u x~?&slWJj橾]G1@)c4]u& ~K* f!{>]7 yu//^?uS’xA"5/ǁ~{9z`ם$r=~4@<.t_Xr,h/'SRvU J- S' i흆a:@Y;K#8 : 77w $Pu0eݡ ..W2E۷ AhGj(p (ug+vNcʹ%{2I2!PeQF="t.Z3>Rkv6xΠu;}(_u_%aVq(b`;xn\AZ#^DD<%CEt(&jJpn"ZKwrVۆ3֍0[‡(H[coUe`ȳQU~[RiSgR Vb+$|_2Kg9N҂''yj4A&I@^akQȡճ$ ʪoWaB&^F8T7biQRSɟ%1,5=$bcrۮʹ94˨=IvM8B*'*F:P3|BXFAdt-!a:֗0S}gP~BiIUk ܹf"jOh{JE;.=im4 Cc'K,9A׼rgĵ9qB4@;P9zE<)H߃K^YPӝ|\]}1dJS٣07SY(4gՃÐMqQ`Q1NpW108J@;&KT!c倓t6Czm6cȬ!DH`"ѥC3۞x_%2 aRn=KAc1fp7[^<2_i[*_}98/%,>cO\Ɖ?3{KP+H?] x~Œa͙sBM: 93YSٜ ,30Ou{T09{kqZPgu9߃%%EQ)hw 5n֫Ѡ7Y}^CeY/chi=i$ OCXt#8GgV}!t}Nzt ?}PF _E kfH`G[V}w67qV[ywp'|1mSej#1v5l[s.xFܥ$;`S%82s>l5pz~t8G/\${YưNP΀I#V쭅I [9lpƪh]IZ +Ea.9/53h~bB(nPbk;i6B,HG'K*/[Ȝ~y*su8+1%s~$X!ue)Jtcʑ@< VtG;zBώOI6t;^_V|.Xb 0ycubógR l{2/:A楏o}E?ڎ}e2!vRW~ 6V0A;tur?_f=/ X6sO ۯrkYn}60v15&'_c̒-=N3TQ.`-2x-rxnx|(;dSi-=sFt0 kpt] cA"yjC8dzPu~#OthWp ,J3LJΡ/GڇŠf34p/G/2eZ/LU<2;͂#7ƥyj{&r8 ǂ 8"H LS]7&LxJ}Mٝ8ـ%E{9Ho {msoҼ_@@6D`VtՎW  piz;.d'PK0Wlޫw|7c卭q^jqwjӇ>[c)8'^CڶP穒#nAm+mro|MHrۗՊc)^b ߗ?Y'|c?9j4זхm+իj#sF<%ALD9ڄEﴕ.OJ_;F}N\iZ)8WRJ(8<\^;p YPȑs֮S"Y+>n-Ws_l[c<)t5U-Gt[tn7tQ*_,&:eЯO9b%ʯx=vY 9E)5D%#0 n'Ϭ-|cJ^L1TlM\}풗^IB^$47@e^J⨏.{`8nЫ.}dCP=\qWiSR ;l zD֏ w:wG&`5ZN }vD:s]IW 6»j$ė?18ڭM2NIKMyZ[JBG+o镂^>`CTXӟv5sm>W4?KInԈr̞j\=52XZbgg"=|&Q |/;y?~|,m/oE3loPںi˕zZ@ZѠj+5=2npn3i=)m`O[dhMי=[dQqukE|kPC#}x-yKqt_nlj(*?Jd]#)vmࡀ.AJ͠6eQ#)6xViH;]PBj(,vlJ~FW1>H}yݻ4܃*Ķg~}fc9"ؑX_$=u]Ӌ۔j̉sIY񘱰Sִ8Nf5]F}._|m#iWZϺ,KP. _?q:'n ܾĂqbuz` UiLT-B K&R!EWCgn9绘[&?ĤbC\Gі^Ka'Ijƒ~YJ)JlqLqa We5B78e :4"K*9(=pĽZ{TıW0]yN|?XGJKWVr?1t?cvLk 6E%T^Y@X'wY0ku7 qs |tߗl2XzE؃8O)9̱VrMp0?FMDf$ 4܅E R}NC4wi,sՆ@N%ֶv(Oz^^&d ]&u:l" e1Ar]̠L:jֽUhFieu.Ս(FmxZm;oʁþV&o } ;#ֺۗO xӡ?{d:֝qs Tm%CFl:p\K.d}X=Rb.Mr7:E(73>T!4|#z1߮+2y άO3:/^_o wbp% uZ)6N>($Y{a?le%bnQ8d=v @`^ w`Em@[Q1j= 6Yi&Gk}]˘ex'lܕ?co0D{^_}2pu; '+Y6. ϐZ#D@J~,]3%(pϣ~?ݾHS;Yo6Ad)\wO *o\56`>?E-3 x->{E 9^#3نDd$7 )>rj߭C#snntrsCG{3{S4߃S( pej(iŋ ˻o|%k7Ӫ=FϘd[%ˈ4]g,@s/تI-!!Jreۃ-,'>nb`=b,%q_#9g=fLI 19>L}f@}-t1 PFCj+`z.&?zz{`Bf) w!0: M:h=N~hFڑ'npbbLI1Il_,{j~(N1OY|OFTh6uŴbR; *.=r 0KS_S1!eUg)Kt n`/`NSM ҝZB7t̟ D{-Xm΍ʡ [n~;=9`E%T%}'-☶pY;W,UT)ͺn|0X޴0Z3G "cFDDIEN;Q$u}6f>d<mHV~)9}γQ(fRGԟϼڐ5{QuTs-*$zh:_8|N0ۊibAAޅHb*̒ac$7}2G潐l-b* QBP%å_z50P-R2Ԋ8 j3mhHuhZ1P<2)M^_>,LiRP3cv{ '= U.`k܇J-qtLD~i"`j}n-[;IM aNҥ]8Opu#퐧vN9Jl̙,;=K F,pGɊ҄)ٻ-( 4Hvܼ.P"k=vC1DԸ8 ^Z2c#3H3jFo\1|lWm-)J,x4rJXD 'td]&dgDpu c!bG2eq2u.x%U (rV=R:!cˣE? +!GX:ixo0T7զwMOQf%KZf9+3#3KA RY7'U)2Cӥ&{yQӘM/NJl41%YANU@pwZjx:[ۄmm:jVڢI {?]e!MH;Z ;qsO_GS)DϺ/y*fU]1a]SJ6n#.w"v 4p%ug,жS.`͢Q,jo|j E+y m_[&$Ls|0JBMZsdd]s?6ʣwl;Qm "F*lmYљN!%6]w(6hɂ1:d6xZnWl48*ɍ{w}rDݬo.N`{\3Bq7 |lt=z9w:]wrT JEO;hlMT[k65fc p:F^m€׮f2Uk5^7wC.xQ@P{2^CpO_,mU`-H`XYLJ@vrYtz'Ԧf}xL@{&fq\p:x8_ѵV02Wk;![Y'~0Cn;ׂg2k0SQS%ʹƐY7gYߤqc~*mWǏ9_rɽU*yHۆ?Q2b咓k]2 aǨ^ 4f@D驄趴wT); >ރk \`}S,YH RF/r_ma"1wDD"p#Ed(UT}0ܠ>9U+Vȣn0֬>%or 0 7L @aS\|$X#(+=[JeR+w#&?d+_%=-wBw&O +7 agϮ3JwBmTE?G(\+.9Q0?\nqk"5,EMsF‰cTZ)}.Ub J Kb9'4y.Zl-q-`GSIUu~e× o rش̞K_LRkǒ.kD #(hw6 |pK>=G~},%a/29b]6#$aDI02H}i!"4LɾLüHcՎ!I#2Ii!=Q}lwt#%Ɩtƭ:[67 QuO.%^dg府6Fmul0.~TnTlcoscڹ@'N-0L{>4o?͂[:/UMBHQ %:8fX),DbKrI0 ')%|ɘ ,T\q07xKw3%Osw]zR?[Xe#z DBoRQB;B/- o*|3M&r!1{cU̢UY$9(M{G+)r5&Kif QM7hȬϟ%{d%wJ?ughPp p*F "i/fqd2e┥[Ea&(A1isϿ%nER)uxR!.aaDgrQdÚM;T愪I6%ҽ -kU,OɠƓ^N@&Yhqo9 BPnFl|iҮpűNNJta`dV]NW$bf]ϭd^ZM$$Fѣ5[%163e~^(2PwCN)4Xr#Ld 9əhA>Z86T }71nvg/3@]bt-k1)#o;2_[Jx䓡xE?{ӳ&۟Uh6uk re·k M1oRKX{p!>VzLi4]XD)]kM2 lٹd|3Edl[r{g2Y 'ʎʏ$H[Ge~8D5$YY[y3H`4*_W3%zȚ>'OՊ E,-0wjDZv@XAy%KYU,k]Oc#R-ӆhU* &Ճˤ\bjwO%k &auD<_uֿD7 gM؇zՅψ}p7/׊]oy֠x!g̸*3a%R,7' E5+u͎ u: 0B\J<$?rZQ3Y1xJj QNm7ܬ`󹋎 C_v*Y%elRҎAV]DZ0rFz$PWr+U'!JR~X={/U d8jѾ@Wo82#tKf쒶wr͋ur]\>W|id:f^V\7[*64RAEvJ 8{ u酝n72 +?S<J,#@6рȷIjD'{*j>:W0ޡ{ubgX]79忘@EX)ѹ4,c̓I?j.AUn;*7b'-@e^ Sc_P4n[(L}~Mԟɜ#R~뫥},7;9U75{C;B n:Z'73UZa˔-'h4 幕*OU.t+RQu/[]{eD8r-=d'-zrȭ[ /lZl:, co0D`B; V uhpV` ѲJ)Q#awo˪IyFʾ(z;n{\h}l`*XTO1vd)wowy$w}iwQ&ruddo7o|"ҕtvd)VzL3uk#Q 3 v Q@(CE~@wִSZCLjy#>sD;"c2 /H>#_W$C^l´b̷d2)F; Ea̍[yvROC_`]a;"GjH7|M^Gc]t=+e* [_b YM(Y( %NO$!S(eʱSg%ôWK֤t|kL{^Gpl#Iêe*NWuqXerC ؞Lǚ 50L; ^\Q 2(P>)ݗouE]>/`S!:D?G"$#Od CƂaf)cpn˝8aͲA)2d~G&.F兪=*6=.8=cϜNpf9;3cgn *wLyӛ ҭ })8s3 ܛ%ԧ3&JJI<9%yyOS =ʌ ~n$Y?_!e+XsbЄL)RS.+|: pTKr-kebg7 tyW+JoxSTF )=VUޅ:vU:vU+{-4/dzR,E%4~yMWQ^ \Z O1: t*:*C+vͷcx1c%D l+o_;Ҳ%'pgg'ϤŠZfFs\ԩtz ?d] 3#Jj)-SBJ'PFrpx&uX/67Ø:X]U(_?}| ҫt1j@Zdu-a&aaOEQnAw9pEd{l` 9hIiOz. q*e\a{ƔB FR"톲jI!6.. vM'/^+hjY&xL:`E b:5&G\.?{᛫贍哨k]B>!lKvb7R,ߛ쫇 {J?%To#P1/[ Fnnb#@%~v]SgZ`rDZl'V$AOZMU)`4dpvd~f{K"S,\b@ L\L9cv1esV.D!]&i}m\8T0?g;Uj৵ў&pŒde)ǯ !,,C+*:hTAc^WI8tpɱɂܟ"_q&t}X{@bCW'֍ԂSR*헗@J9ii> R,o8%O_v4YE6tj y)lra|ew;Dn,A{)0u+@*͠A>;`W99a< sLejADꪌMr#;K%i|+hJw-26TLH:eU? ,ɣxIFG1eJRbQesA5׆Q$U~Z¸z, ^8G _4hg0 ыb4qzYsg LXAuhf8%ọ"?bsx8 DfY2:7ގ)CY{Kz4/%*_Hb)ݯ7)/*%(v6 dB^ pۿW:-akjA"OÇ=(äP|+bKQz&jلVf-j}f x86~ V5eWr4у :]UBz M>SR%!d`xڷe/͔(s?3CuƞED^I ]KX=H[YBd+U{6),NVΆs_g7$ ܜFzRY:vmT~ms S ś!X H&(UDl)R:M8,/ؽ}H0`i;V7TZm(huϹ{ҽi܆_հ~1woĺ_AkjƻW4zaKwߙS!OI,]ʠFh#YV3τ1*޾d^8ըe?% AYzHxw1kCy zO٭&9.u8}9"*9הѷO, ԡ6Z gD%҄ѩ3,>w0 :MDĵؔ Vw*GUi1c$πgEEwCQۇ1#}X^\t@cB$٪ZlBgη٦;3)xȃ$5B5M h]x ycw*Ό@pz~\Xzt6ߐ'DņS_q/,Iݣ_lQHeG(؈ l ]k`;ۅe/%eTW'!c`,+˞7>@dl~&i4:.&71DzQ+!IQp:7 >>Gd7p%vbCr=e`MÓ]m7095;LdԺr4oN-K)t js*PCƞ΁eY]M?b?2?3 # PVP|Xx* Yag4& :X"Nso Ar|q}{ ,dׂ}Ж;㍃o˹=Zpy[`E4%vLY0YEžBh(6 =UH9}')ѐHP"B̅ >&|O'BˤABW;o }qgȊ-> w%+Ұg!ZqBܥ).)_601'WkE wqf1(${8N2QӯDp7ARڪϤQS؇<6Qx/Q zE n]Z(IJ{`fŬ_{ aI,^DT$OF |8B~(_Sc>X>ʉl1[|U$QﭱnIF9#Q2(jFEF5Y DoF'ϧ W +q)t`^ WϠM㡼ոBO$ ŢozC^Sk>O^1f*b1ڮIwA\/BhaA0:t~Ӕ=nY@aƙn)"f=98XB(7HhX[0yi/^@'kLk'wprߑncAŕk`D`%%Vx3:>ښL/j-bB08FyU5 XXX'rXxOO) r0܆(}6 gelmAd-,p`1meUiN t*譵(ka/L_ tVԛޛ:Tge_􌆋^2X1ɊL]h<2H۴&p9h?LJ!J4\Z>ʌ fzn\n?7:Ƞ-Ի5:W'P!9kɉ.72+, z(?Ɖ-)aќ:1dH!g/yo(xmSݺ4+k1d 6V[IKwhЊY [ aZ$G*ƽDPK*펾+4PK67B styles.xmlۮ6}Bp}-9) H&>DjdQc;_CR(K|ڃ@syӫy4I^iH8]=~Ν{WdCp)ssvHp@4ޖq X@2N:Jb] d7{ֵ3ǭE# dwDѮkg 2ջ/If*v40i_ndWS{ :Or҉&5^P}KCzG`O/\K/cۯy aU}>dg :><d\/|)RR 5ԳR=L)qq&6'<߾( $g㻔9OfA$sU#tFgmYkN5LsͪBUmsҧBe6.@-=Euo+qŋY7{4erF:W'ԉo b7:+ziuZ0׸SPATIÎ1N=^m;I9{ 2؋!đ>¥4͇xF֘K<mtsu8{5s=c  DnmvmTȝ-ҧ!Bοvk)JK7PCpy fI9^ Lm}!\oz @k3yh_e%L2.G~wb>FW}$BkCSE(6zeHI|g.Qb|F.RGG]Ǖ<{y!X *DZEMt=w_jC@)(2lU˫B^(Omn7xvړj[OzNP9ț.\EO Һ9 WhCƼfvw#ϙ؛{AjY* x`|#I,,=?_>eI 4)>|g箼J}͢ t*Κ6¹G_ͤ CsWg' \ >.4bM"oc6N ҏyX*8~ae,|%jc@~ܨ܉koE2Y1>RW 囅z4CNe#`_VoR{z}d(asy5o͵SS<0<@M*Qp!sbCe5)Yˀw7ʵnZiNI!86rrh=.BHOhDLeNj-UEȂĜUXeΘ ,Oחcoz2-=~2DDy+UjvWI8A@er;a &8vBo·P8ɠbBb(Io>,b$Uq,8m#0 &6V.t!IhH(!e2>!Yvr .wcw,H[J2 -D>{Rj``2>lSry~m+LL{[1-ouq69;l1xfZ5N NmcvCŵ`wƕCE:oJ']Dy^񮅸Tj=O?{kk: ^!%?wK,pO%V&%"S5T/w^V,GILuy%][]o8yӷH^WnCVI#[t4} Nʕ!~mϖ1QWFK VTrWU!]dkq* l믺@@0sjHu+ 87-ƙvTUngFqBY!B1 peu3,E0zir>3)ւ9sÚN ؔȻ5w:]I%΢֦G"h7W>9(bnTd ,G3i~;~4ɒPhks.91`6.U`ȳV_v*ժ"sc 0ocGc E5 zC>6W[M N_i>瘭 څkT~me}tP (jmJw4}+3Aue QA ȶٸ߭A!78<B-ǟaHދ8$ڧE>"[}WL1=98C˫Vh<,њ%!1W/%ˉ[QRˤv\d~[񴘰8"S0$~R7zF@Roד Q͠sbPFNF+㺸^2/.u/X!:\xe=~ sZ4oPqԽ2 !L?x=rV_gs{Etଔm~Ռ~ EOc"$cX90Q>Q ڜ/&/y"ī.^0B!ˎcR6w*u ?6_qCoU_QƳ媱eNbbWg.$B8(WřߏB:꣖(dLe dQJݽW^%a0m'#{6 K_ďSpk pMr*³idSCG9ou=ᢛF -_{T>\D#5Wn]ݡ o:T .m%-+WUjOTPC`?&s`[ z7F"t+ C>'G:N2ŀd[ cB}g.%`#-Sh<4R!n6x7 U>%B>h _iC/a Hg 5~_|igT P\6sh:0H GF͏-9- gIT IA@ d|D\$]4g@ iaq ثK٪f\ _0UHEb<$G;0!φ>Vˬ-{ŖLQkmgif8L'J3]ntL$$`O 7}"/Z4uCMC'Ưd7sWkM YƦ ;`h]D>WV;8N6w+_/ֆF* TT}M~ë[Y!XiF躔A24$8,\SWF,9Sb `a @GK$MK,t OUG'0?ɭ_>T_ +DL0WsW"ffiiR>_ w`JRΘVUN>=tlbVrw,̼[D;] D\+F{!?>r>J6lPKǎ0I F6R 1C?P(ysl~%1Dj! j}-鴬%O(Dm;8b2%I-̉7̅;;7N$TX3jS鶼46cn2ע6 J?U[7 $vj_0>ipNp~sf1`/e#^8ߡ: cr6=ը{̥\=uȫ휻~~}-"y#c1:QLM{(8"iJ`)OR \Ofhƙ3xg6Vn 2cqZas@ԝWLcC~%tr4Yz{ _vʰ.3qAƿQ?ԤloD-fkA 0"Oqw11q`ݑX'&H.]͒H2NDw?CX .B)e>t֙ v.RBKp.-־  %o5eLB ALgk6l\iokPj!R[Liև [OvqQjUtJX#:(C'o8 y|6kşTȇچΤi*+ũIH6YOrXg[UqKz  &.NC}Nwʴ5) Jt@-_ I&d119zlZD1҈皣j-B.#磪^i+ Zu7%r%&jhm_fqջ9r&pO;hdj{*]ъ7t q >+Qh70FB_ԫv 'jf,}4k}#e;jpb.?od|yDk] Ohiе,;jym}vC2FoCica 2Q~/?Fa/WD;P.nZo^NMLIZb!>{ԊQfIˮPHPȈI>o[(67 ?hDn1L{ZX/緆x 7zG迷ןe__1z6&v_C#4CŸZ?o?F[_7:]??߈lCNot07*?U;ck_TFgSQL0$4l_?RN+ %aR(IExc6qt%S}&!]<!JZ0>/ (eo߆zgUmGT{W$'l)5p[7?m`S ov kc{MшŅ7bit(x& Γ; }_~{}'΃֯9N4hqH)i*gw/"Rá-5<ޖ˓wws_|7n敛.OL DۧSQ>Kv 34E#txvцb$~r蘭{[UKC4'?s;[ 78<ȱ#?̩;:>*kAʑJ> 0)e"zd΋L~qy9g)w 挌LE x?F.1TiGH,gzYC2 \gtt\ A2zbIE Z )WB2U!l }QG .AAYyxP"/eL] LM QYmG%R7tOI 4f*V;/]-bóq: wneg0~2}S ^tx@ F^4'1o~Om~4͆24nV>aӹvv^&]ctƦ=^lAt'vjEpRdңeB4+.&!/mmEeX MoKFZ53k&ޓkҧWt1}/ ~#:i+TvKmv7?a_8ߍ}Mgc\\{~K:I'G<Wg{ nTNè;Nc;l46v˳f4CN6Fц}OAqjkaB$\t`SY}-j!?:JAҢ0QV=jاs,ECgVܳ߸]`ybO6ZZ ' ;Kv;ўg* Xؘ@ӱϾ2aXr G%5U`R!߆flxLRNMߝz6Np;.CŬzȈ2.oYH,DdR =]C//K$J&X*0F( dVhg6cx, )!N-5FPTbph!0\!m$j4Sю yKSխ 86.! Ƒ?0BFw;Y>,#8|FB(Ih]GaZZɽQT u;2$ʚfQ-]͆*Vׅ֜q̵^bϮ[a""pQ4giE0 ݻ$>3xq᭟KPKΖ PKPiBVersionList.xmlAo!&V`w]M/=4T¨]e6u&/^޼Phkk #0*m~_<'x6,bJJUs% ;W[% P >#]cW+-TR%ݟw( A < 7Uӫ[1=Q[?9`]_D'kHID$*Ʀe ͓.Ҍx> HQԃ-98˻d hlMSJ@Ÿdž!/Q#9u#ʯ^i^ )Hqvp' %u!n;qEHyA"2PKZ_hPKPiB manifest.rdf͓n0Y@!Hui΢ɠCBHe,N阝(9@ڮͩƤuD4l2ɺ6ZIj 3}r-11r 6A!5V=n~*p׭4J Pۂz n@;=ۨ/>?Q'w jO.]Fc7zaw0R)0Ц |oӺ ; > stream x}ۮm9n -@K@:UN qy,_:> q3EAr˩0hl)QEQE/oӿ_?qy=51/ׯ~~ۿ?|?g|wOG/gӑ/;әrֳ_;9ux~97w_sߟ+tL???K\RrujԔRߪoD*!&{6´vϫ_D,|$Kݴv=s[KiիlYlL(]xh_y]jcBPD?}$}T=*2q OM?p&ېT1@co~:Η' g)&k`n\vB+_%|造2}9SE*54|YUwzl{ i]~P"!0b4TT#B岋$Lk5veޏʚ3VpaK~Hq5;W[[苴; (̔;f$qx7ʾ*ͷ/$(2nMW?ATE) uWu,E4T4Ow #XC{)>`BBN>T:=>H+oL+29+/hΟ6،&,6LV›V ѕ/ZK be*dJ[8cO K)gpm/T' 4| KpqCK%>Sq]ꗰ;6{:j)eydy0QWfwPU#n\!p\ ՜oX׆;8P65wK_RkZR|{[ O"/nK./^Yٟ=%\:h#3la[>b<,gk>n]K ǷIһ"23D[~joDy=9^ Հo\(5/(}g)"(=w(i܉ (w|ʘ.?+M ڷ|GFs{'cu͈_Y [Uo60×t"C͌sa>PuafBw7JzS_=&̣?v/Z 'Q$8MX [lM|A=t/7FZ`pa;UX>PSͦD EJMaz~_M>_o𧴎suʩ\V?Ǭ% ȭ.mocKS,"v ߿_|(ŵ͙?^\UcKnhNFsr5ѬĿmޖ,ܿ$&m$QHFUk;TtNۿt-F81 ݄0 l0X̻WH"d_,d,bA.)|5xnnЩ(m ks?- ;}]BaޟKch ]rKH.țNNJ)z7́4zQĦa$KSٲ&)Lw3f)>?ZWvv_tt)Q](.9WZ 71(RNκ֭]ަ@;"b*}y-LʊldTgT[hty[<ɛ҇R.G({EƚMf.s|&O4(ZM(R$Vye0gBXv|ٽaU_ =4$-) 򙎜KZ;HJ)3H{ߠˬy(T{\8YYgʪL[`HtO1t48QQ,DTQS@;]3m6,$LT(=kPX Խ^8T:Qf{̦9gfY9VlON :r;m!r^q&p79(攑U9gk uKDݼWW¹r"h,+ !WέS\M iz40bب,.8f[gyV.g\}Hhi>Sf4sGeGSBcϰm1.`.g gMrZn:nj= s>ZӍ P>ȋ-y`E4B:I#Z3 A'e%dՒ27ISr7F3B^ Z1i(hNcc6jwyX Z&WǺ[N)nLR"7*~cA+֒rۡ9i5WCmSl)056at dTJ@ M N Tl)7vw6s|0nV( $)2ǺEWM7tSI ;;=0OPJ~ _>htC0Qz#3!G(W_fMM;hV[Ԫܵ }uRh |=&UiYe፰} LzcV"l*$,^h/.6`1Q`02{RM1|e#-N0Aj٩@榡1Zy0NEIQWyU%*c47:qbͅe>tFN43{,VHRT'~a]36ܓ)j-6smV6@5J xu+XZʆfFy@dy]-`n4"EYOj [AVΩ,Yf(q>i\Y?M p+TT"tԔjOx1VmCTW¹liWTI]jVU/_C螥U>i 6MC`@ ]M0_jsAy .>#zK[U]k+mU\Uᠹ*M6*TT5X9 DlץUVŖ09|8hb,yD*β nuk1vl\c. -e68l\mEX#H*'ºP6}U;ij0pUKje5 s\Ax|Vxr[KX@ WULS-:1eKqt+l;mg#R@lQ(^Jlly tClQXͷXZ'e@kƑN2Bz]tff=Ƨ@ah\h yֵ06CfP8\>C#s<| Zmh:yj3bHwI7uE#5JP4rANT+ ֦u" og'E=?Q'\ZUܪvMl[ؐtDʇB[ZuS>ˮ;٫/{#:AaӖ] r~z~(Qpv{6}QˀoC]0ц:e}fށZ'OSh,ҠRí̗vRdYwUs,1K嵐t>m3=Xĸ N-і##u s۵B[P I)5B=nE)Vᱴ :vh:ek# Xt;gyd #(&Lh6M5I:>h} :Dq2ϮK,aO.A{>԰} p- tZ'y%^Moq(&HxXАсqsEmɽ ˦-7?/^܈2t@3* gTһzPsB- u#Ne}#e/zG-̜̒']dY6p [fe6r[*Tv> u I%4qS#~Þ1XBRuk#֎O^p\@;VA4aHQ=Dg ̑iY~$!V^Xw3t05%wPS+w6h-\v  K on$nMtڍJn(r!Z>}P`FRłA#-@%?}ogw'c90TGH&EޚN eNτ)nvMST| {U]u;}&:-F}jCJjLHvajC:8%sҍ3r3;!hrȦ Ih6bTw%x ^2 6G-ql^"T*D*&;v*.Y>(hyș'ofoy\̩*)FC%K!;}Hŀl0)hBO3ILUwhjM9z5T !̙[QXG:S%NG]HN(j`nkYv(5JZM>4)zwAtZ^9%鲽o0s#Uћ {pl {Jg}}z1 ͍ wG-$8"J J n3BuFZnqhl$7ssEXxhv4FcuWB9v?ߘ+#f(kvW"Wld*ګ\[wWmMUwv짒 by2IĊk)h. 5ɕABy.y}d\}D맜(L P@L 4.<*'ANR@%,F*0P8H6H C+tq\ݻW F8NPFF J~%nE 's.;G$g5_-֐u`8#XFrx/r4jVoٻr5p3RAϱ\i^<\Snf̻,4ݟeX(gR] J"Sܭ`^FpǴI;hJ-)!4`h8e7Ea =z2FߢE:mof[/jCNba,WzI=ʝ (KF"T fXtl&򀩙t@<1F\[CTeDB, N@&aa zKJu3MTTNax`|]?xmЬIJI|ݔ+7fW켸۸[3{xD0昭ۅݭحvzm>3 аVBnR&ɲ $v6fb,!i DUpK$6ϼTZ ygnNcZ~ '=V],NtR^;Gaސv(8^E -Z?S&u}9J_s*tN_nEb'UK5l Z89\]]O„WBܨ!L> 6 Xnveez "`W@ݱdwirї -v;%.X԰-׾j6I?)BMct&JW6"o|6M .ѽuKNWɱ"hW)< pJ>xŜ6]"1%;*!zw؉.\I P8FO31 Z4ObÈ>b93q#yem"Vp"p~L1אd423XBRX2l]IVZBeͧR@G0Bښ6Ak giOY}uP^<6bq1gz#?ryTȥJolJi D-LÒQmdv3mO턏h_،)e1GMv;MDh_˴iqn<y~V.K7AaxuHŊ'p|?38'g[XlVF{C3.a+t[5Mxz`q^㥆@٘kkTpr?o{OUMg^W՟=S>n2?ZĶ ܝf K+Y*wl\[jnd)mF;#n쑸Ja̡d{rfp}Ӯ2r#_xbwd'y`6~'%` I69zI}Pqm2"N&tIb%rQ]W!-O J6UB-[aK| kt+Co\mhd8"{ng PȃۥKXb^@ sJ4 t6^|?<}gw /قӜwy%$/%$'O cb̔~8-wNzSP `*x&\4P@$q'dX:-5֐y>1 ?Ťdi\$;\ QlolpNf ~O-_IR='Է?32sz2_D|J]e~/j &*IIi_@NduE0QKPMKX}.^ܳg⸷~3&&:Fe7Bz7K3ͭɨKvG:0:BEIAA:l[7?A 5M֍1ꋳ#tӾ])NqНi䣛G&ttsݼrЍ 3Gh*HDk5~5L%@USX>KMS!l2_]}Gi;(v]U.(e.N7#녨xJY™ΧHf8[n՜!WY許\P*hda0ihTWw~~B [#43a"ѝ$5/j5Je,ɦ*m}kD7;g45C+JYŐn!vR'Fס\i.'!]FneSq# p$FSpZx욻ݷ?,L,HVCBl?촞_{X\Yp9{SBwZv`2¢9= hxtJiMR 4 x' N4pg~[m+@#KpjUn8ߓyE=$ (-4 mf{>t˕ycK6~W"Y* MM6WPKzoNfWa1> P񔪳ۥ]9j3`ڥx\ v7ԬXJOCnS6_ghpck7Ӧ}`:V]#ln,#?#YAU>ҨGqY F.F[k(ArQ\w1aY6m%-dTƟb_JƂI>8,U#R0'Pvrc>:Hdw+.ؤڦҷIz6yT #|xL(\MCZVh2FFȮLw>xЂ25#(1m8yRtw/2a_pܩbhx4m+\6;/)ފ/ xc{` 0BPenDqq/.6ߦq§Ntl#wZH0n[X_^Sy.7->R7hEfuorTl5I*iפu,O{gAהU{Y`k4G,ar-]f7:$䴢|:Jޗ%I5{U$*ŪE]Vqc_ؠX L! ,%4PH Pote2%d+ɩ=\ r[5BG] um)ꨅ'VY!>Xk:sV: [!rXD)2eP#)Ym~͏&;%: czjc?~0Gٹf,wY/.=7$k=!2DhB,`(NV23 0\2_-DrgNsr:gb#bZ1O˟0iwHqBNr`$Rnet"G${:S#rǴ;vQh#Ser1U xwEa_8K'6vrշWow =|efC3dSMJXYò4Tڜqz57Nq3"W:q/1\ӆX3-^cS㹀zƣ9|c-Ǩ` @ӥNJeM^xND4-DNٕΓ}W%)Yȟ+S.׊>u:~: 7'=s G3BBcI~\O_0~.Mbݤ ;o|o݂E_m&u/y&o>_Իv?{UG !k5L^h i)5M5UbT.tɀEocmc/-cH9x;askH#,Izd7UƆ˕ oQ4{މN0K)F Y t"KS$ISм,hD8hԸ°jG}hPy^bitM>KN"-( Eb@9݉F ΍_fb[Qm~MD+1uQےڔ2KMf ˴;;)3Kκ(!]JRֿؤtzU!f֌MlF1V 3WrQ웄ډ)|. pTPq*xߒ*DqE$1%vu$ L`08̪ɬv!evrq0TvP-F3%* RMPf*D05ajLlZGe]"$CeSIyMd) u,($Wv\ryJteAtE]*/ YRݚ & 1CQb:eP3=P%n'ڎ+"KU$dvh38;)YPPr)mZff$$*!Mb(MI@:O8y--Q0RIY$ˑ"L3R QGXed~T^EY-T?ܞug  N r/$*Jpa#^pp$GTlI$z4 )U*7P ,F]tPs*9hTPU_Yr54Oૅ` ( Stf@׀FEr dݯs׊'(e#o-q[}1H@8TD#SHv5qRg D! c0|*b=^P.tŽ\bٕ4ӮCsEwR*tD?9h^:h|ȸonnCܗF(FM"43";t&U|GȬ%mƜ  <~'1 41C&MM0ç ԧzMGM/>:I./\{mlAf1'4֖O)2uuSuAmyI D~*D)i_2pjXITƥހ|$d+Qh֗BH[!Zr7C)݆Aֺ24'>/Q8v+J y2 ()hzX/bK.j&JCt1覺k6DWR\.`Hy[e~p8Ѥf]JΌJjm-$lMB! W.xENg3~G7Np!hM'Z_T;6uƝ϶QUX$F\% +B8v}ly `uӒHjgzR+zn% JȖ_ (wcѢ.zU3~LÁPM?$L@`ni*%4}aPЫ1#u rJuM} ֨KCIoliVJxQևFqX%# 쫮J)qfGd/_4f!{}@ MpԛL] uxUQ'DǕkxtf&i$&<ȃxC7+\O%f'<%cIf%ˑTWh Z2W&z {xK.y˝ 4~Y#H@A:lᘗ_a }Molbߨŵ#a5 vތ_.@S-V0ү݌,寘`Hw,2D:Nã^ʯ!RD3CHgZPRa=Hjl: % xA9:d-j~˳ۃ17۾:BC9GHlZЃ{:ōɀ%-Q m方X!sV^ʱ<ơ8{ĦT5m>,#25ku:xhoL_ nc0bTGE L.MLlnghܺ37}ppmXh5{:6&S#t/Mà;3LM6K<֧ےe 3_w# <8&!-P$0a-:qôh2* ((| LC_KN+zpqMH!=gNy`sG MuQmfJRTP0Y2OR @=1a5B%O3MOmW{n7gȐw+2 po'#̉qtsǨtSf؋: IEC|%ٜ9֋zlQRNԡ;: K7ި{:|UQ7u8K#l":c""eQ_uVASGny(zO-A%63ȭg}}c?g#\`Ŵڰ# $q#H[W- .e60@)Ц:3B8Wߛol A[`g P y#15M.ci/Dž8 nސy Z5!GÊ[7v) ;Rlu q,)YkطGhyo4[6opᒚRC`s#Hnjibdp3SFRwڎ,RAgOɲ3Q.kEΕ|򑀔UiiBy0:odX\[p6~+'ED$ t:$< UvX r-NAeѩB' ˤ{ڵ}`SM -T3>eoʭ< ;ν9Td8ᇓ&cvi .1,4uWrꋯa%r^zzzOW#sxjIR MNhfU3w8~Odvc!Dh%Zǰ6Y7z5FW-c%0U[nbkDjn!FV ar# πw,q+j٫b 4;G8GnsÔ9P)}9D*d#7Wl^orq ft⦅|mHGZO`/N*&fS XLPs5ybv&o6YF fW~DK|WP@ښJJuJnp S6>K:΄fJ `P(M\Yʆ?r7mZj¯uG cӇݎBd7oʈ S"!g>vZ*kq߉.8E1O^Jgh F(b`Pp#tLkdhsVLOf Kt5wIl=ZTg&$Q\@r0<[IoLjP4\yjK6^?Oֲ[fʂkwg<ː/}3R2DR_$gZIc_1U%j\災fhC}Nġ')qZ4;\*O!{cW$vW aYI;G0Ŝ49 U.|n ɜ923U2age u=-qX?!\oaYզ܌cQ :@m|I)YF>|6큎.Tt R!wJa1}1w{/C1')ԛ#.X ͭqqLjEXz8UGp=!7m$>y ur>Wbbu ӧ ҿ3 ivh8d Ɯl-&U,mmiVn0Y4坎 -̫3AE/ڦ;-?!-Rq_ ɑn©&EDɳĪ%h4F1e2ntE$B(CA(TDz~d%i?LUBs hJ3~|97h5hjv:</5f*TK^P+o5Aҷ`ɣ] ĎT璘J)cm)aȔ8J3͖o2L[χҜ'y0*e~Cw8T6NJxǩ.h 8r2߿O 0MTMQ(yȈ2$tqf=&^vl3n~RG[xKxqڛBZô`҆XTW(dAj#^Prӽt2AǠ"%g9rS-Y2y>aY n`.` qJ\ ʏͅE˯+Fa=>I߿j1Ag\W \)4\͋|s?$ɮb2د$`;𐕥N#xĮm$qA .]܀;KHNBu1p;-  %ԫDaD *$6E&sk7A&)k(EFƭѼk/xK_y،cΪ]h 4s@PO&vMVqZ%Uxo.l%@5"{d,~3` aGmJ+XC/jX֊ /Jofy5"Xs:& 0 btRWi鸢r25pgqfaxȿw4Vߖ,0a e|/]~& 2ۦ2Ǭ/  jd|67rδFd`*ٰI ZVf̛FPC_WEWͺhJG8\=m /w4q}Si:>raZ7caŠԸG]!U8UV+j5!W"#̷vt6r҆[j|٪#¬d-kEq6`*WQ~w:)"yLB,lx$n:uZ8ڃ;S`qx%1LX GPP䔙D)DJG3JH:JK8uqBۭ,1-gFmPHnPɪWX~ ІFk0Y8¤ǻևҙ-()j;%b1ݸꔋhPc:\T0AUQ i.gG7…Koq7fׇzn/ӍNbZs cvϙ xbY%̖-zvg1G"xz䁕$r`P[DVt&0Oޠ? O9Ņ-< CqBK54 $I yhtHϩ-7{NDO _gBr1Ucx)NIuRR5wRȭAŽ*AuiLJM쵎7h&n_(p$Kg$ۗj ثAzo0 o߇|X7_|YWVt+ ne_Y7?1X*B؇ ner.!ʊnl1>WV_#Ѳ_YWVt++6 ne_Y[1K;YkYMPpxRte=mQ*N:쬓1;yMHʋ(0KJ*nl&̂kIW)Ep+҃VYRv:Jq{?,I[F]e ^>cj< A:I660/^H-ƭyǨ$^WFp+#וp2{]v!$$uew2FpcUL#וʸוue2nue2{ 4-/ו ue2{]^WͽL 3ݽ̛{]c{]k@-L, x᠆_>'30QjY2В2o!W#~;/SHc@^;x`AXdOC':<Fdt ]Zʻ{یcg]oJOX|J?oj=ҢWScx`vLF ]p~UxOhX‹oaUwJ0Rb Hw"9{VһS4>;Ľ݌JQ Xptbڔu x҇]|_sb 5 3MX0WZfT) ?s ɱᄟ&]4T '&dc&}9T=zY J= 7>Zؿܷ$p|\K5?.,hFhJd ߗ .܊LbX&|ƥDߚr~(F0+Py!b3@S#ՕyppWbTD@vSWwڊ%_흑&'[tVT"ʭ>Lː46v)Zzq|M/:VZXr~Yf3QX{΁/X54,.5Ddy3/ /ݠ =BnM`髗]`+o\(xz \71e YWupjR=..<Nz˫@n@P+aW3ExTw l"@C:h3(+~T  Pk bnۢo5*(JC)sq!҂lU MQNL8{")kR߬Vɏ4RUץG[l#rd\-J% pIe=0{Rmv BJP;deS5ҁ:q4to~xA{x jt Yk`owGmEA - I4=F%P}R W_6ѫ)|;˂4O5māPRضH-0o0Vڭ%~&Ń3#{*L;%a^Ԣ_7D~176SdOa9A\@p(}@%=h?50Aj\$©_قmo4†bđ (AT~Zn]^G!Iu2 gljX 8F58]pѡpd$èЬzZz H"3#j9pEZWY.a^W*IǮxA*;+65tDw $VRԊ\++c07 NZ)fMW#Ip5H,sc[kxmEM}\4Uj0){@De݌4A #\2tT~iEB{ dfjY L}s hF Ξ)*sCĭ lwxy;[K-g9S,g#j4;,$pJR9w| #JK^U~TLfe,3cУW { j@F BhNr2BYRΗ`f3;鱯tDy"yaC͏(T_½xts۸Ѫ0P2ydȒ@^ NˋCA" + U`ؐ#MR`UFh=v'w~jH'pP' ) /~=x:ŒЛ47x ag[ 3“铙Urި9)zC%̂W fd6^<=2lgIxfuꐫ`<ɖoI^l+t51ig+mR9|H jKNOÒ"GPEayw8+qfawqݩ80Fv\?|vt=Z{8-txh`_BrF^>ɴxB(~J5Y^u> /Length 90 /Filter/FlateDecode >> stream xUα @ н_١68%#AWK|:`RZZE9rj`PqVL9EEne% endstream endobj 20 0 obj <> endobj 21 0 obj <> /Length 88 /Filter/FlateDecode >> stream xM!0 CQSXR7y"M#?H >obA^E 5_hMt蘥9Q;ޗ|<$ endstream endobj 22 0 obj <> endobj 23 0 obj <> /Length 92 /Filter/FlateDecode >> stream xUα 0 =_!i/S(.*Fq˃.Y QOej&ޤ@IIx(-!_"3ߟ$b endstream endobj 24 0 obj <> endobj 25 0 obj <> /Length 93 /Filter/FlateDecode >> stream xU1 0S8wT<Х_[R:~ XE1H`itƓ*FRB&='?R`f3^~]'k$ endstream endobj 26 0 obj <> endobj 29 0 obj <> stream xMe9r%_ 7 ̪-zT,\G7TFysѮGxD  y%F#i45=o?ۿ>~__q=+GOu /z?J׊ݒD)Єd,n* ׾C,-ZiJGkZ|ڲu. ޲O:m?dk&tl%u 4Fjo4n?yG{{e=ZU..ƵasT0aF[8f q7_ _-j}m7JMkih{{zow,M?ԯf3['g|(}%K=30<%i|^r3$.uo'~V#'u"9A/i\}FM4ajb>b?ǒ۴ط_zgR4%@/Ni;g?BʳTM<:e!7=?SMȟ$Lt{H,&Hy <4P&4Ur1Z:}EQLՕZ/a9Qp>Xq}ތc}쀺N=Wb㴽|&pN)b{^l|vSl=wѻg2v>M"FMI{R[nC͟*kj_5 Ǻ8_iV|r/M'kW(<Fl6_vy#`ߓ8Dk KXs+B.5}{6RmLq/n ̗DNTvVD ~k9hQ+L__omTܰ#5 o]7wOl{I]K wl]{6I+TLJ}ǒ+NRe̶ D0Z`şkc).Oس7imQW JG 6~!#`C]{i|%i3jG{_%ڮ5:6YK*#7__m_ϵ<~?U'}Weu~ʚWo}..}Do 54Ҿ 5|kCؒ<9um髾>l6OՌkk owG[f 3T_gvń f@ M;Q|r)7hH4g8|rD?Gj#n]`gc( &F}s(V~+~wP=|;3ٚ[mAi{踷۳i֚AJA#WTΖ T Q!j"zub;1'n!vK7ڑ#Chz"Y5~W% `U=7'Ʒggn'hv%`+cclElXx".wc!xlt'QIeS?QA@*DRFc2,rTJ -2gn [ަ;mFvq`Y?lƸW X`1-;\,HdoO8Մ w #vpmwxe[8w~P֪&E"@|9g$LMV|G~D)ႠUSdc 11Uf^Bơ+? gٟ}T`D%~<.8̠eVA;džNeüE ϝ1+{9U'Y,ύzrj]G-CIǗS$QOdH4 Ϙlq#ƲBfuob(H3V(6]!$sonX2cL:mQ4lxĞC,*D9VdX $OӫA3M+&QyO -P|Ycؑ4nxC}QPOۦTaM/~h3`%uIsy" "|c !wjv+f%G[' HZF#I"[ke~9fK)_o"'e;O`c&yP3ErCqZ89ϮX%$vX]jxH伔W Gb-[̑;*T;܎RdxJx\*knRWQ˜]ZZ߄ҋ+AQgj]R'pQ0}N1hqvoa$hkrDf,8,ǬBD*Esv;CY`YS論J5p˷&VYqdaSDiyKsuk*#fC%2 z#Y:6կ?ژ]ēF&6%>=] Hn_ )8K"Apnp3'F2>Vv3ΞhDwFOP%.ٲ*>FM|Jo*}l+rdns&{*iF֍;3jC+`74d꾇;;g4r̠#{h2Vł@z|"5_ŸݚU!Mf~b}{2ZL☒4ؽkjac/Ȯoƀ#S>lZ1Lf7j\rǭMBư\Љ#AGB]4kjbg'ǦjFlB?zKIR U-HJ5ԓѧ)m.K>z=`sV|}AYښ .mM<%zBt `,^Ȕ1VJ`7ńmc>&':!,oA7[ f=p7Jf`a7sdZ{q('Acs9 {N=R`?[0euT ӨP 1̺bɗm7tM=AdoQԹ,\X My(y@mS #T=1=9ǣ$#{OF!/v峩qBV+kYo r$ͳM4Wpz7t@TN; dan>Y0O/'}q$ &xYYRK)эXA X@ 03Cmyw~-*:jB;`J$X r\u&5e-hklR¿H¾QTRgQR1-ύbP7ݳa h/u6*G#!IWT.;|n@RG=aP:',"%Mr:r$ g:U43V&#lZaX:h$÷_yNy4Q'ںbXGr QP #HIf#&eP0( `Ra~%x~avkֵoK#dOLѽPʎ곍#x;H:NǻVy%)+nɇ~a E>[7Xnf `w<92G\+qX=QrSq{X*MotI<ŗQ tYmв+Z[x78gk4"[w8,e<+Ch}vLU6ʡ-.35sĥeix6NXlU8faWCU~XFWU7<]4)J}MBxc_e^]N@r"p0# JX C ?r4nzCuWÛwUIwalpo!ùq]lhX0Hww6/TUv6]u߈D4xM]S[z3˫Z_{ѹUn䤀`g)ѸI`>q<<AH$_ *(u%m=-ӱSaq=ݗbݣrjoϸ[R9GJ@^i.)9%W9Y`n2fS = \?xC}nK{wBP.N~2UTtYS vTrK4n%ߐ`+Y;\#|{\t\W -3^{;ElDa$w3cOy;ov(&~ 99U1vX;yTlIg|TA/?.ba7oxȎPe i흂Mq\lj'41{E? q\uD}+* 3h 6c1;Hh,3eV1jy sυ!hmhAR+)F h`yhh}_-& &$>u}OAYH!ybc-C;$lc{gô*ؠѿ~yr ?y -6,n sT@G3@ mgҨʡ#*^@rحKC>Ӳ 䧒?tR ?w{ʸ)Y= yCV Dؑ=  E@cH+(uAh|ܙ>|2G>V<=G6ï@`KָtЩqƺgTCX\ w!9dfc~E"g0!K]R|lb^ڸߜ%.́>>O}j1P+A'N̤Y];4Y*9 aj~R 4AQǰ5E8QL#R} 2sq mf)Čv9#C w/ěȭpV_J-x3An!|O27d9pF)"9Ci;;oWgy."J3m0˷yrϺEs>2 w'KFU V -n r^tQutDk=X)!%Bwdr=eVs(ָlj5'(rbpkB[@e\A,S`'u :f8:C%ĺBks=Vh[BD~UX_iIw,]qk9yb%~thZœ/iƥ=B s6:jo h}쎅!V?Æ_lt6މn }ˇ|V) Ho7qzrSÚ ?O߶FɁ>O=Gdu?aE􄟧/{@9FXyzOӗ|ӷ-&7_Qn>M]~>G!<Ɠ~óHnr&ivE!єBm U߾sv't{i(V>xE `5DѝxnnGkYDlpZ8[ WSo7?u䓝;#w rq#s]hVy|f6ƕ40Q|~q:]z 0^6 OHtجZ0h#ӕmc=J ~@8voJح婦`ksBoKch|(&Vlg~V8AǐcưM'o8aJ+pEL.vl %wQ*IĐ'{rI]:ߎknkAd!:QdWjZt1HuH陘b4Nf\ND iq$RT,\N(kHIlUQ(GHa$VyBL(T~GIeUGI!&!`$fQJ;zNIQ j.BI]QLGIj#t(QD})QLo")-b9EKМq3] K5Ǘ=)Xht}jgh=O9dWT<4BX8K$HuPR-n}%Hsv#ljB:{E텃ޢ2zbڤ`xgx_t )^gr[ތ)^qjtimpьA:Y̓z3:`6u{OF0"G?A|{ wB)ڬ;2Ž.3zӠCwi)#=*3ٚP}:`Z~N>Ao8'^Ja}ק@oԇUZӟ$/s#UU.Y|,7z= Sk' f2ƊIVMw@V=P^_%j8.o pC a"LUE 㰅64,i͐L6^ԚAAؙ6Q7w6-u6bydM~W4Ug!F6lG B+{;aA15+,2ifX`3TBCMiբXa]9}F(ZpB+`T(/?>a*kGXb\: @fS7bZWO]iZLjuT ,$|7N<[ yPBhW6!rnLA/$KZm? Չ]tH. =PY#;9j- fч +rhKgx鼖3CJ| 4T1Uٵ,M3˪qڪ`ǟy5AvF3v}%ǷQ=;P 10xUa 1ڥhZ]$0oTKkz"e)~xui v MO_3pa/7Od["_ږNLrKpw. ВSºoSR/ Z\ ux!߫ v tzG 5`~GĎSr!?Αh Z>=KWg!Dx++NW|1_ nTJzu…78嬈|<̎р^I\vKBy3V='Rd˭Ra[5=8,; J)"Q[#{qO܊S7Yuv+̋Uȥk?DI@E\vlfK),'$;N]Q$|iѹ'Goq ~ol ޙ&3D[~c[n6Í&s ROA`mg5kt&!Be=6KfAjhv9\OЩv2x}#/]'qǾϷհ`Y;,<> l|sr1^T]ש91M+N8!~ߛ=Q[8l@Cҗ !%y##cKÞ-uYqzaIy1ag<$ 9L Hr>V)E;VGd@HDBQtd{jCU faHYb(I%vk׷'OYT2r2yw~s1f[AmZ>[ʠp*zX]S:F\|MZ쳁_n/ӭ;Jf%CՓkô1K' R~yB1PkUN 1Dnȇr `-V)*Iҷ@\RJA7Zjr}ȧ)+ ;G?-`$kӳ>QFw {9$zs{.t2s#oYutK٪xC- OnQH~C j>f>'\]~R1M!/#,B 2?F#|7Oo~Ͽ^~,8RW3.#9CcC#}Cxfx.mӿ<~Vӟ[~>v|Ss'._cwVקGv[}<27^}ӨR3~wӞQ){/!CWWjIl"E{إ>ϓOOo{爜$b?Lo{qs}'9 > |/=}O?~$AӯHO A|~v0b~w>hW %7 %&rEOQ)R찗-t82KdYMf\m7nzFŌEl٥߄ܰ .]V_TvծFȲKޟUkGy?mSQ¸YU*MxS߽U{ z(? n}q8w'p6K݇`Bքt͋O{,z˱K\ .pP v>|۞!ИEO[ ~sEUwuFb[ܜg(}+M/v?)J(qNf;]k;D<ym<.zd#]- +QvHMڸg.Y]l&vp@ NWo4pގ UcN*E(wr*XoɁj]Empjb^TSjCRH@{)L8 1x%UGѼ;m,Έ{uRLyd]'KBBa2l[[akl!94l7GI9PPojmD]uKX~A4z閎`m99lmL gƆ:J폛=:<,e;Z_c($tFtٺѩpS|j,faԘ{aYOK 4Y␞ WźAkhf}7RE-Poӥ RI:L S[8jp}\pqg?9cՄn}GƔA2,t(bQ-Wrw?!t2Ǝڻ0 ڻf)8R{Fv2xlJή*9 @A/3hR/X(Kdץkl/x wqJ!|h<\]1Rr$ƁLB^oH@DKhYF]U9[՞ 7 Pq+>2в,|rEwr ;mj]qux| t"~G_`.9Ȯqpu/K͘Asfg<.ZIōv ox ۑ4@JXw7HMs A8_GCo̥qII {ۼY6FewZ=IA:BkYytRwB = y'_8{Y:d>: ]kGYsVިx\{XqFw!c/EYz~r1XSe#;\'`aSb2=Z13|+"5zt,a_p/r "(2P{;W;o*k; 27jDi'9`DG4i;g5+8n ͅjqad&۷ h۬Ғō@ {){6f|ݰ3k08@F}"Ƕ{ڽ&3T"UqFp(QHxeb.B a\1ppx;잶ӕ.jliԮWl 0?c{"@u N ݲlgw2γ_ 1K_ƻr߯ Qf1l"|L zTᑿ?+ZZ׳h1z#>y%*=QsuI@׸T6%AšDU.N-YDCFgܺ(t-w{VgK=6?@1 \CV;uv%l:`+Ad J<שW<˹}aiӻa"!yMPȭF9Ŷ&"ق40w9d0[| 8h;AZG:@VhdKN #Xa,.h5, ZY溁6mXoaIV@ZT U޴B+q@hrDH9i*7qjbh=Ђ0rȭ'Bi u@ڲGVl_ C on8@n@Уk˝ :[ŠBAJ"E{ǻF^u֧3v?v(I6b"|!\=Jڹ_}0ziOƓ:8S^OTPg8׾Pjt ^;T޹H6:kovh^^* XxMJ趽| m#n7,WM8]&RR,u\ mݖpl=&zWú1p[4 K-YJ"ým::# n~WܹӟcWN`ANGNmf9 :\ď~V_\p xsh%R,]r&wM^b>k) ]fWB&v;_3;N.`ٹ+9QEFߑ Gd`b`kcT>>EV_~Dȍ0nk3<8[윆 B9n({˄tP(=F1zd}ct=l,p+_E (0 Ymf[S*PEM+Y8$ c fz\ĝwgs/VpEր*_XVu׾dVѲ*b+EwoW>GCJ1B]-<iLG8ieleol))t4r``8]NF\;Hj-;yI12 7.̗ 7XNr `Q,{B1G-h>Ø'˦_3p4:Vߤ%HjF&V`o ' b)ꃄi` В옮Oviw ߼ w݇%a6w5 !2ś5bFbhJ$+9oT\eqK`Ri) ,dSwQD\'{pd"¦͆Q6%D],tOȤ̢ M$򮅨|d xa|ᅃ(zpEQh.18үЖKSQ4=cOvq9<3}䙧rjVo^9f;xY\qYha)Y;Æ(O N] 6AM]^ju:a9rKIޅp-gD/tj렓{Ộ?@^NV8?B햴7yA'(/J{"+?D4>/N4Nuwrڨ ؀-iqȐv"/ag);GRj]gria7"L4j?pz=uHԗŤ}5Y4>}w)z=MvdwNU|MQ>6}w Sz_l4IGslu_Ι&j|& ^4L鋗!sӷWp aMM&y#SW?S&v_"bwU1R;. ?+P^+M7U;O?[P-@y8llڀ/?ŽDOIXRt+O1p3TMn^"D: G| Vbh!ZgPE*ZY%?Ϲ# g;PRiKl@CM"M j,pvN41$@qwR+N& U=a%f *+Aځer :oCT/A/ְ vj)H'|D 3k*0Gs[hE֡/Zb0P+Ia"rdE)p1-5ҡpCh ѧwFG%!dIM t- kHI!P7k]M$?XKq/vPHKHhst 63K@le8=bjgfE9?nىlz9(uj_ohyJ'R{C2u~/A8vрz~/=Wpy  jX& jr$77gp T)iFR]b_8+ ;T|]E:ckyWige[ mKT ɤ- [H"W$'S'oP߾U^B/6@, Ϙ[6>(d;?vB0=xU~=O{ )̀}i>ú juWw;`'}Ҷ|Vž;o`QJ藏w#e s)}4J~ÐM0}vcw/vroSňIָa ߘI؎fS#a"8 iXc\ aH^08bG:qc85X1CY=j< z0>L+B8_'2<Fv5"y3NנѶƂVam / rthƉup Nwh0g+":o٠^ک}&! B-~t=$YI{A>NMDmpHc?&+2++-dE3ϞiEF~'턖pVHe ~6 ATWT=BX%7y!^nU|`i}–mST%AY ;Hg zʊɚC>MG Spyh "Iˡa2K?,\x6f`<_ꄊ7Q-]搉/z~s?<ͩEK  u^J²~ӌe$/44&l 6 .$zQݫ~Z껷uU勆{&MG)|U+ mfV׃R/- ɧx@:<>_|O{ФI8QwQUWŔqKbrd161gӻ:/Mh%^ ͸X~sOcn{j Wlw ReUn!q1a[ʧۖ6PнlAT&հBΰré& T@tl=,~nź(,pP>/6ϼV7A0#Cvo:)IT frsxqnxSd_4#q;"-C`otmA- Cn!T˜M)o `YpWL̐5qCv2b ٍ`2N'x|"<촋$p\Jgc^puN7t`XCQ.#=_v@V< y6&%=IXٰ$kij$ЎpK9O'~rK[eTY1R(<;:i?Dy B0Cӌ++/歵d'viUARL0H2C.57Yuwq>rL5Րd#G2#F~T l#uN˽|h'z7\'qa}ND؇~[;60 i/ i̘?f ~uiD#5lAn^ VSX>GBAх9̍setGE?(+HXnjw D'ṃS{q6Vg['v!pfnYΏ}v?h/19cb3(E6L,*ڛ[k%#sV38kRdBБv.aH iՔRu0Ҽ@W>]+AWgrxC&}3<ݪ(glao?y_ӸF Ho_SDb,O[gesx]9ۿW= Jp{ 27~'w%!aTtAkF+M0Rb- +׃TAtF%Rv)ԍaLn7^>UwJ><VFD^ A)j/Bώ=wP*{PŴ$$s ASF%2Q6F5E'/ "{j;bZoM'@mo]^gϣk[ou]iQ2ZlMѯ83aHV}:I;G7nx3(<Pƨӭthe(%rF.œg0Vں؄W71CՐ?ْr8O]rq%2Ŕ sf5тy{ Ȝgc\MjN9?dK~g[nH8Ptn)g\c?> /Length 92 /Filter/FlateDecode >> stream xE1 "s U]P⓬s4(j9J,Ipc$ C6FNE}14i!xh!\ endstream endobj 44 0 obj <> endobj 45 0 obj <> /Length 91 /Filter/FlateDecode >> stream xEλ ᝧ`pb,6" endstream endobj 46 0 obj <> endobj 47 0 obj <> /Length 88 /Filter/FlateDecode >> stream x=λ 0Eў)S ; Ei;f'on &90թOn4 JV(fcjKȇlk}p> endobj 49 0 obj <> /Length 90 /Filter/FlateDecode >> stream xE10EwN@m6 endstream endobj 50 0 obj <> endobj 52 0 obj <> /Length 92 /Filter/FlateDecode >> stream xUλ 0 =OiO/SUiRpŸfV4ޒ@ׅ 2ӡĻ}K_>0o8g>KjD$F endstream endobj 53 0 obj <> endobj 54 0 obj <> /Length 91 /Filter/FlateDecode >> stream xUα 0 =Oh/)J1)OɊʳ0+.pOY1PV qGҌ'#*[# endstream endobj 55 0 obj <> endobj 57 0 obj <> stream x]nx_b]ҷ9;i/:m`.sI j'h'AY"jIO08ҒDEQy?yu_Sz_#]cOo׿~ooߣ_ZiON?I=߹$;~oodJ?O??Ia>{Q PXOO?߿ 3/ş}@xQc6wA?ԣ=ϷRW'3jf(ŀ {kXVӚ~8%2ǧ_g^c, !98}ە5W'E黚m:Jk]LWZÖ\v#d"mkѶ4?4g>t5|A=c}'Pmms삷9{ 棘+Gf_a׎I^J{ 6ң?gןc @XL:&J4Ň܏]_J\~t&vgL;54䖷=,L㘛gݚl^IR6ۅGNR!C&OLJ6U;Vw?0Rz:Nk|kWz>N}}hNlC % O8؜Oafzmqu tiRYn̘AEXiACӸ|ܜ|v*1HdXiV);`Gp$" ˟uU4aԫrqAXl~ç ;e~~ۮͦ_a3h>z8im9KH)씀-$e_oۖfU^ؘ-cwql!kvE9eD;:_]=tߛӜ5PBZk?{{C[珃?kE_~,F>V._{~y{ |ZLe;?.8%e^ן?V1rsE~yɧ]  Cާ] qu)-jןSC.PO#@;T0NTsUS:71JS{7NS?cJ#{G-m2hlz\3WoM;Ȼ?Ar (<3STz$K8pSsI5IқTmI&?ǰULQ=67 (=.҇miIKe5SXRZ^9oZ \wl7ILf#E%%tе`7R$2xHZwFv tAґ]!TS+֪Vѩr,Vy:~So͐*wcP> Iç0\ڑa JAjRS.qcS@n`2 J%,HZ˘jzS Ah-ΦhAoM}= Y:dD}Rն#T^ƻ?5v>UifAi-W͔K.vJ6UzBP]pg.QgS*#eƱ!2%њnSv/TH<\ͅU9ܭ%yTRrͥکB!ǻSUԴA}ۘRSMTtm7%Z\ud쳢fc%xIONl,0C2iY`,IjRrM(JQݚv E+-3_f 2*wK[uW&C dK[ +OcMpR_L BI~L" (} ?HlFPD1yl`U b=q`jMFy:%p۠ݨ7㇞"lr^q7Y/bJ ,& 5cP~{GzFӵ,BS4EBZc=,6auVBv)YDr~C>ְS͛sn{T X RK(FAw(03}òMMj߄̠]#C&v p n;L_ΔPFy=]߄O%"2űZLU'mvbn)9nUgiBa3EhC:Ps)Ӱ>{Ccy]`yRT47 4$=10͸yϛ`xQM7/_~NmSCI1Ӗ&{| 6Lo&'fY\Ro28F3[ZtrXIo* ue%sƄ''  m $KgpܠL0Ո ja}mSjU=r(Stʝ9zA5Q"v7t21u!QBWb56WiPʽ2=Pa:@qҔVL:)\dK hi= FhC*h#b$lK̒K}~(Ky;Ѯ<6Ku:(9&W~Aޚ<6_jW?ޚsduLAQO19*Ǡ8;-L% h/ʼnU%SF٬kh7Z~98:ٸbZ8Ly#y)`lןl28*.ǩP9/ VpyR:f~FC]itvZe࿹2m$pCMޚHQtٛuj6l2: S(YE~sUz@{/8ng-rsZ@9˫Ъ!tQe,e[};>U:rlLqIyvEMNF VBł+&|(bC(/ hI"!D{#9P6z |'>bEh+>|zrR;d& _Rm'O[kG&w(+&i֜UjBl뢪4K,sS:s2{ZhHZw"1L 3ۭ'`,Q?`.k DNml6RolJ-=Ӷ=P-'շ6@sJmMvɸ?lz-yWC5R?Q˲=@8*-3;XS:j c ȫ=AR)0OaiIjXIb=+GUi4758b@jl\&Ma9=J+=I mZaIgMn9Smw4 .,~*4i"OAuap#xa QjB.cMsВ[%Jx$ FEsW"Y"k\h%nJ fT[%e\R2͋Wj&P%`E;ЂPƵ;q-F=g,7.KLKGn'-GVL,mriU'5|%}e5/;SկEvȋP "N˘6 :űsK.'Sܺs []EnvIİnxx6G%$)j ]mn2@U7$]Jc }YP-+õDG\tɨY!"l_Gݷ]FZj27kmH>rexifC?)Y0@$cE]w[u;,9yZ"ݒJ^pJT6w^8;jP'ܡIEA[p56+nZ/O–85]2>9;>`Lfm cLB v'm{gsz;Œ.@|TNXn3*Nӏ(ys˟R,+T':d NF]3үv@s`Qz ^oKNꜫN(A̩U* zc-ZJ[ $˨ݰŞu:~*>ZIMUֶͫP@"o(֛szG*gR\'95o5 f* E#KԖ">CD,Qn\ܝzFM]l<EH(/f}T f:t6& r P©*X`uHJòL D)C !=Ia!:1VEh` Us*ǰnܽ(wiZ"KBI`{}@o&g,P?G UlTIϼMl6K A $S!RQ{,C 0+ZwCe;xU60-}-dBW 3SAGxFje$‡I0EIA3Dް i9COxI6*Z= uB(+weL],[:RfT`Ue2<},tZ*&E 11@{hyʻkɮ8"dX'nV(kE2qbOљb5+%OpnK 3*ə(U4n-y77aI$ /o*bDo@" ˃<39#XqJco `g#WtQ TN- ì|)jēY8bV39+kK'=P؎ 3ٰ'v59{:$5Дt B{ZNq-– "y&$|5ivÒHt-a M\ 5V nUN&|:[g"6R6-H{)Bn¬Y$o+6Hhaz.GGUIe;l!]+ mS2R`wѩ>c'AI124seq [,~K 5E@rҝP!+Xp*CYdj` H:UYNX$]Hs?nLn\#Rz^N94g+ PU i_52A}~3Wf)Y Iz/HF.2қR}TUog;H\*IUf՘"LJݱ~۲3|ڨe;řU Ze923};VY1,nԈ2IMb| G&R:Z)5ZZ4)҃FPSըG흃Ѕn7t&Q1 &f"ĸJnF*xZXj&7|K jPۗ HM̤IRjƜ6_T<8"_Oa-4z1b?EnӍ0q2U5aNf4/oMΦ%j# \-5ݴMn/m5,Yq5`sMpm;yc[VI`9EUס Á-7նT?p)ϭ63$*-v̈́}Fa Kr))ރUMw$&%.4/D PXCvATYP!z veukXwDW&ծ$$rR,܏qj K`V6-7F}/)`Nݤ–z*,1TO/I6*tRC5$f`7^ct%9+Aj3-͚;dDž44~| w a XB7ry2q 0{;$5"]65[qq+]Gbqj\+M-T><-E 5ݰ.MN iW b~L1bUwgӁsfBM ׼G9Gz0G&LGbhu2:W:Z > w[Y`o ݆X}_tRf/C \r}oհ%LvW RUF[싢D}Oǀ(6A:Gn 3j=zRC#U hbcqEw<[D#Fj?b#oH-f ! L[H"צیKPYվD5"EF>LI^r^>+ٳQ!auqpCL.1-( _!F™e!gON%IP5v]Oڒm7U3Ci 8 )@&s#sJVtZz+3-!3MHG+qWҢ j2@ T身t 'ͫ!~e)NAm A+؄U Q_LGe49yM2Zu:`HO6RÙU)j%m4f2"%-njJA*C;{BLdI{,%8wIK%tv,5#Љ*ћjM#Gs);*FX+&?ܛD<Vu Ր!}..`Er tY^|0p-*pZuD,˜c-@Y"G`1Ym ڧ?h:uR 9¾E!q>in[o ͑Q+dӺ] Jd@$ aIuz SUcVxy-CAA T$: l0"-Rxh\61}zX .I&b.~(\ $leQo8ŕ-*03$=ؠCJioBCgܺ9z y`b #Aj%ZB^}t|[`}+nC"ii=OؼDۮLbuؕ ܫuûWyɖFUX(tNe;\$yAtPP ρ-htB.9uZʳfύ&S`HI8U-з=+P3B@.P*Ht{`ŬE{(l]3UUhQ=]M*4sJa<@1a5 |T (Z'3X$1XqgzQ4 *m5bg~yXǟ0zǼ6\D#|?Qh 1#l 0w5Mz/r]Ʊ#OZ6":m{cy9 HNS|ӓOQš#٦ISqzT-%sX 3GY.x6 %Z 8aʖj-QR]9aM*}#jlIu)K ,;X1c^̡Y R- Aw3 :؁k#;t>KUGo,Գ) =0{yGO+z.o)+m 8WU-HPj h /JL&4/лք7Pu9]+ inj |%qIBkˈ>J j> W:@Brju%6Y**R]tcXLg5GFbv$Y׆4$WuJ qST mrmvéPˮupg:P;7?$ XGۍj;E9fBe9Y|4\}Vo )A"n:$|ȼaAu8o`u.+"wkHJSm,3ůCa2fr=QwL6W Jt`0 w`3D^{hYʛRσwWt kU# up$^q4@-ⲉW`= S@7)Խݪ^85X?2B4:̰&bZy6ܗȱY2iNUxn4;IKQF1SX Aތp]$cݮ6akl u;-wtnLa@q@_n 0j.v`ہyzB3\ll-fB;QcF,X0ՅI![!`B^vpz@x]AV2Ū`DPl5$|!0,Bfepaʺ#99 &g!YR:;kM-μ{|vqb‡BOC%,U[t3jKsn5=RÏn$Kg#Npe&uGT?f5rƅ=[+WԭtPqGzu{f nIGeuxǰ}e4, J$ "tuY _ƍx GLR,e%^$C4ffv/bN-唞2wTuo NlC%u8/ͨ.A8I. AF?~AQ*eF~b0Yvq@`LyE  G/ќh#"ȍ%x @I*Y-*yÐe$g L⩥dAdIӑ]Ϋ|l4]M+1b:* .lHA?,41]ٙ!a/`XžHE䮀Yj,>D'z>ҠFStsPX oX#ko(RHu :`H;8*m*mEЄFArVǬɮ6OvuF@PD$N[>w'ysy8vrx!BkM!ȶt d8 ܆z@ż *YNߤ^B>VOCL=h>wɆVO+dKv15bzKthX t Npq)”zhC :yzPnlP CJb] #U;)W79Ɠݥ*%9;,;a}VPImw!4b֔Rf gsw`)#@nZttPc;5 5a&w8M0hň>2b3Iq0%p(Ŭty skVU7!V`ZmfٝH<NWS_Z%_196}4OpqZK_fX|.E8%LV|ASAnŃamN;ةF٫р >2-6v.Y=Ov_oPQRgCV/?m|Mwv;]MUޞ(ooE38󋏶uHҏLM"]e=0HZGC%̹e9У e7 etWh7w̘N\Br裂~7ag,g"Ɇg}{hhH>~f+5ժ3R:jf:T7 3!n޷vjny^,6[ԭ VAUj  \˽$Jj(v)j\T+ )N99Xxӛ|p_Iԧo[6XB:yD-b-ATR\f,V?u HS}N1'6U{va{5u/w2zڤM:;zыxs [UTnYѬ]=NҲ[#Hw_mZ˾rњmZkO5 E}{M:㼂5*Y#" 蟋Qśr}dexR<7J0 hMf9pYq]|[{n7/ɏbWreo烤dB-yZb,(x4D[?9%t텣XpM͗ x@bqJfMd *ӧZE*S3СuXu ?XxL1؈_M%^35.~Q1({xa$~ 1.L#{"»jۀxs2gyϠRK~fŦZ1O Zn5=ShXe{\B[5UxH-ԙ c16b݉H _Z3-f3޺RrtTIlEi%)@Lc#' CVz7,(&R=vJ&֔;/A%FXkepG~cp>P6oꢱ}x\aQ=Zew]+!>PC8 c;!H AG W #uq&뒶aVԈ [vTf1 nֱ&@ƥLjPen&VWpqwRuQ3eT7>.5WuEXqɀE3  fŒhuiP*]jYV?PFo.:N6l1Ci߁ЩPp|-jU%aD]EeXƑٹn&arvQVcZL2 DJ@?-w">$рX50 !F&#a  y5a CvY!،a+pɘ\ u>oЎZ%ģc3Neۊ=jwF0S`KIKỳSk<*^Sj_4d#_7oHJu ϸ4`w[Z;mWv}~lO5wۍx]M9]mMks $r3NqnQ~aO-Xa'@mR;| NMhmгrY'6yv>bHH`P7)S|dBodorQw6xgA=pÍݳo)8^v̱1UY6|]Qᇴ1 m57m+Hͧy n,Ay&qp&ӡ椑lat{\^;Y]f5Ef9qZ~{]DY{s ~ H;u.xA5^ʘ#y i_P߶ؠS708T$mMY7y#iSahpagT='S^du+0#X\y$C>yX+y\u3i{eOy}™5cu%\FI5*B^(UoL.Y{kE@񨴗Rcq@%֋tȺ o!6 lˡF;ӮtL4rf;4N4 1\j:6}( [#FĻpPeQ߻20Ai`Fnq%#`|jfD#Z|kRZ 8xWZh h3DNE3!j33>XM&"\먆־i 0~%v8zWmU:3%9QJyN=v1֍uKWu&htL;C]@]:eԐ^N,[:ɁazB;$e` ,]*ɅU5❟CA*`w34O_ٿ.ݚkoP(9nӊ&v{&9$DyHy+`7okQ]M՜)0)bF C]SouԡQ$mSx)i(XkxD[JN*^AqOPz!Uy2(O-U[⯈(O6`%{&UQ+Уj%lf׶Mv9 ݊1 _;jjUK|D[ޖ66@"ɮrhM*f! %iN'TXFrD"^rn;q˲G`u}"u 9nC_` %"mj&PZZo|f xiHp*']׋]_hV Tg٦næ|1{?ݕݠt""Z#(fKv"Faloc2hxUᱲ \>+J.'f5it Sdwе })_sx I@E l2g4v*:%+`ZO}֥c[M,0F@3 s!3Hh•մuVfp32Ы]xweE/6SR̩f5Ҝ ֬RH 9x3VF'kd gw_`GϲD|+vHxd A PF*R,#3АA2]& eolX*I<'(@Y=.a%^t %@gq f S-orf=@S=- \ZU@*oC! #"fko;wG6M.o-0w:ןTqlK58UG>$WQ UHE ĦХ,i_K׳pdViHXG:Uʁ!e!VCoG栾w .9YnUKNVU #ؙb.JHJ2{țo}KJ(WZR|ټ NN v`zɶNLs8OB @?\ޡ n Ku4? ~t ̫Ȫ0Pn-xI@}7iv&a%+ҲT{Y`ǥNOV`xF4Ep/,;vbp*0hZCtQu1#쬘"݄=!@tqPU@ |>?z{Xchf>hA:,qR6b\K\Nz)N {q-3bZl=[QU`.'x=4nPB d]y%x=89 n-vhqh@WΐF\0 tp Fd_K03Fv>jg`F] }јcx@gQ5rgTꫛOzT~h#<~|Tq`mwi|<~etC9z[tnOu|aк\R[jPZKy`rqK|{r02M(;>I>U@k 6&7SEo")d[􉀅>e`·#E  xnJ;fHuZk+6%(]m ȪE>:6Pc¡nxHӈ^tU(R~] ^^ ^kvhN$E77$^StN:e%f9r']3Q`ʥ뒻y9]IcrmѥI5ƳxyU1z,^^,^^\0"a(59(C5vz iT=NU9 ؤ!*Fu%$A<}-AFBzx$A*hE(d=sx'Khp`$@9e;9Bcɩ yd' ;F%x#h6@OOvT9U|Q A8%iۖǞUx55<"9 Y|1E?(` ΀*}8~U_нAnlG2fzHCI>sorgIͦ_PI#;0EX2sK6Y81TRM~C/tY4Łn# <ߢr&_AtVi瘱$'S^hTt\\mTd'~p^)ڹ8gY~oZX"H|%뢲zؿo=` /K%Yj\nZhlu*&IQ]a;u# YUf2RO{Vwe/=s%NVEMP$թXJ6A566RUnBt.+IcY,VVJ+kqoʊ)~/edפg NTr[فʅQT>MY}6WbۤؔSUbMe(]( c:RQÓbS^@^5(ztTPsŬE;H+Ao=mUD۞LmsvV{wWuRM1R"$Tu73p:ST{(hͥNWT*S}%lL{`s[A/d1W5f@C̼A9 v`ڣU@};1l +d]{!{ئYyF&}CtFVb|ZLLˎTϊtt3څ*q $ IԔtqF=Cboځ9g1/A=^iz4w;tA̬EO ? t[Z˫c 1q&2UIԷH~b{bW_ZnE%L[)b>(vXRakbOgK` QIJyQAx/ /aT|[4 s`Kꔋjp:lsAY;ZfF럌}{?RS񞩕iTNӛNE_ɜB DkQ ř򬰀[+V:*=Nϛ\}RJQD$F^luMfжu.zC玒%UKo)W9žVT3w#)ta28wK(rp.d u qwR ꭤƒ||zkX%eKmF)v )zI[R cfՑRH„v6- sH)M%X&В4hJdLxj.R+ v!0WkR<lK-ڙE* 2:Tj&\Z͘y6Ң_b ^.Jhd[Th7{hr._-\i~Z3Mw"R5U&7L>\BxȌO1Dc}PryB޻尤L0K: T:e¦ @iN;vH "aўA`/ԎI[ a#[!l. T˂}a|@#.5tQɍ6g.P%TAPPAzc1ܦ(B,70UfK2crs %9$ mmy$nSkK?>iL ȗ՝=BUqIlS/ˠ}&#Dꔡ~M]uؘxzC\r;fF%xV@2 AR USm]LU[54C{!E̱:(ypӉ{rۗE v=Evl `\pW]{^%}{Ogb6Nnc6欿|m@:cWJGxW dU Ev(Q!I|9 hhK.pԧPOKqZ@sc"b:DG FMm٩'rB>"Q+9ct,<؂]{:iyV5h!E.uƷe`jU譝ZnOiR P=Qf[1rT)KkAXJnZ/n:Z3$A; nWgꊍiՀA]Qŋafcn=Uͥ6]jF`_dZʑ^sZ,jaMnkFF 5ҺBi1YݪmTV]h:#"cP8SR΍? 27JhVCVm]xT s[/RI!B*媼* 4n5ew0Qb\X^^H*N ϝ6']Vwr-D\ezkHzPvn!v}Rgv6$9yjjw_]'`2t<03>&=y9Qǘ<{ XuJ%-=+pS.f{Bv@,ʴw@gS`)*c`ퟒ^Qs5u'WP0$=]{*t4EA2;>5尜:w#~+u2ްL«'8|p͗?GԪQf˭Z %$og^>ڢ#zrA x]rC *op;H X-!fݕlJAɘf %02xr SpnyX~).7s8n@yAUPϼRNV\%!PgvRta;5߼8tpq/B(S-YxufCtm=< dYato:v\=9bf9*@'z-2FVL J% U% o BӢ9(RY n|Lh|%qGR'uehK8 $-UI^,ٓh{C*HCI'Wf tX=-ѩrPbZ l$`IޡPqh]#F[U:U!RpXEZy dEtс(@`MXi<愲[EN79DȤ] :X=(Fu (^@zib;/W1Dsȭ`XVo+bX+c,{9 c3Q[InWd.i( \G;רO],ǵtX6rfj*q;3˘LsuټT9#TW.C[[+QnRPj&}yM2#gק~wѺM5Ծ b $4'|\,x ,CxmXw`o^%w q+ad $y˔WW}05pqچ.*PM*4weaL J/3Mmk3^u?]r·.񂙊p_To%ϼadOtr(D}s{~Y|=~߈!NƷ+2@Hgۿkzӎ`5J;^Oo.\pCL.PM}[]!SYLɶޝk\2ׁ:;MUqf}Nfm5 yw l[+wVdxB}k)pfj {ou2kBs9nоG8kr eB{yp# @Mod; ȆNJ,y3~Z?> -.34&7AU|{b͠N H%(YNwIa"è/\_l|*[455HuG~²FhpG vη$w̐ _sT:4`Is9˪#̥/H@M+5> 0$J :u6/1|bc&+0gX:Ц!0R3ݎrԫ-|rUmlfC: uȱ|ܣ<,5.Cl(kCi\XB3|\6g`^VO*"pJUVpqsQ:۴&S$'E­vߎ=Ѷ:|ň =!kpssZF t~h!zt!oq9rU$Og44O|_CHW<>Z{;4-;]molEV@j&M6UzDPQ* Rӻ(u3)\콤=J`˖=iiUɸY*&*0u Ȼ i##Ar t}VX$iK7bpOj+ԏه#v.[db"ĤO$Ȍ <~!S7/'ca@-KH'>7![OJ{6u]̂BH+.D_[ywclƚ݄*Def=PyTmh%ک3XY 2Y-U鶍}sQf. m3MmWOlKZ&c- NZmy0>4O)턋$b7dͬtXg/D=6`Ȩ70[(?nEZSl0bR٪yBΪ/zyQoVH\jJNJ+ˢ枥Ty[R?ތ+5MuLlv%-mbJXJrb ͣJ\*$ϯn7UUo5=9&uɆڇwK">EՂrJrS`ܼ-o1RGTeCl)h-g3,eS؞AsQI7GuöAXbnDwo5Z{gt|x0f\*[6uRO)8WSgӡ" ӽLQJLJЙ-X}XQj a9kX^f^&mh蓳< ޲Uwk#x~*\=V>oge8 pnwv;l"rؿVk=N(`쬛пtl;t:rYvt%/ m_iw&015msŮq*tP jam:+~L0IE9nkכ5kla4~vqfa$z3aF~ uP2h.NSc& xP>l |o}دH7C-VM8;:1#>P`vF6X0m bf3n^۩ёBNugztq4LV9XYEu63t4sw0Qd(d(|fxcyw%rֈd^ٍ[u)pʃ m$ z!gf2geۑkSaEq֑+87K=ʓvuݗw6Q oUPd*6;Hh7͐;"|;<$2rNlp_aazƏsdw˩&Cˍ ҵ-AiKgdzA%IU[fdֺ rKΫ袰akPT/Xw âeU0Bzw U=e7v_v"ײ}aXpud)}dwVD. 54]A^^Q2H)9u*q@9^`<[eȜP)QgL1vUi_X8q%Lw1ØTэqݺG{sY/n:ET:Ud^c]'?m#o||F 3/$p?0 O`x?P.a3\C Y`5͔j@i [nBݮѻFv[ 7m wy]z}]6uud2  &tBLO1@B;؊Fpa.ȸn6Ev ̔SJVW4ZUT0؏rdP0pym^t~S1r*;o0ߘ{z Rgaspy5n?c=@eT/:4}qA\˴Oc6jVX[{poG.A()X鶓M޿  o'h&ˁ^IHb8FtOYQ HrRo(6 tҵ4=:봘kJS/kOb^K!2jGصqbԵ,F.%B!J%'?XDKM,Zrrè70ն(jٴ6p{X~GLt+)f씸ˣ¦Ք)ƳFA2x(O+7*[˕aq+ +p &z0q, "'kl[U8.d#%,]jDd4/uSU|sB ZU(~HtfV Fv\!gvZ/{)1v #TFR^(K5K73Zi vh(@vd1ƴlPtJܡh"p풍P%ϭ6T8OE'h8fz ľ@JH=c9f\ Y*_t>n6 $P06pQZHs%e m)(4x {^v3v79`@MOEuW}3i6ӎc2͖jq 9mTԨĬ @Jk>trNHN NS( QMpxg(jU\{<в}|EҎ}c&8ǫV .4rl[m776+n+_hiJxqF)䌪Amt,T0j:S,%)݌|rH˦zUញ01fBy4%Ƞ l55M"dn~U;}L#BZ,@5'ԖJI7~ҁiUpp(.JKNnnB[ ֹ^ƻUfp QMf'w MW.G Xq( Y3UfjD@?myɝ{ WoEZI)r ڈ`nZU|p!Z`thNDץF ml+N)~۹eFUHNXuƛzț]&ͼ r!Hknwaݛ_ht D\Aiy?2ہB(Vg_ZBZ~ ;S-Aw\chQXokzg :mT ֭`= ]($ԂsD[Zd6[s|2n836bOYџO+~H-=gnGgZ`? TTPL(8P!9{J ݾT1LEQ6Kx [c|cU욌m VR%qZ4GhӼ3|:)FJ U $NC y 29, 5hY}p!νѡ\P(Cm`§AvT%6 N D9&rTnT17*AGQWlOW3mpA J.KBԢY=d .Z"\|:ڲm$!YmcapH2kR6*5_]V¨~ժjP#Hi;dswwVw69/^V#T.L7q"77 SꬊfsiTMNeueC_xqx)= HjXG6WWv[| >pJmW{ )[ ϕj :N=UGum4mmd=<( Ds|`oP*;=wB 僳e͈ԑg=<[*$xG92Y{yd2FU(ca9{-DtbuQE+_WmMۖ#+)u2CxZ:⠩IOfxY1}m/ mP\f*T쪤wyoyڛ.OS`T*-v!| M7;Ͳ {Av >Nc< 67vڌf]ѱ,9ַ\LŒM#ޤ}3t C OgZ1Xv[ogv&aA# qKp[pR/_&%5wH*Pᚢg g:2 Z>MsI;oTZhh}ܠp.#q|K o^GncIit6Ik"u `w\PU{Z]w8_kswiŸ'^ʏF ?_@S^vJvfj & xF0`ܭiwa9za#lQ9PxG:ho۝-B䛑mcV<3UgAD? W r>yZ(X|KET @u|A4u$o.yELmbU躋mx0aNK uۆ3y&"3MhAm Hz`ϔN@jnM|͍5lM>G{ΏYIG;R496Xo~Y-< (Y#xwPr~lN!;iyL4"ݗ&1L(gL#p7!50,kF} guS ChhYBn Ͳz7G1 s 8! :GK5 SmV9`@V{x),-/=`PFz;Px¹Y| ,A Tw/u n-m!'T} =x*pE`upgVKd#J4p0`lhQ| bJrxD#y$s\gӎhh?^M"7vUkqx\hz u4|T 6`ϾlS[ Yga`٭b0,JoYA EU'bѪQ#j`,RH?Wx"kO\A#s׳\ lP-V(Ϙ gX1o)_QǮqb, 7b+0\*90!iMFaR}.O|⭗I%oi&lŪӼ '}͗)T3$4f-׼P'jJ'7,L EsUuX(vWo]F|{+]',eXAUpf{aLӛL|rQ$KO?Pywyf7CSMMI4VjQ0HtP/$qsk&_/jݞɾ֧ogc^ i'N 2\7CC6F $M77؄ьӇdc.Eu1%V'͵<),(z6r;»^U4YH8L3& ͢W^orQH+NV_ vuaE یM>gr{n9Dπ N݄"Kꖝh~v~ oV/tDkc} S^!NE,{˶M7&]zy7][+U^m4 w.r"WH:^9钖 +mE`&z\Č[;3#~ ?""gbĈDzC\UW଒8.{%Hnr^_a}ZLY6%] 09ޟQto|+hO\.]7_d&$P;ޗ+ x`V2JTl-uax'xvʱ*_UЀrG?|t7+J޶[.S$?qiw ~e.|/˺u6=e5 /ibq8T.IOϻc)YVWoknnsUzƧ]]c^bVx J:yiؠQg0QUh@`ND],S嘣QG/]%z3.YWtȇ:#:Q398Nn w*C4^7- x}pzA39rD Uݳg P5iF=W:U=Fx;H(BVO(]|e3 S'+W;5<*cxp?fу39f{DX?Zp]r١ G'!ͳÝT8B5(d6=AFa؍ϗԆe@%Y(^b/NuI9h*p}\(SZ:k ZiQ^6Bh[CV;liWw!Rn=.}ٚY>EGw̌@JZ/h9& -E̚9!L>"x!"x@ްj^~ƜT 6K|`m^9\Li0ht^M&'!|D8>@QЭG"QbSIێn=n`_Jˀٮ;>ڳ;-Q!$xߺ/b-̆PDOԈJKX.vwY^LK A2㍗SzB؄@+?g5xa:*U *_ͮ| ]? |iS`~!->G}P&ܥ<Ѱ?`Azd|mHq2zL{~,ߣC6"nQ&:3nU':CD,FQud8|2_R4MͣvmTbSQ^kI!#:S}[G{ e:}rӜ*uJҳtB/]s1~(K貣t?ͧ! 5j~NB($+<Z4/[8{$s\r4LuUVCj= gBH_X!Nj/[/STCď&UX+"4j(jHP+5r!y.KzR46JIlB~.SaHĂsbjl*i4!hP'+!p~}pt|I@˶Fi%)9P|]XB@J9, oF-Nttij-Ky{-E7#ރ "pO!@YB~Ct1n>20aS}. j>gjuSvZR$`DO#/^u᙭DB ]vԥ#x!^ȸ<`DSˀq> ؏ rѳͶ~ FӁý!+©2o3)>g,͘niF&;9\0$l V%o:OXUˤϙ>"Ə#O- Â۸r'hNj?", ~aVӁ2 ^>$Z:!b5OrC `p7!$hM(Id NFY&[Ԉb*@4Ɩ/ AE24ܥV[2qQA3TbtO,Q.=DTkg{X!o# E8BbS:. =lꆂ#3<cK`i-6LJ7f|շ3)dN9,'OxqX"hQ}Lg{J|Vc" !ݚN`Stẙ#o?@$> F@NE1>8:'b+; Y_t+-L&Btr1JVBE0*HxWc_œd }ywW뛆iuAoCc|x;ho1,⮯d}Due_7 endstream endobj 58 0 obj 33841 endobj 82 0 obj <> /Length 91 /Filter/FlateDecode >> stream xUα 0 =_K)iq 3 SNņ-S=MfA&OdO #M.sin% endstream endobj 83 0 obj <> endobj 84 0 obj <> /Length 91 /Filter/FlateDecode >> stream xU= S0w ȏ4]z| yD1k!v|@MTIܨ86L}ᑲBE`/7u x$r endstream endobj 85 0 obj <> endobj 87 0 obj <> /Length 90 /Filter/FlateDecode >> stream xU1 0S8wxJ-%Fѩ4I},pMtTZЩ6%5\ԗX,F>r-z$u endstream endobj 88 0 obj <> endobj 89 0 obj <> /Length 91 /Filter/FlateDecode >> stream xU1 =ᡉzAki5nHF5Ґom4P UERޚoh񈜫zuyxe᠋x"#4 endstream endobj 90 0 obj <> endobj 92 0 obj <> stream xUM0W\۲`9 J^;}ڎBV4zo捨Uۏ׷Ǟ*vt~mPQk3`m|L[l 6ܪe9ZfplLPM5mn!Xx07H`2B  Cq<لm6ATMeFh9F+ĘD{l"t]$G%|PF5[#2.sUD0͈?mC396y\&R˪]p׷bv߱d-1tu3g$N-yw !6 n٬Y PNz}3]]Էm83\DĈMFlel_ykTYQ ==P_s"lJS}gg@6$z)> stream xz{|Tյ^1$̙L32O@ h!12H2CfZB}ACZp[ok >J[E*ZB+Z^@N}sk^{>Loc~'}; !Mqujl ]݃ם!D$D-YٿIB;lNDo?=`ۻ#7!$' gy?}77xϧG}RzX1Bʪx/%un+:xA FSJjbNɕL:+YFd {^t l&dڻTZܐM>#O)5y$등!ꁝ$g++Rrm>'O6At1&`5_=qwcd-Axw/~5vHF  ry\}F}\OI3aT$V,o_0Iʨe$36a3q R{Y݊v=BʢU+-+/klK\\vႚJUsfϺb3JK rs| j1NxH-ZP\XTXkKA9/!__@`Bn9 N%SIb*`HGXۻrS־IÎχL+\9Pۆ:piATTHM)LV"y\^aҨXim0h\R 'l,`, =c)wQNy#V 5kIAkH OfÉBDd%DBB̶N'CDmf[->zyu~nm 82ֿ/[éZ47ilA#c$ $m0;MnDU- .N "U}Wx| 7L,hGͰsD!k_֒dQJ  q6ӑ 6?풦8E &נw &̟{|MU`2j8%'4RM&@$V1|}A96<ˏl(ZmoSg2IGXђPbC j+V;\Z6\l1%hᯙX]VmWS # ֮Q%JjY\ɵm *P^e-ϐrRA ),v%ԑyBwrǗPK8@-Ü#|eE˒&e+[IPvBv%l-$t! p>VuT36mE3(uܚ*!|nRVK_"N|NOENhm ` ڜP6ЋTWdVFIH*f`L+ߗ֛>ܫ~_}2 6:̺VQE>ԙ[NcEN$,p6)%m&ޓ),H>"Y$ӥ/ļ, VH{=@pB*a7cK?+/s9sY:mP!7 JK待]bi},[hS{!w+׻rӬz4C^nVW&ﰦZ<oy"~ȸ !{iPJHU722Vu}2aw&΄D&$L8 & N*律^/i:͏PG1Czmtsb@x·Qdչr3s' ݏ!G;͖B8 ]J \5sFl, حR#ҋP1K&+/U2s#U l,:fmLK] 3Tb&jqɏ2'9/P(Iث/|£[d&%W5 /,jUW@ G]q3i0t %֏aܮXoŘAqoƨ'ApcۥTXe 8 Hp*{YiHt|9~,{0 lpߡmPϤW<1/#}-׸!{o7+0;Q i:j&tbkn?&b,Y,wC7ݾޖđ#Uվ;vp_dW K̏g|B:]ݞ>՘nIjfCjy1K$$LI&bYjIy. |आ )ª~q56Ѳ%% ^8Dˏ:G(cNⲦ]fw`ҧsy)Ôg⍦t7}ֻasn8冗GnX7T! kp p憈 ^t :ÌNFK alI7abf:c4o˻ku <L>7le"\v=9>7LTZt__:+5 l3U\>`/K&z(K}S]Ri5n;k,9_v/sv+;0? ˈ }J3}јBRYN8j<aq|yEŰVpl[aZU$7^,e`M2e໯HS?4M ^8;ݼ\wS_oxU}O=/%^L+ԫX3H *3L&bE3!rZ- T:M% (8N hE*0B,q|X_8F:܊W>ܖƍz셣yUQN=k,ի7wZ*LƗs w㇟s}x| a|$xM) ,)<(i3D[z^d5qgYsOo usⶓ(B-^a# x^w6a[Eh+)pK pNEXusL2H24-M_~E?rx&7c$zmʲtc#X':<CJ]p”ր`>Am>hRO3$/!M Ȏ'{,8:4pygu12Zo[6 X~Iuu |Л'nϩ!xQ{sqi&S^4bC ,hZYE;'4[NA@ːC15."v sl;S՟&BcgqOKǏ֪@l4A3oHw8$`%X-As$(#Ac ޓu ~,=#q{$%q%̥؝c+}$q%xAG$x@|MJ@)S IpDg줸7K* S97UHtLX⇨ J\e䑸+P'%CӯejHIjH;$AiHH)H`HO ɤ 0VMi;߆I5jK* 񕶻&/\|sr+UZb:3'wMW< PCg@)>1+>Foy+w$a x F)%E BZ* YFOX&u('z |Fk[:!uhDj~+WF#6 EgDHM Vq[Aq _(9[Dp"fQ$@<QUI4Ӏ}r4ސLvV5wz_IuIgj2&:yQoLR釴ip" A!-gMyϫpz| lWoJAX3_1PĬG8AL'Z>N~.T '`d4Ah{ SA0#dߖ4CFiBFXa98aUjne $clIF7ªq# KFè< > yAkFa ,`|#lh#͘FxLR)@_J_ȋÖkVG|ha^TUg O k8yw?aX\Y'm8z2Y~i$n=>v' &YOEQTO-`=yz{C}9zF}'tF okG|O>z(k%c wHҦy8R ʶN/˶YmXYo6A 2m`A{-VV+כ xѩbYߦCzTGXnEpC27ْqsq >N͖_ 2`t.N?u@Wϟ~_=inˍ׮7b>޿|Cp+}eC_wm{[_u!}ćB*LOFiSĚjz ` W}a*Ҭr̜=9̂\4wV~}7~od$ϷcSou?UϷZ>#oe~3gGcgիu1j?D!D?O,ӔK~x !H#?ܡǛE_/O}oS[čGڦ8m*1~fEؠ&nj>n>1GIצ{Z_G0ݰox͔)HH=0TП̰h"밼_{vf&@,J a͓Lҥ hm#^#!'7Zۀu m$f&&BpO"N#NkI&?5Gf dOmI'"n:#HdL (xFq:^D't =rbUڮX<܇^Xn ýq9WL6ttt="r$މ  uSiyL2HS<),_ñHoM0P}]=BysgW{9CX^\EF Υ7 Yn }XgWZ9F uuh,xg0NguDt x'_ҁ&z}MLX{_8܋r8 Xh3@{j7EaTEK/ ZIC"ݛ1bt!B8n$Bݑz:HwMҷ#G҈ phHDhrEq+ǣKJ6o\V9|X|K4-EӽWFtM QO*'kc(@3vEXWwqomICRZx߈*DdbD0n!} 2n$e[Ɣe "e !Ws+rMzF]Hߎ"W&M%1Rnv6ŕI_MգkNl wr/r9]LwP{G#,B:*2p$z!ƕnF&(]LZ/Z%Pbҷlg_$9GݩYxV5n|n1ribmb2bpڏǴy%m6fԄd gQSO(נ_)Ghں_qZRBcr{Qqoyvu,dOyn;»ou=h5Z\mfQ91we`th^*3h؊PL+ _LNRNAamLq+YQ RDj7Xks/嘴dXvw/6` RnMRr,XeIcf&54 _rAڍlՒQ,dhtQd~%,A_1ɱҮEJs5+,89*&tAj1;k'EJ4aYʲDT:r%h\1gy,؅8'lYPd<%f&H35`{)8N9(#sn56]d )~\l|gk}?d1,_&gdAi(XG!rs}t\)(9z*rjN?Ӊ\O^b^{{7OiV4%= U޷ޜ9޶7vq 882v7S:H^IWy.~\VnzDF`K9(TkOG@V}O}CΊs@nR0w<'n%/';T2T=wH 2dcO gQ3 (Vn9#޺2o&d43|%meV|_{]]w+fx"P7Gx|bk(MyuJӴ,|gԭ_;xv77pnJQ]wq[_.IS=RfkX2_V^Kղ"X,%K2h9nvG4@=`Ɉ~lqU'SY2۞ +W m.R3uI%65$†BذNHM 7hE+XM0p,I$V#OcC)/'`8Eb(2bJMgE endstream endobj 99 0 obj 8927 endobj 100 0 obj <> endobj 101 0 obj <> stream x]n0E|"W#!E*M_0YDйfznk_iw/޵Ek*alcneA,swrƺRJ rNJ8CT!GF ׄ)ȏ#qT F"Cgfr0G%#ˀsGrO?J7YDJ_J쟜?BO?" $L?+F貶[kùޭu@G3io6s endstream endobj 102 0 obj <> endobj 103 0 obj <> stream x8yx՝7ò,K>KgdN|;&ķq;>[dKm Ivmp0\f9J)q rtAeBYH)¶biqҔw7#"~H)Ay%E"{'c'!|;0h#{_H0<{|+/U$?!40vn,ѐ׳"T)smA=cG? `N8A>vS~8~?x}DRQ5Z>ɐlL1-TSA/4 &;{xIL4$,!z3 Ù JI!)#.OQ. ~A2a ކI ]?;4iu' d)B[S03sm"U$ ]6s[w949mυ:>|?MLg a8 Th+*!-M@i\.{fJ;TG3cA3)d=3)pP-}qSt>v{PwoKru`~";Ξu:/,Idm!N /xT)PPd֊nuӃӞS!hqLϦL[0Bɭe`Ի[{:\2W*~~~p;^p7x{1a߼53AM}a̺&LiPӠC+ٱ pz+m+V;R1,-—4EjΚYT֧嬬Q[G/X]=-x{9mIyy1-<4񕮦侖"{ҩݷUtm2[N^ ?lᑺkNɕޏʬH$Cz=!i|e;ܙ:3GJ&*jr9iFfW+I}{ڱf5 4[LjhG/?;z%k"~7{?=%IO8i>epZxV?eXnVsmڤGʇa>-)Րj2!?I)Xb3$5䛭n5+ly X@>) yEcKϮc[Bi4P,!Y+V-IL,{=bɊT><ٞ{ w>pl8tѸǒZhJNM5EAA@ҫu۞"ij{N_$wۏgMCI s[s+CS{'~c/ϝvՎo38t݀_9{0z<{Aiq$kU̵ 어v I+wOsE=oFf1K)?=Z?ԜtSfVg=p]>tz2߱O;:'~!r>eqks{?s~{ci [uKm-lPN1ӍxB '{oR,0忶7}Eg.Mz)7A-apd6@*qp2 6B>wvrC6J>OS!BOƀ|P h*AFyXPa ܠZӜPaTk^Qa=$T8 kT8{UZPk0p l7Wa4ܧ©"Y Ƃ}PxO$8˽%bueU ŦP$xbxyrӥbbhJŎqo_{=ШOyFQ珈e%B"-Hҫ˫W^beTLZd =b,<]bhbĈ$#H b'qط5<<(`բ A-JRo?7zb144ZHp,T*q'*82`,I49/E#h 8>"FiHpX5!|=Xα0avcgHe+‘$s,㸎 ch#x,XeaxYD$Wm n)F'Q&=> qpPh e8A|@"C1T cDcDؼso$'VƢX,\_Q{rZ/-W|/'WKVF7`i&XiiĮ0URq=ʫ%0p,Z "#]0#:<|=y!0REX m(B(xP>f7?!qZ5B=L 7vEElxޏ#Nj:ȡ"<K_J[FtA=±歕-X[l.1ơ]H fOD9?u9~Uj%zT7ӤƙTeVE^fƢX!Ps&X(Ic]wR/n)eFģj\Jֳ؍u |6qUs{TuDUףe?!U:j=5D( -Bd+5CnzY2(o7́Q^*{!J +2j}>{e"?Bv: ([G#z*W+cgɧFE= 3Jng_UxlE%[;2$[2KFՕGٙk*ì˔ajyÏRgB;"c9oH #2vE]ur}W)k=Wep,2>nP^Xk+ы'vJiU3'^bKO*vz^ҍAc̟(e9a]q ̵4!uO֩s# g/A Gz-ȇ~Ga-BNjV\8KI $PW p^ň\R U܉|ݏ%rv0G,s$"L}J#"(ݵ;¯έ^f[k~xMMW7l4cC'^$ْK>!RB|<ߖ.<N&S'гlO˓ғɩqn_#"Rh I'Nto;A%±c 81#ұԬ'P#+# .> endobj 106 0 obj <> stream x]n <PmJQ.iG)8B6iI֭>yuA`nNUf\ir-K[fck(Xrw _t8N+_}T]_0\ pSor[/`/xFq"'%\BhQri-A~.H b3yCxN]=r5yOȇȤ?S|3r\G"n-xKY˛s}40vL,h}. endstream endobj 107 0 obj <> endobj 108 0 obj <> stream x:ipu !O]hER O -ɔ")J@2 (Y1b9n7n&c-m7H8iXvױ,'Luo}(@́ЌP. %cC|@drj `~njW3 F#jx셧6~lCK'eodyX[4_22?3=ɻ ' LDY/~`ݙd"y~%V%'S//y;K4.ɜO/Y(qXuT37z_ϾEc eotb)Ap/̣{4ގN8E3|fVхXHƸGM ߀ #%xѱl &r?̔pnҺ3Էibo?s_/`}F) L e ?+u@^0]xڝˀG<*|U=LCd!P_(ʳ➛珸Q(b tm`]zWؚ}[^]eC҆O>66 ЁuTz# ﻣi߽;vk떖ƆMu5 .f-*\g3 kib\ܥ*>Eil}dUaUzz8IQ59(kPא$'o$=+h۠PdNE^ı}~v*Y{{9lH!!.iXO:)F\XgP:"X#pA&%v#M 2Sֆ}+Ы)ܤfܤcBåm0/+a_Uҝ}ҊJO*iAij?}.Q3y7@QzzSL55C~].EΫٹ E) Idb1Z΀}Z龃~M钣*Q_p }(-ʰp&s g\Є \Z攏22gE=Pm󚡦7(uQVŦRK.+STᘬk)IV8R-9-.w(d)X Ȕ\#5O'Uo4 ,ɋ5+ILT uh ZZϕvB`}Mv8|@xh.(xf߂ 3 Ao`a#N"3YOHw<\'pGҐuAFRǼ-V*6Rp5iϐ\S#bA]`(fKIʡl 6þ:RsuC=u(_^|K`Ge(.;.!`K Uh/w"oۍغM`Eu7½6aK_g|#Z?3?t/,w+=uXy.'`.cC {A?7ћ虲j-3UVir)4(ɷ VlƒUXV**| ?Y*BS-Uh«Ux * rT8x&%f%5ԑ (;&7[Zj&2S* (fIx{xZzx҃J#WAξi8DC*zE~}tRQ:(/R"  ,6CP MAnU!/))IzlQr:|_Nn?Euۭ]s{U6^_IG nίJ녗_毼H73q,0+첽.xssv1ks.vqXZ8([(b9nP]iN=KvmoA2m Z*wUz;l~0{Е}S Ǩ&;<-EEfQ,W -|b-(  1)Jk;JEʋ?#֭ {oydZkO=^hUW-/HrYe4VJ HՆ@u5l}[T -?IxQ0! %lLr|V8U^v Bu}ܽ fezm~l%}ke}̶ `_s1:Mvʁ@d^n痋V6ƶN<2'4N|ɉtbƉCNc'88QsbS%'^NL:y ybVwx~F]n.^gh08'y!ǔ$fk{lK|/'{^7WXQ^nS[J%My"80X։*&I ^x1$U»Hm.R2_A s^Pq I8'as$H(sǹ@`ӾZ*Lsgݽ! bgPّ{ ;Zv~grݝKg=پ}#mڝ&T_{Gг_=[ˏ7vx(Ӂjko@rѡ^]vOe7^a@8/p!ͿW`[:cSLd$,Ռ*T4#o mliܑH%)5Kě,7m5 CMHNVbtxO:#)Q}#.3TQ6mij]L`V,-rj.91y}8r*2Kg")"hp%ybrr&fئg"T,NO$IkJuFt< 34)D.D gk"L3p6NJfgX(͙P*A䴚!+3h&|&U/M*D/s"bVfqVY^_~y0Id]A^n-M[tX2nJǦ~L: LK%\%( H Hq(QeD - $ 4Ap]v&:Q;ף ~,qڕaSbtz2M{ M9O^j!bWdRl[hƪ 7#2{sX3L]DK>8;2Ex-ĉp,̭2ۣ$1̥&N{sx$u] q۬?rG<Y4I2彥rN>r:Ӝ%<+E,)7a3̵Yu Au]UK8Gt|O{Ƿ1k}UFp2 8[ܵx5+;VI|yla;Hs`4\lQ*mDu`9_a},$4;迩\fO1}˶<sfRӺ܎tJ}&y2? <7kGOJ,Gnrݜy_T߄$eGw`vf3E>MNMH&=?Zŕ\;Xf(~}+S7f~+1LgP??/zt陓os ?=Ecgxs2-$Lfo$tbvг[vGONOӃk;aᏯJo/QL^n> endobj 111 0 obj <> stream x]n E|bIHQj#eч8)!}L[ б}/f؛@m.^aԆ%UZ[#,zu0fuM{tlwz^n>O]Ź+L`i!,܋k۪XaF˟cu@K܊ f'$xaF 5 /QjUv>*P4K>}2WYrWY_a/_SWim?Rx'i8mwκ  endstream endobj 112 0 obj <> endobj 113 0 obj <> stream xݼy`Ǖ8\\ݣ95LK$@K8 F@$$cqm|;%C`d/:dc3N8Ǜ]ò^~gF#gkWw{5JLdDjB5s,(!iٹ!0!᝝׵Z!vBV6Y!'y8 _yl?B;~oRYrk{*~v~OZ0?D1AH])mYW0_F;0e¤V3, l-pܞ, ? fgϚ=8T24\V>o/!?4w,!VCS.HM>5_9 z\=<BZ,6$qbMkuOOiTdkZ%,fGիb11+DK=: RhV^lv1.E)TN<)4b!H6'3 1tW|*[7xeX<xHi*Bqq#qY[A1~(U85\l .ŁhM`kUR RkE8(!UR©(CEH1 4O%ǤNc/btQnXf94Fݬ|ꃧ}|P&|>EDF|0D1bcz޽w{*zIe@ p]*Դ`%.Gv|g;t,yR^uYfq'ʷk<{팔U^%;c©7 Ć^ԇ{9UkMLz=6S#WIM-ucf|վ19yADFy_F0(B3)R9ˮS)_1l5GSV&gpZ^-g4#Q,qm MwX '!J!$7-".(ǺMϞ6tʟCyQQ O:xxyπǷC>.+IM\AY2Y#4vb1bQ.mDz`=E aʂ:jIvPpKm 3B~9wL~Cp^m9'N`|yrWga9.?ɕ{9d:3l\tlv@Y[ h-0E؝45XT  ZNl8恣@ZAS`#E[D+5: ѧީkJE\HmtfRE [~nG#}kO[ W$y/4L|ֲAxwX>DVOØXd9#Hf}CC~ÀzCK%x?Qq9^ˆ=E{JRB\xFL3ɬYEE&6\:8k=C0EkãEl5d,7 0/ 3 6 p> 0 a: %. cI0A C-Ʋ ax' #a6%hBhgp& ? E& &Xv0X}i0 `RAZ=O)B{SZ8Ų\L:S$ׂ;0Ra K_K(ץNB e s:QI]Twcɛ9USSop]fL6VJaN3ܼeƨoܓ%U'>bD9ERYΞeFq=ǜͺ\HYts`́q<=9$bQY1t嗇]蒔)G,u<g7O}K殾E[*GBs{ob[*O^_ˮnH]Huڬ|G#lniB,[ :"Q#g'硅zy(a&v8.psʒ6?lF3s~ÝժSZRj/W~J=~8@ۨ`R?ب`\ß(>JҰLa6$)$~jS۟fEdҕIǣORTt4z u~S4i7efhA/}pb`:?𭴎_M±zT' 6t⦺*LA|%7v>+O}ޔ4^Nc.&QpJш|*,?"+xwEܢʲ/xU sΎfvG/]|=]R1/a#CrZMUD 8.t႘ ".\0₸ hVt1 At̩O,Ӗ HLX<蔝 JRa*{OK_.L Y5!a&j‴s?G8? M(DZq*'f`ZI\$u'Z2\yKp a"=٭} [-pe.⇞"xg>Vs;L$*}n.'LYu;hcb3: HИR>`b񑘁h !i($„mG eK yîl߽G3{OeЃwXղ1g@nHus)?) 7,c{ y-(9x1[^tlrPZr6˝ lvT,Ffϫr:%hFR@PQ |,RZI3]]40ig&6|vt{7C>x 9w~ׯn}LM^}m͟Yſ}*ᬛ-C!7v䟲Ǒ;8ߵgp=/"hDC\n=E=Ϡd茣[>ꆈJafϺarCntÀzscESPJ(hM>b.j(3rݹwͷݺg-Yɥo~?:^û:}R#9`3ܚLg&1+Ǯ?N=Eɯ;F7p>>\GB[얌D5Y#[DI-Gjjjګ b p/"䯒?Cck'01fݚap ҭX`dpz'T;ax'pNa' 98PcB8?zlv;>jNTRr4-&W˯lMn'H}*juͮyL?~g\U9H0}|RF$xL( \vmR2l&yAחe7,b#QWAAƛ 6CE 3` V@WAKWAuU*WWUp FQSULR3+mVK"}!եDq*`xX,eNiN bUPB.(_x~ͧ"}kUu[oO:?Uv~1JEtN#[j+O.ilۏP[wF~^Xc?y>?nٵ+so޳][w1>VM{wɼUo?5`p ifw72{npw* }wطѧkvKVNX("Q ɥ|ct]$J" @D0D31:LbJ>L5_*ɠp~ʫ<̲E[27zg}w4Mÿ_+4b73 TmkzmXk` >f^3coԣZ=522tNTe* m6Vui8aY^V َla|_UE7,x>{^c_/.N޸y!M}* en<[5nmp]N8y'N CbAY:l𰞾(f 3ah&ffSMqQR~Ħ*lx0lc93Uoɟ|ww, NW/[csHοZaӛC>=¿\BvX,kcu8MDVGfX4F V'RSzf SuϳXNxuj^{tK(dGvecOјt[-†Fh7C %f.cSL[R(Ec)A9R XVx|w9hkUW[;!sY\` ђYL[_4 Ay?~0 X#n@XuӱH!P! _էu5 aRlCI+;Vy!eхE)4a6/G?ȑݝ{ U̓+s'`׳(S|:v@,A~wQZB܁h#d굍Q=cmf0CZPoj3fa f8g3fNaΧhXi;RO{Rhg!n!@VGej6.Q=:ijΧm;t܉=qq$ϊg> 'oJn-*(XQ V b6A<}]dв{ri;8=v13(ͫK&>tl$+k!34c\[@cҘbQFt;n.uhmh ΂Y3 b@q lBY,%`봘c BkC-f_Q)ǔ-A1LPLM0;\v.;O9#xgZ4- =όv 7KZ]=.vAqE 8:mڀ< y0Cy?]{R2j?~BbRYTF‘6'o쏟}])w:w!z}c)_6lo?{RuEf+GX8<%0Fhv#D +FE68: 0DGk͎@{-oiJHԍW" jQ!t^% v{ <7~ 2+n R@t6s4@gbPO4~&;+~omi,\cxޓ2)օkks.V C,WUY>7rF¢i5Z啼1/31xG%7exo b޳1`;W9kVGWlR-̭8#QzGC -:5f: SQ3G6vO;դnkM޿Yoh; sx[T?UTn?K}:ImW~; 2:<,F#R::Wa pT &fj)V IsTxR>+N ZR@.0X^yU4o=)oU'M=:\Ig%\egxN0WtH 23GfRP3ڪ.{yMWb-|O~O|!TA{ s2^XFCp6#!ϒ!UY _w5srH[>_Peg%7y[O,vm,ipOF; : noz@Ub6/) -zkoe+?9$Q (Xn(gp0<;he?RAH.,_gx5bSZ-XRU~ahE:5x %0ZC%+̦GYb5*#ln![U?LOyޛ~G~`ו^y7~7ݹ-z:؋]͚sjӳg=s^zmh}lɡoݠ0[ ykiFMc9A-A{ *m 6oHF!ނm04)`1dAx?Ri9$C%XJ?hS  2Xth3y)O󰟇uA?:$XO-א+SZ \F8Bl^E+M(i.aوWH= F|B\b*l|{mǷ+1]UF^۾1_ho .'ށB86:ݫ18twFysjt>l '3o #57L<3{ִkjԞ-ݡ?(x ES _Y,W[Ms|♽qc*o[Ygc~1gE_NK24 hU S)~Jksw(٘D-DMJ9Ĺ#3y0+n"#דDZFq"m J3 "4F̋2T66rVH[HD=u앉4ciX`"͒y$iqv&ݞHkN"#i=ީD@fhI3i367i`LjC/ )3QݱIlcg{8yXZ2D\ݽU\U|:Zk/Wv5Ѫ[{;6ԻW,cLonS2s' vMboSK뮦k{[wv"KTX4vM]-b}⺶V lnoBv{z;Z:SHֽM}]UM}P.k[Z:vvaĩuD,m¹tuu&z[;v}ʔ~eһZ{;:;CZ;G:۱]}}]M]Njա mڐbǮtE}ͽ]YSKӎΎ~l)dhAB=M]E5{z{ZqWX3Tݹ{VZ[[q{[;v}2^hK{Qۺj҂Gju7٥ ܟ\Sso7t6c+{B+nJ9S-VYu= ~*\RXWƕku=HZ@(9xn $cGO_q_Ggqwк5tx}=i%-DĻ Mj&ݤ\Gz)V;BE2 ]JJ\Ed9{lv.t+3hnS 17c k,MoW$)ͬRs'كhB2҇Zbm]fK]~ٚ]ؒH)OKaXoCDVʽ>,iڪ&H"B~[ŪL6L9lm+܍MFzzɹa_ec#^hYRiVOGэPp$J4DBk+2֕NM t>&F)Lл>h]؇Hǧryj"S]XOqމKh.׎Zٞ.ڮH{nʷIrӖSt7EE7LZHTXJGmktIz$fBH E[4Ě˶R0]6tEGےJm3ѓ:NjIʛJZмҦ?k7Q S9V7C*_\ow^JN%,D2SS9Lך'_SC)1IhWJo'!? ʉZPf͜KmY؁~:>Jb:X{XC88CuYZ @;$$1&XFo ˪ʻ[_Eh;{GV1J#P"_B:> {"Ex k7N1 }t Fqa>c/1>mOْO)Ǒc|<6ȿ{K%Dř[n݁wjvXgGJFzFFΎ^| LԺSOT)&HfQ>x4(CŁVܟ~FvY}i;xziY;:>7<;kl}`{O=7sMm <InS$UN MT |(ePGRXVRj%M_v endstream endobj 114 0 obj 13540 endobj 115 0 obj <> endobj 116 0 obj <> stream x]ˎ0E|=-EHyt,IpH@YǷgFEqQU;|{a;E6C_1FCnZezZa/<UdMz}sz>/®߁7[,wV.߽`ׯ`RK=j;|WI~|;hأj]_C31&㻆 -< +x[ORrop&|{7 N ӿDK %,9t?ZO-+)ovøҿ^l:\t1?㥺{qde0M<ͨoț endstream endobj 117 0 obj <> endobj 118 0 obj <> endobj 27 0 obj <> /ExtGState<> /ProcSet[/PDF/Text/ImageC/ImageI/ImageB] >> endobj 1 0 obj <> endobj 28 0 obj <> endobj 56 0 obj <> endobj 91 0 obj <> endobj 119 0 obj <> endobj 120 0 obj < /Dest[1 0 R/XYZ 56.7 723.3 0]/Parent 119 0 R>> endobj 121 0 obj < /Dest[1 0 R/XYZ 56.7 253.9 0]/Parent 120 0 R/Next 122 0 R>> endobj 122 0 obj < /Dest[28 0 R/XYZ 56.7 566.45 0]/Parent 120 0 R/Prev 121 0 R/Next 126 0 R>> endobj 123 0 obj < /Dest[28 0 R/XYZ 56.7 181.1 0]/Parent 122 0 R>> endobj 124 0 obj < /Dest[28 0 R/XYZ 56.7 125.4 0]/Parent 123 0 R/Next 125 0 R>> endobj 125 0 obj < /Dest[56 0 R/XYZ 56.7 735.3 0]/Parent 123 0 R/Prev 124 0 R>> endobj 126 0 obj < /Dest[56 0 R/XYZ 56.7 370.65 0]/Parent 120 0 R/Prev 122 0 R>> endobj 127 0 obj <> stream application/pdf 2025-12-05T01:45:36-08:00 John Johansen LibreOffice 25.8.2.2 (X86_64) / LibreOffice Community 1.7 Writer 2025-12-05T01:45:36-08:00 2025-12-05T01:45:36-08:00 2025-12-05T01:45:36-08:00 endstream endobj 5 0 obj <> /K[0 ] >> endobj 6 0 obj <> /K[1 ] >> endobj 7 0 obj <> /K[2 ] >> endobj 8 0 obj <> /K[3 4 ] >> endobj 9 0 obj <> /K[5 ] >> endobj 10 0 obj <> /K[6 7 ] >> endobj 11 0 obj <> /K[8 9 10 ] >> endobj 12 0 obj <> /K[11 ] >> endobj 13 0 obj <> /K[12 13 14 15 16 17 18 19 20 ] >> endobj 14 0 obj <> /K[21 ] >> endobj 15 0 obj <> /K[22 ] >> endobj 18 0 obj <> /K[24 ] >> endobj 16 0 obj <> /K[18 0 R ] >> endobj 17 0 obj <> /K[23 ] >> endobj 42 0 obj <> /K[16 ] >> endobj 31 0 obj <> /K[42 0 R ] >> endobj 32 0 obj <> /K[0 ] >> endobj 33 0 obj <> /K[1 2 3 4 5 ] >> endobj 34 0 obj <> /K[6 ] >> endobj 35 0 obj <> /K[7 8 9 10 ] >> endobj 51 0 obj <> /K[17 ] >> endobj 36 0 obj <> /K[51 0 R ] >> endobj 37 0 obj <> /K[11 ] >> endobj 38 0 obj <> /K[12 ] >> endobj 39 0 obj <> /K[13 ] >> endobj 40 0 obj <> /K[14 ] >> endobj 41 0 obj <> /K[15 ] >> endobj 59 0 obj <> /K[0 ] >> endobj 60 0 obj <> /K[1 ] >> endobj 81 0 obj <> /K[25 ] >> endobj 61 0 obj <> /K[81 0 R ] >> endobj 62 0 obj <> /K[2 3 ] >> endobj 86 0 obj <> /K[26 ] >> endobj 63 0 obj <> /K[86 0 R ] >> endobj 64 0 obj <> /K[4 5 ] >> endobj 65 0 obj <> /K[6 ] >> endobj 66 0 obj <> /K[7 ] >> endobj 67 0 obj <> /K[8 ] >> endobj 68 0 obj <> /K[9 ] >> endobj 69 0 obj <> /K[10 ] >> endobj 70 0 obj <> /K[11 ] >> endobj 71 0 obj <> /K[12 ] >> endobj 72 0 obj <> /K[13 ] >> endobj 73 0 obj <> /K[14 ] >> endobj 74 0 obj <> /K[15 ] >> endobj 75 0 obj <> /K[16 ] >> endobj 76 0 obj <> /K[17 ] >> endobj 77 0 obj <> /K[18 ] >> endobj 78 0 obj <> /K[19 ] >> endobj 79 0 obj <> /K[20 ] >> endobj 80 0 obj <> /K[21 22 23 24 ] >> endobj 94 0 obj <> /K[0 1 2 3 ] >> endobj 95 0 obj <> /K[4 ] >> endobj 96 0 obj <> /K[5 6 ] >> endobj 4 0 obj <> endobj 128 0 obj <> /K[4 0 R ] >> endobj 129 0 obj <> endobj 97 0 obj <> endobj 130 0 obj <> /Metadata 127 0 R>> endobj 131 0 obj </Creator/Producer/CreationDate(D:20251205014605-08'00')>> endobj xref 0 132 0000000000 65535 f 0000129039 00000 n 0000000019 00000 n 0000025261 00000 n 0000142903 00000 n 0000136365 00000 n 0000136486 00000 n 0000136617 00000 n 0000136748 00000 n 0000136881 00000 n 0000137012 00000 n 0000137146 00000 n 0000137283 00000 n 0000137416 00000 n 0000137573 00000 n 0000137697 00000 n 0000137952 00000 n 0000138068 00000 n 0000137808 00000 n 0000025283 00000 n 0000025570 00000 n 0000025613 00000 n 0000025899 00000 n 0000025942 00000 n 0000026233 00000 n 0000026276 00000 n 0000026568 00000 n 0000128583 00000 n 0000129163 00000 n 0000026611 00000 n 0000052436 00000 n 0000138323 00000 n 0000138440 00000 n 0000138562 00000 n 0000138681 00000 n 0000138792 00000 n 0000139055 00000 n 0000139172 00000 n 0000139284 00000 n 0000139408 00000 n 0000139538 00000 n 0000139662 00000 n 0000138179 00000 n 0000052459 00000 n 0000052751 00000 n 0000052794 00000 n 0000053083 00000 n 0000053126 00000 n 0000053413 00000 n 0000053456 00000 n 0000053746 00000 n 0000138910 00000 n 0000053789 00000 n 0000054078 00000 n 0000054121 00000 n 0000054411 00000 n 0000129289 00000 n 0000054454 00000 n 0000088368 00000 n 0000139792 00000 n 0000139897 00000 n 0000140152 00000 n 0000140269 00000 n 0000140524 00000 n 0000140641 00000 n 0000140754 00000 n 0000140865 00000 n 0000140976 00000 n 0000141087 00000 n 0000141198 00000 n 0000141310 00000 n 0000141433 00000 n 0000141545 00000 n 0000141657 00000 n 0000141769 00000 n 0000141881 00000 n 0000141993 00000 n 0000142105 00000 n 0000142217 00000 n 0000142329 00000 n 0000142441 00000 n 0000140008 00000 n 0000088391 00000 n 0000088677 00000 n 0000088720 00000 n 0000089008 00000 n 0000140382 00000 n 0000089051 00000 n 0000089338 00000 n 0000089381 00000 n 0000089671 00000 n 0000129415 00000 n 0000089714 00000 n 0000090551 00000 n 0000142562 00000 n 0000142679 00000 n 0000142790 00000 n 0000144068 00000 n 0000090572 00000 n 0000099586 00000 n 0000099608 00000 n 0000099812 00000 n 0000100253 00000 n 0000100553 00000 n 0000106737 00000 n 0000106760 00000 n 0000106974 00000 n 0000107342 00000 n 0000107580 00000 n 0000112927 00000 n 0000112950 00000 n 0000113156 00000 n 0000113491 00000 n 0000113695 00000 n 0000127324 00000 n 0000127348 00000 n 0000127547 00000 n 0000128107 00000 n 0000128515 00000 n 0000129541 00000 n 0000129600 00000 n 0000129836 00000 n 0000130001 00000 n 0000130229 00000 n 0000130426 00000 n 0000130548 00000 n 0000130718 00000 n 0000130969 00000 n 0000143365 00000 n 0000143480 00000 n 0000144164 00000 n 0000144364 00000 n trailer < ] /DocChecksum /391453C46F41332127BFAEE83575CD86 >> startxref 144755 %%EOF apparmor-5.0.2/documentation/Techdoc-eHFA.odt000066400000000000000000001735121522511161100210360ustar00rootroot00000000000000PKHF^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKHF鷟 Thumbnails/thumbnail.pngPNG  IHDR߃rPLTE            $*24$ !5!)$6!,'3! '"5! 6&)&!-*&(,.(-2,21,4<9-!;2)555850???-@(;M';Q32A5:R>.LD:XD.ZH5bN;gR=CBCCEHEIMIFDKIKCLWBK[HMQKRXSMIXQLQUXFXiE\rI]sSYaS_m\_aK`u[cmTeqSg~[ep^ivZj|bNAcSCjVBn^Ms]EmcZu`Gu`IxbMugXgoxjs~vme{{{]sewh{zmuxmWr[rcxluaxd|ju{gnvw|ãƨҹŪֽʸַųĴŸʵ̺ȺǾҼɷʶӼԾsdIDATx{LSWpӋ2@N"N[dCFP[t 1\-U`Aq7Ln8P 柤f{S8~z```````````````````e?k(%Aydq*&*c{(I0c4e]mNn㢓+]CP  eqGMܛelgQIweCzG&Hn Z;Jo>sKIlXMX.Ϧ Vt;&21Ȋf8P3FjǮ3VD7m:)4Q`XpZ\~6pR9 }g"]̲5/lPEFJ;I *0&cشVq,;2`xi}}#MTi.rsro d(iψKGEi1ٍRc>0e7׍A6}}/GJٍ>~d4c4rz&"/mt,0O4O0XÞc蹰kxt9^73o`?^fF{ZVQq}TXVՇ&m3_-󗨶̔~'jժMD Z2e e^ &TYv+Y]VDg/l6MLM%l=&-2h-]IiF^#GsVյfas½b2O_uMc8%?l RI|dMۓfN7t* iXDJoM-6Za 74ʉ: /R#ZW)9sTf.#*I{SFr3JTEzL\Cl ]0 \ƻzLR4:ϳW&rseܕL.1st|XKn NRc&Ol,beF w>\.颼+ңcBZASo23ܞ/*[fw|;vDN0Rۜb= g vI{7Z,.w}l F [INߟȴe)gW%JRY.:7%nUmUM7n = ӭ ^ܾQQb7#``````````````````````````C9/QIENDB`PKHF layout-cacheMR/Ca=}{}OwVF~ XDH4H::0u1L$,1 1YR4yz-7ι|u/0e8$Q8n`F00N!P;&v1K '#DΚ8'G3[PPHdi۶7y&ti;E-iaV_ }`qN(H6Fl*θ8 xK44x}G/yq1mQMxpOMqTW.jLl Jy)ݐg-ig`sfrQDz xeRP7 vU/ '>p1}gzX4.1o1-42YP˖VNܫ:aFyQHK"9m-bW/=xPK-cT PKHF settings.xmlZ[s:~?"{ 4LҐ雰Hr+h '_v|^cqJs7w C.ӨskLx4iN5Cz]Fv*@nHn kO7ʫrvS4|n^{**]]]գ U=V"8eJ弜/,4^/d?@lmslvS"Jo|SaRZ1QV^Tr݃)rgNՋJ0ៀGˮ.gŤ8@H) cDLnJl)k!I0^!6(sP~~&KFqkl_n]V ЊMjUQ?[X? D3v_+gW1͠2R-05mi,7sX[%]:,0vRp]=Bdn/qkT(/مT1Coj#!ׇ/Qh?,61Qx(JN#łY0,%&oP e)"|qp'fD$$[HdYUx .܏BKipFrćg _fu5՝bs_W$Dg>,>8)SLw^q3!,я*k''#h@ B؈;qikR |% ;(5=`j 45 CQ G|U@*/ Eo#?O8J Ir anʰ%TLpt̗ @<&PeKT>Ygn~˔s_dL|ս' ~y&Fxz6^RϑU"pdKX0dp`spp$EhPxJMc7Iq#a m&O>;5YP?d8B|0Ub9I0J@n~pM]s,kg5-~995efrX0g>hn`hQe[Aڔ?;F41֘hن8;Tgpؓ%ψm'uK ];䮽*X-Ͻ6I_`yK86Vx^jWO F=_j&"Wi$LVs,(*ihGҖRK STJ|-pɍZ)޺v.(w&1a$d&v@0m5zU~!ԜT&5m:m~Zw,=LX*wavVڟL4`78^+34[]ٲ:茕zYfՂ юʡ*=vHBc2J緫& mIԧaO%"iD\8_½ 3_(pSE0$7弯7oPKqI5H)PKHFmeta.xml]o0+GE4M4uZe"ǸȘ$cS]y/´R{Bq]HUnO w~~\BʂZXQQTVTZr4)J[hmC!>76%i]Ђ3HJ - ;4R> Tz#pZ/tink,/R}o/;ȳTJVn_QyT+To2r.Va $١{y Nxf!bG .mLDz'ßïQ}nx%䊟.f ~# H@NICo najtUok]JeJ;eݝFʚg͢> aj"Y449.Nl$e~t# xJd6(h]5 }g{/PKG9PKHF styles.xml\ݏ6Bp{mٻk{/\Mwh, ߐ%J&e#N>]p8͇#ʯ~ڮc='!H|?obAB|0_8`r+C/g=E=i=侗K)tlx˻Nh}elΎt,xSsvbASICmL`lq fI-K4gX,`yט ^S$_1 hϪGs=f[xgR{!E$ [?SA*tE$Y;JiI ZӮEbпsJ2nOW)x%NА ɲxH?bn\_i\E%yduJIh%5_U/G)c1J9ZVL!8S p!9>Q+&f ۴Y렩_VR( o>/+?16S|cݒKmnI]%z%W-ڥ+ǏQRb&;͓J#K7k8FUtC$4~lֹ{ IdB;h)ͳU Q}+#itՂS&bB<,ybf›]eL}qiDgS%+whasAӟ "Ģ3Ih(@=*(2)7]DhgSP:zr @'ݝaN9a8K͇jiY$&@CH+9['K IX`;̤gN^ipV&BV5׹ý*_}Tz Bm^_?|$u'ԹG(NiK[QWo䎝x:2j3NkqVb:EVНbuSr'A8/w6BZ&_݊-C^{~#CV߹,J!r{.8VjI4?5hK+ &@sGؒS ;f r~w HR&?:2ʻ6l^BJ9p=΁Ot'Ql ޴zd<שGHq,u|Td1Y C;_4` eת}/W̭o +h$Q{}D9TlCy^ _ο:ox~cW +9cF=\y|;1^pqƓ9)$mJ0ܥ%`!Qi-~xd7Ӳ)^] O1 >s\ &M=5=# уf MJ /1,[NЦTq϶ S{;y%M<0ca{tYP'9uXY%$\Qy)|4{dp2 ǹ}fF5[ДꎜP A4E#)Rl^+jb@R$}Շ 0tDǠukVݨUAu(RxoKPԵ*9'ӗ;{o[Ɗv|_6:F KXt/ɂTWQLm$}%g ˼i\m3BPU}\|W ꉙy9GWBq$r)H jJ2V#iBJ0mA#I䯨^J>EIB_rPpv#y9- rtUM  )#⧗ 3S"LL'#`Lۧ%linʴ|v_m;~j`Ȱ~[Q@3M? Ƴ -ħVlwQ$^p+P΢uӨo}Kq~Wە 1/r},k2ՊA!~qWe#4ߘ|?PKs+ RPKHF manifest.rdf͓n0D|e`9\_X/.%}]'*͡]fh8C;4`kg*|>y&ڸ^jj~ *!eI^eYExE%yL,Ƽ FD>} fy%N:90;:PD LچL(-&)}܂Gm-c1su_5R`""?^v}㧓 Fz{ ?VG5'PK=PKHFConfigurations2/menubar/PKHFConfigurations2/toolbar/PKHFConfigurations2/floater/PKHF'Configurations2/accelerator/current.xmlPKPKHFConfigurations2/images/Bitmaps/PKHFConfigurations2/statusbar/PKHFConfigurations2/toolpanel/PKHFConfigurations2/progressbar/PKHFConfigurations2/popupmenu/PKHFMETA-INF/manifest.xmlTKn fU8YT LA!o_j>U*V|{ѻ1@xϢ@2Vc^z@dC[IH6iIѡCz=1+K^:,_\+n\k %VY<r,uJ# 5Gvdʎ՘Kfwng b?qên0QcznB>{dxZ֯uso8AcL2d\#+YPKw,PKHF content.xmlkȕm=YdM [ԣ[RkTգ;$NdFTe֟n/Ћ\ pF{lJ$8q?7kSQ}wcL-.e{w?|3n~YZE2^6v?Y=_z߾9$۷qFm ӷxn_~˞?I.?O1Vm0?b$x8ZbTNדULRh~{7ۢ!Y7:KSM~&^+i{d5]iArr`Zxo~Gdwe]H6>^+jD__->㢨⚺n W?t^D0._t^֋h: b~մi7tz_܇8:~$ڦ`[Z&Nh}SMd_fuz$27N/KKIq7铆7w#E W* ~.f hƤA>+U|ؒ }Wf.L"Uf?{+A[f}p)G:+5&'T P[rl>v;8zJXo? nBGdOy3a*XeXcy~wOEq_֏7pӐ$߾~w}~m*Gn_vоikلki-鵓pKCGm.Ulv[.#=/G,dˈû:H./߇$ +%I h ~5dRVE\EaB\h}Wsp*_/)[k;U ȣRcտjYճ~=p3eS`?"w"߆ekv2i'ug-&>%Ւpf:k#&NCNwNDb+MEsYo?n'[F!xd]\G 9XL2_6JiBggeקQ1$z"_[UyjQFKYZRXN]wT<|:eLjϾqv{-R;xUݓ˶6?ǻ㿝=7^kb~0vVÁȫltS\C!VSӟ]0M^gE#t+YRw ڿCWLeh6Z]9Rw?~t}H]cR%}7J_lg,a_Ag_}53t6t3ôņia:uaں!u=Cj(ֵ Q,}/em׳z6`3rKWݫ Մiaabô0Ybvaz6B6uyg;} ѯ/Վ~/6(+~D?ѯbY»]ݻ!0]L0}a=ߙ_A6TZV*u"ɏ@p(TH E<GHD)C$Nqrߒ\OHg(![fE)Mvd^Gk~޻pDislRe0>u9,cYvv̌tϗ{`:GW*Pi0(_^Szt$a_~7SI|U)wyVC]pyV,z0m sA8,35W]ܑG5a‹fO=L!MWyOYT Yn`ñS}]$yiT;FP=(w %kzw1ws->3\qts^6xuŭn_6xu{PnsGɖ>~H1һki+`ӾG&mӷ#_5=JV=9⑏}Vw=lReE t$xxk2쥋7't{M;']mͷSE*}>Idq]OZGCJa !{}r(Þ#_pw*DFXcԍGp;ȋ4<@ "H릪uHFcl&Eysު$./:kKGVٕ2ˁͥr (8cpğtAV/:'2trMvO}t;-qcߊ廙w BGH+@)E⇺6骘?D&蓎.zh%<3} *?&8*ráU8N?aN@ pO>j?S:ߠUe=hGWxm+aCNO |_ybązHА3C'0<_`nP6'6D潞/86<1[/5O4U .yB1lPQWs} MZF7s7ZJL n+)VTTn*֭dk= r$԰64Κ\r歇rޑoRw7EÆԂ ~.'ZTJ_'87Tot$+qAsY^հm97k%xjʼXz ^{p̔|(&xCFXjӜ KY2U`WT{REh_! K/-/Xu^X#sq#nC-t,WEKEף7Hw",ye8];ͬ5}_W{5߶z5gW3{J;ث93[W j׼^͜=S^1|^\eg)@ H( )PR)@ $@qHR)@Y@ HR)JB g)@ H( )PR)@ $@qHR)@Y@ HR)JB g)@ H( )PR)@ "R@)P (J(I)МR@)P (&@tJR@)PYP (JR@)jR dgA)P (JI)НR@)P (&@xJR@)PYP (JR@)jR gA)P (JI)ОR@)P ($ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PbB{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)JRYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)jR gA)P (J(I)6gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P ($8ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PB{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)jR gA)P (JI)ОR@)P (&@{JR@)PYP (JR@)JR ڳP (JRԤhςR@)P (JQR= JR@)P (EMJ,(JR@)5)ڳP (JRԤhςR@)P (JQR= JR@)P (EMJ,(JR@)5)ڳP (JRԤhςR@)P (JQR_=`?0W+ %N{l1W'Jz|z^_1`W0W)W21 }z^_1`W0W'Jzr%'Jz|z^_1`W0W)W2w1 }z^_1`W0W'Jzr%_O0W++ b_I`Sdab_I`O0W++ :J>_O0W++ b_I`ScO? %z>_=`?0W++Un]BFU֏`w- d_$dćw78 ȿ~wn/N۽sj wX&{nI%"5tmL?Y֥܈L<3Xϣ0p+lOi>e,C:pNjxw:ImH|nSP~ɂ$L.w7$~}}gzA}Wz]tIrXV7%A񬩿ᷮ^WTM\ClwrmhuEm?yǛ/WItw/}{,w7$^R/?l'}n,OHQK+6ކjA1z-^;^ypcW} L/!XdYeBC@> +C:] Xݹxiaof'/s!\ZmB_fCf v.>vd;0-L j/2/4_%Ϫ<76E 5v]c;8g7m*u>A~ICY}5+.tJӅ)c520^ԊD_DKZSڶ3ØGCb!t yXgM+=eаz d6i^< @^E-.ǹ5b Uw}q2 ѧ!  u_hu> +/!i!(|gC: m:u764YУ!`WUv#zm,(_wѾy{5(F_wChWw4Q8eAhBѯ!\Z]y؜q4~ϋX~ c=֦_fC9~ u7cأѧ@wZFn.ݩ0Ny_+JfѦ[jYocȯHnwK\TH>ؑ+Aѝ#Fw`1 o`1ot}}xё2>pa|ۀs4N~(VIgjiDld'/ W/V6I<5)J}_$X|:`OV3;-]-ÛV&k.{UsW<OA1n|'K^ O}.&ݧ(-}%=NpQ~>hpovi/ OjHa=N*{"p+W%{}K-.IxwX~%a+E0N# &VSQ3@ drOKזK.^fNVy>kQJ?ܭؤc-̆Gm9!A*T,[#F[mOD)kH;?mlğej܉t:V,Ht-7/iW TfUI6sBfIQ^'IޅD4ɑ7 -ɭ'$L-:SVJ:y{/mEٺԇ!,.ߋD>w:ҸҰWy?Չ=z$т>s,?E~CtN|gZ وWdV4H!nԳvIہ#`@ca^?&w?Gk/=T@K7'Bk׷*SUꓪ&[|Hȭ5E2HIàEHԛ*ffF-C"20o5 ktH?NR7ʵXzX󐦑3>nmf(waBJ#5=k;8CHyq)wX'G$hdV]! ry_8㓪%#)~a$7"h<F!xI_:emw[ `#]$gU+e&RנhuE}kޓ߮iA8! X//珅xf٩&tTS Iк˫O.->E,6K_=;듌nCwy}L#78+ {I/}ެFw71fn,׵g3&Hg 1æI/d*Uuҽg7 o6^[X@7WCy]O4m/(R1 ^䶫hMTTj~mhd;#8pz!Cݮo}Jڡ~1giDerC2_ATDatJ^_XileX;J䁌"=ɇ;GH)b$~hlsR>bV]$qgh;w7HY&3OL.ͼF%nާ-Y[3wl` 5{O[FZĦI,;|T^JPz(X2ᳶd8L`;V3'?M]+ҳufPXF^z<\=cN ^~"Vn.an _ώ"I`٧JS3V>(>곙@llLfr9l?ǿ!Y`Sޔo`Ajo fAb-=?SnZ⧟yG kTn7Y=@܅'K) ~T@LճfE#mz§OYkax5>w>a>aODSsl)m%݅ױ:Ѯ8VWj9F?YYue縗~Ypƺ6^dz^ y`mlm3kKeIu^%,;̮1k xybvUQ|gfxykJ;qͶmkm5nu|hXv;l0Qi n ~ZV:fs4B+[F u/Bp@N<zlu 1TU6T4T ufZǛz%C۳=m'3d 6)tb {SkK`qH>`[+gf*3*P_ӗ F*p[BO|* m6;!K[ݖe۲gò^_z-;egڭZoe~Mr< nk+jqIF5ˆ%oef7[[٬?>q ݠ/|bNnx'pF]MFV_}\Yպl~ZO\ESza~w m.wj3\* QLC<)Cb [\t\̠s'{א[h;iE%޵+Eù8wԨϔ8kg @ar bq#MPN354  ԂLօ>) aУ0zc玄m s_,̬- i$Tͩ82fq kFNB=QĒv9C6"ۜF\6UHݲű" aQʸmYh9-;"3fK^4#[lriˍҖ]-.wlƄNydQD56V-bd|45*K dΪV*l4V2oVõ,v|߲pOjskgݻ,Nγwd٦O?W7Tczܩ {iSf,! ڜ_혭ݎ-LuoS k L%JA.^7A0juUXLv4a.ۑ41SYxBhǭT|Jx0B`5[&>TQekr;Ҷܲ:p-3&>XHR%e%R$T%E S 2}mYeUUf7l@b&0S|ȊHJE$U̗0wូ.B'EZR4>U*TI 213ߌJk͸5ʄ`˩.'}ZQ5R< -3:C/*UVugO:R)۞ hC@gMn֢pSDCX4סkI)Γ4{\GYg'΄/D[ثpeBlfzcE1*Q5A>dReY䀗5em&ʔsR*{X#j]CIwA o4DG6q Lx(=6r`,ZY)UefsT>KH Qh{m~o[Umwm};M3b$ICnI@Hp\UW52S'QEhuQ_$%}-VVg洗bMcCRclJ V4N5Ԣ7 ˖7s 7\"vrfmC6p8^},ǝylR _I \_Ղ)j S T":ګxV&3YĨxF"ڕ W[1cLK۳ i ͷ@wez(Oȓ0,bitAJÆxK21U{ӈ##WX|;uXOn3PR"lA+|?w*K,OOxx9xB Ȓ9"U2Stȓ3Y!Y1\>}uHݹG8zηۜ4ηv?sKXâh[Li>d4eh vy5$Q/68-t2(^4QLiNѤ1Q*P"L1q><$WAV/?9 g6H*HXfy('_٭{%SqyNPl$eǼAز r/,oe"oy7զ_u6igz.D|=vCPr´eh`I~yZ ]1yF*}ā_Ӿv;!C8P'nLuk&IB{eߞyv:v旙J{W6~Ϩ 172']^l0,lh74jRҋ+,Ǯ'>x^u]:өl+t.L7*_Ӵ{8ܭi9\-2߲ХZ pSlp[wvVLX.(°LXmb-=^d=68ٳC1591yMt{A8aC{ S;}D{6ۡG9Vw3M5iY8U৒CzwtJv&-6v6N8'XBNOf>H7t1ي)j~yo!i_q:ߚݽ;vw6۽P>j }YXƖ|J-ӺiNIm?xBJ ;,rMȦEj;?y(^UCЏkR0)|l[QK) HBK4XėZi"x˘/v2ȎePd+O)Qǔ.".8C9{7$ 6n`).zMYjlSʻ<$-?iMCJvpf[A8@hٱN*x'4R-٤pk:XY\h"b+#hEI[_1%TX՟%ZR@;֞D\<27tEh/|e)*tvո/`V^j| %x#|N36.'n\YptAɤC~ž[RoH93}8Id` ',ٲY|e>hk|AK^2, !Su}7$x{j-t]]d|]_+csdZE:M|(~2JB←J_J;l܉`l\N[6c;Lw)xwCBXwqCɴwrHv!B8| ꖼ%wxSᲞhuN}DBFC}H$@@@ !9jϵO̜vT7G7rlZ{eqr,qr~%zcbc'kjΑMbTwةcX_1N1NoFH&ѵ8=kHVTa&UϚ oP$ʋ !yiw.Cs]5Nl}QO-Ӧ_h1VŞWVʟٮlNw65zV0q@8}͙v5%ٰKRsI͋R=LgcwW<B_[,R{rzmjVϋs Pag]=\sw^~G7HXmjꭱ~j{j~-+żɼgYq^p=f%.}NNH;qH=K4w{?^Es~'A"*TdT2xPV1r--Rֺ[s^;esqT/Ze.s)JOYm~d0B.ۤ~IObΰdy|5`Q%Uiotr̬v"zǟp[MPHM2е͠tBZy;v9xj3?\ \jp|`Hդ8Ipm  lgf/,h9RM4 z*JN %|I|u }uK(:Tm wuY/d|p!(j_mi)\/r]0RbC:Y]6{늰lWřgDIcq6gS3OP+ & uHZpA_18*xJDXv{[/-mVM|ehMYL*p>=_9e:,s3sĽi\=1 XTvffSg /4>_e[O/":}|i9"FgXY lkБƥðr5mͺ0ڑ}5chݢoVVLBOڬl>dm+TǟPݼeAAsU Gc G#sBPH"Vw¨m8;Wx=0gM$O>65@Hœ?a,rXYǘI:-2JtfUa'%:_I݂tdeWDjMSgD#bI'5/4O§-Dazf8f,;Kq}"{vMêX)>_.vg4 icp=~4[ESDf~=>Cd׻=gA{549W 9_=)ɽOb' JnO?&3 8&Vr,DbrՑFh֗f'p5U㲓#YtaAPue2][Tz [kXz]-Dײ6s>njc2k.ᮡnT>!}:b>Mtk8RD9|׭G ּ=gN|@Ug{΀&ɯjyC_ f+6̲&8G7R~*5Fdm&}/-U 툁$fM?AAzf=~8Plgem[s[)5۸pֿ&p.RQ Z׼Olm[װխ$<ֿ1Zc+$s"Z281X:Ta 螯ci WRSJ mMA6Rz<^vf]0Q x; Q[2-sy",ъb&e|ܒ|<ϧ,=sjin|3˰2YK"k -m[J3YyF7뢌npvG8<;W'7XY낎?>ZTM6V7Y6o%$ MnӜpi4C沠LrPNbJQYbod`wٍ 7TPQ%Cs TV e_E|m?X?c,*F?4*D /euAQqlKQuW{ES_XsmL*QRiZVC%m'ʔl=KU>'j#(kLMM2Tkj*ʧ&]{Lg=w+)t#(tJH ^3CQ8L.ڙ酒¦)+G>pSPJuwYQC]/(O{.XhfVJ-'8BU>3}P ܼdeHŃ_<+ e;}YI+k6nNt 햢֏V#Qv@ef;h,<Ӝ%m[)b`G #v!H<5-U y0<>B2]rJK" HR4eu&SΔ48Uz BW>rgINYЫO;7Ïy~3k|e|apb|b|a0z҄|)h*!#yTM/žQc<;iJ5J9΋my홃d۫J}0Cpc-Z?yg7IOlhud >|moÆ<.ExM>W[ΓBg"G!U>Q~J/,43NC)8y!y4 <+lZ0ȧ9 :xc~dSR|݆M.$')U6\+gh䓼uh:uS_)ܩTL141R+oZ1F`T* s.۷2}t!,^TxA_8*^Ԟj!2QxkRE7%eUtSj!GEѻ*˞&eLa^t4HFOGdC # +?Ӏc=;AԪU *$AAAY5م,9CUϐtB2 )lKfb=-5}#|Osu6"ꌸKiV*W1Je rD55th6P1px1mc!nD9> ,,o[A4cbAA>@es%9Aw8ޚk7QuP(xZOEOgoy:1+{ɯÏ/V\7]^7)a4;Sn/y|kY ]9_i}Cs 蟟?NF!t"#߾eLkhBvé,_)7!bZD`/"\UYL2(e' 72\K&GU֛%-+=9)nػ﫧 AGXvd rAR庎\Ln)WP*;VE}HkS8I7s.$$yD f [Sm*oL5wNT͌K~-x%NSWt?uIcc _}UK:qsM#x)7nXa1lE+4`uWzwM*xI̎A<ˎp,ޕ;ZĎ'-6+kUMj@;*Cu$~sƨ>}ø1[*l7FǤ~=Н0eLmO"wgN1|C*5~=׶Nf;, Ȣ4pJ)nɍJuOewLAKٮ i?0?&)6LLekkcӄղ4jw:5g0xgJPRVWo7b;ȱ&FM+9E(d)7%3WjqHK͘<-ku-#HAZQBpDAcx3HN̼g2v PҡyˣSNyh8jOtWEOWj(?Y%}9#]i'ENڟ0l!f=h"U;/piNIƟ~ޖSاZGRq;$LiV,y8㶫JI̗fMa1{"^c$x/'i܃9{Ϝuq۪nw7.Bmmeт-w'-?'8Y,JxSqi*3.]]Ԡvܢ~lnQ'xT&'su^X6)%SIӾ5/Wi{+?mo]x~<S?KQ㞩WsRk 7AkG.):&ݦe1\ˢ)6,YG1G1c@S_)^~;64=ĤHV[zL<[3-dԐEcyɭ~kemq.OQPJMSsz3sUy>iLҍL!xLs턔Ş9LL.Xє`3H712o ɭR ёB30 әH0[TOCzyVe3Tm;o+vo~~Lzf֐s|Sː*+ddZ@{j!2, ߎ zA%8H=O.xM"UMB&wYiߕZ7J$]oM'nGR"^+X"7m{T̾˄1kc-vlhU֋0 u$J| DA_Ib_тLj/X: "^6֫;Iv}J.$/c7nrp(!x"m-v 6 j0'+LD!\w*cD!so.+FKjo6-)jȼuV YG%yE>z`fh#(j3;i S]j\eߴ.V߿վoפl$q>5f=vH%R-lv07 S_ S:ѹY&޺rɤGEjyӫ=lyl~/54|- e)]t³YXvynוM%b9"G.K*}ʔ9l r+.ȋU#qb}]|N^L|]#1yNZ["Oϖ.Ru{pTSfߓ7 W_gy+RśBD~_O+ʦ_>^L m%ᧈ~Ѣ۬DP6H~*|źv5kڗ7=s44mcio[jEnKtWTڝ9a|OYyU ;.o7HK*+%Os4YxݹrAa_)ʆcu]YK{ ucxЀ 'd'dGz~=zݱ|[;'kb]|{bxdz(Zuw]o/>En wq|k 1wdY$Õd%@3/#ضU'e&1H,*mjFqp[6I~Iϸ|r[ԂO|]ۤ qq)^wbv[%ܶ}tܶ'n8x%#ґV ;383jYAa㆝YG k̰&iUC)ںdG1SG1` fL^=XRY[N*kT֞3C=yeTNVONٲ2ZûOdgBzB\ ьۖI g^W:BBS3ɉL^Hhw]=)}9Ap̈́%xw{NZߝ>Lg!˖;,n6Ǥ=ӔgܼbÃ$AG6vKI`ڪ[%ԣ̺5c]5ow{[$bl̬U՚^K;CI>wH d%M2 "#_(!zLf[ˉhٶ#]k(!+-EQOou8~ˑ1gq"N9^(3a.7^Gjiؕ(kuu?d/5~=9FA򦎀"׷mjWK|NUu/t贅kexŴ,d4!eۏ˲߉ ~fJ(m*x3y5SW崕MNioPS:lsD>ՈZ%c;u+ce*iT  cQrVNo_ }k[sox?'.!Yܳ`yV*v8HxS[!zBM=NEcM`Մ_L:mZ_|A#mbн!^~͖_G -/nF !+z1h/:]C?Y/Մ?pIX w%6„]#@y%f` 98j%GGb0ޱ=(Le~vRΗ ӿ^͌{8 QC Kqtb{41`()9~+`P77؁B4Ѕ,"іe5g3T ? b0W, uH7u(Q1,Ym@TC3l(/1ypV٣r FHB"tvMW9 BI @\a6KEUiwz;7 ޝW}^y+wN f聙?0͏#fNhsw4 t.9O,~Lzs"B9n85G`s<:~Mg4qr6|a]-jMx7l&VdT^]VkxǢ}W/&| 7ʗ1b! l 1/6nVLXE,5A=s$̓g^ K;-4l#g4~ 1HL>u{ɽrϽ4?/LN\QX3IdK/BiҜ~P;{TNko"+Fn\X/F^15oa$vv5+^/;Lvv>-6Enp|րENT7#Nͪ+ڐ4Yr0Sin33{+ ָĴ ^ 0bp7Sn,&<"];!V_c :!A4&H*Zg+Ƨ`ܦ6W&Yidm9ZKF |}CC”``K WÄy_)r=hٝ^JZe"I%y/;-(5ZvL]{s{G+imaEm+^|yu\Aҫ:p1TQP7u#Wd}\ x[Ѣ~xRb @ëAeɇW rUiVxnv ]B_;]嬱2\r0E!ԩYog7`IGu=#|buOE6 xJ[urx,a#E ^DuyL졢˟$${WfSt;*A˔cU"W${Vc^jOWHvOV$$ b dˬ]}C%{B;KzU$d6,SR,*&jUCmN%fCg:! 8-䁁P@D3 T.s9Wh(su`C+*]V鲻AeL^Џ )獫P,%2K2 O&ө@ߨG@3Uxa`E5U}c\pIν:y'Z֢19邃;uRn,; z㋌ FTqo +<)Hjz /ez5^-/P>hiҀ%d %d+H{` Z<6z]iN>xr.|!? Ng9;*ǢZ! bv1#z͛i+4#Q*'qX >p:PGNZ^ ^\q hT*6oM9=@g!h푦+z"Ag%h푶;a%g[kw)v>O ' \Aj' ̭C 0[JEJW/kEB!"D{6_Cxb窤5VvĞZZj%u8'Rķ[k%m2Vp ` -Vt]rtv\w]^Pw7Jm ۙ[ƥCc[Uo _O '*@TOTC6Q-`yBH@ /Z ea.jݾ]W ?r9r#\:؅_q4v/2;vfh/HA` lsagVشo(U` (Eh7llOqX=aǪPдȐЈP[c6) _VO3x3h(!լSv?]MWO,Z{n(_T&D6^;I[>x8؉C'T9@B|{j}mh#CÞVE9͵VvG+*R`!η"Dúg LyS$|cۡ0&B*؁:ܾS_Zȷݒily.٨I"@ *9ձIj)7N~:]β1TBPX`rxlE>fdrj|mύeB""zeXemepoW:׋&&!\'d|'TtB֨|iqOny9ĩM7 arhjuAƑsvR#[}#aʨ r+dnT.a[~^p?~ ,Ξ#M0zGgh푶;K\8\=1p8?%q2qѓ=εi?eOhN3|<(nu /_ W UraSu^vDz.3&Yn=`qΖ 9?*X4+4Vr TU7/W`(O͖֞?vW Z:ANСU 4Hntd0GZOg$5ukmυB=Ը{r_gM^zJbZ5#1~kuӤF(>i?Y?9{_@KwNF__ym~tsS1=SlaKQ@ADT+@>^3{v nALZgԭ%E~%RHL QaN;ZNnA" PSm^>MwL>KP‡la܃J9&[D SXes5&naI,vxq@F{ .Pe< QyT4q0&tL 'gkanyz`d۵8x"f"0Y>`N/`rͦPJƠmX'r)?_Cν9B8nDk~p1 D\oqg' #)[eկLeooo35<ы߳0J1L0 3bgFk]v0WG싅hIHW-̻]0^vG*MB昸 ;3P`htșoMT`M\;"=.OEnqԶTGf9|w +5^9f^[ ڋԳABSs/A`Ɣ`;0y o״> -|$(eS3yzY05%]tTE1SحW+gv yF 9OYei%uѵ׸)a0#KgODKr?!pI(=];Uh+/&^Jܳz-{>h,y6ġOf:aU,Ne57oN|Rܜ/>|UlV"ʫ oKCL00+9zBfSŦt9=vi5$ gY<@EOn|pS$.͜\dŎ*M[yICV|xA/%w/4Z*bі(JyԭDE)\>Nn;̄𴄠wT~ ?+0s诅-*2x ջS` Kr|zAE(~D \_(LtørOi:S v91eF%YM-|HUk)]?[QB1#(`Kx =\oBLTV{ xtrlqΥRJ$p\ !/<۪ M R@?1=1r|c#WLˆ!FY:+H|zݧW&8#1M2#1 ADkGbJ<1oǝ21ok}Gb,jGbCJ?QxʑWEXb2SP_~a$FEKLA1ʞ=ȿPbrl%~+%#r,67>#,|=uT01.uj r$F>DFb*YȺvbYϓY`GbdybGb1K;aZݑgp$Ff^H\;ǽxdljnt5)?-Ix$qfrpw쓫r.9_ 18M\^9\OOyG<~hyɞ3 Qq~o޼0]Un\MfiRa?7b >|ن Ϯ8 `MSej0p@JK/ƫ` kA8=c% G!؄+鮺q9셸QQOGYgj&6^}ãOzxH̝%rxb~H:ng@:l3tĂBvgp@Ա;#N _$q: {'q8O85g[ _`4 ZU@0}YA$22[3*7֏K2Eu?-D )Z[]H"->xM)>p,>mDI4l߳r@$@.PN Ž@ @il5I&q"}6ri0963")} D|.Փ힤͓o>αH}8dL:UC!jQCMl;8CCY(?:)5yLHiZTpgӲvpiYK_`مG]pX }y-/ZxP rz101]b X z*(Z嗆"?>t;aٓ#xmI[$stq9y~x*}ӥtv<ӧtz/@xݩՒ8S"QάG䄬92(CFaf9m5&eg"۹t"{̭Sjߎ. ]6vetK_l_,4 H2 װ' ϭq/˺+\"P-x>gW!I 3J `x3VPñ\|#BGWOutW6PX= 'Ņƾtdh_/vP]/|YKA tǙb`X,vr t"eQ`k첷;,pP {!$<v~& ' #P|jAnc8>mwaCp4qxQk1nzS1ar+geKgP #[idAB |vaoSvjE6=bzKR3e>{-ij:|İзTÒvBfRNЬ,fVܬt)M ^@,> "٬܎fd\y6EN*rQurĦ O'.#Y\F1"{&kFAY*=pg{n$ MnK񅄓o?w>za7H š%#`uOF7aآHlnq@Uz"K+K J:-5v?~[?(K Ҍj&xi]DWSzFvJը.雼$Ea6:3'BOq$h'5LY~NAcFcy4Ԩ.T|;hT|¦Kה3l|yHI!$u%`nb=ݽ(PENbAI#';!nUJ=+T5ĸ̸"ˑ$.KTa`ݏtjޜ8{+6^M݆q"mz1_gU*4gNtdl]oxcOA$y &x 1y}<3NWdr]g <&;|xǮ{OLt%B #k7~j@A IHbx8NF'dyl#/A e+8pQNT@Me_egse &. ]v8},h !aֵXlӲ]@q2|} .rHw숓vt Օ,hAW4)}01{814^ʅgQہ yFP$?{ u3SيDu }ZD~F(d=~c>~/Y2Jg;~\3ו@y3` ?{LU/YЅ̌|zER[B>Rhb_/i>83^nae*n LK1ˆl١S1Aa 18)Q! Q{z?rNWPfb Llsl+ 1ad3,aE8^g/Lzv'g7}Eܕg1W.IEq:EȒW>4n=b u*k81媬30lq/ $p]f(T e?o;0͹Puu8`?Clm6 V!f6ֻQؚ@F}B5QJ( 7Rؖgm=&L̬ b)-rmgyjoSֵ͂ŅwNyQǽu)Mge D81{Ex9_>GKŶ=K~UQL-[@lXn ojbA̞xAXzk kWzͶ#P SQj_NP!~[>7H}GݫuL@ 4'zPap<"3m#Wl. qJ,z~d2l ,÷C#Bɂ׿pp" m*|Xqs˵&޴Wmո]yFFÅ6mŸ#'mie{~wa%&2꫁$> v]vRa=Kg%;ǎhm6"<23=sy6{k$X@pZ2T ךE M Ae:%TLu/Ɩldg*N9Bԗ9c$b2.sVՋ bM @n8rf+C.|P ٤gl 189qh s64<dm&,sNN$#g }MhX&ɜQƷ)8;yJ(ql5~L) $K$斿WYHf&&$Q)tgsr&l($YcA a&V }.^W] :\{A e 9og0ɑVU`d_/Ux3L"`Z҈f !|b&1$w6]'}o! ʫ&L`1?h6F @g_}P9^W074IVv=>->saX|!.捶)*}̰ yFpUif ! *2ލ˄.~Q71^P f 3hT$g^JA'tC/)t"qvD,`6dh }JCs( VZ@"˷k4Bǰz`QlĄ4%ySLva\؝IH'e"?}.Ƥ6#Iͼq0oO^ẍp%Cgk6Ζs AУ7JыGAM»SCD!+@" &k:c-tmM.po(m=O/SlF(RⷄF50ߙ%$7.*pb/!ڠ/Iؤ9ifR/c\o7w } <|禽;J0YVIiI̻ZDsJ'_<T^8ӲL's0j$`^nɇVaIӜRQ&H_XHx3&n? }k`Пe8")iTƁ^j{9[ +74,؄@=*'V@G昽/Jx@_AqbZɌt&2PZX2 BDOQN rAnut9Qg^(߾8m#.R՘y9!sU 1q/J'/ALl Y|?ďDyw_[I-FgAz+e9ea9T$ . ] 5Xl- sGg#cBOn._AǗ9+bK= !Yi9Ղ.V(_ A5bcz#g|& /kWq!4Zl)YJ&m)^a^s#ȮunYՋſl*Z+qoJ:V {X8đcM.ܫ'U$|5> 9KI|*QP 3XGˊW{UWʠr=t6 rAV DP&掺|)+ M)WSq}eed( S RZgtC`eOZno/5B * b~2t:m5DP={]rbڷTV^`.OOujlwn踴{<ٿ5BX+>.0Md2#U&窙~TR+cW+]2/gǮ]9]{?[mnm_u۝Yv_+*Ɗ⑺+cT4ۯ_bόVHܿe<^^B5䦭ite%z7+ʒ[ %tRʦ_'ആiOnXdw@NJNߝQF'>I2ves8veؕ pؕ+{o/ dDd,?wrv={_-6qĦl$*OI.X tQ gɇUL:Mi:v@-7><>!8Ѥ7 dŒ)ߤ Vs[ Es#|FLd {@l"#Hʆmq&dn|:qBs%EB Pa (_C+c 0$8gq3л0jeL dpE 6@dc.DSrmoz&z\gctV5{D#:x\C.@/VjV'an /r$FUύ7f>Mb*sňYjcoշ>7j&zja]e .M_٧+лy؉ ~?v Y[+ rb؎a?CE=yKЊXMwdVA|œc(ͤi7o8]L=c ~s_g7y2#*)}[:|־(/4C3v=?x >$D[ %9S#MfdaQfBg8}%6f<d u`H{9cT,t5C 45pN zBHrJ~e 9^+~,)mxϱ5sMQk )]ԲCحYJ%C_|a49sbgMɿgK fOC]2Gyy+#[Y׿J@:K(_%3d$H~eLwmَX#8c(W`H1L_M磑9l:rě-sjڔ22<oF !^|%dqed׌D2)]eKbBTbkKvyvCرW7 z_/R>zf^]`R5دS-w"^C{71:[XP18XmCi]׍ ++2-] f:FUWGeq (>gj{L7t;K)Agv(hxmwid,Bߌ,E;eX.}}_6jn_UW̩ez4s8-:@/׏jd! O|{kx5[p=`?-DLlClAp6`O0)8g,8)8Rl<5[C_=$=Q[Q (k+&,45j4Π#Ϡ75_ [JoVx o{z-2+{zV x!{z9s{z47G c-er]6h/FCȺ8f#UJ`݁U ◟ރgIW@ɟC\A%W9sȭ$lBHHw=֗^ nT3q K5CbnɁSOT2Rήe[^;N=;UP|3K,R#"^‚<>6˶1x~Hbt]~=S.ZU^AWv)D6 0sVjz@[< ˫ M1헓I7\􈄁׊߾+N ӌ<ﴺ:^u(6Wt&˫Mp/]om1C-!B1Vɺx[0f]lVE˱40\)MY^{2)QQM˥Oxw.Ǔ%Cڭ}Z;vGq۔_͌x-ѕeK XoG ؆_g,FL_bIU:>Ijo^ZӮFY7V`[ȹ+w~;m3,1il,pPz X&8Q!;81^di]{z_0aUtWšB#a(>t1})}hb: E0w?x !w*`~/'pɳlʶ5> bd]b=⊵hO!C /.,iee$Ӟ5^&bj _<ʰ9%<J2{8&N%^!Vd=,]lczf{5{g?ka 0UdCV_{|I(MQ`oSʓ13볔KT7p+մ?8%=Pz30sE(VKt6AM'! Cy5^:2K#6ľ> ;ưz=*-L9bЦV<6?o1r.{x/A. ի^74q-}6*6hFi$RL&Y9OWEmn00pY<~()8Fk"]m(~ jc]Y*V.Ͷ^ea$t33׃#Z"9d:mkjw [KHAE~jbXxl+DNY^=UKf1ZUd-("!yYRIWYd:Y~A5'l%DX0`%-IF9\R[tGklawѓxh?-tbΘXO Rm& o}BF>&! F~/`遒3߲?dzo^,dArۗY/N {\oG4]BLJ+ ^w!'A7qhsM&L6.O|Ņr]EݾOؙ |2֐ǟb?I 8īPRqM№% ~2_%춴hDCg2POu+ d09xOxj)^F4( A^lMWկr" "Fw$ U#沇&9R.l@Q6\i>@ͪF"ibmDrA1?Jg4v=^ LoWrU);7fq >8EY.Ԇ{wҴ^v659qš>gmlyR^%h:Po6aוNFn-\8*  k_XsFS e"]n6\fl=2C^/}o';ʛ`/ދP(M.Q?1Łί7)2 T^VBGsZ.sep{Ap /np f4O-x砺b!+x{X6uC^o`BKqW^-9]ScvZmqZvdH.׭9UuTJμA#-Ǹe ўʨAvmfG=ؚbDy@Lq_>Emϯfb`e$0ncW~1zfu;q_\9>ٵG7'1Y=DnL"u\}3r; JT08j5-VVp"Ï+ ICT IW&d '5siR$ݿ)nK[z]įeYvq+> #n~*j$Q\3ga`=UdrhR_` fziz7d?l0 M. jԶ&7}ryɋs;\:.sC\Ro u̵j<_t:=!(/lI78/) u䖓х#52,ě­ {O*r' Ҫbaeja"-2A#$!GrߔLn:^t-FxY, Qb0): `>ŇJ:W;R."+n!2![^`XQJ/tAdZ2Oo{loɆ:]1(yߣs~2]'d|_H0mAX^sc-Fx`6!] bAb6lIũ L8e7*dNM v 7.296506 -36.692966 0.500000 v 8.514620 -35.476959 0.500000 v 8.514620 -35.476959 1.000000 v 7.296506 -36.692966 1.000000 v 6.324892 -35.727196 0.500000 v 5.392566 -34.723518 0.500000 v 3.909492 -32.914867 0.500000 v 2.769804 -31.239882 0.500000 v 1.947016 -29.716267 0.500000 v 1.029239 -26.120234 0.500000 v 1.405360 -28.343626 0.500000 v 1.132001 -27.245932 0.500000 v 1.029240 -20.506203 0.500000 v 15.999998 -42.962337 1.000000 v 15.999998 -42.962337 0.000000 v 15.260092 -42.572731 1.000000 v 13.764388 -41.703697 0.000000 v 14.514069 -42.152603 1.000000 v 13.734939 -41.685524 1.000000 v 12.942796 -41.181595 1.000000 v 11.518011 -40.201790 0.000000 v 12.142940 -40.643219 1.000000 v 11.341071 -40.073448 1.000000 v 10.543138 -39.475895 1.000000 v 9.154169 -38.358936 0.000000 v 9.833358 -38.917732 1.000000 v 9.136038 -38.343678 1.000000 v 8.455268 -37.757084 1.000000 v 7.869088 -37.229717 1.000000 v 7.159270 -36.560928 0.000000 v 5.394211 -34.725368 0.000000 v 3.924014 -32.934235 0.000000 v 2.774636 -31.247803 0.000000 v 1.940135 -29.701513 0.000000 v 1.394559 -28.309425 0.000000 v 1.122868 -27.192978 0.000000 v 1.029239 -26.120234 0.000000 v 17.865017 -41.910923 1.000000 v 19.727621 -40.669075 1.000000 v 20.989883 -30.487076 1.000000 v 24.227337 -36.962704 1.000000 v 21.906021 -38.995846 1.000000 v 11.381945 -40.095016 1.000000 v 10.815524 -39.675224 1.000000 v 8.001699 -37.343979 1.000000 v 7.301169 -36.690411 1.000000 v 8.718463 -37.979927 1.000000 v 9.475924 -38.619247 1.000000 v 10.256423 -39.245579 1.000000 v 1.029239 -5.535439 1.000000 v 1.029240 -20.506203 1.000000 v 1.029239 -5.535439 0.000000 v 4.761098 -35.246090 0.000000 v 6.717828 -37.214886 0.000000 v 8.988346 -39.187218 0.000000 v 3.161567 -33.357559 0.000000 v 11.477942 -41.058769 0.000000 v 1.922523 -31.596481 0.000000 v 13.747661 -42.535645 0.000000 v 1.021943 -29.985332 0.000000 v 16.000000 -43.789474 0.000000 v 0.425399 -28.528395 0.000000 v 0.109168 -27.295216 0.000000 v 0.000000 -26.106907 0.000000 v 18.391722 -42.451046 0.000000 v 17.865017 -41.910923 0.000000 v 19.727621 -40.669075 0.000000 v 20.798811 -40.864109 0.000000 v 21.906021 -38.995846 0.000000 v 23.054428 -39.152580 0.000000 v 24.227337 -36.962704 0.000000 v 25.173744 -37.315845 0.000000 v 26.107744 -35.093243 0.000000 v 27.056599 -35.442902 0.000000 v 0.000000 -6.398148 0.000000 v 27.774689 -33.171108 0.000000 v 28.638821 -33.612099 0.000000 v 0.509550 -5.954788 0.000000 v 0.918158 -5.416970 0.000000 v 1.208706 -4.807224 0.000000 v 29.038111 -31.425402 0.000000 v 29.894957 -31.880430 0.000000 v 4.650217 -4.760341 0.000000 v 29.948507 -29.848698 0.000000 v 4.583811 -4.072606 0.000000 v 30.830366 -30.281687 0.000000 v 30.547186 -28.437580 0.000000 v 31.469709 -28.829988 0.000000 v 7.743419 -3.991212 0.000000 v 30.861818 -27.256960 0.000000 v 31.846458 -27.525003 0.000000 v 7.958917 -3.194899 0.000000 v 30.970758 -26.120234 0.000000 v 31.961023 -26.807238 0.000000 v 32.000000 -26.106907 0.000000 v 10.489840 -3.175107 0.000000 v 10.911841 -2.239217 0.000000 v 12.965134 -2.269320 0.000000 v 13.455921 -1.227151 0.000000 v 14.480927 -1.610794 0.000000 v 16.000000 0.000000 0.000000 v 15.999998 -0.857075 0.000000 v 18.482809 -1.200278 0.000000 v 18.435408 -2.026988 0.000000 v 20.965618 -2.194827 0.000000 v 20.863176 -2.979459 0.000000 v 24.000000 -3.182964 0.000000 v 23.694893 -3.873033 0.000000 v 27.200001 -4.021627 0.000000 v 27.326279 -4.775409 0.000000 v 30.970758 -5.535439 0.000000 v 32.000000 -5.052633 0.000000 v 30.970758 -26.120234 1.000000 v 30.861818 -27.256960 1.000000 v 30.547186 -28.437580 1.000000 v 29.948507 -29.848698 1.000000 v 29.038111 -31.425402 1.000000 v 27.774689 -33.171108 1.000000 v 26.107744 -35.093243 1.000000 v 30.970760 -20.506199 1.000000 v 30.970760 -20.506199 0.500000 v 11.381945 -40.095016 0.500000 v 9.513083 -38.649792 0.500000 v 8.385881 -37.688759 0.500000 v 7.301169 -36.690411 0.500000 v 4.650217 -4.760341 1.000000 v 7.743419 -3.991212 1.000000 v 12.257585 -13.020544 1.000000 v 4.771930 -20.506199 1.000000 v 10.385965 -24.248888 1.000000 v 8.514620 -27.991579 1.000000 v 14.930710 -18.367388 1.000000 v 11.321638 -22.377544 1.000000 v 14.128757 -16.763306 1.000000 v 5.707603 -22.377544 1.000000 v 6.643275 -24.248888 1.000000 v 32.000000 -5.052633 3.000000 v 27.200001 -4.021627 3.000000 v 24.000000 -3.182964 3.000000 v 20.965618 -2.194827 3.000000 v 18.482809 -1.200278 3.000000 v 16.000000 0.000000 3.000000 v 32.000000 -25.279251 2.500000 v 32.000000 -25.094362 2.500000 v 32.000000 -26.106907 3.000000 v 32.000000 -25.279251 2.000000 v 32.000000 -25.094362 2.000000 v 16.000000 -43.789474 3.000000 v 18.391722 -42.451046 3.000000 v 20.798811 -40.864109 3.000000 v 23.054428 -39.152580 3.000000 v 25.173744 -37.315845 3.000000 v 27.056599 -35.442902 3.000000 v 28.638821 -33.612099 3.000000 v 29.894957 -31.880430 3.000000 v 30.830366 -30.281687 3.000000 v 31.469709 -28.829988 3.000000 v 31.846458 -27.525003 3.000000 v 31.961023 -26.807238 3.000000 v 0.000000 -26.106907 3.000000 v 0.109168 -27.295216 3.000000 v 0.425399 -28.528395 3.000000 v 1.021943 -29.985332 3.000000 v 1.922523 -31.596481 3.000000 v 3.161567 -33.357559 3.000000 v 4.761098 -35.246090 3.000000 v 6.717828 -37.214886 3.000000 v 8.988346 -39.187218 3.000000 v 11.477942 -41.058769 3.000000 v 13.747661 -42.535645 3.000000 v -0.000001 -25.094362 1.400000 v 0.000000 -6.398148 3.000000 v -0.000000 -25.282578 1.400000 v -0.000001 -25.094362 0.500000 v -0.000000 -25.282560 0.500000 v 0.000000 -5.052633 0.000000 v 13.455921 -1.227151 3.000000 v 10.911841 -2.239217 3.000000 v 7.958917 -3.194899 3.000000 v 4.583811 -4.072606 3.000000 v 1.208706 -4.807224 3.000000 v 30.970758 -5.535439 1.000000 v 15.999998 -0.857075 1.000000 v 18.435408 -2.026988 1.000000 v 20.863176 -2.979459 1.000000 v 23.694893 -3.873033 1.000000 v 27.326279 -4.775409 1.000000 v 10.489840 -3.175107 1.000000 v 12.965134 -2.269320 1.000000 v 14.480927 -1.610794 1.000000 v 12.257310 -35.476959 0.500000 v 12.725145 -34.541286 0.500000 v 16.000000 -27.991579 0.500000 v 27.228071 -20.506199 0.500000 v 20.678362 -18.634853 0.500000 v 26.292398 -18.634853 0.500000 v 23.485380 -5.535439 1.000000 v 19.742691 -5.535439 1.000000 v 15.999998 -20.506203 1.000000 v 4.771930 -20.506199 0.500000 v 19.742691 -5.535439 0.500000 v 5.707603 -22.377544 0.500000 v 11.321638 -22.377544 0.500000 v 15.064327 -22.377544 1.000000 v 12.257310 -27.991579 1.000000 v 8.514620 -27.991579 0.500000 v 10.385965 -24.248888 0.500000 v 6.643275 -24.248888 0.500000 v 5.892253 -35.093243 3.000000 v 7.772661 -36.962704 3.000000 v 4.225310 -33.171108 3.000000 v 10.093978 -38.995846 3.000000 v 2.961887 -31.425402 3.000000 v 12.272378 -40.669075 3.000000 v 2.051491 -29.848698 3.000000 v 14.134980 -41.910923 3.000000 v 1.452812 -28.437580 3.000000 v 16.000000 -42.962337 3.000000 v 1.138179 -27.256960 3.000000 v 1.029240 -26.120234 3.000000 v 18.235609 -41.703697 3.000000 v 20.481987 -40.201790 3.000000 v 22.845829 -38.358936 3.000000 v 24.840727 -36.560928 3.000000 v 26.605787 -34.725368 3.000000 v 28.075985 -32.934235 3.000000 v 0.918158 -5.416970 3.000000 v 0.509550 -5.954788 3.000000 v 1.029240 -5.535439 3.000000 v 29.225363 -31.247803 3.000000 v 4.673719 -4.775409 3.000000 v 30.059864 -29.701513 3.000000 v 30.605438 -28.309425 3.000000 v 30.877131 -27.192978 3.000000 v 8.305105 -3.873033 3.000000 v 30.970758 -26.120234 3.000000 v 11.136821 -2.979459 3.000000 v 13.564590 -2.026988 3.000000 v 16.000000 -0.857075 3.000000 v 17.519070 -1.610794 3.000000 v 19.034864 -2.269320 3.000000 v 21.510159 -3.175107 3.000000 v 24.256578 -3.991212 3.000000 v 27.349781 -4.760341 3.000000 v 30.970758 -5.535439 3.000000 v 1.303771 -2.908893 3.000000 v 1.400000 -3.700000 3.000000 v 1.400000 -3.700000 0.000000 v 1.303771 -2.908893 0.000000 v 1.020696 -2.163923 0.000000 v 1.020696 -2.163923 3.000000 v 0.567285 -1.508538 3.000000 v 0.567285 -1.508538 0.000000 v -0.030020 -0.980961 3.000000 v -0.030020 -0.980961 0.000000 v -0.736383 -0.611959 3.000000 v -0.736383 -0.611959 0.000000 v -1.510609 -0.423054 3.000000 v -1.510609 -0.423054 0.000000 v -2.307544 -0.425262 3.000000 v -2.307544 -0.425262 0.000000 v -3.080712 -0.618455 0.000000 v -3.080712 -0.618455 3.000000 v -3.785019 -0.991365 3.000000 v -3.785019 -0.991365 0.000000 v -4.379391 -1.522244 3.000000 v -4.379391 -1.522244 0.000000 v -4.829164 -2.180132 3.000000 v -4.829164 -2.180132 0.000000 v -5.108106 -2.926658 3.000000 v -5.108106 -2.926658 0.000000 v -5.199949 -3.718287 3.000000 v -5.199949 -3.718287 0.000000 v -5.099338 -4.508849 3.000000 v -5.099338 -4.508849 0.000000 v -4.812140 -5.252238 0.000000 v -4.812140 -5.252238 3.000000 v -4.355103 -5.905100 3.000000 v -4.355103 -5.905100 0.000000 v -3.754884 -6.429360 3.000000 v -3.754884 -6.429360 0.000000 v -3.046487 -6.794442 0.000000 v -3.046487 -6.794442 3.000000 v -2.271226 -6.979053 3.000000 v -2.271226 -6.979053 0.000000 v -1.474315 -6.972429 0.000000 v -1.474315 -6.972429 3.000000 v -0.702230 -6.774955 0.000000 v -0.702230 -6.774955 3.000000 v -0.000001 -25.279251 0.500000 v -0.000001 -25.279251 1.400000 v -3.247319 -4.893621 0.000000 v -2.922517 -5.181371 0.000000 v -3.493821 -4.536502 0.000000 v -2.538289 -5.383029 0.000000 v -3.647695 -4.130768 0.000000 v -2.116966 -5.486876 0.000000 v -3.700000 -3.700000 0.000000 v -1.683034 -5.486876 0.000000 v -3.647695 -3.269232 0.000000 v -1.261711 -5.383029 0.000000 v -3.493821 -2.863498 0.000000 v -0.877483 -5.181371 0.000000 v -3.247319 -2.506379 0.000000 v -0.552681 -4.893621 0.000000 v -0.306179 -4.536502 0.000000 v -2.922517 -2.218629 0.000000 v -0.152305 -4.130768 0.000000 v -2.538289 -2.016971 0.000000 v 1.314518 -4.446240 0.000000 v -2.116966 -1.913124 0.000000 v 1.378560 -4.075560 0.000000 v -0.100000 -3.700000 0.000000 v -0.152305 -3.269232 0.000000 v -1.683034 -1.913124 0.000000 v -0.306179 -2.863498 0.000000 v -1.261711 -2.016971 0.000000 v -0.552681 -2.506379 0.000000 v -0.877483 -2.218629 0.000000 v 1.314518 -4.446240 3.000000 v 1.378560 -4.075560 3.000000 v 27.228071 -20.506199 1.000000 v 26.292398 -18.634853 1.000000 v 20.678362 -18.634853 1.000000 v 17.871241 -24.249100 1.000000 v 16.000000 -27.991579 1.000000 v 12.725146 -34.541286 1.000000 v 12.725145 -34.541286 1.000000 v 12.257310 -35.476959 1.000000 v 25.356726 -16.763510 1.000000 v 21.614035 -16.763510 1.000000 v 23.485380 -13.020819 1.000000 v 0.000000 -5.052633 3.000000 v 13.564590 -2.026988 2.000000 v 16.000000 -0.857075 2.000000 v 11.136821 -2.979459 2.000000 v 8.305105 -3.873033 2.000000 v 4.673719 -4.775409 2.000000 v 1.029240 -5.535439 2.000000 v 21.510159 -3.175107 2.000000 v 24.256578 -3.991212 2.000000 v 19.034864 -2.269320 2.000000 v 17.519070 -1.610794 2.000000 v 1.029240 -20.506199 2.500000 v 1.029240 -20.506199 2.000000 v 1.029240 -26.120234 2.000000 v 27.349781 -4.760341 2.000000 v 30.970758 -5.535439 2.000000 v 1.138179 -27.256960 2.000000 v 1.452812 -28.437580 2.000000 v 2.051491 -29.848698 2.000000 v 2.961887 -31.425402 2.000000 v 4.225310 -33.171108 2.000000 v 5.892253 -35.093243 2.000000 v 7.772661 -36.962704 2.000000 v 30.970758 -20.506199 2.500000 v 30.970758 -26.120234 2.500000 v 30.970758 -20.506199 2.000000 v 10.093978 -38.995846 2.000000 v 12.272378 -40.669075 2.000000 v 14.134980 -41.910923 2.000000 v 16.000000 -42.962337 2.000000 v 18.235609 -41.703697 2.000000 v 20.481987 -40.201790 2.000000 v 22.652317 -38.520687 2.000000 v 24.703493 -36.692966 2.500000 v 24.703493 -36.692966 2.000000 v 25.675106 -35.727196 2.500000 v 26.607433 -34.723518 2.500000 v 28.090506 -32.914867 2.500000 v 29.230194 -31.239882 2.500000 v 30.052982 -29.716267 2.500000 v 30.594639 -28.343626 2.500000 v 30.867996 -27.245932 2.500000 v -3.247319 -4.893621 3.000000 v -2.922517 -5.181371 3.000000 v -3.493821 -4.536502 3.000000 v -2.538289 -5.383029 3.000000 v -3.647695 -4.130768 3.000000 v -2.116966 -5.486876 3.000000 v -3.700000 -3.700000 3.000000 v -1.683034 -5.486876 3.000000 v -3.647695 -3.269232 3.000000 v -1.261711 -5.383029 3.000000 v -3.493821 -2.863498 3.000000 v -0.877483 -5.181371 3.000000 v -3.247319 -2.506379 3.000000 v -0.552681 -4.893621 3.000000 v -0.306179 -4.536502 3.000000 v -2.922517 -2.218629 3.000000 v -0.152305 -4.130768 3.000000 v -2.538289 -2.016971 3.000000 v -2.116966 -1.913124 3.000000 v -0.100000 -3.700000 3.000000 v -0.152305 -3.269232 3.000000 v -1.683034 -1.913124 3.000000 v -0.306179 -2.863498 3.000000 v -1.261711 -2.016971 3.000000 v -0.552681 -2.506379 3.000000 v -0.877483 -2.218629 3.000000 v -0.100000 -3.700000 3.000000 v -0.100000 -3.700000 0.000000 v 25.356726 -16.763510 0.500000 v 23.485380 -13.020819 0.500000 v 21.614035 -16.763510 0.500000 v 8.514620 -5.535439 2.000000 v 12.257310 -5.535439 2.000000 v 17.069290 -18.367386 2.000000 v 19.742414 -13.020543 2.000000 v 16.000000 -20.506201 2.000000 v 11.010116 -30.487076 2.000000 v 27.228071 -20.506199 2.000000 v 23.485380 -35.476959 2.500000 v 27.228071 -20.506199 2.500000 v 12.257310 -5.535439 2.500000 v 23.485380 -35.476959 2.000000 v 16.935673 -22.377544 2.000000 v 16.935673 -22.377544 2.500000 v 8.514620 -5.535439 2.500000 v 20.618055 -40.095016 2.500000 v 20.618055 -40.095016 2.000000 v 22.486917 -38.649792 2.000000 v 22.486917 -38.649792 2.500000 v 23.614119 -37.688759 2.500000 v 23.614119 -37.688759 2.000000 v 24.698830 -36.690411 2.000000 v 24.698830 -36.690411 2.500000 vn 0.706495 -0.707718 0.000000 vn 0.706495 -0.707718 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.465917 0.884828 0.000000 vn 0.490563 0.871095 0.023274 vn 0.514173 0.857687 -0.000445 vn 0.536753 0.843739 0.000474 vn 0.490454 0.871150 0.023485 vn 0.558301 0.829459 0.017249 vn 0.579221 0.815168 -0.002133 vn 0.555665 0.831099 0.022612 vn 0.599421 0.800427 0.003333 vn 0.618033 0.785913 0.019404 vn 0.635569 0.772044 -0.000257 vn 0.652763 0.757562 0.000276 vn 0.614614 0.788370 0.026897 vn 0.668694 0.743269 0.020013 vn 0.683911 0.729565 0.000000 vn 0.669344 0.742640 0.021558 vn 0.680855 0.732389 0.006531 vn 0.704957 0.709223 -0.006202 vn 0.732670 0.680584 -0.000107 vn 0.720016 0.692361 0.047044 vn 0.773270 0.634073 -0.002102 vn 0.772957 0.634458 0.000196 vn 0.826766 0.562545 -0.000923 vn 0.826331 0.563181 0.002184 vn 0.879896 0.475165 0.000975 vn 0.880022 0.474930 0.001902 vn 0.930194 0.367063 -0.001971 vn 0.931040 0.364885 0.004847 vn 0.970354 0.241647 -0.004432 vn 0.971617 0.236446 0.007294 vn 0.995823 0.090906 -0.008562 vn 0.996213 0.086949 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 -0.000002 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn -0.658870 0.752257 0.000000 vn -0.658870 0.752257 0.000000 vn -0.609147 0.793057 0.000000 vn -0.609147 0.793057 0.000000 vn -0.554735 0.832027 0.000000 vn -0.554735 0.832027 0.000000 vn -0.491092 0.871108 0.000000 vn -0.491092 0.871108 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.707107 -0.707107 0.000000 vn 0.707107 -0.707107 0.000000 vn 0.707107 -0.707107 0.000001 vn -0.595430 -0.803407 0.000000 vn -0.609142 -0.792681 -0.024538 vn -0.625871 -0.779925 0.001132 vn -0.611404 -0.790625 -0.033125 vn -0.644995 -0.764185 -0.001251 vn -0.648745 -0.760917 -0.011588 vn -0.663667 -0.748005 0.005856 vn -0.682173 -0.731191 0.000000 vn -0.677151 -0.735729 -0.012969 vn -0.707107 0.707106 0.000000 vn -0.707107 0.707106 0.000000 vn 0.209316 -0.977848 0.000000 vn 0.209316 -0.977848 0.000000 vn 0.241304 -0.970450 0.000000 vn 0.241304 -0.970450 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.210003 0.977701 -0.000000 vn 0.210003 0.977701 0.000000 vn 0.253520 0.967330 -0.000000 vn 0.253520 0.967330 0.000000 vn 0.309642 0.950853 0.000000 vn 0.309642 0.950853 -0.000000 vn 0.371850 0.928293 0.000000 vn 0.371850 0.928293 -0.000000 vn 0.435243 0.900313 0.000000 vn 0.435243 0.900313 -0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 0.488343 -0.872651 0.000000 vn 0.488343 -0.872651 0.000000 vn 0.550421 -0.834887 0.000000 vn 0.550421 -0.834887 0.000000 vn 0.604470 -0.796628 0.000000 vn 0.604470 -0.796628 0.000000 vn 0.654929 -0.755690 0.000000 vn 0.654929 -0.755690 0.000000 vn 0.705238 -0.708970 0.000000 vn 0.705238 -0.708970 0.000000 vn 0.756603 -0.653874 0.000000 vn 0.756603 -0.653874 0.000000 vn 0.809460 -0.587175 0.000000 vn 0.809460 -0.587175 0.000000 vn 0.863118 -0.505002 0.000000 vn 0.863118 -0.505002 0.000000 vn 0.915176 -0.403053 0.000000 vn 0.915176 -0.403053 0.000000 vn 0.960763 -0.277372 0.000000 vn 0.960763 -0.277372 0.000000 vn 0.987500 -0.157618 0.000000 vn 0.998455 -0.055569 0.000000 vn 0.987500 -0.157618 0.000000 vn 0.998455 -0.055569 0.000000 vn -0.995807 -0.091483 0.000000 vn -0.995807 -0.091483 0.000000 vn -0.968658 -0.248398 0.000000 vn -0.968658 -0.248398 0.000000 vn -0.925430 -0.378918 0.000000 vn -0.872890 -0.487917 -0.000000 vn -0.925430 -0.378918 0.000000 vn -0.872890 -0.487917 0.000000 vn -0.817857 -0.575421 0.000000 vn -0.817857 -0.575421 0.000000 vn -0.763079 -0.646306 0.000000 vn -0.763079 -0.646306 0.000000 vn -0.709277 -0.704930 0.000000 vn -0.709277 -0.704930 0.000000 vn -0.655794 -0.754940 0.000000 vn -0.655794 -0.754940 0.000000 vn -0.600895 -0.799328 0.000000 vn -0.600895 -0.799328 0.000000 vn -0.545393 -0.838181 0.000000 vn -0.486393 -0.873740 -0.000000 vn -0.545393 -0.838181 0.000000 vn -0.486393 -0.873740 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000001 vn -1.000000 -0.000005 -0.000002 vn -1.000000 0.000000 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 -0.000005 0.000008 vn -1.000000 -0.000000 0.000000 vn -1.000000 0.000000 -0.000002 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.434455 0.900694 0.000000 vn -0.434455 0.900694 0.000000 vn -0.369638 0.929176 0.000000 vn -0.369638 0.929176 0.000000 vn -0.307915 0.951414 0.000000 vn -0.307915 0.951414 0.000000 vn -0.251682 0.967810 0.000000 vn -0.251682 0.967810 0.000000 vn -0.212678 0.977122 0.000000 vn -0.212678 0.977122 0.000000 vn -0.995439 0.095400 0.000000 vn -0.995439 0.095400 0.000000 vn -0.966276 0.257510 0.000000 vn -0.966276 0.257510 0.000000 vn -0.920577 0.390562 0.000000 vn -0.920577 0.390562 0.000000 vn -0.866005 0.500035 0.000000 vn -0.866005 0.500035 0.000000 vn -0.810099 0.586294 0.000000 vn -0.810099 0.586294 0.000000 vn -0.755476 0.655176 0.000000 vn -0.755476 0.655176 0.000000 vn -0.705040 0.709168 0.000000 vn -0.705040 0.709168 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000004 vn -0.433007 -0.901391 0.000000 vn -0.433007 -0.901391 0.000000 vn -0.365222 -0.930920 0.000000 vn -0.365222 -0.930920 0.000000 vn -0.300931 -0.953646 0.000000 vn -0.300931 -0.953646 0.000000 vn -0.241159 -0.970486 0.000000 vn -0.241159 -0.970486 0.000000 vn -0.204151 -0.978939 0.000000 vn -0.204151 -0.978939 0.000000 vn 0.284842 -0.958574 0.000000 vn 0.284842 -0.958574 0.000000 vn 0.343646 -0.939099 0.000000 vn 0.343646 -0.939099 0.000000 vn 0.398464 -0.917184 0.000000 vn 0.398464 -0.917184 0.000000 vn 0.444468 -0.895795 0.000000 vn 0.444468 -0.895795 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.707107 0.707107 0.000000 vn -0.707107 0.707107 0.000000 vn -0.707107 0.707107 -0.000001 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 0.992683 0.120748 0.000000 vn 0.992683 0.120748 0.000000 vn 0.934789 0.355203 0.000000 vn 0.934789 0.355203 0.000000 vn 0.822378 0.568941 0.000000 vn 0.822378 0.568941 0.000000 vn 0.662005 0.749499 0.000000 vn 0.662005 0.749499 0.000000 vn 0.463024 0.886346 0.000000 vn 0.463024 0.886346 0.000000 vn 0.237039 0.971500 0.000000 vn 0.237039 0.971500 0.000000 vn -0.002771 0.999996 0.000000 vn -0.242419 0.970172 0.000000 vn -0.002771 0.999996 0.000000 vn -0.242419 0.970172 0.000000 vn -0.467928 0.883766 0.000000 vn -0.467928 0.883766 0.000000 vn -0.666148 0.745819 0.000000 vn -0.666148 0.745819 0.000000 vn -0.825518 0.564375 0.000000 vn -0.825518 0.564375 0.000000 vn -0.936743 0.350017 0.000000 vn -0.936743 0.350017 0.000000 vn -0.993337 0.115246 0.000000 vn -0.993337 0.115246 0.000000 vn -0.991999 -0.126247 0.000000 vn -0.991999 -0.126247 -0.000000 vn -0.932806 -0.360377 -0.000000 vn -0.932806 -0.360377 -0.000000 vn -0.819212 -0.573490 0.000000 vn -0.819212 -0.573490 -0.000000 vn -0.657842 -0.753156 0.000000 vn -0.657842 -0.753156 -0.000000 vn -0.458105 -0.888898 -0.000000 vn -0.458105 -0.888898 -0.000000 vn -0.231651 -0.972799 0.000000 vn -0.231651 -0.972799 -0.000000 vn 0.008312 -0.999965 0.000000 vn 0.008312 -0.999965 0.000000 vn 0.247791 -0.968813 0.000000 vn 0.247791 -0.968813 0.000000 vn 0.472818 -0.881160 0.000000 vn 0.472818 -0.881160 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn 0.000000 0.000000 1.000000 vn -1.000000 -0.000269 0.000000 vn -1.000000 -0.000268 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.959624 -0.281285 0.000000 vn 0.959624 -0.281285 0.000000 vn 0.985402 -0.170246 0.000000 vn 0.985402 -0.170246 0.000000 vn 0.998374 -0.056996 0.000000 vn 0.998374 -0.056996 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 0.447213 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000003 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000002 vn -0.894428 0.447213 0.000000 vn -0.894428 0.447213 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.433007 -0.901391 0.000000 vn 0.433007 -0.901391 0.000000 vn 0.365222 -0.930920 0.000000 vn 0.365222 -0.930920 0.000000 vn 0.300932 -0.953646 0.000000 vn 0.300932 -0.953646 0.000000 vn 0.241159 -0.970486 0.000000 vn 0.241159 -0.970486 0.000000 vn 0.204151 -0.978939 0.000000 vn 0.204151 -0.978939 0.000000 vn -0.284843 -0.958574 0.000000 vn -0.284843 -0.958574 0.000000 vn -0.343646 -0.939099 0.000000 vn -0.343646 -0.939099 0.000000 vn -0.398464 -0.917184 0.000000 vn -0.398464 -0.917184 0.000000 vn -0.444468 -0.895795 0.000000 vn -0.444468 -0.895795 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn -0.209316 -0.977848 0.000000 vn -0.209316 -0.977848 0.000000 vn -0.241304 -0.970450 0.000000 vn -0.241304 -0.970450 0.000000 vn 0.995439 0.095399 0.000000 vn 0.995439 0.095399 -0.000000 vn 0.966275 0.257511 -0.000000 vn 0.966275 0.257511 0.000000 vn 0.920577 0.390562 -0.000000 vn 0.920577 0.390562 0.000000 vn 0.866005 0.500035 0.000000 vn 0.866005 0.500035 -0.000000 vn 0.810098 0.586294 -0.000000 vn 0.810098 0.586294 0.000000 vn 0.755476 0.655176 -0.000000 vn 0.755476 0.655176 0.000000 vn 0.705040 0.709168 -0.000000 vn 0.705040 0.709168 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn 0.658870 0.752257 0.000000 vn 0.658870 0.752257 -0.000000 vn 0.609147 0.793057 -0.000000 vn 0.609147 0.793057 0.000000 vn 0.554735 0.832027 0.000000 vn 0.554735 0.832027 -0.000000 vn 0.491092 0.871108 -0.000000 vn 0.491092 0.871108 0.000000 vn -0.490590 0.871391 0.000000 vn -0.490590 0.871391 0.000000 vn -0.555807 0.831311 0.000000 vn -0.555807 0.831311 0.000000 vn -0.614813 0.788626 -0.008587 vn -0.612366 0.790574 0.000000 vn -0.669448 0.742755 -0.012402 vn -0.665248 0.746580 0.007973 vn -0.667651 0.744474 0.000000 vn -0.704958 0.709222 0.006199 vn -0.720016 0.692361 -0.047043 vn -0.732669 0.680585 0.000106 vn -0.772957 0.634458 -0.000197 vn -0.773271 0.634073 0.002104 vn -0.826331 0.563181 -0.002183 vn -0.826766 0.562545 0.000923 vn -0.880022 0.474930 -0.001901 vn -0.879896 0.475165 -0.000974 vn -0.931040 0.364885 -0.004849 vn -0.930194 0.367063 0.001972 vn -0.971617 0.236447 -0.007291 vn -0.970354 0.241646 0.004430 vn -0.996213 0.086948 0.000000 vn -0.995823 0.090906 0.008565 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.992709 -0.120537 -0.000000 vn -0.992709 -0.120537 0.000000 vn -0.935016 -0.354605 0.000000 vn -0.935016 -0.354605 -0.000000 vn -0.822984 -0.568065 0.000000 vn -0.822984 -0.568065 -0.000000 vn -0.663122 -0.748511 0.000000 vn -0.663122 -0.748511 -0.000000 vn -0.464723 -0.885456 0.000000 vn -0.464723 -0.885456 -0.000000 vn -0.239316 -0.970942 0.000000 vn -0.239316 -0.970942 -0.000000 vn 0.000000 -1.000000 0.000000 vn -0.000000 -1.000000 -0.000000 vn 0.239316 -0.970942 0.000000 vn 0.239316 -0.970942 0.000000 vn 0.464723 -0.885456 0.000000 vn 0.663122 -0.748511 0.000000 vn 0.464723 -0.885456 0.000000 vn 0.663122 -0.748511 0.000000 vn 0.822984 -0.568065 0.000000 vn 0.822984 -0.568065 0.000000 vn 0.935016 -0.354605 0.000000 vn 0.935016 -0.354605 0.000000 vn 0.992709 -0.120537 0.000000 vn 0.992709 -0.120537 0.000000 vn 0.992709 0.120537 -0.000000 vn 0.992709 0.120537 0.000000 vn 0.935016 0.354605 0.000000 vn 0.935016 0.354605 0.000000 vn 0.822984 0.568065 0.000000 vn 0.822984 0.568065 0.000000 vn 0.663122 0.748511 0.000000 vn 0.663122 0.748511 0.000000 vn 0.464724 0.885456 0.000000 vn 0.464724 0.885456 0.000000 vn 0.239315 0.970942 0.000000 vn 0.239315 0.970942 0.000000 vn -0.000000 1.000000 0.000000 vn 0.000000 1.000000 0.000000 vn -0.239315 0.970942 0.000000 vn -0.239315 0.970942 0.000000 vn -0.464724 0.885456 0.000000 vn -0.464724 0.885456 0.000000 vn -0.663122 0.748511 0.000000 vn -0.663122 0.748511 0.000000 vn -0.822984 0.568065 0.000000 vn -0.822984 0.568065 0.000000 vn -0.935016 0.354605 0.000000 vn -0.935016 0.354605 0.000000 vn -0.992709 0.120537 0.000000 vn -0.992709 0.120537 0.000000 vn 0.000000 1.000000 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.706495 -0.707718 0.000000 vn -0.706495 -0.707718 0.000000 vn 0.000000 -0.000000 -1.000000 vn 0.707107 0.707107 -0.000000 vn 0.707107 0.707107 0.000000 vn 0.707107 0.707107 0.000000 vn -0.936329 -0.351123 0.000000 vn -0.936329 -0.351123 0.000000 vn -0.936329 -0.351123 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 -0.000002 vn 0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.707107 -0.707107 0.000000 vn -0.707107 -0.707107 0.000000 vn -0.707107 -0.707107 0.000000 vn 0.611740 -0.791059 0.000000 vn 0.611740 -0.791059 0.000000 vn 0.648789 -0.760968 0.000000 vn 0.648789 -0.760968 0.000000 vn 0.677209 -0.735791 0.000000 vn 0.677209 -0.735791 0.000000 vn 0.707107 0.707106 -0.000000 vn 0.707107 0.707106 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 f 1//1 2//1 3//1 f 1//2 3//2 4//2 f 5//3 2//3 1//3 f 6//4 2//4 5//4 f 7//5 2//5 6//5 f 8//6 2//6 7//6 f 9//7 2//7 8//7 f 10//8 11//8 12//8 f 10//9 9//9 11//9 f 10//10 2//10 9//10 f 13//11 2//11 10//11 f 14//12 15//12 16//12 f 16//13 17//13 18//13 f 18//14 17//14 19//14 f 19//15 17//15 20//15 f 15//16 17//16 16//16 f 20//17 21//17 22//17 f 22//18 21//18 23//18 f 17//19 21//19 20//19 f 21//20 24//20 23//20 f 24//21 25//21 26//21 f 26//22 25//22 27//22 f 27//23 25//23 28//23 f 21//24 25//24 24//24 f 25//25 29//25 28//25 f 29//26 1//26 4//26 f 25//27 30//27 29//27 f 29//28 30//28 1//28 f 30//29 5//29 1//29 f 5//30 31//30 6//30 f 30//31 31//31 5//31 f 6//32 32//32 7//32 f 31//33 32//33 6//33 f 7//34 33//34 8//34 f 32//35 33//35 7//35 f 33//36 9//36 8//36 f 33//37 34//37 9//37 f 34//38 11//38 9//38 f 34//39 35//39 11//39 f 35//40 12//40 11//40 f 35//41 36//41 12//41 f 36//42 10//42 12//42 f 36//43 37//43 10//43 f 16//44 38//44 14//44 f 18//45 38//45 16//45 f 19//46 39//46 38//46 f 19//47 38//47 18//47 f 20//48 39//48 19//48 f 22//49 39//49 20//49 f 40//50 41//50 42//50 f 40//51 42//51 39//51 f 40//52 39//52 22//52 f 40//53 22//53 43//53 f 23//54 43//54 22//54 f 44//55 43//55 23//55 f 29//56 45//56 28//56 f 4//57 3//57 46//57 f 4//58 46//58 29//58 f 46//59 45//59 29//59 f 45//60 47//60 28//60 f 47//61 48//61 27//61 f 48//62 49//62 26//62 f 49//63 44//63 24//63 f 23//64 24//64 44//64 f 24//65 26//65 49//65 f 26//66 27//66 48//66 f 27//67 28//67 47//67 f 37//68 13//68 10//68 f 13//69 50//69 51//69 f 13//70 52//70 50//70 f 37//71 52//71 13//71 f 2//72 13//72 3//72 f 3//73 13//73 51//73 f 53//74 30//74 54//74 f 53//75 31//75 30//75 f 54//76 25//76 55//76 f 30//77 25//77 54//77 f 56//78 32//78 53//78 f 53//79 32//79 31//79 f 55//80 21//80 57//80 f 25//81 21//81 55//81 f 58//82 33//82 56//82 f 56//83 33//83 32//83 f 21//84 17//84 57//84 f 57//85 17//85 59//85 f 60//86 34//86 58//86 f 58//87 34//87 33//87 f 17//88 15//88 59//88 f 59//89 15//89 61//89 f 60//90 35//90 34//90 f 62//91 35//91 60//91 f 62//92 36//92 35//92 f 63//93 36//93 62//93 f 63//94 37//94 36//94 f 64//95 37//95 63//95 f 15//96 65//96 61//96 f 15//97 66//97 65//97 f 66//98 67//98 65//98 f 67//99 68//99 65//99 f 67//100 69//100 68//100 f 69//101 70//101 68//101 f 69//102 71//102 70//102 f 71//103 72//103 70//103 f 71//104 73//104 72//104 f 73//105 74//105 72//105 f 64//106 75//106 37//106 f 73//107 76//107 74//107 f 76//108 77//108 74//108 f 78//109 79//109 52//109 f 79//110 80//110 52//110 f 76//111 81//111 77//111 f 81//112 82//112 77//112 f 80//113 83//113 52//113 f 81//114 84//114 82//114 f 80//115 85//115 83//115 f 84//116 86//116 82//116 f 87//117 88//117 84//117 f 84//118 88//118 86//118 f 85//119 89//119 83//119 f 90//120 91//120 87//120 f 87//121 91//121 88//121 f 85//122 92//122 89//122 f 93//123 94//123 90//123 f 90//124 94//124 91//124 f 93//125 95//125 94//125 f 92//126 96//126 89//126 f 92//127 97//127 96//127 f 97//128 98//128 96//128 f 97//129 99//129 98//129 f 99//130 100//130 98//130 f 100//131 101//131 102//131 f 99//132 101//132 100//132 f 101//133 103//133 102//133 f 102//134 103//134 104//134 f 103//135 105//135 104//135 f 104//136 105//136 106//136 f 106//137 107//137 108//137 f 105//138 107//138 106//138 f 107//139 109//139 108//139 f 108//140 109//140 110//140 f 109//141 111//141 110//141 f 93//142 111//142 95//142 f 109//143 112//143 111//143 f 111//144 112//144 95//144 f 52//145 37//145 78//145 f 78//146 37//146 75//146 f 71//147 69//147 41//147 f 41//148 69//148 42//148 f 42//149 67//149 39//149 f 69//150 67//150 42//150 f 39//151 66//151 38//151 f 67//152 66//152 39//152 f 38//153 15//153 14//153 f 66//154 15//154 38//154 f 40//155 113//155 114//155 f 40//156 114//156 115//156 f 40//157 115//157 116//157 f 40//158 116//158 117//158 f 40//159 117//159 118//159 f 40//160 118//160 119//160 f 40//161 119//161 41//161 f 120//162 113//162 40//162 f 40//163 121//163 120//163 f 122//164 40//164 43//164 f 122//165 121//165 40//165 f 44//166 122//166 43//166 f 49//167 122//167 44//167 f 123//168 49//168 48//168 f 123//169 122//169 49//169 f 47//170 123//170 48//170 f 124//171 123//171 47//171 f 45//172 124//172 47//172 f 125//173 45//173 46//173 f 125//174 124//174 45//174 f 2//175 125//175 46//175 f 2//176 46//176 3//176 f 52//177 83//177 50//177 f 50//178 83//178 126//178 f 126//179 89//179 127//179 f 83//180 89//180 126//180 f 126//181 127//181 128//181 f 50//182 128//182 129//182 f 50//183 129//183 51//183 f 50//184 126//184 128//184 f 130//185 3//185 131//185 f 132//186 3//186 130//186 f 132//187 130//187 133//187 f 134//188 132//188 133//188 f 135//189 130//189 136//189 f 135//190 133//190 130//190 f 51//191 129//191 135//191 f 51//192 136//192 131//192 f 51//193 131//193 3//193 f 51//194 135//194 136//194 f 112//195 109//195 137//195 f 137//196 109//196 138//196 f 109//197 139//197 138//197 f 109//198 107//198 139//198 f 139//199 105//199 140//199 f 107//200 105//200 139//200 f 140//201 103//201 141//201 f 105//202 103//202 140//202 f 141//203 101//203 142//203 f 103//204 101//204 141//204 f 143//205 144//205 145//205 f 145//206 146//206 143//206 f 146//207 95//207 147//207 f 145//208 95//208 146//208 f 147//209 137//209 144//209 f 144//210 137//210 145//210 f 95//211 112//211 147//211 f 147//212 112//212 137//212 f 61//213 65//213 148//213 f 148//214 65//214 149//214 f 149//215 68//215 150//215 f 65//216 68//216 149//216 f 150//217 70//217 151//217 f 68//218 70//218 150//218 f 151//219 72//219 152//219 f 70//220 72//220 151//220 f 152//221 74//221 153//221 f 72//222 74//222 152//222 f 153//223 77//223 154//223 f 74//224 77//224 153//224 f 154//225 82//225 155//225 f 77//226 82//226 154//226 f 155//227 86//227 156//227 f 82//228 86//228 155//228 f 156//229 88//229 157//229 f 86//230 88//230 156//230 f 157//231 91//231 158//231 f 88//232 91//232 157//232 f 158//233 94//233 159//233 f 159//234 94//234 145//234 f 91//235 94//235 158//235 f 94//236 95//236 145//236 f 64//237 63//237 160//237 f 160//238 63//238 161//238 f 161//239 62//239 162//239 f 63//240 62//240 161//240 f 162//241 60//241 163//241 f 163//242 60//242 164//242 f 62//243 60//243 162//243 f 60//244 58//244 164//244 f 164//245 56//245 165//245 f 58//246 56//246 164//246 f 165//247 53//247 166//247 f 56//248 53//248 165//248 f 166//249 54//249 167//249 f 53//250 54//250 166//250 f 167//251 55//251 168//251 f 54//252 55//252 167//252 f 168//253 57//253 169//253 f 55//254 57//254 168//254 f 169//255 59//255 170//255 f 170//256 59//256 148//256 f 57//257 59//257 169//257 f 59//258 61//258 148//258 f 75//259 171//259 172//259 f 172//260 171//260 160//260 f 171//261 173//261 160//261 f 75//262 174//262 171//262 f 175//263 64//263 173//263 f 174//264 64//264 175//264 f 173//265 64//265 160//265 f 75//266 64//266 174//266 f 75//267 176//267 78//267 f 176//268 79//268 78//268 f 176//269 80//269 79//269 f 101//270 99//270 142//270 f 142//271 99//271 177//271 f 177//272 97//272 178//272 f 99//273 97//273 177//273 f 97//274 179//274 178//274 f 97//275 92//275 179//275 f 179//276 85//276 180//276 f 92//277 85//277 179//277 f 180//278 80//278 181//278 f 85//279 80//279 180//279 f 93//280 90//280 113//280 f 113//281 90//281 114//281 f 114//282 87//282 115//282 f 90//283 87//283 114//283 f 115//284 84//284 116//284 f 87//285 84//285 115//285 f 116//286 81//286 117//286 f 84//287 81//287 116//287 f 117//288 76//288 118//288 f 81//289 76//289 117//289 f 118//290 73//290 119//290 f 76//291 73//291 118//291 f 119//292 71//292 41//292 f 73//293 71//293 119//293 f 111//294 121//294 182//294 f 182//295 121//295 120//295 f 121//296 113//296 120//296 f 121//297 93//297 113//297 f 111//298 93//298 121//298 f 102//299 104//299 183//299 f 183//300 104//300 184//300 f 184//301 106//301 185//301 f 104//302 106//302 184//302 f 106//303 186//303 185//303 f 106//304 108//304 186//304 f 186//305 110//305 187//305 f 108//306 110//306 186//306 f 187//307 111//307 182//307 f 110//308 111//308 187//308 f 89//309 96//309 127//309 f 127//310 96//310 188//310 f 188//311 98//311 189//311 f 96//312 98//312 188//312 f 189//313 100//313 190//313 f 98//314 100//314 189//314 f 190//315 102//315 183//315 f 100//316 102//316 190//316 f 191//317 122//317 123//317 f 192//318 121//318 122//318 f 192//319 122//319 191//319 f 193//320 194//320 121//320 f 193//321 121//321 192//321 f 2//322 123//322 124//322 f 2//323 124//323 125//323 f 2//324 191//324 123//324 f 195//325 196//325 194//325 f 195//326 194//326 193//326 f 187//327 182//327 120//327 f 197//328 187//328 120//328 f 186//329 187//329 197//329 f 198//330 197//330 132//330 f 185//331 186//331 197//331 f 185//332 197//332 198//332 f 184//333 185//333 198//333 f 190//334 183//334 184//334 f 190//335 184//335 198//335 f 189//336 198//336 128//336 f 189//337 190//337 198//337 f 188//338 189//338 128//338 f 127//339 188//339 128//339 f 197//340 199//340 132//340 f 128//341 200//341 129//341 f 201//342 128//342 198//342 f 201//343 200//343 128//343 f 202//344 203//344 135//344 f 135//345 203//345 133//345 f 200//346 202//346 129//346 f 129//347 202//347 135//347 f 132//348 199//348 204//348 f 132//349 204//349 205//349 f 3//350 132//350 205//350 f 198//351 132//351 134//351 f 203//352 134//352 133//352 f 134//353 201//353 198//353 f 203//354 201//354 134//354 f 206//355 207//355 131//355 f 131//356 207//356 130//356 f 208//357 206//357 136//357 f 136//358 206//358 131//358 f 207//359 208//359 130//359 f 130//360 208//360 136//360 f 209//361 166//361 167//361 f 210//362 167//362 168//362 f 210//363 209//363 167//363 f 211//364 165//364 166//364 f 211//365 166//365 209//365 f 212//366 168//366 169//366 f 212//367 210//367 168//367 f 213//368 164//368 165//368 f 213//369 165//369 211//369 f 214//370 169//370 170//370 f 214//371 212//371 169//371 f 215//372 163//372 164//372 f 215//373 164//373 213//373 f 216//374 214//374 170//374 f 217//375 163//375 215//375 f 217//376 162//376 163//376 f 218//377 216//377 170//377 f 218//378 170//378 148//378 f 219//379 162//379 217//379 f 219//380 161//380 162//380 f 220//381 161//381 219//381 f 220//382 160//382 161//382 f 149//383 218//383 148//383 f 221//384 218//384 149//384 f 150//385 221//385 149//385 f 222//386 221//386 150//386 f 151//387 222//387 150//387 f 223//388 222//388 151//388 f 152//389 223//389 151//389 f 224//390 223//390 152//390 f 153//391 224//391 152//391 f 225//392 224//392 153//392 f 172//393 160//393 220//393 f 154//394 225//394 153//394 f 226//395 225//395 154//395 f 227//396 228//396 229//396 f 181//397 227//397 229//397 f 155//398 230//398 226//398 f 155//399 226//399 154//399 f 231//400 181//400 229//400 f 180//401 181//401 231//401 f 156//402 232//402 230//402 f 156//403 230//403 155//403 f 157//404 233//404 232//404 f 157//405 232//405 156//405 f 158//406 234//406 233//406 f 158//407 233//407 157//407 f 179//408 231//408 235//408 f 179//409 180//409 231//409 f 159//410 236//410 234//410 f 159//411 234//411 158//411 f 145//412 236//412 159//412 f 178//413 235//413 237//413 f 178//414 179//414 235//414 f 238//415 178//415 237//415 f 177//416 178//416 238//416 f 239//417 177//417 238//417 f 142//418 239//418 240//418 f 142//419 177//419 239//419 f 141//420 240//420 241//420 f 141//421 142//421 240//421 f 140//422 241//422 242//422 f 140//423 141//423 241//423 f 139//424 242//424 243//424 f 139//425 140//425 242//425 f 138//426 139//426 243//426 f 138//427 243//427 244//427 f 245//428 138//428 244//428 f 245//429 236//429 145//429 f 137//430 245//430 145//430 f 137//431 138//431 245//431 f 220//432 229//432 228//432 f 220//433 228//433 172//433 f 146//434 147//434 143//434 f 143//435 147//435 144//435 f 246//436 247//436 248//436 f 246//437 248//437 249//437 f 246//438 249//438 250//438 f 251//439 246//439 250//439 f 252//440 250//440 253//440 f 252//441 251//441 250//441 f 254//442 253//442 255//442 f 254//443 252//443 253//443 f 256//444 255//444 257//444 f 256//445 254//445 255//445 f 258//446 257//446 259//446 f 258//447 256//447 257//447 f 260//448 259//448 261//448 f 260//449 261//449 262//449 f 260//450 258//450 259//450 f 263//451 260//451 262//451 f 264//452 262//452 265//452 f 264//453 263//453 262//453 f 266//454 265//454 267//454 f 266//455 264//455 265//455 f 268//456 267//456 269//456 f 268//457 266//457 267//457 f 270//458 269//458 271//458 f 270//459 268//459 269//459 f 272//460 270//460 271//460 f 272//461 271//461 273//461 f 274//462 272//462 273//462 f 274//463 273//463 275//463 f 274//464 275//464 276//464 f 277//465 274//465 276//465 f 278//466 277//466 276//466 f 278//467 276//467 279//467 f 280//468 278//468 279//468 f 280//469 279//469 281//469 f 280//470 281//470 282//470 f 283//471 280//471 282//471 f 284//472 283//472 282//472 f 284//473 282//473 285//473 f 284//474 285//474 286//474 f 287//475 284//475 286//475 f 287//476 286//476 288//476 f 289//477 287//477 288//477 f 172//478 289//478 288//478 f 172//479 288//479 75//479 f 174//480 290//480 291//480 f 174//481 291//481 171//481 f 173//482 171//482 291//482 f 175//483 173//483 291//483 f 290//484 175//484 291//484 f 174//485 175//485 290//485 f 276//486 292//486 279//486 f 279//487 293//487 281//487 f 292//488 293//488 279//488 f 275//489 294//489 276//489 f 276//490 294//490 292//490 f 281//491 295//491 282//491 f 293//492 295//492 281//492 f 273//493 296//493 275//493 f 275//494 296//494 294//494 f 282//495 297//495 285//495 f 295//496 297//496 282//496 f 271//497 298//497 273//497 f 273//498 298//498 296//498 f 285//499 299//499 286//499 f 297//500 299//500 285//500 f 269//501 300//501 271//501 f 271//502 300//502 298//502 f 286//503 301//503 288//503 f 299//504 301//504 286//504 f 269//505 302//505 300//505 f 288//506 303//506 75//506 f 301//507 303//507 288//507 f 269//508 267//508 302//508 f 267//509 304//509 302//509 f 305//510 176//510 303//510 f 303//511 176//511 75//511 f 305//512 306//512 176//512 f 267//513 265//513 304//513 f 304//514 265//514 307//514 f 306//515 308//515 176//515 f 265//516 262//516 307//516 f 307//517 262//517 309//517 f 308//518 80//518 176//518 f 308//519 310//519 80//519 f 262//520 261//520 309//520 f 309//521 261//521 311//521 f 308//522 312//522 310//522 f 313//523 312//523 308//523 f 313//524 248//524 312//524 f 314//525 248//525 313//525 f 311//526 259//526 315//526 f 261//527 259//527 311//527 f 316//528 249//528 314//528 f 314//529 249//529 248//529 f 315//530 257//530 317//530 f 259//531 257//531 315//531 f 318//532 250//532 316//532 f 316//533 250//533 249//533 f 317//534 255//534 319//534 f 257//535 255//535 317//535 f 319//536 253//536 318//536 f 255//537 253//537 319//537 f 318//538 253//538 250//538 f 181//539 80//539 310//539 f 320//540 181//540 310//540 f 321//541 310//541 312//541 f 321//542 320//542 310//542 f 247//543 312//543 248//543 f 247//544 321//544 312//544 f 121//545 194//545 120//545 f 120//546 194//546 322//546 f 194//547 196//547 322//547 f 322//548 196//548 323//548 f 196//549 195//549 323//549 f 323//550 195//550 324//550 f 195//551 325//551 324//551 f 325//552 193//552 326//552 f 195//553 193//553 325//553 f 193//554 192//554 326//554 f 326//555 192//555 327//555 f 192//556 191//556 328//556 f 328//557 191//557 329//557 f 191//558 2//558 329//558 f 329//559 2//559 3//559 f 323//560 120//560 322//560 f 330//561 120//561 323//561 f 324//562 330//562 323//562 f 331//563 330//563 324//563 f 199//564 324//564 325//564 f 199//565 331//565 324//565 f 332//566 120//566 330//566 f 332//567 331//567 199//567 f 197//568 120//568 332//568 f 197//569 332//569 199//569 f 200//570 201//570 203//570 f 200//571 203//571 202//571 f 3//572 327//572 329//572 f 205//573 326//573 327//573 f 205//574 327//574 3//574 f 204//575 325//575 326//575 f 204//576 326//576 205//576 f 199//577 325//577 204//577 f 208//578 207//578 206//578 f 333//579 172//579 228//579 f 227//580 333//580 228//580 f 181//581 333//581 227//581 f 334//582 335//582 239//582 f 334//583 239//583 238//583 f 336//584 238//584 237//584 f 336//585 334//585 238//585 f 337//586 237//586 235//586 f 337//587 336//587 237//587 f 338//588 235//588 231//588 f 338//589 337//589 235//589 f 339//590 231//590 229//590 f 339//591 338//591 231//591 f 340//592 341//592 243//592 f 340//593 243//593 242//593 f 342//594 242//594 241//594 f 342//595 340//595 242//595 f 343//596 241//596 240//596 f 343//597 342//597 241//597 f 335//598 240//598 239//598 f 335//599 343//599 240//599 f 344//600 339//600 229//600 f 345//601 339//601 344//601 f 220//602 344//602 229//602 f 346//603 345//603 344//603 f 346//604 344//604 220//604 f 347//605 348//605 245//605 f 347//606 245//606 244//606 f 243//607 347//607 244//607 f 341//608 347//608 243//608 f 349//609 346//609 220//609 f 349//610 220//610 219//610 f 350//611 219//611 217//611 f 350//612 349//612 219//612 f 351//613 217//613 215//613 f 351//614 350//614 217//614 f 213//615 351//615 215//615 f 352//616 351//616 213//616 f 353//617 213//617 211//617 f 353//618 352//618 213//618 f 354//619 211//619 209//619 f 354//620 353//620 211//620 f 355//621 209//621 210//621 f 355//622 354//622 209//622 f 356//623 357//623 236//623 f 245//624 356//624 236//624 f 348//625 358//625 356//625 f 348//626 356//626 245//626 f 359//627 355//627 210//627 f 359//628 210//628 212//628 f 360//629 212//629 214//629 f 360//630 359//630 212//630 f 216//631 360//631 214//631 f 361//632 360//632 216//632 f 362//633 216//633 218//633 f 362//634 361//634 216//634 f 363//635 362//635 218//635 f 363//636 218//636 221//636 f 364//637 221//637 222//637 f 364//638 363//638 221//638 f 365//639 222//639 223//639 f 365//640 364//640 222//640 f 366//641 223//641 224//641 f 367//642 365//642 223//642 f 367//643 223//643 366//643 f 368//644 366//644 224//644 f 225//645 368//645 224//645 f 369//646 368//646 225//646 f 226//647 369//647 225//647 f 370//648 369//648 226//648 f 230//649 370//649 226//649 f 371//650 370//650 230//650 f 372//651 230//651 232//651 f 372//652 371//652 230//652 f 373//653 232//653 233//653 f 373//654 372//654 232//654 f 374//655 233//655 234//655 f 374//656 373//656 233//656 f 357//657 234//657 236//657 f 357//658 374//658 234//658 f 375//659 277//659 278//659 f 376//660 278//660 280//660 f 376//661 375//661 278//661 f 377//662 274//662 277//662 f 377//663 277//663 375//663 f 378//664 280//664 283//664 f 378//665 376//665 280//665 f 379//666 272//666 274//666 f 379//667 274//667 377//667 f 380//668 283//668 284//668 f 380//669 378//669 283//669 f 381//670 270//670 272//670 f 381//671 272//671 379//671 f 382//672 284//672 287//672 f 382//673 380//673 284//673 f 383//674 268//674 270//674 f 383//675 270//675 381//675 f 384//676 287//676 289//676 f 384//677 382//677 287//677 f 385//678 268//678 383//678 f 386//679 289//679 172//679 f 386//680 384//680 289//680 f 266//681 268//681 385//681 f 387//682 266//682 385//682 f 333//683 388//683 386//683 f 333//684 386//684 172//684 f 389//685 388//685 333//685 f 264//686 266//686 387//686 f 264//687 387//687 390//687 f 391//688 389//688 333//688 f 263//689 264//689 390//689 f 263//690 390//690 392//690 f 181//691 391//691 333//691 f 320//692 391//692 181//692 f 260//693 263//693 392//693 f 260//694 392//694 393//694 f 321//695 391//695 320//695 f 321//696 394//696 391//696 f 247//697 394//697 321//697 f 247//698 395//698 394//698 f 258//699 393//699 396//699 f 258//700 260//700 393//700 f 246//701 397//701 395//701 f 246//702 395//702 247//702 f 256//703 396//703 398//703 f 256//704 258//704 396//704 f 251//705 399//705 397//705 f 251//706 397//706 246//706 f 254//707 398//707 400//707 f 254//708 256//708 398//708 f 252//709 400//709 399//709 f 252//710 254//710 400//710 f 252//711 399//711 251//711 f 401//712 395//712 402//712 f 402//713 395//713 314//713 f 314//714 397//714 316//714 f 395//715 397//715 314//715 f 316//716 399//716 318//716 f 397//717 399//717 316//717 f 318//718 400//718 319//718 f 399//719 400//719 318//719 f 319//720 398//720 317//720 f 400//721 398//721 319//721 f 317//722 396//722 315//722 f 398//723 396//723 317//723 f 315//724 393//724 311//724 f 396//725 393//725 315//725 f 311//726 392//726 309//726 f 393//727 392//727 311//727 f 309//728 390//728 307//728 f 307//729 390//729 304//729 f 392//730 390//730 309//730 f 390//731 387//731 304//731 f 387//732 385//732 304//732 f 304//733 385//733 302//733 f 385//734 383//734 302//734 f 302//735 383//735 300//735 f 300//736 383//736 298//736 f 383//737 381//737 298//737 f 298//738 381//738 296//738 f 381//739 379//739 296//739 f 379//740 377//740 296//740 f 296//741 377//741 294//741 f 377//742 375//742 294//742 f 294//743 375//743 292//743 f 375//744 376//744 292//744 f 292//745 376//745 293//745 f 376//746 378//746 293//746 f 293//747 378//747 295//747 f 378//748 380//748 295//748 f 295//749 380//749 297//749 f 380//750 382//750 297//750 f 297//751 382//751 299//751 f 299//752 382//752 301//752 f 382//753 384//753 301//753 f 384//754 386//754 301//754 f 301//755 386//755 303//755 f 303//756 386//756 305//756 f 386//757 388//757 305//757 f 305//758 389//758 306//758 f 388//759 389//759 305//759 f 306//760 391//760 308//760 f 389//761 391//761 306//761 f 391//762 401//762 308//762 f 308//763 401//763 402//763 f 327//764 192//764 328//764 f 403//765 404//765 330//765 f 330//766 404//766 332//766 f 405//767 403//767 331//767 f 331//768 403//768 330//768 f 404//769 405//769 332//769 f 332//770 405//770 331//770 f 338//771 339//771 345//771 f 406//772 338//772 345//772 f 337//773 338//773 406//773 f 407//774 406//774 408//774 f 336//775 337//775 406//775 f 336//776 406//776 407//776 f 334//777 336//777 407//777 f 343//778 335//778 334//778 f 343//779 334//779 407//779 f 342//780 407//780 409//780 f 342//781 343//781 407//781 f 340//782 342//782 409//782 f 341//783 340//783 409//783 f 406//784 410//784 408//784 f 411//785 346//785 349//785 f 411//786 349//786 350//786 f 411//787 350//787 351//787 f 411//788 351//788 352//788 f 411//789 352//789 353//789 f 411//790 353//790 354//790 f 411//791 354//791 355//791 f 345//792 346//792 411//792 f 347//793 341//793 409//793 f 348//794 409//794 412//794 f 348//795 412//795 358//795 f 348//796 347//796 409//796 f 368//797 413//797 366//797 f 369//798 413//798 368//798 f 370//799 413//799 369//799 f 371//800 413//800 370//800 f 414//801 415//801 413//801 f 414//802 374//802 357//802 f 414//803 373//803 374//803 f 414//804 372//804 373//804 f 414//805 371//805 372//805 f 414//806 413//806 371//806 f 356//807 414//807 357//807 f 358//808 412//808 356//808 f 356//809 412//809 414//809 f 363//810 360//810 361//810 f 363//811 361//811 362//811 f 364//812 360//812 363//812 f 411//813 355//813 359//813 f 416//814 367//814 413//814 f 413//815 367//815 366//815 f 404//816 403//816 405//816 f 412//817 409//817 414//817 f 409//818 415//818 414//818 f 409//819 407//819 415//819 f 407//820 408//820 415//820 f 408//821 413//821 415//821 f 408//822 416//822 413//822 f 408//823 410//823 417//823 f 408//824 417//824 416//824 f 417//825 410//825 418//825 f 410//826 419//826 418//826 f 410//827 406//827 419//827 f 406//828 345//828 419//828 f 419//829 345//829 344//829 f 345//830 411//830 344//830 f 411//831 420//831 344//831 f 411//832 421//832 420//832 f 421//833 422//833 420//833 f 420//834 422//834 423//834 f 422//835 424//835 423//835 f 422//836 425//836 424//836 f 424//837 426//837 427//837 f 425//838 426//838 424//838 f 426//839 416//839 427//839 f 427//840 416//840 413//840 f 416//841 418//841 413//841 f 416//842 417//842 418//842 f 413//843 420//843 423//843 f 413//844 423//844 424//844 f 413//845 424//845 427//845 f 418//846 344//846 420//846 f 418//847 420//847 413//847 f 419//848 344//848 418//848 apparmor-5.0.2/documentation/keychains/AppArmorLogoShadowFlat/AppArmorLogoShadowFlat.3mf000066400000000000000000005320051522511161100314330ustar00rootroot00000000000000PK Y[[Content_Types].xmlKj09жr[(΢FBŅ!r?>WR;y_7R0X:y?Ys'gT63xYCL| C<)!h`ԋ#񾲰,f-N5XWgvQsɳ R]a0ŏ׸ВxR/"X_(2 Jj7@PKPK Y[Metadata/plate_1.pngPNG  IHDRxz(IDATxu+4rԒnɭN'ێ!Ap45U8at1h zVf" }Grr]6eW7Td.ti7YU~<{?|`?9kk{گSZ]O}zW^{>䓽O/{;PԀh@zq`y[F?\!X4 (Xfdto5~  վ*zœ'`(d-U-_) 21W[,""kw[H!ic 1bm1,nƮZb*/l~uΫҷ N.s9@7NvwwoL=~aZQySzRn1f`\FzC dAX.;wwYiqۭ*,M*'wSgEhFƌmQ\3ɘ.~{Gݛ<\VRįr*'ގ>&A;?GZxNzR`fA@!|睬 _X-W{}̙iuP݄UO\ aX.Z/~{ۿ{{@u݈q٬!Al\bQfw;wP?qoooooEr՛rj֥f۬k.4.8Olx$RsP=?EȭOEdHv߯5FiC0p:X E{];&"ʉ+_=hE|飏>~ڧۆdFo2Nכu󻍑(AQ pQ ƚЖBMTH̾Gʉ~lnBTb>lPr^ ;;:zꩽ~3+z_l #E 5*t#pC⣥xx+JryL sYEM`$ȸƯ}J `+'0iW/н9JTn}0G/N*7[jefV`̨ #=/UP*5G89?{h(_#J!pʢ'? ;V.wRрI`  qBӧM 0"f%CIprH 'ʉ4UΣm:Q`S>yr#fs 7r[5W ٽT~鵪wB(nNQW4&U97UQTp!>̯~} W@P_'{?5[Րo^8iF^/ԧ>kNt}+'8Ka:@@FB gT:lbA/y_:6Ȩ*"N{ߜTIWBI$1 WG0KS8z2!рP s]ۘPw}O3W@%I 'w#DPZ"&e KX1|vӏf/}C&Aͽ.cmh?"ʶ(IL lݼ9[@$VVOuM z®t&QoEG,ѢR% eqnʉb EnbZQ;#!CvД@X.ا,nQ9+RgN(-J=QYQs6jDIvFB|{r>h)Q=?bO|̙YCQ Ku+'*&vNFIFf]ona[esXʢw"C %C Uo[5ѷ?o`Ģ$21稺pr`>xZ=,J#->̺պ5022ǴIyVf@jy)k2R-v[2N8,z5i lЭV12_2Y1m"0`v"kK(UbҧC#]I\f;?7܇[fd Ė͖iu}ۿ%J?@|okDeç r Fh39w.6DF!އ~|-3}V?rЉ&^ ~aQ9rzn%Yde>hk_hC_/.4jN>}ۡ.g@k:luر M8^ #MUQOh'B?+T_y!0g>mnE1*͹Aҭ]1&kZ?xҢ$@ЗQ0߃RABNUїseϹ$ Mkcaa9]6j]=nv Hk(P3W_lC 9H`QB5]fܵd`}UL0!(Q)HEtp]__?8M.uaֿ u,?NgիYR/VWwB(.ҁx]J 92VQ[RB˄2z.dvZ{("XHߗ̙oKs c_|ggZBљ#23 "zL?O@ꉱœZɠtD/DwMi)ǭlM6z2@G03/M}tq7y'7Y[^Z{]/SAQQZj} (CP,Evk5#P@_|W7j̈́oWjۭ؎ݤcpr{5URha`:V쟽'}Q4;+—f՞Vs-kB/l͈|2EuzmӄpTWNN<hDGlnc%QErT`kzk>8JPb8_\1^%[ڤuͣC':&9UNԊ85pЄlR>|1w #_13mb6Ū(?7ē6YNj4 y.P.+ѣG|_WYuOR7+N!?Ϳ+Bnrib ;+tk7wJ^W>'77WR 4@B<9vۆNwL(F-`Xe9^)= +o-s|Gy|mZ$rhn55TtXJ UOhߴCur%]iw}] &;ēccV>~",E" ˮ⛻D9>H훻 ڍ`t3I-sоH@v eu5{Wc#2sܪM0+%;MfTM~meX=1v )+ ÃZwkACv\>wb`>x}@_Β5±SiITO`,}D&XZ?q3VHąsf'7!M6= [9*L>@L%C卿R+mQ 6`}DC>v,l=ݥIiֿ_uQl?}$2 wC>wᄃZx\n'MUOIEI&='C`ʽ`9}Nj|ǬI~:y`{($ Nvf;pN!02f3B=hRrBQl_DJΏO(ryieN=VK9V+<)Qo,7(cS%%ΓŜ'N{ oW ޻P8}&RʮfSɁJ&TM~bm:|V >}+!&c0VRa=QkSΓN%~ؽ~UL{d;&[$@l_RVʹUtnq`F_}t+oZ0!n]A.5d}FB0y-JU,kΓ4"mI{}o(5P)j"\ZOXїC{}Ô@,AQ=?ial}!3o?qn(ɋPzrrُ=X/E{Nx'ѣ$^6 "!ؚ' ,YuϚVAYC.,wA-7y9Zzro[!5n]KAF^녹L3-Yc6rs?URpB5&BNwª 5WݗhثӖqѰnj Hf;:RA ۠-;hP-r2㮥[22f?*tBT4je(k:^<%FfXNAG}*)Gj O9`Zb}\0Zm? 4Iѯ_Q0!eJ gQQɘᏉ'/^dňI @']rʙr~XVC) n^H }W zze~Œ~d*J*5Sq;!njh@ 9kʞMRز5!Vtˮt[l }<*7\eosR{/K,,VDQll}.;F ~傇~ oMQ^+ =AUZ?O}e`Ĥ+JR]8Y9 0T=KL ɝql+\澭'0` ƪ(1VYEldRUr"H}=h` We k4) T WmO{.>'}pX`(^; D"k '6,hA۲ _5#T{Ms,,; `'|K޳]p"#V}^i9c{L'&ZuPQi V<ߧcǎYA:woc[!bMGTTNrp񥵢zm?f!E[I_Z1N@%%~HXi ~zET%DN`QP;r"&sm Xm/~\G}ty28LJ㊚&1SCX5UGȑ#KPϣh+w'4c.p{8aii*<=u`b47PɈn]:Ǘupŋ{?7vww;=5}m9կGZ.fWbĬ5tyB^4OXv[i3&dLCtPFh Cg.;ܾf˺ˤF=t $'%.7jqSp2nu}j{x7 vgni L[NR>g*%zE% m-`S0~5ҥA=P@9;"'Y?;ަ$~uQO,j{WnNT(='6zzNR.Vkza-}>5(cQb6!(0[m$ 0֭En'}4 EMKҢZEzR^ͶmBc7pO7lk;],ԗWE`ɘJ9D&5#]SsVᛊ\m3_:!(1-Q(4ˡQ YOJ4?p淾u|YH^ʜ Z34l?Z^D93^,>-mgJ4`kL;Sڿq6[jwcryk_# 3AAӁkQʉ2Օp;nFMFݬiЉ2kZɡXg=M=NT=/hFKV2T&82&JNg)n&sJck[%5A~2q}1Br~[7_5))l5ƵoTHjэ>-ESLWn6^*rAe:ָċf)$UrBz;!%0X9"(JQNoڤuLU{%VL~"G7.5 4@` w}Oɰ[9)TӰәwE}:7}(:4M|f Ix~oIGvj'b&g4qm4JpmM9^ZIL=DnJ3-2 =|j& (mN1fhX7[_K uO_k}ȁХ,$~7y`UN)3pPr0 sӷ X) #y#6$vLDmw`j9!sc@GMd5^Il ۤ`8QCn}xR 'PĢ+),mm:6|@8YꙡQ  XV,)~衇Χo*o:wW>j{g(7=Nl,jbzgdlzf}bQ=&׃v$MsR ncOj't)SNj@ORώD.e1vy) 7jg)MM P7q51#7E#&rx7׽J`M诳Mw-dǐ.NN&"&X1lzXsHBUE1둝`I[9 } v%i miFtu,/$7=䖛&ʌ~ NޮՐ ^Ž_M0gM;ѻMwxR<:!]&lA~i;JRvC#w%Ƭ1H+sR'6든xI8z@֧Qoٳ&}>zYI䖛n^aՉ 'ߍTRT}luiY~? sog(7 pm @'On([LlWspO2̙R"Zyij%QoA[pR6B:i'$Ad*)uN&t:9~xm>A@m_WΤ[,ALeZԤiA}MLMXb\*:ѩ9{f~k:eN&r_[L{ӥE'cē{4 nx2}'}#|[v@Q1S7L>GM*:C6k(R s''J"'S1bַ':vW$t;٠q y탣 C~" '3F_cuI_f-+ W{OcifJ XKNH&}h{ h@5{k´I^5{M{w)%`@nd!]]ɫ^ `A[m{mn8 NdDdd`tؼ0*CȀY$b&|܅^5~ڷU,9uj%. h/RQ/sOtwΎ! P8)!fmׇ}UK<9 "(8m:uf:CCLV|t}˳UݖnZ2ftWM9Qۢ%)ΩC(p,}=,Rؐ=E|񯬛:i#W\DjVn"TON#IHtWEۍ_ s`Bߴd'oS+xi;"%L[OEu2)g%C& ?+4F}ݰ0BM+9@}gY'4gWL~"`inbQ6HiD{|{r.3tO>4Ė7+feR{-_U7,R,U|L (GM 5 "&59;w.d-b-WTnJN(IL3F'Z>kH$sXЕ輧f.Β ֯tö^}ftc mQi$1ʍQUmѯ_OiK{\߄9Xm %,@t^-`73tmAP3\+o%O_F$v*GVRF (vbhU&>Jj-\M_t˂sѩJ{ѽcC|B*Q ;bN1]Q2_~奪sCEŸuN"8-2i^{NTlmi E;>>}c *,vB`I٪}Iezi+f*勇(( j:=**op K_WnY=s?@p%g+Rk34v uh &)0ܔ*BKkSZT/ œmFʤ-+Fj6j=7 pCyb^aaaN"gժQ(@*e US(n^RI*BIwDB1/;mN* cQo;'>/\j'RMz y;d|n}m t 3r VL. e'(gmr"]jĉmc'd4 ڿ 79\ :H[ Ҵ6s<9w;.;Մ'Ia{QnA)q{L .갪-n' #&9C_)Pj|ͥ0%+(gPޗ xy|_r,huibQ>(@PG ~~eV<hbNL=+lyLۧE8zULTQ3>^-۵{8^[CĔUt&U>J 뤟ѻdFσjHe: h;[NUj8E4bm6zM 0 ln`L-̙3Qg=ˍĸG},MR)Brcځ[EO:TٻRP8NJ<[9qOILƺNI/[~SZT NȀ6I]QXBʭj4@[(3eR"'HIK#'/sڳȂ]FITSQ:tucNjEGLRMFAo2냦_{! YZ?KVNEuGQ2icYVIM_]bXKtfͪADnS&RjIl꓃eM8YX>N3&kNGhDӊ ?kK9ʋY @FK7vׯ&}>:TlA~"Z?_69`ZΞiKdaH/}`9kSҍE:Bmg21gH"'%ȉN~(=L iQT_Xs:N*v'9$!Q9C \\8+z5dpݜ;>mP]G0`F}$9K7]1ѡOr6m,)Ii&vKRh`ӔڶW!NiN2rUS'n_[#%9^+#𿌛GźyJvCrxco 3O\D\hQ:7*}0es&)58BCo=o^_bΒNsZT{E2ocb(Hk9U&L˪ޠ @u؁e&17HMa1xxp`!(qquȫ'V[aI9K3rrz{'!Kf 3Zn3ܘ~ +fӗGnQ#`F.W7L87MDNdL?u.xGKH;K,mI*py=Qȉ)Z2ƭ֌y[^ZM׶''.d5fa(MKu=NJj0- 37>mύB'EDdܼf"W7vc:dNd!kǃ_` stŒ2W[̽bΒK,rR]G&f|'MK#L$)ZDӚSMVOM4 pRb?r}]/PH?5wLX)ЦHY9BCsHVMlΒ9S9{lT8!mI'21g@Ap2Z~d^^MDzN@xvHcRۺᆬv+}fT۵v+F@}H7FP{}*49ԍmZr%Cpb#nqr%}% u r"n_.T\w>@py>U&&JWӱ6SM9e4ã-:Swݿ+W|29pzҬi\Dn䔉OxfT#rE)Gm[%(#ԥ#0gӘ ubI="Ţ)"ƺնN4=WjN5&5t製tD`tïМn˩CzQ=ǏowvSu nxcɢ%s>ĒCD(bB:oH{Y91bζ-r˄$d9MXut%+U(f%2銒t6Q!I'E؇X?}:D7a5ElJy3)inB)t~tm#Y=wׇ25* q=Ηx݄d7]غ%-Y=L !'VNhL#u}"'pj>7%LrhpzCT(ݽOtoj&ß;tVtT{e&( *K'On(cX>!e691:Tt= uWXW7K2g1֦kL Зn[o}z6{1Txx/6=_ݞ|!wԩi#QA^]7Q)eb"4Fj^Tmjh"?}M8_;DiZ"LgiVeWcb!XD84vJ7a!I6j_ 0:(._\հz q/#0X0icCM?./)qC&&*E3 .; `l^DIUCKَ6Q*Mdڪ)~" >6ѴWén""-Qsi{jh`ƩCA7˨ѯ|qtqhypoҌy-ep_hBʩ*?m{Pڌ`"uch&fX\mD{T1 ӑ7Iߧk`WDiVXRf:QI}fL&6A}#}3v8%U1v PFh[9_[ft3>R ER`{ 折ZT3+4M >u*FKjKb O+uEI&fjUp2M赇9ՠoi30ۭo߉}է W4)!0d̼q e*>+e|3;\-=8Uw M(]`oجVˌ赕K:LޑC]c/֮2A8ٜ -wiR* p$mCZ_d,*r5YֈkapmWQAǍk$,}ԉ*9r|_㥽iN 41'25s!_{_ѣ%9 $?I]d(xּϺN=jʵ$FPQ})"@gWWޤgB5CrשV~)^43v^3a73v*Z-/E:8>r֐h${~h ZbNd.:&nѯ)2рQ&Jc 'cɑ7nB?9{%rDӾpW*LzmmY+;K*gZvi逋-&[z>)) M[ nË́>:J^ fNEPzHnZq2IfÔo|, sDpˊ05ԑEOR{6 6ѴeeiuTcV)Ƿ{hUAh,|.~~Kjj&b}A.YfƜ$E2_oyNSV^KdZXR\JeRd8$'G@PçjN4C7:f{n8$F]Bu2$ _äñZ-yi h}g{K u,5Xf"ՆzT[` m" VJ fw:csKK¦\'VMاr@d+{ۯ _VV ;\_>mNmZT!5t,Nh_n:Nɱafl +Mh͸w cjݢfvw 5?Kݮ9&:5hT=vt韪LcI_evY̙3hڤRӶRUQ :ti* efy_>XĞ՗L}{pYḮ OD3**S圱~>%u֐˔x2P/y_,VX.WĪL|BL;? +UOp4B͛kc8n^'Y^wܹN/،ۘm϶mݵ]p42TS=>3`7!&^DŒ$4NK\ZѱOnbpHhMJUˈt:ΌٳĀ.dy5eR$%iaĴo7o\ g)d4\*ꑦ%Z>ԬeUIDx8PqVX)W^$ץv(Wݬׯ󛿪xx 0cԞ},G!HDK~_K(aLBIoM1cid_[eR2^jOΜtNzPs!56Rc~>0Ln.!xrhUYgd4J"ZݼMaL\f"3{6e1DM$ 857$>Akj&|n?ԦXo/SLțB>64;'"tPM$N,9>5G,C\d ~֍!+k? TCeT[#l~[)1ڍl_3QLTY1 \fԤƤVE鬋R9M4hUWENF<T^GѦ q}=eFLo$#IBy7l^u=n=i(ߜv-~XarnSq~w}XrfbIj_Hj&.,Z~I QMgyzmi3fmPm/%E"ptlƜ}4*GNh _Ծ}4JԍdjSƿm`dfʕ++Оg]$p9LъcyGiO6~ri8t$Su˗SxNfbSanEv[[!zk 39@c$!O}S >=j=m]DCB_G%pai&ٴ5=S$r5ʉ.i*|ݻ"FJaRENT<(mQZLK4\].p+YH7\jHwMw3[{Us( p_ͫQp6k{/8z""ވ&%گ>M<)EKA^Iu;erfBR9GI:9TΒR$nwR 2"~3%F:Nu1=nw¢%p5;UKJmv.\k1>еOr,bǺ[X!unKGtyP[3j6u{{WNZDDsa2E,E0*"{ 1ܡe4R*U?\|b w)gd 4چ=ͳ|Rd81;91~h9L{,S0|~~\uqg`:߷47)J~9fDdS;ZT𙶈Imi[)i͎ G{u1% G`vM6Ȇ;XsaЛuy) FLDєI^%%2ta´9L(JbDb1i&FOh&_9o* 1uZJ9L OM@Y8V5%V)@/I 'XV,I P^XRRؔ-ٯ g-r%-18;(Lkf"yX"1D[IU0@3ÆEE$}w-T3T9L}R*1 GM6k4l{YګUDw( >yrCe+k\5ܱx 7"5&er3(Sd$=;w[&59@L3ScPh&,m~3$ERK3HDڷRg &Ľe}R&%tIRNĮ.UF5ξ.ll~HL9ۓ nYcY6LEKLr9Ϟك4 ?$MLX&kE䤕ZA$3_uG0Lk”"z}%)ryPi-Aͦ.42/_:*ಢfЎ?Qsu9ujjm<}mjGK*N*+6Yջ473KO D;u9LҾH^i y^;>%Wh&,osLe6?0+TaYG `>}zCR0 ֠O:ȗ0C=t~,)6$e}W;Y 7u)Sױ> 9H1E夕-y%Cpäz_+:,mMWiN0J7zògup}+>ph9k[}A2h@=A8ùtQGQN*h&*LxrE]iy S*Ra$tu]Lzrtt1M%TyڠgS?eF_z3ICS;. ˍ5ԁ ő]K77Pe*Gzpst% 갾yi&zDJrlGPRSA ħLTz7-L;iꚺH$m B e ;h@ n gqms7Ȝor[ۚD qb".]s 4%ti(vZa\c9\qSQDӔvb'#qZ6䍝ڇR̲EEE7С>ƮF?ǁ^3#,THhjKD9/9S_3Q1 0T3QB*."g~5 ئ@p]L}$#H%r']HJA.]r>ٟ͌K/͌-*-}O-:TTR%JNo1%񵼩J#PpXgh!FS?Nbd*CОƤC5QR%-NMMNY=9lxzEFa҃q[ wًv`Md|mtoZ2Zzl0U%^ш.b9A=Ǐ>JD#etI\gUi!CEHRۍZZΈZ]F# őr *t!S꠶+:_Zm T zgɜ#ZrZ"'ZDW:;OX$)jK"'Q'0&̩^Th PDN̑%jRCcKިr^b.N1!W;ki=fx`Y S"Qk%*DB̈́$KEKRˢ#>B,%z~r, j+W/S }-DnI*>%q~[ODm?A3ژ!3:C ߴYcG dFRc[]bZqpotټh'u W3=ގSו4t*{K LoGΥF*DTiYgBzrT,*e}_xᅕ&79w.>.u VzELtD^+j?~|%Cs(ӲoB^Dj&t6a9-JKj&LIORSMd(9L}k ғ{)Xa,]SSMկv>u=٪T# C?~kȰv7N5 S&C`i3z:D%}db%}5f Okk&rz,6#2 C,œ*'~4ORh*G >urWŻc.-gZCeRz*^v m;Q1بb^SN$Ihϒ"{C|QǢ#EeS^TnX-f7R&&:].k#Ž-e 3 5 97 MEnS0zF&/ͭ;uGzHmoowmsŁZцFڢ&c.3m/Zk&8yQOev1:80Ghs;7X@(CA>S%;MKcp6GoEJ,1„caZ?DMr*2114ZfbJM Bt?t@-5ư\k`?pn'v`h0f4{0qhgFR$0' MTc-aiJGI\ug?YVda>g!h51- OM)w1SH9%;L y~ۚθvt5(A8z4Ap o#G{b M,zG)U51.sĀES&)r aVm"'5j>#|kYNнޒBK. XRZI)<ԧAEvIj"76#pvaZ 텢%敹9kjF׽hDxVDZ2TrQQO6=РjV^ofPX:i:fԞ\$]X{NS*/fy!sm͖KK(2 RFj;|{@{bQ>C=Q=?{A~_x} D#~oGNWdWFEWjP oG'8L}&i&jѬt}W@3pף x2`Čcg$qs"`ml~h8=[<ϛ(R4acǎCv"wGwNh$b0Vtd.wC@AH3/yz^k[`(("' s|VUXd4PVN3sTorŽY}&XmFJ!67m:\;?mgƱ3cDtaDA:~%}&0 Mr}SFXƢc"'ٚZ"J=F5Z5DT5]6 ΌnIMw37n zktr)~tɩ 3pATSzj73b H'伟EL?*/9Rּ$7ms)Tه>:QQ %TM6Xf+LWM3Ma$_tN&rF¢%-mG^b:_!Y ډ8B}>f'ܰPJЧLBOPrhQۄIG0ШyDLjҕ&RTxai=E,j H+A/E 3 Ҙz4C&ڦb`> Z 5Q>BLf7ӡZ+?] xnڤB7)akTi&&:h&dj{YzėV.ispݪL+.rCɓ2 Ǻ5q m]tJE#%:?>@T$ɇ(^g[/%}Bm 9P]7C4mﻰt Xah7v+1̅zC^Ϝ:5XƾlsoEV_4)Jz(T|>յJ}ȩL 7]YX]:[9Ah@+:!FK\J9} @ n!C.;Ht%M{FM*8 JXjL)5mLl E4)`'jcf x`˘]yX _:sfp&:pHڧ2z2r)ɢmW:Kuz}}+LԌ+tRULtUU}4pͷ9 ef[DK0u+?Oe =Rª]JWdo  k+cgѢC%~/s&yJ 'l l2X`RB0ە) :ԥ-6do[֖́ݢЫer~)ʭ0&k>U 6O`Q{FK#8H& #Iu%F)w~?M\z(5ufIcNf>w0 z  yl㝼Nt/o$BͩWC wedS{)'@AyEQab{N[Dk0Ih|i6R_\e}`(#GG֖.Qh^n~A1(7r=Gm9Sz}LĥVLՕ nE_c,Е*KJ%D@i$* gS#(^hG3LfkKSWjݕAYVT=nT0j3v%2`YR@З c!p%UXIt5ǖG$gMC'L \M;R Q]9:)2fcD"ϽVDKֳFΞcXaFId-A;rdcKݖN")mk%:(kYǘV)$BLUJneU,:b/&L omA_.XꂨB…nj]ŕ6Tf HI4Iۄbǩ& ;s\&]J 3@}*/]4jqsǴw{Ϫi%j5UǾy̘@0ݕsrTKUwŎS%}jLM"Q P (Ln.4^7kҕ(B&!+C5| [0xL똶H@uDQ<\1cII>;3 i&Bi%fl }&bm ٳYҊmyf{:},X$`l#7VQl}>рCw~H*h&RIn P@J_B{Νjn]3YMz5%Cm7j$- 6)m68&.rrtYXyzVU>%ja[Wd…sV5=xiuY针,,*R2Jڤ_M>fԄJzP@7ђ_vɭ]RKSP6hK4  +BIsRPHN$#P[rIAv>IZXIDK&'Z-64d7rp^/29!qSn rV{_S b6SFO$I_zoѽd.Tmg{iiѨRz?-#b 32'N9OJۿa7#9?_DJ֖kV,]VI냡J+WHl2QK:ڎ`I+ʎJʍPC?õ)61q@r9IB[D^ާZ3*TY(k,o4mJ-fM5O-0]d ^X+SSzve_>PcQ1!mY?R0~9(h3lkܪ9K[o&nV-]J$Lk8!Оy 6QSH? QX]dN;42t*9rsL:ӧsrP4r!ةe37HGK/ŢXR.;] mKXVM6LKn]|i)3[3 O}Te@Jլ#8w!x m0môpȑSz EG\* {B$ bjKrtM:> kW$; V2SNX9틾51wX fRB7P)7뜂èf]>Ut%2f1^8HF-(EFK3j}6Q;N_6:2S҄@ѣGAnObM흹97|^VQ 5ms}=e)t)7=͈%Uu.hKIM`zc6NiʤQ3]T$Bǡa@ur &#}D^=s(ԉ$FLfe%b"PČ|s+.M Lep7~qGQ1L,&J](B؝ܦQqz%>b ">K%aĤM(*_-iwqikE_LvIG CChtmMz'hK q:([R mk ZבJM^Qw]ΦL|c:`" }m_rb TF9H(%z %HȘ5:kx-Ub]˪(T{' HEMh^ `rp&>W54]hn9RÇR FZdhIgɗZk&,m3s"$9y6qY&yrC*pfNa#Vu?kx≍ȑ#燾wSKXE .z9MO?4Wv|m̩SSr0 ߼ZtI(hXI$Um^Kڤq~pbh@(nznSѮ1:S?4B%=?ߌK;L&ׇNXf:;-1+U/ڸɠɨT9M%^:MͣwEK*E,ZRaik/~V@{gvo*U&C&w KΏ 論]LЏ;p۪Ʈkla9HBۋiy\J (G{zڤ.cDM :\-P7@K7a-ԏo:d2%b˳a VF_DeN|rQUϫM 5O`QFFdq#rЏEK:J#DrL؍W/2~r 1ВZ2fzy32h2V2>2XQ_Qh^F3rFMΔo^i0UwhIRӊͦ8N81 uV_^+e}!cn '~eAA?? |>Z2؊(z"G4,ko65M X];Q̴V>o̻EM;VYbJ h%rV”Ɂ_&* W$< 4F*M'U%M1 DL 6䨄ANG r)VG׭W7|z.*#]r>et&\TIbJ j !(J'm FM|ǔ["%]_spҞWX.hcS&wS%?TI[? 0vwMm>~KhH&*1DQibDb\0R#6Xj?LXD59"SX̠JݣG+[4:}ggz/05iKcd d2I>Bϱ["m9#&"7eRtN`EЍ(@uncE92r:EJJ;~W[S%(bAQiK&oKeFEeD۾w- (W*1 @TȶC0tĬN_St:xܯ"J퉷4v1Sp"P$7wnCe/^~l\ qT&hpww1%%"?{#16S ]}Ѵ{߯I]?8p] pVv3UO i; n7ؿw(@MگI`'KگY[!G9j@]W7kb ^tm& ܵf,Q4Q bTX3 n^v=D8Ki8G#!MQ;TƯNo'pJTҥmvh ʻzCS D^UgrlU9ݱ^s_i$&\ǿs};2C`sLKv}1.7\Y?; |z`ԓ>Mbˢ9(#5y 094FrȫO{cfycǎD8EԳ=w^|)OmqsC} 3p6B8O&Q{?4O)D$ED]V$s^g,~nf`,6&r7f]h,E 5LQ 3pNr߶HK9I7GH[ΐ{ 4 ~Dl~ϦɰЩO9ZaHEӹ񿢜vCp1פ#hnƹrL(rl]n$={zլ_ =g> ?͆>m)ԭw\l}ȭtnv\9!#/ӣj 9 WJi>J؍=?SUђ_6fu.qQLP?x'[o*=8C1FG EV3~!??3D6cCdôtHH?;Mz˙Ԗި q7&=/ y>T N&g HFܭڣq.iƜE#p`7v^ h^o,}5*xg7E 6~J,M"osy}IJbN=$Kryd!2mAiIƨv TI9r>$EFG-NRNdYs  o&[rEGږz7FXT$)LKS.^@AropQ  $ŭyzX`TNKEWnɽJ_=zaWɜ5WM83 ^w]#ݶ$lS{}V'Onią:O 'ФKWI=yJn2 |^ 222JX angUXw5dV6O`NFMXãd_=esX4⠥TBJ =GЋ0%Ei0Z;v,;?}zC])!ivԍ.;z~#`~ˎ8y-ete6Syw=o3y9?֢9c})aNC@`#k΁nܷ˨˘:;w*k)/CZF}GoJ%j >Q{hcS}{q-q[2zFX5˗q<[h`-p[a(EO9sjhQZ׭_߶+ٵɈ m̰uX|;y)礘qKXŢ'9!"(u?>c儮u/jVT@0t&Z&>ug'LDJn#''NltIjDJ$ZKC*+EfÎG~E'<"#5XB{u_!E2nro/s%1 7=OzMw5(/ZuZaݣG/R777 y'W WL.\h}L?O L @ m WBN3' 3goX@_[}yf#+ Ђ7|6:ɽqdZ>=ů}o2ĝy! hA_ԄN3=9Ι{g;~p!f9הv3ֿ]u':o yfo{{;k1Z3oCs~iQkCA%: -u +?wĉY;u`[XFo5^jչCSⷴ pT {@t4%O7Iii{ך7fCWG {Ewf+ Jl7혱V7mz߄mE 8:Wbhy7]g?7}fxꩧתH>͟׃ۻך :9d# /g_x[sahK $1S׼'!QswMC+/=~oϜ/| 3J">IDDnYk~cqK`}p'؈99r P4@iX4`cQ!?37@"Wi@ky7;裏&RрP H7򐠝Ct/}K8]<#}n_ hS /zT9#Ύ aJ/_U Kn%;AA*%h *Fږ:6 chm! 00\DLx ~z@x5=y~ri D@Eyр52'01E@cW#~B# ~}rRCDtR__!u#?!D6<8StiQqkj^NfO`AB?0\>eJ%nKFB%c=w &SX"?yM#?~|5V bG`15{DIENDB`PK)[QazazPK Y[Metadata/plate_1_small.pngPNG  IHDR>a'2IDATx] TU. I>>̫hy[j5_ԫUɯffFfdd "`0foߗ9b579⡇4L34L34L34L34L34L34L34L34L34L34L34L34L34~ؿYfOHEn4_7fF `ʕVkgQ#1d:״iѓvӥDӴپ'Htݢ9sslР[Z]~3]9MƍYOl6px 84k&"kR1lNa}VZ]RRR3.͛{cf fY&Mb#ڊeֶmʰ,?E|Y~]\Ysر6KK jݺ5222e ƍ- @xyO;?_OW;;f*w̾ftV رXdba=7w-`P.7)W) 3baf4z !SLx E/P FdTjr3O aT@.CڗT(vVZ wHE̯!h{{o 'ϙ3G2P~&$@#K23 ##.~0k:O0X*Y*Jx<@CQgjc8/O,|:44 iYX ꝍ;u] #ѳ:eK+,;vyooKZ  j߾b)%𒢕3^6ihNL!JMM2apthU$~9 dv({n&!25nܸ-$bLԋѿ/ 7`$ܘ1CLn<;QL>b ~0c,.A_#?9xpE~o- YCϦO>-}B-:X1ݯYŠSǺ{nDK3KK؈RuPSrbm׏U'I`+&=lpVMOweKA[ {O ֭: '<;Ŵ-.ːz#O*;aW3:z LF *g7awaы63^~Yd!a!m X$RUi{7aH:tPGbyNu@A/f0} 5y?w$U!PbG(ry7@/N66>&>СCWj0y5GxNpSj!&]~ 9mLX1APp,v2 ? ;m?^,Y.obSUe#?P a p`W fHpBO⢗b0ny$X|ID@+rAv"z 7:uH\?2chHti'`D|NRoN6kXzed "[uQE)Eyypb_pRor!@e8mq*Q"+9V 'v*)L[፺>PR/CQ :;( CJ(.2+ GF՝!cUh47/5H 72D\pPm*0ٱ^um4&w|YPPBKe(r*{z}2\la#N[P*0 *!*!]dpTIîUdUcYգG >0J4nzLF {XJ²phoi2̓pxwo(aD/X:c~cƌYwϞb2 >aQ<@YOY@56Lk< Jv| n*+Ao߲%CA@l6Qf.w<&SL$vzF7,"[AVSiqq/c8GDiX'O s.ss7~~$~\>=ܚc{מ-8b,!?.~NS.2QnO?3M;\uK7x)X)q½fMM6.x3XA.A} ҩez֭.*H#U>T87RbAٳg=!T`r! _!4NDfSpd2,+269$}լtRҮ$j8"^ч29ŨÆ%%pVKN`ʄV"VqFk%Ip!s`\4VxP׫!>pcQ#Ϻk޼ ;KRC= h=K/tBYU}^?l}} ӈl&!c w!,|kOl,1W/EX/jdmn+͟0=_ˉ(/\zx`: qL9,s!78JxNi4nJ 'Tm{MLj^?^JtVr60<8hj0۶O~0eUr7cD, /F]a+3jOUnޤ:888G-,4Z49Cȯ뗫pT 4Ei}*u}&,l*Ei9fXRZҜH^+o*<]2R H#[Ƚii "*y&K8o7]"$y űrUęL+;4m8pmz=l^<N 7w~KŨi}4 m2z1[|Qt@s2J<6u-K҃]Z.2S,B6'|pLاݵ&Mx(HJe,lh\B&K~'⹻v~ɀ*狎83-PǬYfyLzbء&j+0GON|cCMHuHȉC7| KL1&M{tMBvo` ;~cz)V9d4C}"/y'O1F*"ɪJ>SQfRH~ P&Ǧ4YM'ȚX@sTy^[:=7V~ps6>7'\fA!Tcoͧ$0 AI_~eԞ͝][<ﳳ˰gҡ}xd$`f·348 `@NqcԶG(`"Řa &~~YY`/Ja4xP|>4TM v)3՘t}C" Ԫ}Z0):~=)x{L&SxCc!ֿꑟ$LA"qu؉oG}cjLP׾J P@08sXx琞={NbyĶRTj6WwA2wݯ_U)^2O7Rw,❚/<*v ?i2UON fLk<:D` qzG'\j۶\206YkKH@v gqƭCYY2iau.\3 p$&K1.́UH4 eLh `څ(y&ʣ =`;&J;9T Lkƺ[#0Qce# J27Y.%eA d*wؑcu]_6N)lB\>C5 XC=ڴ [9*vߟ!\Ni:Q?#QEVUs9 `u29}򫯾Z&| ~ׂ `R4^GK^<&ala \e w_&BO=AѬ>OEF)M%Cz=u{E uʔ) |9Ut_VeO&L}]UrH`߃^#bӦvpb[yv_ֻ]\@LLfw7cc@is}`RRtL%`Pil _)Fy%yLw:"tJGBf3gO9SK9Z'dEt)+s+ڵj<6jb A={uƂg#|,MHH\_"C^'l 1 ,F<@*eOD)qo{eejr&_L^- &߮3#LӻwU2CzHPz@No7> {x(zrr~EEąL\# k#{xT{y:϶%Ku5}w0իu{ riΝP3g-,kL/L.:v{<ݺc<Me`?StrCZ>~b9p,=@) Lo]5;hjL^U30kw걷Pω^ٶmI [%fOR :Nu\?-[ cf]_ק&wbqu|u*+@F^BFPk d27y6qYIxd&ڦ:n/2&W*ta+C&K&TB?%W[JNq6#0M0YAXl"_kHa!SXsS)IzM<z=UnݺU~w]_u¶TEW JP!(xk$CuP ɐ5(ܼ3& ]-V+>[&@%OiB N^DfժU84ӡ~j6 3쟚ȑ#Z&i"X Go='lBh#QڬY`Lb^GHҿu0a)`-f2g9zB5N*񹁧/_Ɗ[v* kۉ' AjHTmҥ+A7m$`}\?tc$B.Z,T(By앻n!6o^\&!J:y: dK6e#J. WL~5Â=~9Iu$ovTT64\sXѱg ,H=35xF3f8RWoꂕ۵k#xާ 6ncfOGD𮿪ɨtj߇gX!Jo+\lْwR0z*h4&f;Wm61_)E:u2 urǤ$ `5$[yaSC"$ҎSΝF[6ը,ofu=y~TȀcoG}@db`8y/0;k]y\ݽ[:;>e A|Mus,>@ UdS]^h<f,)Ce@+ؔl^h<аcǎҍZl3P_?I ~ ۷"0b _͛7޽y˓_^ Px<ݠ_^(Rpq4|xݧ_ D5@ ǚG ߿_ͧ$@ ;@ɸ!h#~3[&U ;wg8 G?'APK@)$0e,*]d`OHDƅΊCrz%!y އվ5PN(&Z@ $8->@!Sՠ~'ńΊ*z%^yAV~}㖱 Kܻϋ{ +=-NPwM %R2IIJ&M {^qջl8 G"4?KJedoΊD= D"I_E1Z➺-`$/?A]ˀ-F@~6ZkϐS͓Y*w( b附@&w 3VUD-k ^L'f?{{^"QR+]55KzsO /{CmkzjZHb`_CM 4Бw*WY߄^17d CkKW~kR?^#y+U({r2 BVt^WXN8oh]V!*W* H+S$*S>ӵ>8DeZBj"cɖ"9qDYZ\^"P +M N5'`&pW| hP";"?k 7A߄`w3T$ ]5[VlЉ^|&MD/W1-&!vD$P$mGM`D%r#0WV# h'Î DƶJMN61;90Z]Z̅ @d2wOdn$*rvegLKլvnIfoT㤧hr y&dsêl 0?ȶ'Ms3H!V~#B÷V_# *# "'{Hc>F<àssAݞz&oDfd {9n$$( 2Ԁw gl2'fEA .|̧+jO܏>l\^2&ʞJd\j[(&$Ep]JmqYGܖ&JQa\f 0MH J;َj@ 0+"&J RdHRLV Y.%vA*JأA̖WoHrrwN$&4y PS:Y.~㏟ă2(p"[+jD8dlُ}}dpd #Jղ {_֪1k߸h6͑@+APޓ\ <\$ƻ# [&&=$8zu"$T]W;'IɤkD"o9@e]2:fV=5x1ro&QSP6 h7.5 ]al56io%& r}{g˿J\69d 0;a7QgP>s/ޭ VؙioJW{I@+# *IUIzigиhDԧyEц?vHbוYF٤Ǐ b,wQ,@\`nrČy,HGdjoe'&.U*LgRz'dE p%d)me,CĢAգ^iEV'ؑ필@U=zE&RGM'䞑S4z2UHؑ`5}pՔַqvM_YiB#5ZI2 pj.4G; w FPgH` <{ZpJ#/?J( #ߎ-'VUZd)>;%I>KIֳjEދM.ȗ:Z]D&z4R?A@͓IgO{b" ?Ҽ'k^B;URB,C'€yr̭N=tLT#ْ$^^ki;hc;yAwe67JMF#JɻIOB-I901O }ZOLB]'\j;udN`2l>Ekck HTdvܞ J"dlaݻMO zfZu%@$^r_{AG{b?"`תщ+AVZ""ZC*fxt D7$R)!55+Rmt$RaN똕 X;K=%iIrw 03)G=i6UH󞞔#0.@kxI" CH1I#бR>k$2*yUrT3zGmٻ'6'GB.lx.@GZ 2qjnk*Wa`qDϭl䦲L5NwOԞUHzHT>| `8{o GB a'ʄ,Wk8/`ϴެzjRi9бRޮ '{d(,'p -<ɸhd;… b$MkbeW=J:tGPl$}onn>$X죯^7@Tf-yI2`gWb~;Jqq} ؽܗN Rۗ'6¤D7`q^GBa$yIGS%Y'6Z ^zzf#&6$N |qel<$O$HKVbF)_}qrp77HV|2hh=/U/Mg }M.Ȋ.+$g}<#\$/I\%9K^ۇ].y jIRX>$, +kkH^nʺkB]WM*&NP 6@\2y| mT*IZY[#y+'[ '#*ɣqlI VؠV1y}tW)k2I3'%(hIbAA+UD-̓ `E9~ PNzR3>HTRnլw($r]>9j $_"?a$ lA;-׎3;_ï5tc.w33VeܖVIňdUՊq~d,Kwe֮Slex,bw6}rX3)(GS(U[?4T'>IŤ'UMh5 'EY;^;&gU6q9b]DLwK;&}?}y-Uba WHM$+'< `Ҥڶ =0YarIRV %Bz qYr{vj@ dVٞբ^ O[U.l架O&A5U4ӎSn3\Zo}Sen7ݪ$+;'" E7 KTy _6#dN#1*R- =S*7.,bgvNʊw2lnxT{7rPJֲl++qyin ZYi 7t'l%DKz*zX_IjH bloR_:xWQHwORZQ3_h(!mIw^̚&mG?;,.dq2/YBAQSU O F"H@jMH^XW(ݧ pT]7圉*7MeM3ejAP>SUotNZ{ˀ"Q h2k֫'2JK5)e2z^e'gwew"Xg;b IHKW^P3/k{]d4(̻Vob͘pWc6y;e\]>*=$KyHpFK5Nf$}?.p j^V=Z0M""+*m+^7M2*Lf(1Z6 \N R+YնLz pL D=5E@;we XUu \&!6j5GQ++]v7b_IJ kVITt rR:[sg5 {۝AϪMU"ItM\nA+e|%&<Öճ"25t*U#ZNINNʠJ حsu$#W3&hel_\&QM2Nj;RTNlbQ©ByrF==$@pWYW={ϜȄ3"kH hM)GJ䷑ - oK翥!3OVXe'G<:C̥J.7?|j'J'&IQդjΪϼzN# @k2tnRIOQfkCy2ʳ* \ == nGgkjgiYդ5Qy #W&'MeGN{&ZcALfL"y^;N&I}5OgD.N&uǛW#o%mVڧ v "GoRFw]5ۮ[7#0'*ڵd:j|d}oxգN@|o޼ bj'}no۸&:)k`jA}6["MyΝ4'իNd,SFWe/W䤯iIUNY^@|', d#.V 4Vr33}:nbyߛ3,dR8! Oh KUM^v`jg7'߭JIIS+U7.U +&hI\UA9!AxA_Ia:yҗj@JMeilɆ v-$Kʕɾ_-ϡQi:s D|5V5(dGPgUW$LD$ g,@`%F&` 6Z `Z6߄8'("qIn߾g.-UjƮM*$vZPfvΌ$H XŶI.$$_~b2G;ET/&%-d)XH|14=Dlf3RZ:iI6 AXMll;M0LwI{jW4x8vUu))U'HI>+$PTh{(6)JR&lWK'Ek"UD/~RjkA=æ6ݘ&YZobO׉pιzUVYq/mE']5) ]޸ ÎDưA"*`Y JDD%y䙨}WTѝʱWbs2еX )2nS2{ҊVgj"~n͠\_?TRoBª$]5~3TL ]VMt]kzwQCuqKJoʟg%*/DJtಗ1-[Հt;a.-3!JLVM*F]/?IwF$qZ]^J%SLgMd2 $?kLn$% z'8g^&/{Cy%ϯGVlGPL9B}Rj: ?Sk,͓o2|SI,3NzO\e^=#+ꋕUξDɤҌ/Q3^9[!0g4i uR]rZ͔gl.&wP$MZ'V{[\ U}t`b$3TdUpy*ĔmywZ 塰7$u x@ swk,I///L3wJ^~|\TL"&(x ,WwNԥc퓘 945~ +Hy֙y'ӣp.MsE{sD&qa?N2yQ bXyǨ{{d*g%3bc鈁ry7`)Xx#Iy.;`{rfj*Wźp3|vW 2;N=3=夸I@Ue`3Q=zorI]|z͔`R|&.{D:Y:igj:p 5N aG'laG 2j Nl;2v|4i&DH,DJGy̖2U^dY:UOtJTVӑHɨa4N*Ԣ B$IDhG j1\QhJٽu5'@׃K&D|ʉKGYPWLRKMbd]G'ɣ)xӞo<MvأF9T\dJɓ7N+IEVmv2?Rқcp]gPSbR Q NOxۭtM>t*Am{Z9iV*:;gWp]2&K#crJ:ͦl(`qrڸg뜎i2DD߄=kH$LF|eR-dmD3RÎoU[Ll#&8]![.L4=(r \+r}Vy'LmLbvQܮs:J' ?}{=e la6nNMX>@#?P%uF#Q]'eU)oDj=de8kDz௜ٔ ^0] :o=|[huֶ{_VԜ2G*MPjz5@^5HTv0dw]&sudҠkuDltV$MB|3WM"j@7k~r8k .*7-T/ +!YA)jɎUdM~&&JG$L1r ͑ Rjgq]c^^FWT9kGV&QIݒIνm|[ADdt&[vSz BJ2^ꉦΖZB<`I_$4Z?HI"&ֿs}̘mJ JlQ:"n<[#t8%P$ч"J{qRGchvyvfjo+=PP&q{TpF' f0L^pþ /`LBJ&Y rfgɎZ0>[#+Ye,twe1}n..p ^q/sC?#o;Ҧ6+v=e4ax/LU]9$ct%yߖH&}I+<oI X z¤7n޸#-@ d C6<#^61U#)A6*gy&j][*OT4Y0KF&H6eo ٻL6'G-)H^^)lM@u,3D:N7j5Y6.Ǩwz Agf=|bHMۖ=' nzCMϾW۝0m~TUPP?@'%&0“/C<1cx{{_ϗ/Z+GMP1t.xFK#hϿzu닐WKL$)ZZpxJo='e43M["$6QZ5N(w!ńL"syZV.ϰ]Ji3&z&0(iɁN]}Z.83\,7Y۲IM= FJ ;'`:Z49Kj,-h&7Y'nP πQf{&&?ڕEG$2JtCLxmLZGC*d{6Y;d\nm|TwN(i+kdQȏvy%TX]^ԯx&tk%zl@H”P^-9UQ3LBlOCN%G u;u茵\=m&K)c vGRfJl{ }y2j:ɴMw |-dKƶ^V&I]L1O&s3>5gz5,0]d9=撞χ|?Sh3-k, 7|Rdd1 Qv2~g )\陇''iZIyխʟ dv>̚0=EYVK{?'UGMK2㟿d'2&&=U|Ix4M#yնuUK6*v6WMjRF9:4,<Uv+M[r￞tvSR-L<8 B .֮V$u<\U^A;;fdiut@:廯(ߒԸVY0ȃ=3iv4lQF)Q;䙰G/?ldy&=Llڑ3\d$]L߾?0ꗰ> -c馏~z0FSL5ȫxd/HCZRk\ AhRY]]eJ0P2Y~b2UDNHƙz*癪I$K0*v1҅tTJLP/#[^ E"o%:&diW4N><=;J$޸F~GaS%DVf&+jU:VG'p؞Wh}+lHzYH2ajm'z(W(iDFD v3ej5a%jQkltɦ.C&B+iBIٽLk4TNJ#+hl{AmK2cy*NegJI)Om쿈%dSk]oFO26peH=,[e@Xgo[tKZfZ278HPLh=<L ]n[ v1}`9)`"9Z^w Phrp2lzI Wm")'j,;HKPk# =.d=%NLx&S;!#]WW} n;WOi0:]L5 `;0-+|w+͒զJݭ)oG$$fӿl4y4HLx+f$*QD/a](aNWK*ޘ~Ԑ;]+xϭ\_*'b rSvʊ$ ՚Z-`$ThKl4dlsڑ4DvH:c-D+\jwZli}f2;vzSDlɥZ<= T.ML<Ťjke6LD~ItLLTh:2W$Jz;rvExG-i0%7d y&}Kft'լE9!uҶm(yxm*&)͛k8jɄg&*[enQἄJG`#$)ig?lEH7o|L V:\Fen)-++M*@f0ܲ<VgT"&Ӯjj2{׉fD$:$b}BS0z6~ R'=j#9Ht CuZAPDTv<31$sXTMz@͌%K(3M-KZ6+>JSCjкwe3Nt#9enoe%*aRb3Q\H-6%aj)fM @'G3(k>LoSAW d!ځ'n։n ~ :g§Zπ0/J~VcЖӑ.nXITvWu_q׽?POWHj ѯ~QCiX "q֒ϮgRV%{&,QiKWM @'Gv4r~Œ`)hЗIEhVC6-6:gW5sdD)cY+ʒOgŔ|uCk_JYJK'wu$H ߓkMPd *I̅D`a։f\M<龒gP@/(>xTd) ON3].hN/O_nWɾL(#O]Ik=#{h-7gՀi'UJ&pzz(ZBPPj~=KL0Wb /,>bQ> $ ^5Ww?Q, u#\J&^>-դe*ygw sUR 0aD^+U3aSZ39kgvϊLdݓ~VYGj#㌦))M2UgJ[M_|UkP᭰t–٢r^LV/JCZKj Pj=wqn;fK=EdTp&4?S x&( $Ͷcz jWٔ#xt2񒀐+6ak薿,A1dݓ{59KoJ2V#%@j,ߥMq+4;v-3ӎgžp`JÛ[aEMm>3a[+cTc}/)d+H"eb?(AUy }&أU Ii6M#^f llZqa)٠F./߈aRͷPDKgKm٠Ca%aI٤Ldl7Lz"+[̇]k0 Nˑ@`ˡەd=@Nf/x0CD-j0g DEUwtWEDfzg>ZՎe  nOOlzLj;M5z&s%$3K҉F͏U?Ab{x5Q Z%"U+ZTl¶$iҴ=4UvܛTBnv?t-˽-{WBvA0Z.yR2 ed\b L4$ rˉbi4?yf29\3+'@Y.UЗ_ޗ"PGK( P2^S%x T20YŪ'#>es7.2HTr4']pRVEY;v5؝ᛰ*}}f-uR~^Z$[*JOy2YI!j@ ٕR0ۡT^=Or@fcexŤ&e^=]9¤I*sO}4SJƱ_}u vc&ûLW%LBg 9zTq5g]Q,Vl"LciKAi}&KSR1̜IFXۤKwLP_&Sn֨io|dҼA5 *k $I4<=LT^ )e?ln71XB&wHT;naqO5L5a{%V|f=k &~\[&X봴@VzyqJ +:Qe%yS% ?3Ge{ س (y}l^J"rNWB^{fdVh%M枚$MVMUM9M4N#<7BW~rUݾ6I5#0 ޓDEN.4׳%Ť,RJJM={R5RZmy=RW5Yt ޫeO$*;O}bB}#rI4:춲6SyVSUAFԕrC6gu*Km3Zq]-V;r$JG$YKvPSne ak5i @Y+*ۉNZ.Ur`hQSDpt#e$"GǡΪ:iK_ *@U5%CtuĖ rYn+-eJW&, ^kI Ld,N՛3}&Qў 6 ׭][6gA`>4<0g'yJ КoW`4H/I<\բ5q+{j"mv䲖@t&3~mPXm.5U2fLج9K n#[v͝k4rIKw<ärI/(3jL-$/pijsmmYus-HP)&%&QIeiUZR?ĕD8j$) C] ޾UH皬=zbR.vLDI;m3,DeG Sj+6S?@no(֡ ٱX&ߢ wStKw%"QM<qJ'7h7Sغ R/ԥwKɰPSjK*6yGPH?xoY_oV Z^Kh9ezЛ!5F^4Y% @ ʠdo!LZaY6~v6lnJHU|'hm|sqVANZb!ʹo3>xJҴ) /إh}o߾M&~I0[~/$fg'Sh%$lUTYftj.@ʄ x QPdǞv?ی^IvfNެp(ZM^7 h+Q- #j@f$IV$W^1{V@ٱ2vlfJ/i |:T$^v fpF'g]ʹb߽-AJ3 Lb#~_6dNׄ@.Y %=}Td^. (;y}߿jת;${AA-C-yH>#"bK/DL z&JjO4$ꪷur[ j*Ϩ%Z&,Z&)K%g'uib&SQĻE 2y ɆD,4 $`H2ɽ#;V2.Cl3*H4N9Ԡ>6Y[IVʒɣ^n$A_j4T59)%3l* 2GΔK+]{Jq/G]^byh"uRze/Fͣ$zAo|38eP ul#MVz*=Nz(J%N_[]&dnb`>OoItpaD^^g ?T]-.w3I,mMLjLd;+F F"~t9~Y)\ h'?ܳ-h}ɥ{5Mv\"&#.aDF2qOk#HILlIl^&Q.(wW: wVU915iHP'6R7K%so`&p2ObҠ*@+W)泣%@::dTj+RMf JNTe=ivAxHU);*;5z{Yݗ AE~HHNcd:x @q#U8 X wTٲ$e"!S:pF+}yO-)Iɽ4uIҕqjL[ Z6ܥ BHм_T2z!\Qz؇%Nf\2rI>̃ Bj[8x56+>u!>z935g 4k$*#c' 8:Mjg;]6(}W 88?%4A#ψ2:D[ ԩէݗD}zpk"*C%QgMgH-EԀ6'\ךhָ1wD%fEAON9f{%DVj@Dg}T gL+؝ UA{">Bƽzً>jN:w$UtK%Q8S!Kj#=H^^p«^/ͶU;OVԀV&#%s# ^ Rߍ% OVTz& <.mc6! Kw[ p>2\0zHM$*xgּTBxaիn@P[ۖɖpIWmHtHWvm߅T]{%'3VRA;Xw q)Pg,%'xW$lb?P*_>"lWdc͛4G̅c9M*&Jt.Np[ %KvԀ,U^G pf I| ߧ yѾxTX"ӏV77K a)$$ C)WJ z\_Xh8 r.AW\b5苩O^Ƈ7`U ^H8ThU)g&~r` L*|䰣 hЯJ^$E X9ҤLqO{@E &gFӞ'P΋ -}[0ƿ=gI@ |T[cq$]t *@ Cv+@\MGP$*h #o%Oiޓ>sOxUDLRfG^^p_9=0K(.9 Z$Ы-Kk[LzgL(AZYG;J߿$`7$x%g c&q ?7r aET5X3`\Rx_nwIij@B͛1$,h`ұ? &Z .% L`#s ɀ\!C*Z|o79qP.)ȡCripA^ D"r ? x&ݗP)3 [ő$^'IENDB`PKXFVFVPK Y[Metadata/top_1.pngPNG  IHDRxIDATx˳mUlHbC$LI2AR+# $ K&*6a4cW2ԗTʝ{%p ΕD IC^ɤ^ }ayc1ǘsǨ%}{1}AЇ>y\~ 7@DDDDDaw}_MhEO}m+_¹\ GT׀=% o=} #_9xOw~Ai:X4@<^ku9?ܢ>Gs!8H;LhD}x$~)'ꟳ )x> ]#""""/U3;ߥ%q$7]_[4{o,$A"""" V|Gn޼yxոHߣTYV8H78i}:`g};&"薨oͨ t i oNJKE@ۿ  """ SNj;V=f!5 j@p  .k7kȀU΀ `DDDDPp6`/Я8Uw*@Spt i;L}Vmh/.(}WJ0C T]# """ .4cY O7]@^Їvkm @M{?@DDDE],]9 <1h/P8tS*ꟳ" GFh;5 VV.rz<*| ЩMD[?@DDDFH0Sr# 5ȩ.&o7@DDDDDw @G꿛?tKk]' """8QEj*0R] Q DDDllcw3 ̰o?Կ `@KP`ʷtK'䟜g$HGW:bQ.We8 ΀`]nz9"=ޅԿ `qY?@DDĦ7}k''a|d<96 tտHտ 0@Ttk<[7i5!\o|r  %멧2["#lQ.G5_Z=}Y7A^:ua ^x k[)m/wMpPbPS AAA<*?g}vq{gKu ,У{\'/"߻Z.@EqCK?|TwYcog٧!`p8VA"""6i`s(i@W% s >s.AkۣIZ*NM26'_'A"""6i< _̂}n9 ޓ'{@oYMZ%?j T'>o-J Y _UA"""6n35@]Xv?O~АuOgXTJ߭ %`&b>\P; ݃*w#߄Hտ 0@k[Xy8] U\NoB5 gDDD5(\#\M8*<ϭ?0hQ.&ڮ o\_YA"""4Hw{ ŤuDDl,g[{;̺/u[U ` >WػȤ |}}dtԿ `@Q]%L < t CK]T[JgcQ; D[ܻf@*[ʕ+տ  Pz ࣽJA^:y_J,KL90h+r: P*["8'ɏK0W5&",Q._v꿖? O ]wX'N}U)?s{{&gO[m'/ɳ.e d{#GD* $=.lŘ5P" Ts]$݈F"NRO~ ?_;'wv@F[J KެMR7_t<bR`"}j72+y{wI`@裺{[~?U9#}A?5._N ]d`Ϯ_X9۟ Ih _[߯oE?g!Pe8e L rX#n=꿧;`EDF"WoNv*~v޿%@upU.@E㶲qƍ P9n G@S[Pj UV.,LV02%ϋ `5.SG_Dƣ swU+`_Կ/_J^2uԿ@;_EJ/'>q.Pf*_3RK\PA `UCs]VO_pտPۂ7t*?ڇ>?""ߨ;1g$,/u֓="XO9.@>?dGDA疗 `/Y+-.R<Ź~7<:TxG, f?|˸f~oOO`zoԿ `E@_Z=.@<% Q\F*ǽ~]b[k.@EA$b`?;ɭD!OWՓ__8D`W@Ej@-v|X9w Vx P8.\#7q(U/}sȀ=-] Z0ߥΓ׸N?w$`.@F_QlyDD ʱg:H}hڗe7r` ~}uԿ`* q>{LdPu_u`sS{ooB}#y{A=HJ3 J[ 0`־K +\K/q*ǽIlp=, coıOKз K_gE8`5? SCsΪ?QA3ݗQ\rܗ.Y??bA=w '>>{{OO[ko?y#ٯ!h9Nt2bRΆLe?hs$`ɠ ~\/ QjS?uF=!x=G8.A? " ݵj+K\J? xHX?"69//e`g6.t C{`G=s?շr?.@{+ T5rܫ00 QmPO{/ko|-]gܓ P;oʻ@P%p\ˬnp% pQM~#6n APPo?G%bo8+"@13N>s ǽ9Qɓ3  ]?X.~!,5S.Pμ 4m \~?hSk ? /WaP92.b`ꟺ\h/+90"@_tU Uw@GCv'\uԔ3-@V /" e^@pnm[|~;|5]X5)6~;n oEfžѻ%@UX]e8k5O'_җNcuZi @Nv.hE<$v䲽4G Tl/YCOO@?~\I:Oj_i}f@N#YGg_MZ52Gs]Z[f;g9/0Z_Yq~G}U)7G9OHD`E"/pDQWmGE p@j/{Vw4m&c֔qP"gNp^~x'kqSE ?DqAӿ _͂~iN|+M U9gb zУ{y x4-N<$ROk/_>q~駟>{O@6! %Ї?[7KtUZ/VYV_ yωtO{4[U9 kyѿ }s<&+W΀?]@ f&{or}ܗŭ @_=_"0'%VɃ"@ .gW.dP@=Y_0H`L;yτaU) Z3ڧקD`S)0Lȩ'+r=Dh!Tn{LHs#%3w`No^R&?/S{&qesaҞ~ /0.JAk"\e# 9S#v #-6@/ @ @m4?1g @KmH<%fz@IH'Xr^xh"`/)GtO҅ૻ3={2|?4Mv*=vVP{w0e:=K]`9-}L~Y:q @G~*%,n{q?f4'?Һo;w}v@  ׾Mp\Կ$Ԕ Y\ jU- x5YíwK ,ߠ^fS0i k׆~qH5U^+{G:S޿%n=PP15hx $h\^?UR@j+K\_eMzԸ Kʁ=R#ͷ~臲 ,rRpĠA8q{g;YDHS@ 5UmKԿWX N] P"@Q+`S9/-I^QuyGćRh5H{"O\"hs]:ʊیi# b??͌RsHG"O>SAߏ7H o7AyQ0S? @1T~ 9.@M?_`{9U{VGȨCGG-?]3"x)p;\5@7I.pGD79qj$z~ p[{ U`_ .@F%?\/#񾐀 Aoն}yH5T"E Ω.o4tzV^(9I ߂@\s?SSѷȝ?5aO!U29kO]o2ER>0P!E.o`31hR?w\A0J.]"oBú~&pY0CS3|-kbd@MR?0P&Կ NO!-R?+ Zp Fe`\q;H אDMJ*0RK&Կ /3h[.xI{WQ OaY90YGj` n !t"%e 9F濨OOE %{<^XGW\v4@ĭ@`sf?|s# h *_xol#ici[hpPopտ p! @CT@ `~j?jH@L[&AO(iu~RR;1h/PoH p.X _c@p_\Gg\ ﭐ5  xvA;+~O~9uo]-]cQh;\ t_D K'w>zΑy9O\^oQ'%O[Zgucďr8ZTBꟳ&BbmvZwR൳Ԡ?Ux `It_MC!=ߦ%i#[*ѭOnyR5ApP#-RЧ?g9{S _CL kOD[30R@6g!+/`~N[7{8;`|~X@"!\5`oBpP#57_? OwΎPH < 2gܠJ~{f{ ~I9Er=XK3(@ggEPt% =.|m4\w$I9[kuNԿ4 6zQtgn ,b$%$:V:Z#OmWև-׏n{Mr`MZ@pR"5*]w7L ,,6 /r*.qMK9\Ч_~_Y<\O2&nP# Y{pT!%Nտps+\‡~O?U=`Vof\Gsn}\d;:g8֠O%T-@9j]8TS0Oտpo_ P">U$@RQ9q3v$7.Ӝ!uós=Om/!Pvk%.H 9/p߫C{!;^Mkɻ>_w6*=q+ЩE s PP+s3@?\)c*Al{ %𾥄@E8`Wv om/9(w{E}o_12- ; T▹?-[ L`A~.00_PH|QRF \CKK$'k*^.(?3wO}S?HS TȨՒ4D`q*mR`#Gs@ A3u rb EտHտ 0@Ov{栟BC8C[SO]$?1SO?z#UCy]HW[_Ej~._.`]Pzk p.T, P Pid] Tb@@.;5{@2j)y -1H8(?W>aSptuH3% G`F{Sn[Q&@\'ora8JR*òv K} od]ax{` HG\.nVk?w?Fi$>W_]Gg\ _# @X_cO 2YEZZ|z*]5 |i+@ =ta{$Ͼ/O]q۾m:;W$^l#tFw"8xM+m!&w94@-[?S:N{"<m .TԿ(vckuGOR&tw.-\?Sa>;}#0uhl_Eg-r7BJ_&pNkl.& pNmO`?S@Kg?. 8p p.W&6*PRJ`FwK`@]4@ϲF/ww )[@k[wZGy;u*`Y? X9ߴy/'͌.o* Q.7S h\p~ @8w96z7;q zKZi{}$HB=~V"bRB`71Ш `PT0h{KW_ѻLq^s Qw$03gJ?7:I@_?Ք!"\tfhjTqH ^zUѬ PQ'薀 鴷ܰxr=y  @wfnWW3]Y_L]ػNqί\d]OY_JhBx+ PQh{p@?{ N`|Xp$on"^/-o޼)J~\dMRb0bd` ;)__C CϬWL/% Pw,V(j=N6(iuSp/ݨX\KPϸF6 0K_Xt_@;h;\P)*ͩ v>@C߱Z PPOd~ .m.֠_sRg6WԿ28"XQs[-\kI8%*.@A{ A(F )] pX\Y\',PS3x)qf68tε&\pBO XR0|갠(}Yvk @=hG|:U7wݵwYRBCe$-?KTlU\Mh0s:XtzHE XIs Q+'3+|}o.Aܘ&\`gҶT>t Υ.P/ÈZFg\ .BzJ j—Gঅ^?[swsŨ%H6 W34@}n1ڧ?v P]K7GF~Hg 4˿Զ>Wv'*HԿ.@ͱ{_ e/u%F?C׀oꟂ{-W7;}@$ &lSŻ7%R/tf%H5wNyK+?O>:5@K"߻$O=D`]=>U#]NMjT>}ie\ܪ`Dӝ@ P*w˭VJ ;e?F裏_ׇw[KT?-Ww< 1?\tTюr:v+~nOhxFg7nXGv$ /5l4e_Ts0@Jjv^sS#\E]2m]d``"6E;`E/OL\W.@Cc۫ xTgydC?&?8zÂ9s`: F޶]&ߥP Lo= `=K]q1Ls2թΚRi x֙<{ɭ}ro~*PS.WVm$E]U_ڱDk׮5.@Ccw)K:J@Lu[Կ `~x_alqKh.k{G?&~b'(`Kѳ%~ˉ~T=u 4ǯew+&n1WJKgwKcyw 5.Púrャo O;z:=U5{ ^ >8M@CSjHGz~ %"U.7i,X]`t+l- CQ@)][r\.Y.h>@p]]z.|܀W { /LWO#B= 3.] %c\ qr=?}sĬBvmnMyp-?_>_B2@~.?f[LEG,PI_ש?w)- jncT#?J ھ &lXq%j0JR=y{$`O?™K*?Tr꿶}whpcoP'?J{5r%:7 [d˨ PQ68⬀?\oM㒀}瞛Wpr"wz ߌ?wJpT}F޶@1Qs.i5%bM VV2~&ql ?~NX .@&\ϙ'qpιtԿ`wJNP@=ٟ? @GOKOpV^j޴/ F :v ? (Dw|PbQ t:5+` K]E8ι~=?h7sPG@) ?4A_O;o9PR."Ro P2P ΀][ qa?~*1BN$n,pKԻHCw4qY,ѧjC뭷ʺ,@-]:fkdK<{ XQ {F(/ܽąRA,N8μ;PRЗOg@D*QmGE t_Jk]q5ꟺ㮔 o88L7V@4NCIz> x"Q.VWM[ % $iqGi L ̑꟞-Jl )vߋ7%_<{92 V6M~}5Ǣy$vLo*EZR_׺i_X_ GT~6җJ4>>=^;Swv|?GC{ @M{®VVk n@>t;`jLSw&-`mx.djkWwjB æb Eػ%hL)7nXGs] '?gue#r*8a[}Y ]|8.~hXk\` PTѪxNSv0ZwA ggٿ b(5IwO|K (vZR/T@@4k P9SܛY$߳ "v8ާ35>zrOd=p?%.D+—!{s%8#f\#GtK[xI݈ l҃E@کYh Q.D+"{4%Z =iS`&ZFDPԆe>XߞA$zL rC D#(?cFLjix6bȅ|0}?8Uq꿳rpU.dV<$%%tyDZX Yd2]S{&0:wKΤAmE#ߚ)xW\ PPAD/ZFt^$!g>^z_3q@Np VEz'v9F-o"- `Q?.\7*Ol&8Tef ?~3Fު @I{[E< `~QZ@- @JUP1V~=B%@U15Atd'U=Ȫxs-MjmUAsWE0z%tT/uv 8sf  l+Gl3^ ^9oQ"F1hනWMv??`I$-.B{/!_2o4 gַ:mFuÚVsN-v'1,@s,~EDs=wꫯA_ %Wd7t_L4uS\ Ts]yvXH 5,%Z߶.>k J]k>uI>(} _"9g7p)^wT.F!wOuO (2 9‘YTk H]o2Er*0P'5HxE@72k0LwszB Thoֳ$qs9tPǡUBG-㤮mj ȩ.6pk@,"5 ~ʺ!: #/qoͤꟺF?|ꑠ}_GtK떷jPS^.6M= pԻ o9JFJx5`zgf6@zZު@M{FISO[*-y6PfDCA'lV\@y\wY0C:1hZ./MIT­]CctrsOEQ&-puI– P7I{  ,ˑŴ>pf{׻ % } G{@5ʾgNB~Amg=skG<8mGs {թ ?Hߢ/>;N N|?MK. 6ZY u@Kc |ǂ(9 u]uD_=9*@N!?=1P0t`7! mI]z=. _J r,տ_ IOj4 z!RE~#"SG߄Iop?o8-p){qITx\f=m:L=T?D\[{9gRu>dϗTϞn@F Ejdw" tտ` `1n)@NPpܛ huFߏ{[,@}y p"jPAo@wY;@-]g/!Cr!@8%o۳Ɂh9Z:u>|7!@TG;)QyF &t6F[?4Yp`d[[o_s #?L˿DZʞ#;T>( 6!P rKYoPZ=1NI"oA)Vn> PP7~qhG;G ?|P=}o #,m| `* Q._hYu\d pԿ^@4pϹ;@zeC,H-T/ 1pkP T[@rF`  i#q߂I7dԿe'n%QVМRTKi۔9b #K`[>YMZfrj *2_} /u+Z  9*IsL;"}.,tv rտxt@_Zk@<7/J0K{} .zߙ -W7cM-Γ<%=%?xfk?PaٵHf*Ht#htN&Z=ON+"gkK P#d:joP ^uKI\R G@?v9ON&8o\g`]PZ?؁enEeأ{V?g.]r{{8 ?`>G EZ~`h8Zl8>k I-.<r\ 8xis=\'Q.v8hz8E& xնFt5.@Ccg$` uA`p?pNck p|` `W `5.D_:nF@J0A5nͺ<9'ޱ^gX>k?uTmj$V;mvL?rOrMm^o0r APE$Pv5 bvjt|,0 kwI  g]&@@Z0AK`@t0RV;,\'_Bԓ?1#//Nsϝ F.1 F[#@z]8P%.Z4A˚5f|=zjK[{ 8Hg1'\o8H pzӠKI|/ةҞ{ÖjdtSox P=[^?kPZ# {ী zx,WHU ` o'8SSWpChCV=Xm9z n l UV.ַ]eL$-pJ`~Qo^0B\B3@k=o:>)!@ѹee {]'e=/,CZDZHQK$ }ng͙B4s.@[k}?j=UcP3F9}p灇dEZjӺ;h2J~ |N_<|ZAVTA]{ߝeגG?&(>Qv#u!0>}}:d xm=z;Z,ГQ,A9߹/#I14kȂoꟺ㮘%- T僽&sC<H=|KB$- h9E,.@$.P/Gw[CN]*+Կ^Pt;FP`/!J Z䐌DoV ̫H >i ZΉwMﷶ4 P,{O2_(?uD s _d;o CEOqڌ지2SB7P% Pv7pj:+m@ ʽ?%x` `-/THX-Gl-C8 mB&Ro j};t~^#>;k׮9.|ϼL/0u@ [h JvCc1$1 !mfAW7)2PMzu; IuV#onD{V?iz,~.Gz?7~G6FL Z T!'^#n708]!֕[#xN\,ob 8K=.][8X[ks(ENWbV _xOP\g5:_T?WqN H ?$i8O@;JT} o5VY.@-駟^Kk\q_NS gՏV\?` ;s8ޙnj.CQMZE-EZIF? `.@CuQ;^|E>{  .Ɂ kmZ>zm cme;@!ofoRS=tAa+@l?beߖ' MINKo ľU @>U.[9O% . V\`{%)V.@+a٘xK Q7lMo-m3tnz"tQߵn.޻| >gQ`  Ay `^2TyO ` _r [zVo7~i׻Pb[#p5 ~7uɀN.CQEJkK]+zi[ל'8{O}hGH<.u2͎ZrF;Ɋ%TR@}n1K8 yhk Y6FPַ nFy҇6TrV@tC>'}vp*|ot6F'+ᡸv?/vǜPַt+]K.Y}M{=c. ɢDʾF3? MBsf&{8pK-]@ V*uo4UYַb0pe; $`/̾f)4r2;v,fn]Z<]zov=ONo1e2J.@Ӓi^$ : k1}n)(Ksjc]${lR_<=p&TwIb0K?]oa-9 )?ᄓf j#pFKӀ7U0Z'n:OL|:Lrﵵ N=꿶$ ta[{i˹Z Eд.@|]$`/Xx~"Tt_CD-f&P @  0wԿt2տ~R ,ZfF=)x_qENHSs -ak Y+`Ľ% 5}R` -^%pH(9DGj樚T@ Ws x(.I (uK(uUXy8 G2`{xXI|mGC.ظRo PK)J\ oFyj|$8xtMFv 퐗v[DEj*0R* UV. KKK Po]큽7[jM'n};M_)O-kFQy t| *s; h| .@CkrR.jx32NѢ mNGU."o|v΃p X,2@*KT3-4]RCo""<@IzF Hտ omt""0 ]qWIn{{o `&kr҇2{r@g2,0 q1o28z{?`Ŏvfhr @Ϫ;`A0e@M+g8Mf˅Y8xOtr%ood񶳉p `\"DM@>z`9ھOq-={nՏ%w5sl0\t"xSO9 \n׾7tnmpԿP$"0B#Ӧ;{A Eqƍ}=kX)1yp:9"߃y+ "Rl8(o1xs5PR@@,sIG?]a"y(:Htw@Ǟ~G'YP sp+aꝘh3=*2k % \K Px܋3ݴmy_Oj{~&ھ*,qY>MocmK.AiDn8ƒ'\g6~ ` uET${kǕh\ @Yl=)Q;~rBZ`1GA'6C""tVG5u֠_Z&P2`M@{]/&Kdu( ׶ $/8CA&@C}J6PL3 ,Iʞ ^{ɟ<7>\ZB"o1^D~79򸴴i ~C :(@kĭXQWO(Ic6nԄ%(Xd5R K'O0O"Q0E u8,PSᨀ=;iKy7`X56@8)<:E @ aOk4\LK$GL@pUHRR@]1hpxO/CMVx׺}#X'F{dzM@~,#`!u ?-c7!o8ym\K/qߢ\3ٰf7IGmZj dsIFE[pQ⁻X#]b UV.~+yd@OWE qV%n՟kr;< xGRQD|s"pH'Pqp ԿTxim\I@\RY,C.khQQjkscwL: ?{S.}iiUώ..@HoP?0@#"*h!yߤ&8\ /r*s iۙ}q;~=TmMR U}/&m1VsLZś5Wh+! h+STR+[\%>NrK<~m؍GMrdy `DҷMr ``l`5;?w*ZJ `o_RI)x)iKY}_}ud2"9?0$%5hesG޶\.lŬ>(p^G|?w ¾JAms_>|nxV_"$dXȲ `HҷKRovgmq.tUp_ \@]J 0e-S!-\'ի4q]8DO";n5Q4ۛ %yzߗX]dUs;Ϊ D[N$}[@-\'?vZ K4.kU Im5@0M,[''-PS-0q.@Css h[l:b,;G FZ?{"Y\a .@Hwpz{0RK^6{ ] /!jQ5gm?>cSKYJdu `@λ @M@gZMΓv@K؍X NR%8hOS\Wε׀D+`_p' j*~F ܜ:btJ!8=<ىd@E8$0$* Q.>Ш+K{_ԿR4So-=r@hq@ߺ{٠+AK]cr"Ro jε6T.C_/K:LhktKx3UعD>cos E+B &ls1hpyxMn &Mտ8)?%ZDM0BXa}K7`w?R@;%r覠""Rl8s1TɡGMF.T UPkI3:e@30in}nRϰk5 WD4$p "0B\hf9g@| P"VC?8yWz>zG:/C]}_K`@bt0Su|wF$!r9ȶ8#FoJ\C-\A HXL 4$!\ν_BS渫 }K~r JN5`P׻%@b$=.qP*>O^ES3а ++ލj;pZeχ _0&!If V/'rB%Ym ?~fm+К{O7O0V"09d`$L^3=@E.܈+[ѷ귴{*Q N~{$8w5j ~w|j5Xۿ@>b "KxJ*3W-DJ>3KJˤxW@DJpPl~.2C2&u_KJ 9(1-Nyr<nnK%*z$=T9xsmy%.wGOGE -ybf74 CRȒ{Nc6sv>ϵkn2@yRv(Mb>.A`6>7 ؑ<>D BU^pFP#:\l1q]`J&?P xH.xv B @ 0X?vP=*޻_=5X'MBf&:-Lru'yRýLx0-.dq-|~ےIL&r`OX H~s>4{z/>tZ* [Fs]m1%]<Ow #p) xywc ]" k#r@ -!Kкƚ$J8nuFr%#}n2ͻLZz0d~Xono /G ?w3E$n(htF Pd=(I&?Te6@;qǽrc<je#Ɔ M0?ndk`sPR."Ҿu. |F T?΀$SH|p[.T[4J0S$UװҟY\\~{y@I{HEZ;hh׸  >G[LĨjʪO_s*=WG݀QҖ㤍qz%fF:&:%W;W p տh{TDxT.%.BkNɈ-bt[=Y$`Ϲ?: -f=t} j#r5Jߺ7>-#ȀԿ `'|juj׭MJ ?*:yU(̝JO>4JP9p\DlG%,Ss@xqMx/V? W[k Ԯ Ea]ٟ7٨},5U|Q P >t8ۯ%T(L]gƣڸN܃N  #MV :jpտ ࠊLZR.@oS."7x$`)s\7'!M#G{\j;Л#M*pRK^=@w5 +fF'<+.8եHgx80YzoZ䨊$ /}oXW4p౷Y0C#gIXE&uX\ u_vȱi!kLGMQGYyoh [46 V#]·ch,'ꬊ%\uWk=, C{{3hOfG)󹜀yA،qtm%RSb꿛pj &-⣭s jijraތg {h5\G h sf[Y=M~Jofo:n!7 `<un2/w$w|Ɇ%Y9,$/ԯ;^iXw7*L@!"o P. @}<հH< WF)bػ(H\X-%=pӡ7;w;}ȍ+7nդ& `pTreDo@W88$D<oG;4$<-)Ч}oepV@S.yiw#ɀpR 5hͪI5}pU2g@cFlL={AGl URf*% =[=S@d@E8f(&ڮY9o:~OZZPgK^K52H;o|5F܃.@h `&u.cbt07J$ 'yncxo#6.Ay7.`/ ]%ֆvRfߗC$R=1/"b D#\#&\o8E-Az\ rQV@f{]QFFD[ʢ .<}n`ͻQ(\3@xÍA$D.!vr4EYN 4<~/{&{f|˼c3C- @ <]þjQ. @Xt*wÚlogk] kt=mo) Mv iz> x|g& <\cTrָ ?+ n-wle{kYsE\6U^%q5~$M;ܥ; 87>,,h OSsεJ S s}GFl])w?("H@:1n5" @KxU`ăy m_Fݿwǻ* >{ ^D'=u xSA(3Lnb'%uO<r@Gr@@-]bz0pKZ^QZ@=9՚D;oc:.|.7}iε/Mtu@pM]o2&eb8)"1f[WC# )rB{" Xdkg$Q_K$@{=MAԾŽ DVJuJs|k8Al s(@<-\/&=ZC%ԿPJ $`H qFW^[\oЯ\)Xڠ{-n>=`5_BMkoBzI ਈDZ/>LZ%R‡w8m%s%9t`w (>(qׯ_~/'0׾@Mq޷ TN))[-|kD͹5RQS0[J Б(\59& {*" SOG]/9ϕ$nHA&`}u&-hXUP@yܑo[ݶ;V <4Bk|8 Mk IkuAZf`x쩥+1K &-yoQ/EF RRR='=K>G*8ρ}_jnNZP}$,AR*? 0 z0`mm8Y9(v{{ѤWFL^5G_#.iP堈fWvrwF< 8: ztlJ qflx@d?٤6XjlF^1CO!R=>Pk#_M$T qIo\`Au^埳ӄ)GFGX ;akv y#t=ԩJt#+`_^| `%.?f{!8#ծw%rjfVZHQ.@FL9e"- zXL.P͸.@K<*`+֨t/܃HZrt) w͎p p ԑxop> Pb}o;Rw?:_i{@4%^j5F  Xd@MJ `Ojտ؃V|p[ڊFwVwے P9.?rF5::Ұ VI+yvxF @MxFHD̜_XHHDI<MU͊-P`/l#R@;u7}Jr-!h+^.6f'h}V ;ܒsk[PkliCmt2j+4 s9=[}g5A2pЬp.P 6ߣ"`r'a@uŖ8~ϩ[m{;ֶK*슇%rimp/&{0T%VD&Y=}]p{ߓpv7tMFEpc,mO=\'n[_JP:1b W,]{t@R,]cu"OCqthk8N ` `l.l,C.^\!E8u}z'-z8sjRI>3=I{kq$%@ @FD!8=}VmYh _ҟ罗N״kw^%ّ!P^]#!&..[ZD/EfɱJrr hz[@-]}!VO3sRU q:6dֈp$A 1K`@u@\ћ rV1^XGk-\Cq" >vLqܵ @L1 0h@R@h z19p\gHZ'P'Ad 11hp,qέ{8>IJG`=+ǵy_;C32t60oA)=[.cd;TW'@Mo=$F ,P=Tnl,0CsK@H8Blո;h{1h V - `qi\qƴ?7E?= DYo,˦'a P,(㞋rฅsK_~(q=s$0?"@H x5 wp͍Lw4T'h0s,o0 PCofOG,I4۰.7S 7wv``.@#; ?"yl& EP'a[.@k|t\by]!@%@ `~` @@VO>],=뼀}I0d`0 >N*LJ p+@0ιjK ,MHY.2^ P#qpNp $0 쌉)-T)`ـ"،`d(bMEp s?sܑ%="H޴hS,A|s><5o׺lx-IfŭJ@8~}Q`UZ@!DK\%) xz2NŹ5LހG"3#j{fsӟ.vE¿ל'?>i6uw'@ pϑEBt[ fGt=\N4(?%mYo͆Xs\gI >4)`Q!jsG྆aTKΓBT}6ڵk w{91tBFgnԓqg7c;`.-{Vg:Hc{ C\/S!V# V{V?dÚZ `1h {dw F8AL\c=NmMBf$b`s b?\ kSN Sx!__;?"" `([eWkS#p>(q{쭵׸ c90r=e`R_T;sk5@5Kk\q"焴] 0|nyZ= HnT^PR`k];o/qΨK[m`01MEC Н ՐlQSv3JK\KTiK9sΏ瞧F2=f ?yy-#" 1 h%տfFO hӕ K}#" 1 h&/:O7V\< ?"Ⰲ /%.Az+SPḛsj+`D P8[ =0ub]D[ӛ^!q5#τxyBiF P8.We^`Ի 6=BzTQ-aP9p\X6* 9R_޲pLL8@Wi:%a|* TP˵TGdK\fa < !X+Y$`.@減K&#?u2Ǎix[xoX 1 T=湉) i9mI h G.-h_G%H Ѽ3{0CDDDD~b\ $`0}!8.@T[;H?? w:[ ^B0=Z g1sc"^DD6l`{V5Qr Fc-;~-g}v$@#""%aK.@לֹ*v>: [k l$ s*F#] t9N r}""""!}?* ;ulY< kyg(*YVilЁVL8`].@+UcYH_@c~~IlTگ':ҚJ_ouODDDDoT{jI"m󈎴D?&|8^r#""5$`뫏RnԎFeDDDX-ͻ2\_GDDD=XGM]pݗQUzJ963 '>"""50X>FbDȊX4pۺm4bX


PK Y[Metadata/pick_1.pngPNG  IHDRx%IDATx+slyaU/0000000!000000000pȥe^$G^ݽH"I$I$I$I$I$I$I$I$I$I$I$I$I$I$Iِ$ip<$5خ#gInapϜ$Iг5#F$I5F$I: } $I3i@OJ$N^]+> I {?c@خDs:Ij]=;+$|FtЎO*@T9j`gj$t+11@$n* ($tGBHhvyI4C I$i@E} I">$m|HR9M짳]Y/>Bڏ9GF e3A$q񸣯!Hj5g.r#1 H@V43Q!ȏH* EHjHI@@HI @N_DvCri#gܣgȂtφtH@Rjяg.zW@?g;i8sU/ |M :w?R;`WJ p!S pה6C;)vGpT~&ͯrXΒL0]n,kv?@7.*=+GY2AƏ'X#9 p"`GX,9VF0y#1p!KFj mW*vA"T q2Rv gFEQY5@}2Vݿ0/ @@v( */@YTE@W釗oo@E쎀;֩FWg:y0./ïczt% Po_|zg3d +3 ؾ?әq6Q8]0}hC rlW93F? (?_@`=s68:wFuď4lHgoY4@p! fk93F,F؏삀_aav@-̄ _o莀L#cn? z>:{ yu芀̃ck!`O@n08qWҟ肀jCaߏ=z1-# j2j9K$(9CO)Z*L=2ن?L@5{| F%@ [\43@3F ?T  `!ȏ@f{>yb߃|o?]p@*WG@k섀5G @0!٣{>(φth@gxsބﺯ"!r@*fXՕ mp+1 6Z3<(d(@7t@@ ~5.Z2* CmP>\P `3yMf'0a3C4#@_eȀ x`)vDלlW"A  ~h?^ß<`S `خ#)^ 9Y?J32]#i3`<:" _G|]Ga|hN 1g @t@@.*,@ek8C][Ux?z@%p0`A60>Ȅ ``gA޼_ѯl1f?]?rx% !`ݣȂrw@@YuYᏆ@Z1w&j#1 +?G5ڌGdz^+D *tG@)tA@5ƳT ~;#wA@6?T(膀_U߳:#Pr+#53ޓ <dh7_@숀;$ߧWD@!0F?ƿp0>d!`_oҷ+9| 茀"1Fm>:K6+GCە{,0 +I#Gce3A Mƿ1fﳯ60sƿ "77D!u; &}lc #`@_+ #k?,B|;6Pǿ*lOwwf`oM3Pe3#~0莀.?V\ ]_l#`pu2@DޫN^%ә`# =Fo@" }خݣg@5ds/=;0Pنmh?^ßeǕy#FYZA`%fj @"D~ܟap Q XdC@!+٘;_3mgn:2v6#Ap"`}X50p;^00k3 ~5@l;'?5 <솀{)ǯ>)A`B^)c@vA@1J30md2OW,cڏWG@W\{,џR@7G -#V=ֳ5#F?`vt8G_>_eW5" @~+4#QE3FߌtQ `| ~vGB3w~ <BxE~y<NX:_m9K'bpgsd<v@bygTR#_m@GUhSN@s@0K0L}g ߅\P,Vox`TF@3x(J"@YTL}5~;F?f`"2"4~0s Vތ?f3 "_{z{J`f +z3-:# XTգ{, PiG 9CF @7dU#i@{E~4*3~X= h6A`c@_V_u#Peg%*!_&#;v3h 0cV+F?`Gx1h Tt? &`G@_d8@+tG@Yi?ڨ}v 1 iǿ;"jp#ZWdxN|qPWMfNf 9b\gJXtE@m_ ;]PP:3@s ǿ**>QA+^yGWC@'}1>Cg+ޛ? Uz,?5숀&?_ [80d!yNQv(=F+z3;fދ1Q, p# "]1Gϑ ?6xݣ90 &3,2~4` rs C_=97A3@Y슀;3+3{42, |TD@ƽ1 01g_Sqg vC{񏅀^Uww * ~x 삀,V?30e# +Vݟ e Q_`0 ]}2@udg~_xve:KzTE@{ᱍGgX j|:o)yBv=K4`= }?GWjpώ~ @ #?#cbp茑5Q8dE@-Gbl :{Wb~m#;jc`6F6 YP憟9b\g~I @hl5@pd ` w.d 8C;^w+"iƿ̪@_=G~p `7DᯉW':h2ƿ  .6lF ﳳ<ϣh6W!1Y"q  n6芀* >V膀JpYUeGΠU~m !_ * H@UtQ("wgOޏ P|,\& `gF @T<|? 3{]LHc1ΒV;=z,hx/V9X|k"G?moQX50 aG!`ֹf zg`wy-<y>A0sg@edXy *"3x=@UTLyh]0eF l 0>!x/D!2A$S삀7!"`4# ug =" g׿_ A`ؾ?y Ejl#Y2b @=sh f=sl膀{GtB@5W=9ѻc"`Ya]5Q Lc `9f @Ed_lٷCǾ3\g+!φo?X=?!V=X0eC`-&#;!`ֽAU@ςˡ`Vܣ G *6" ~e%(g~"脀 -_?wil?^Β$f+r'dl#FY2@:}hpWs@=z u *c31=WG?Pi"ِڻ_Gs zTA@xUyd8G/:F cygTW~ ɳc=1p#`ֽ3w6Hd s2;OG~N}Mձ@Z#?L8|6z;xA+T}߾βLF>n qh#Fy"! ~cτ]9G)@i삀,ك՟e 38gH @wdY*2j @Wd3GΠF @7TGx3mgapzPiG 2Vw;^[:LO @)쌀;17\8 TA@#o_!",dEȂs V ? [茀2舀lgAtB@0q, ?"쎀' Kǿ**b&`W~m `ώ3$<_2 +^g_2^k_l#`rp3Hf?fFC o_io_iPUn_)Pn_)P Uo_Pn_n nd&ȄX `!2 U0x*@Eh J5R% V*H:6*@7쀀/Nnt@gx@NuJeJwE@Gۥ;ʾX &%Pj% P-^ I1Uk% ~Jh5___ ŋK`_gg3t?Kp_ge3t=?ᗤp_eg3t-?&ᗤ1$g%\\?A_gf3teP0vlFx•?bS%U\Zqzb|޽L%s}Eaa4=?8}K 'W@wTQ1+, KD,9hfK"j x_d5|Sܰ  ؆FKZ N"5D{.Yl諃lA\'UY}ȵ"735$EEI{k+jR+"Pws KIL)QY]yi "i(Sd?y"Y4kj]A.=[U/s!*%d4:Z)ggh ZZ!&q`VWd<\HX| Xhr6܋Et&uͶ*mA\"J P*DN6*-HB\5<*BX )ƚȀa_te6-*X(.U&<UƢs\PlAi{ =[*D-h#X Pz"*[C5P,ZZ.f'oaJ C h'&tiY"DZƷw,fA;[[YT RũWQ{qB@Y[j[}Z[ϷS#mChfãW6,S#Q`l)ITp0]r1}<<ϻ|3qwwޟ.2Oizw8x:ӣכr~Ox{=O@n|ӗ/yx~!%45_'K` +$NӡF/a9V"G~sTR' s. GBo3rۅH[4 NLLbGJȨmKhK(Xӄ5. Uk`@(!9{ZPRKchA/a%aK92iԘެ#@#cs!=WuZ#kkĉu?85jAr˄LLe\& h) fV-BhK|L#["Q;sOpF s>y,.Q r3Q ٤3$#Zȵ0ތX̘Pa)#&cv9#);Ki % }4[L9Mа>KpjH̾Ojx$!fdn`>`z%' # "BM5NlG6ܬ%~FjhgY3fcY) R4&D%^03&9N׺"Tb٧&[C9:Xhi/TN#iIQ!<_]-_PK 1,[PK Y[3D/_rels/3dmodel.model.relsK0E}M~ʺ10Ybuե)I&= }h~sn|P (+Qqm?tx:ObYYtJ8c(5C)VМաtґVwr͛ĪK{WSN>&ǝM u9⡭E5Q>GC:lx1RVnŧJ)d<BTByȔ)d̘ByȜ)d,ByȒ)dB"U\)W.] \B,p YR,hKZ @.mh\"ٞe qȥ-K[$ˌ\"Yf2#.3.3ky `PKV3o9PK Y[3D/Objects/AppArmorRed_29.modelN0E I*Tbؓc?P?;y;wpavvmpV:yח?L@Na8rhˆ ]qk0Kk/D$5i]pK#W6- cΣA4͡1& ̛ Eߵww*ɘb/z~8>}(}pOo_/޾ow˷Ϸ$y~[Ч߿_?_?wy~oOy0?_An~ۗ7û_ϟ{~_?7sJ{\xiR_?/?Vύ~mFۦئbMη1[% o(lϬ'޸yu{0y:/R>z ȓЭ.{_o%P(/vm;}c[BLWWQ).z|k`FZEOK惬1QJoۉ3Ro!`a۾kMmWE&o㭅|tBV_{XX,n۸gVSE緎/vn)R{h#=s jm󮳓t+l]%&f Esn)DOuTbx;b.ByTb# z: Zhʩ3LaZ)Fi.xK'۪ 57os}xir{;7: 9yoG]1ޅ>ȥ΍O,RN)/;7_.0wK'ZMd}E9i;wC#._kS|'l\`" P_V=n|__j6_m^^)!R]-RlBB[@CV(m-kUA3Xl1f\,>zEܲvK(U۰b-CJb%UܭSVgmڍ!Y(ݣJ9Dcم,RcI0w!5 ܢOpم!s%hK9FU,Veaą7/o U+m¨L%RZY+J9Txa35=gna_WUr[DǻX@jҙsyIV 2] "Q.tA={.HHVS:?#&W{}ՓR$2.Ǻz]l%1,|̗ǷaX,ۭA/EH姲P1[Ew`[ O K}X4le%F7.\osbAQ]P+jUKDCVa-KmB:8huR>ߧ:)lJOf'G_}fD^7#nâ #jx0劜19f2MTim](1= Ple=ɛj/\_=nWd j} !y.d]^]{UB[CyjmɻקvԪ[S)le­pjO ⋱OMntƏZ- y$Pf֜VtҥشzH:@åu4tAtqF-!P9_Y㜱)T!bl } xϣa+' & :M;M{cF~,Any5:4:)7 O&tR q;6mňBC7&r% V 6!qӊ)~&rh04khקq(!p6ݨ>bE`;Ƞ~zix0 5싉 Bd;!FySѣN'm=v:}`tعM. !~)[N۩X20UOSr%?dyI`,"r8 $;Pzq8ҋHM9KN{ d!<!ajCL@obS02[n ߍ6Хc1e4#](PE׭L G=RP{}ԛ& 챯=09A(i P@xНjE¡"FUtbѣṈ^W4! teF-s:_d~H"8C2!=@֋F'/&;Xbj60k_u ZYU1t*S `Rx'&z[6t_B5Ɇ %Th&v120XWt7 h*[M )<' ؉"[B'y[0$b=:4b! 07CxT`5QEƪzUj{-ZG R:Od!G~[գXZc )hs, Y)sT`2k6x]E D Qӏ0CU*fo ݚ$'{h\j3vIoSFfɅ(M#6 @8o ȲA_'V `<8(6o1B5D:`Si#9_ramŽhBeP1 [.Pm!xj7*G5 =WBZ/*7vʈĪ,7dbU6XHĪ|6֫bUQҙZEUPXx/jb\]7@AuMZ栶MN~5Dh&bBU3ޞC=V<Xobu5fPYE 3*> ~U l[9~uY4c=ȓ>,i2^r*'ˌs|yĜO >?`ݯds&Vy>X;` Y׆؁uR%!w2V[.Ql3e ԏca֙sP,K~ڢQżZB2́U]lNqXL2>`^f, Gb>9Im`񧣊ml0>jګM~=maØ^ml>`r&{m< mMxFY'c. xX`F|+l>`hc9&W\PK*WH>jY S.ˬS PbLnj1;a3uS%\[xKh~lt#$83CպTT!qVcKd#s)YO͝ZOLLx3kHH[E.]:,tXtҾbۑhҶX]=CJVpx8q²vO92¥[ wԛd&z3{y6OjMߚkhMB9ha7|uvps[L1GP)m)N[QRK\>g6q _%+DGN@V DWiP]!e"(j6PTGWL(0z/7syND5?%"ww;z"4zrۛ Z\zrAۛ':j/*2w ֲ֮q+iXS=Js%TS|vG; gۛ:wέ%{{SYgAϝ47;ըl5Bjht;#ʟ]/vg7ه !-I(@,?ɞ%n:l"?f)U|| |>'=#j}]ިZΉI9=Ԅh,jpBeRpw?-QJ~KșByIȇg5!B^̛$,0N5B`7V7ܚu!yt" ˋL+.\Qw޹+]wʽsow,&|A#-./xoi~{3궪{Yܾ婳1m̜%#:a8fa_aO3ɓ/tѕAqqmCOeaw56ފeu֞(R/ hD-fQ5y{QFr1X̪%=^ " DDW]Bt =,Rj̽P{o ^G䠳,rΉ~]Y酺+YAi^ lq\ 9! ⅚aj۬L#u񚌸ok1!=@%i4up/xo5oJa)+*yg #Z=tVkX^UF]drIEIGM0CU _$ZY2Ǻdgau/4xh7PY' !XټUIQuHfV8>gKf,.Z S \mJ5.PZ5XCK =-e8Q}Y60=Ne!ZQhsE\'ǹ"xZ hyQ2W'#@,aո3G-|(dQב 1z҇Vr]^I3,#UL- WV];-Ɲ{] Գ!$HGUNř` U SʐP`?NM`UjS!\ij shd - I &UlUh(/7ZNJܣVE21 fih(EfbJ1*F 4˭kH^M]M$bͱCCQhтo 5+)g]hF8zvu^kVaN-dVTYX+l"GS6z}(g⺦`J&j Ӣf8 3JI<.P(Ih5lHJ qYvPs4F/S1fG!*(P"IR@,o`WRE4R+k䟒LHBFS DcdSJnQf7>S .5k0Sn3gNj,DbGM52%bkhB"KS҆FMJ=5w٨ k:8Y*]JwwI-8;`9uQa۱Vhd -=;_&,%s$5vN1M;vUit֮) 5o֛w^y~ԟX;X<Q quxřuqtpm㢦ި0@6Vk 5fn̢b'hd5+Xj0b4]cQ_\g)g`emz4ѮѡI6E0p~Qs4GO&6y͉ڂ&N rˉ:{~G캔'joٚ}`qCz8z54`ibF,v~ybpiW[#]e`G,XjVTMO7x 3ZZ2c]&Np1kNflPllDҵal>2sͺxxD#cS6ʻJp9mo')h_PBs 6j#MV?o Dej0KaSvko[PYL};8| ~;.mW-oҲv djZS&]%+DŽGr^_   lԮ*T0EY*"]b-d];ɑ!#e9fI. \BC[niO{nۜxF;fڱI!ƶj$M&V˹f@r8fb9k$+Ya hX㔚$mܽ:Ɂ 4-uȊ"-;V dTT\(ֳ[u N*Ֆx[t9{`ctxk/$k[~:t Vk蒩%ec~?!mYKg syEAi9AAW#wMFoGY^*ldؠ!<ǜ[NyYĞ~[\M)6]mf6[T.VP<  跓zOi:,8kC6{a*Ȗ^6לTI>Kz魸]zm/䕓"2E.G۰ׅPϷksg[..uZ.YLY>Y0)tk%mvѺxјyvz{}Z~ppp͢0oV O"[0ӆg z̳/o͚0_Z?,vf$NY /_~fdpA]2-k8wѦ.ƖVan5gEe1ٳ=Lrܢ>Vfl֖P-M L̐RO[E C< 6ns3FE:bn 1] ]SN sb;~Ra7}dVK3 QB|j[jEΉHŤG7,cyP~fzlp֛VAjLE,ωG{~|/D۰U薒 e|B`zĸў̑8R,ي*Ӽ񩪰 DžV9<*FWbeڻTJR1SOO)rpXE=a!L",V.Rta`炋nʼnb>x=u>:ɕBX-䥥L,7#gRiK@/?c^TRքYg"/W׋͐+-MnӋNߑVwWU Li-}7UՊ2WCg_mb)4۰͌) *l!l;71V o?waj=nJ?o i3hk Ư m"\6M; ݆shhVb/6-Rl 3l^Jھ3De`m桦-= o(~jZ2Gv'1#"n<!Z#VtV G"?jE,7ضS0ђ0PT6nVof  ;^3Z8~eYf7FpzՋY\d7;+ je:KRfqgK%¦D mV6{]NV)@+3[U/5$O}No{5#n̘}}Nmw[Gۭm3&+Wwr:`Pq1Q TɶYY!8k>\~Nr`,{H `5-s4VBFXưџe ݑG̗c[t͏fI9hl$c)]yvvim;{l!鶝%\;llX9pKǚC!Vy]]`}uhtbsiZ9i5^͋>TlAbJLl8 ''[M-64V3(U$5KÞ,)]2ֺKNa]1l fWwž/5B#fuw뮘x5Wlv.\<8|^|Xr]<dhi> a"_\},c Z5It4T>2>]h.~2\)e DWT*y̗ ɛ#=yȖMٔr=ofِ%QS4t7XG njgM]z.aYaa``Y``__Y__^^Y^^].]Y]YOYA.A@@Y@@?vF ܝaxJzVAε L|?Bh& Kqt@l%9 9ZzH1S:wŸu1c.DӅS ,`;T]!XmOΫ6PgU6'Zn{&_iUBxP#Z>~ڌYM)%kzmza l>ew(X|JUv(XlP;J+1qZ+)`8dۗ(+a2 sαoԱ妤a9J*){"SRG *W DD(eX9 ]7)X%5zpj=>,% ]װ>IFok ebIQRHppXcŀItZ.Ýi !tZňNzRI٠fQJ`q L#Wz,"&`q D/Rv3R`?@Xh,LsVRGJwR ( N-v ,$&I%-XpR)㝐2LRWz}հ>z}հR#@)ZE B̐<݃$RD*=|Q\gKqb<)Y- LY8G$@t_.%B{ ˤs@X2+H)X3=0 yf\Q.Ya.gEȰT{m'_P,Pdta PPBc?Xֽ)[XoEY>t^c%X ፡G$oxEwv­kz RrVk}1MlQEqɆ7k؜c?uے5G6~2 iAN:>ڠqwu uM'r66P5ubsbVsPto9`Qb-ڽ=baRSRAx婎5L{qgG, vܑGukLN{ Z+܊EMLT[لʭ#{Yb@jB-R"7+XF$RC9ei\+_Kb$- HM$U,֚&N ͆v+'\ :쳏-F2{ -,qUդ|GLV4;%1W - -de!᠛,( 6x7`W+4گuY+0"#R8\͠NxV>NSPbUQ]NVCH ^Epg! Vl焹rmᇄ_BqFK L#? ?,ZYSOٳ3wϺ0HPHa"kĝv{A˄;9Zfr`AK|9 }р h 3^qBT]ԕT-^84Wb?{$IMHx+L!ISh 3&mb˻r!?0N(In4vHjFT'ɦ&!lwoTf oIH*CϙF" A7Jy,H&%$_2:<}Y}yC C ,bb1`BUj@!H\G\p8idg*2m7*xbVT(t4MCJM~:ojv* Owx9SӶ~PCbYAt jD/ĝU(z MPl\UϠ' (j%P945D*v롦]薥:lWf`m "T )NȠb{bn'Uȁ*V9$kv۔ o<jU9MJ0reo1+8^>ܯDxE9Whҟlc=9 xlg-D.U2=%2V |<`5\lgrnﯜ;grՙXϋGիVzIx[xNa5 N%x[0VuS_K rEXu|-D$ЫX, Xn|@DZFTe޼E+{H3k!NjW^fqBGgټ-pmEO _yC5ك8V:;]3 Ι,Jɚo'{qnqA +Tbeid7anAX?aK4@/U1@bp) =8 ~ KG݈rL4oDgPg,NRS ]8[,5TB7X{ f;|U= T&f5M,mU>OiG3Ŋ)MAƧ&>4٤>[X[ uŒ9/u[Y4 0"V56 {t&\r"XAS" ,m띈q41jؠYD ԎXݢ%ߠ,g : -m#'OLoĂj wjT# 684jYXw !b`jDt0i,f¼ >Y]8pdc5fȦM[p zlaTBW2V,|5y}V KI؂Tl>kܯG-le<Ypf9AyN)M-i?J4 NA#mu Nދj_QkjEr/W}j^܋huZ-[ 1XHOXƼi_iⳅ 8~կ+Yp\b*xB%~逅ql+ۚ [}~5UjaC:1fas;e#w^}$LpH'ɼbtc_~9tU36ߜpo3HCa~5%7xIH/xI' _Aumo,4Mi栱A%Vq6H͘ ,X[?*6tѯкSqsPB`a:kFl0y`F, mF@,lc,GcЈoaÈkwhĿ+4Cs= 6uZ8 2A##[0McZFSb_N0T]-l/xmqoX!uqrxtW32GG˾llV~!{f &ٖXA,X*6H PԫK P[NoAA- ۵%3Mhk۬} 2#'#kӳZoƪ]+NiX7׳VcWsi< 2rn &mEx~|ƋL2"-~=`qpBigĿfCAq%ؾ)}G<;d=`qE:`[uòdgAUtMEKN Ӎy&K^W{;ɆXtӡaFir` AXS:N3m^8tv+_Xn*w`cn&ol= TLzUn XܯUL}h`Dg+|b&h qYDŶ63؀("a?v3!{\Ʒ<`Oo4FXӠ~H~O`XZ_=7][G_Mdq+*=D׬s*#8H4lrXRcC"+~g{ sd<`$ӞE,+xgTG&Va76fߩX\V΄JmHª"o2lڶOAh6˝'sm!i' Z5A|v&V?ȶw{0?SeiOX|/d}Mt\=к7;Sd'͂~um,(Ųz2 ƪ-j:&2aދ'->zŒ?(_RDox+ cGm07j5Ɓl6Wb.> l0A2wЯ~߶?~|Ͽ}Ow~=c|?PKqGm?PK Y[3D/Objects/AppArmorRed_9.model}[G }Paz.Q%QNgD:_Fuif{dd*oͽO__x/???_߿ 痿?}˟>|>o~߾ӗ>|ß۷?}}zn?~ݽ_'i'˯_Wނ>ݯ?mwxUKo~o)L?rk߾Ͽ_}|_?7ss/>}?Ҿ~>}oO}_xlzM鷿z}7íD%?yBCQoñMŚBu9oc/㟏 J{QXmƟYOqBm`jt^rZc}n<BFr&}YO\CT?۵Rn Er2r#_r^ FZmE_jkMn>56/F&*5m'ΜKPK냅m 76e^irh- [)>xb$r|yaU"gdRmWP m͓s1UyQt~bB)-5j&Q>R,} >簠f;:;Ij6LܵZbbƐ.[4x(B\YaO/#"7zI%<[Ü}%J :0KhBXm[1o׿q Ͼ_S~S6Wއv )޺׻yqXK |$ޥ]\bJ-tɞ/{qo<3ptD]s7>->4"b+fI~?wv&b !0QڰPh eEl66mH)Br?Z)?lŦm|,$U- 4dR_&bɹV4Ӎsm6Q⣬Qd-lR ?!"~?OR M,n-62FZ=knhDʧ@.ݨ%⤲U9%<.dK Q}'. /xE\j1rd* #.yxkZiFEf,t]Qr-wږ Y_9cu:7':hP֠DKZ]]qtA, "-؋tAREZζ06a.]$ gο`>& yv9bf.٘g ^߅d<= nfn5|)*D*,?Z.TRxlHh];MĢa++Imd|4'wzkt jrZ ФU]"&Ə5 hX򟗕6h|/gҙA>IaVzj5S=9Ҥ{5#XqN)P)WUt֟(ߌMO̹6lz7ͧJOMhBTb;,NT~ƔIw='إeuu!߫zt`Sk M>VzޞZ_NNg+nu7S{j_Ԕ]|j_pǧ6~jLsȿ'b5[Ͱ6m .?ŦCrթ.H eyC8uJ>Xlwq}>W6[ UfmЪO ۰ݴM>5mCq7nԶ >ƧmnN-/(nn?qC}rG5ܰKxj~1p=ZaR}n|^-MĀuwJl޸Yz7YWlnjw|jw7~ofLWT怷ve%.)[A<kyj("İlz7-,-f[ Sm~=BƺF.-&QݍTS(܌Y4,3>0SDul#O'jtm@.Kvk#kghn.\3/B&4Zܵ5A1OQ1 nƚ.lތ]Ct+ WM-,krܝm6:Y-F)<4ak8?~oGqF-!P9_Y㜱)T!bl } xϣa+' & :M;M{cF~,Any5:4:)7 O&tR q;6mňBC7&r% V 6!qӊ)~&rh04khקq(!p6ݨ>bE`;Ƞ~zix0 5싉 Bd;!FySѣN'm=v:}`tعM. !~)[N۩X20UOSr%?dyI`,"r8 $;Pzq8ҋHM9KN{ d!<!ajCL@obS02[n ߍ6Хc1e4#](PE׭L G=RP{}ԛ& 챯=09A(i P@xНjE¡"FUtbѣṈ^W4! teF-s:_d~H"8C2!=@֋F'/&;Xbj60k_u ZYU1t*S `Rx'&z[6t_B5Ɇ %Th&v120XWt7 h*[M )<' ؉"[B'y[0$b=:4b! 07CxT`5QEƪzUj{-ZG R:Od!G~[գXZc )hs, Y)sT`2k6x]E D Qӏ0CU*fo ݚ$'{h\j3vIoSFfɅ(M#6 @8o ȲA_'V `<8(6o1B5D:`Si#9_ramŽhBeP1 [.Pm!xj7*G5 =WBZ/*7vʈĪ,7dbU6XHĪ|6֫bUQҙZEUPXx/jb\]7@AuMZ栶MN~5Dh&bBU3ޞC=V<Xobu5fPYE 3*> ~U l[9~uY4c=ȓ>,i2^r*'ˌs|yĜO >?`ݯds&Vy>X;` Y׆؁uR%!w2V[.Ql3e ԏca֙sP,K~ڢQżZB2́U]lNqXL2>`^f, Gb>9Im`񧣊ml0>jګM~=maØ^ml>`r&{m< mMxFY'c. xX`F|+l>`hc9&W^PKJWHBji S.˴S PbLnj1;a3S%\[Vxh~lt#$օ83CպVT!%qVcΤ# s)iO͝Z O&LMx3kH[E.]:,tXtҾjۑjӶX]=CJVpx8qK²xO92ʥ[ wd&z3{6O2jMߚkhMB9he7|uzps[L1GP)m9N[URK\>gq+_%+DGN@V DWiP]!e&(j62PTGWL0z/7syND5?%"ww;z"4zrۛ Z\zrAۛ':j*2w ֲ֮q+iXs=Js%(TS|vG; gۛBwέW%{{SigAϝ,47;ըlEBrht;#ʟ]vԟg7ه !-I*@,?ɞ%n:l"?f)U|;| |>'='j]ިZΉI9=h,jpBuRpw?-QR~˷șByIȇg5!B^Ư$,˨0N5B`7V7ܚu%yt" ˋL+.\Qw޹+]wʽsow.&|A$-./xoi~{3*{Yܾ橳1m͜%#:a8fa_aO3ɓ/tѕAqqmCOeew56ފey֞(R/ hDfQ5y{QFr1X̪%=^ " DDW]Bt =,Rj̽P{o ^G䠳,rΉ~]Y酺+DwYY@g9~.j\&jhWQSC0pqYVp;[Nbzp zi$D*jQ>YAj^ lq*\ 9! ⅚ajܬL#wok1%= @ʾ%i4vp/xo5oja)+*yg #Z=tVkX^UF3]drIEGM0EU _$ZY:Ǻdwau/4xh7PY' !XټUIQuHfV8U>gkf4.Z S\mVjE.PZ6XCK)+=-u8Q}Y7L0׮=Nu!'ZQhsE\'ǹ"LyZ*h}Q2W'#@)-a 3G|(dQב &2zڇVr]^I3Ь#eL- WΖ];-Ɲ{] Գ!&HGVNՙ`U SҐP`?NM`jS!\jj* hd - ӜI (UlUh(/7ZNJܣVII:1*fih(%EfbJ1*F 4kH_M]M$bͱCCUhъo 5Kg]iF8zv…^kVeNdWTYXKl*GS6z}g⺦`J&jU Ӫf8 3JID.P(Ih9lHJ qYvPs4F/S5fG%*(P"Ir@, o`RE4RKk쟒MHBFS LcdSKnQf7>MT Ѯ5k0Sn3gNj,DbGQ54%bk!hB"KS҆FQJA5w(:9Y*]JwwI(-8!;`9uQa۱X hd H.=;$_&,%s%EvN$2M;(wUit.* Eo֛w^y~ԟ(X;X<Q quxřqtpm㢨(0@6V Efp̢b'idE+(X j0b4]dQ_\g)g`emЂ4.ҡ"I8E0pQt4I&O&6؉͉₣(N rˉB{H”'”o(ٚ}`qC{8zE4`ibF,v~yb(qi[#]e`G,.XlMO7x4bZ2ca&Nqŋ .2k"NflPllEҵal>gҪmVk.6gF4B $E|j1մN\%ߪʗvͥ-~cyp2CG1 1G=[zf-,Ӄn_WjLJ}-fZ9I-J5:߳Rm_B&֭OJ[`r YZWF[& Kڰ/<eJiMFscTjj3>^E/˃Ļ5X☦. }5n{RғXoaӯWǿ8Wv%Xi/Rm)CLw]1ԅϖW'(>1Gjm5 66Mo- jcX[4zK15q|i Z,GfYohdl\yדS .Z߻$e{ " Shr7b[mY*B2m1Z}f),Ca_8~*nq__3kqI?pǞooeʖ%NZ6.ZD!5BMk䣫}0Z Ľ4Caၭ5\<\Œ1=a0}R]kǗ<#9R;|l] xK~+] "ǯk śk+|!d|XnK+=:"f)}"2- 喓Y0Mo$mt=Asf-PhUklc,0)ŴlOf.-_ E,rc|imdmM#r0 |qZRm96x قTm|O$tzL6B^Mv.,K*tf9Vπ4m3oqh<'^GpIo;]-I޷wJpv[1MTYVfl֖P1M L̐RO[I C< 6ns3FE:bn 1] ]SN sb;~Ra7}dVK3 QB|j[jEΉHŤG7,cyP~fzlp֛VALE,ωG{~|/D۰薒 e|B`zĸў̑8R,ي*Ӽ񩪰 DžV9FWbeڻTJR1SOO)rpXE=a!N",V.Rta`炋nʼnb>x=u>:ɕBX-䥥L,7#gRiK@/?c^TRքYg"/W׋͐+-MnӋNߑVwWU Li-}7UՊ2WCg_mb)4۰͌) *l!l;71V o?waj=nJ?o i3hk Ư m"\6M; ݆shhVb/%6-Rl 3l^Jھ3De`m桦-= o~jZ2Gv'1#"n<!Z#VtV G"?jE,7ضS0ђ0PT6nVof  ;^3Z8~eYf7FpzՋY\d7;+ je:KRfqgK%¦D mV6{]NV)@+3[U/5$O}No{5#n̘}}Nmw[Gۭm3&+Wwr:`Pq1Q TɶYY!8k>\~Nr`,{H `5-s4VBFXưџe ݑG̗c[t͏fI9hl$c)]yvvim;{l!鶝%\;llX9pKǚC!Vy]]`}uhtbsiZ9i5^͋>TlAbJLl8 ''[M-64V3(U$5KÞ,)]2ֺKNa]1l fWwž/5B#fuw뮘x5Wlv.\<8|^|Xr]<dhi> a"_\},c Z5It4T>2>]h.~2\)e DWT*y̗ ɛ#=yȖMٔr=ofِ%QS4t7XG njgM]z.aYaa``Y``__Y__^^Y^^].]Y]YOYA.A@@Y@@?vF ܝaxJzVAε L|?Bh& Kqt@l%9 9ZzH1S:wŸu1c.DӅS ,`;T]!XmOΫ6PgU6'Zn{&_iUBxP#Z>~ڌiM9%k~mze m>uw(X|JUv(XlP;JK1qZK)`8ۗ(+a2 sαoa9J+{*SRH ֹ2W DD(uX9 ]7X%Ezpj=>% ])װ>IFok ubIURHppXcŀItZ/Ýi!tZƈN|RIݠfQK`q L#Wz,*&`q D/Rw4R`?@Xi,LsVRGRwR ( N-v $&I%-XpR)㝐:LRXz}հ>z}հTV#@ZF Bΐ<݃%$ZD*=~Q\gKqb<)Y- LY8G-$@t_.%B{ ,̤s@X2KH)X3=0+yf\Q.Ya.gEȰ{m'_P,Pdta PPJc?X׽)[XpEY>t^c%X ፡G$oxEwv­kz RsVk}1MlQEqɆ7k؜c?uے5G6~2 iAN:>ڠqwu?3N>ml8`jĠĬUrP;*6?A[{)%^{{n|H  SkBUTFRHY$hP#C@85`*5X)P(4 [GĀDՄZD4oVHrlWTӸ*W86MIZ\I6Xi55M֝ [!WO?t-g[d ZX$ǫI{ivJb"l "[*Z3_kVAC A7YQ Fmo2%h8˓_ V`4WE4 FPKpAŝ }𝦠X][h.~9D彊d ΜC sM 6E0nR>F"~~ X>gg,$tuat1Dc&;d+2,< wr26#![+V3r@#F f$ℨ(+ Zbq"h4#@U05H~3kWBs3[ssfV-'|MŖw(B(` qPh"^r $,?OMMBr $ߨo(QT3?#+D^ a= o$ -CY9 L zKHXB#) etjy,=X|Eżc B^Uf«*ՀBbB͹N*EKqFT dbnJUXŬPh"ylu**T/8`ms@4#qmTHŲ|WM($\_/א|4Oibim7uvx BC\:[*⯩;5݋^t?a*}Ct2zo:$~M7K{,)Q>Zzm`wuTRܲՏKl4ɜT9f,~EUob5hhᡕ!߁UZ{&/觓*cK1’`Iū~ƚ~]A~i U>åj uUgO!ItmBx$wS^A?Ȗ=vhhby]hGCtTǩvBgm8\YC{A8+hx0h%^3v`Mz`5viX}5=&BX6X݇jc #/rpy&g MO9>$2ml wH|66s`UybWq}_`}s`ݯ?Ф?{rb'X7dAB39[9 E/\Xez%Kd) xj:h/)"X9 ` _9 w`匫3*CW9u)1>`5 _xj>BKd#`ꦾ7`/"t(NZWHWXⅱ^3z,tzkʼy-:~5W flBĝ+b&㌅P_uϲy[<`ڊC?c:jq=`ӭtv)t3f3Y=`5 N2? ⨃Wtuf%.'7 n&~.i E7^bnS*5{q~W~,,h YEIgϠXP@2q4XXUwk.)n:Uv#*wԏzALT̀=jXx=%|ꏵ) Ҏf;`7R:b "]fOM[}Pv iXkI}/MxA%s^)붲.iVgaDjl >4M#DDcDX";06h5cԂ7Aǩ8EKVA?X6tE)Z>:FO*nވմ+TFClp&iX[6C>B.8݉4aXy8}6ѻp j̐M 2Xk9Ʊ0|ָ_2Z0|xG~swbAS[#/~Rga' Q h6F+Da1<7*6`fO֋~^=6*V+?[Ab~%-t6y)r/Hg Tq_WFU1K W47V5j<†tb]-/ wH}8FZ!6H. >N`y7Ǿr@ {g,lP98Wf+,35baÈjJn;~? _*Oz w5ۢ9ߐYhdAcm,4JzR-lX,%Z'1A,XxPTlPG_u1 ,gu9`jXAۈA#ޡY0b9pXlcEC.ˡ†&Ј 6V5i܇z炅 zxmq@dbem:ك:G,Ga\ƴfcžaĩZ0_4߰8 C%,f0x/4fd}'T7dB6TMȍ-;$gYUl@W;Zz ޚbZkKgJ78I9ъ= '#ֶY1AeLcUG3OFqצg!޲UW[9>6MogQ-^@>xZe"DG3MA;<e>EZz:";` q%K}Sbxlv\Sw1ztb91·e=`ɒςի4;Ɂ-4>ɁM4cE Bw` ]-dC'2z[7-Vx9tf>`Mp"V>T^]8SL&Vz>mݩXΫl;_}4&KoW|2LG#␳mmZg\QRYEb5f,C"J<oy49h@~) !%9:3AqۑcL vznjaՏZMWVUzYUFqőViX>XdžD6Vx +x /I=XV>cZL|om S=`;`1 nUElӎ^<p!zuoE/6v!Oʛ%YP>eOQ%IŁ A@-2B-j2+PS{p)S&o/ A6>yUU>r%fruli홋^FQbA;MW|jkX7Uz/GJS#ĸ˴ls~`ap6HYj(`ޗ#YdQW %?a-)d^˖f^MϪy?_5bW؏?jػ/WEϲ`__x~_P#֬:-X,ڶI]-YRKV.|O+?׀0>À=B"w#}FY&j݂V~^Ӵml5>b < iHg?qWE0^MHCe6%#6/aSaYl[A ᰋ$]mJnxNEDzƅKwaH JK!zFLlxe<51?|==gs O~OAp_o@,vixUEaa2kp%9 ˗c0tJ*ן Y|AZ\t~7 Nc:~:kp)^f_( 4B<RQFa,]oNB{ a#q)8Sj <~q˾6sgR 5hy8ٷ*jpYoa7v 湣\mY~ݯT l *3j ZoYIlU&']9{ObWNK80E_6r`ԉ &P9.l 0s:yvsUfrګL,.<9ȑ"&f#S *|hB;}!=BUCGfi"+'ΣbkA "q3jrjЇqx7yD&  >|u2/LLyJmab7Go, A0isW7׊=3`b-;a|0OϞM/z5{MPaKoY!-M佋@FnusU|6O=hA-A5(+OiS۠M]z/> w]sbuYld3W՟@ Ӭ(`h F"O~l2 ?6x0R! c`?F& _I6?ěX2ͻmz@8'DT4Naθk3UwWv(ݢjjcv e0-ZZ)9ʶ;_V\`5{1;`Otsf{L[ `EJބ.^êu:+nnP4n[gRR9󟙴]ٰf;\/Cڻ_d>ܗ ŬbzR?ua~ƀهѷ G&^2KzŎY>+>Иx=TcE78;9\܈Qf`H:vc) +-RnEo ChNsU䎻M']ņ?YVp|h>&^)NlБ;{/ud#?g9Ycc|^W>[y05z•нODr|'W ]258} OZgyl=S䐀ƀ&_g&P~HԤ5 a!9kҥ,K: (0`rtTU]͹8+Pa ̶{c!Pvuk0tOiȝ 8.m{aXh&} (H^.2,h,%xf@9q8& flPQeqZ#G`[f`/^q,fxFJ~G([caTEv6yt ~ l> 4_eUlԢ2m fQ8_ ّ"mZ2  ja=k:-}Ww0 v0t:[˗ ܵaM{Jf4x@?_~gz|GY:km#(#d ?d<J(BvE$ϵC AEoj22!@Gzl>N>_DA@f,Z8f !OH4d'In"Ãeޜ1kMY g 9 .D %hËxtA:c Lx xW-4d?/!NpiN1;nFjcH8p|Lđ8҈7AJ^" ]Nw0B(֑iq __%쥵`zC˕>%Xw X+Q">DHAMo/hO* "h(V3zeMuBz`2 I0à {.>L]dKN m쏸Ʒ%kL w$[|ObcKa|,q;0,Gfo:I{`atr9z2>_DP;E]mTVƵ StלF”5d&hz9S[YT1G \bx()l@(&drhk|n3 K$d= mWeWjr}F'xG 1vٌ+:VTK5N,#e 3a U 79 I]֗M/wś3Mt޳,JoLj6p$O۔񬛸v,! +?nqc%D#_jSnXeWa\uk5q 4pMJ?m8 pbk=` -h8A`CkTAAW]˂G@} ZdN@\g'}j 9+'h};)WvS:хHs t4ЃplMyZ1qRbW(ZW-i18PW ,O;}YVͷWrUKa%|HTNb FBH4Ї)7%!7޽wa^ S/@ƒyi;b,g߀?.8ZW3C/g 6sKMri iJR e??o.V1Rv.Uu~č[EZ%X'0Ȳ*Z"Խ Zo4ـh?h"Ov(X'Bi {|鞀ao{B(SWtW~65~43 ܩ=vK>JtD!PR}霷7Sg>3<#F s\ݹ|-T%/5}##4Vsas`4ISD"x3 CS ArX6JeP O6@#U[gY2\hH> =of7k #N(Jb%HDPCd>v,~w(ck-\0Je4LPVja<@H1 Uo2 "0PjFZE'} =(Nߑ{6M&6akvب5l[q1[ɗJ5~h~o\ǿР[i/b s|s -=ͲN(Bju/֣mg{ X˂朤]^bhLoUUm^+eVFXɅZ=#"#&CX4räl 8.XOz%5ěgqm!>ˇ S`8h;a` xu_tW&->o$<OZ#֩sg)ؿ p:$qxė;* :n`h.cx,c4pC8F=FȓbV]REpw>P2;Jboݷ}DfN* <* ?HPAxPTAxP= Hxsfd eߐ'tP1oJ}wRPIl/z#~l1?bRyvkjB4RPGX$;09{5fYbtWlfv.R E.A*/JE pnl5g0e7t+&|b&5maۤ^&ZJO#U= ww&:z0pc{لݘ䗬#E&Es@665QGvKՑ~vf|h&>94{lջ;~)ފ!hfl_M?@zYO V¾9ɬeuNA"a8'nl#Ty>r$'@"aM%Óz9 !{гPA) _q ~,'%{Qp}? )on_ R*?F+cݗ],] z%+ TtۢU[ݭ[hw W+nvpEY^5G5 gKGx2oZ=dqG?ċl)SA#j;>g>p'/W93O6v_4u6So᭒nH/w~X߁/&nK7dȍ̑8;>?G߼'wpZRKx40#s1Ud^(njT<%+_I=֐2 1pya:Ir(]G:-t 6ȅH݌Z="f"tI?{!.Zm&Zc(Չ*Jv;mjukiR'u7Խ/“s`do@R Sfz&+ưfC췚D5-Et8j&S7G1kIl1ų@0X:H}}WPk-ս⪼j="r%dlQ@kb},v:bI|ho>Z+#[&:Ǜyl>ne"/.*q=_X2꾀-n>sZq?Lto\؎B$ o2sa}q邴,:%3sZi). R>:]$n| 44?-11SLuf:p'̺>E<5fNOp.C &Xe#oR*8@/QgezY`\yI鼮LĦ @+(tIСɪ΃ųk ű~+^s :%AjoeE̙߸xsͷڬj@O "QϳbWlwy W|to[5ϪKNGݾZN+?qoU~x]@^"Td1*֬Z7[W+H7[Xp Xa~6&fk!zi}*6~S~{`=,w)جrnDbe_ny W #r.HJWmQ&ڝ5v\J<݄lS;o,Nw{B0@B ^4;x|O w%LsaymF; HWf/ s9 -5|d:ˊ\n!~G2\>2:3 e ֏Xs ؁Ֆ7Q C(n$V]ܕuese*d']6X{n /xfΞi'74-C.6 {6FVj^i nUh L ^W+pha Cbc^%n:GB1X{=m^/×93/׽uq ~Et˴7tQK62Ԛ(?R>#k~hxwTr(x}ʘYM m~DnfiAaJns. @pFϹz r7͙]Dž撑Yx9@cvDJPܼ6ccm8;01W5 Dinnjrpwa81pd۷u@yXZ.8E׿Qa`6N 7ko *rG~3Kj.|urp?]PK+`i*PK Y[#Metadata/filament_settings_1.config? 0w?E bz6W.w7M޻ܭB2U^fP%X/5X};ms=IX J/jq8ؓ&ku~+K[6fx$UPI \ +3h_mcs ]#n\߲ 4ҟ/w#ųr g6IHMPKVOPK Y[#Metadata/filament_settings_2.configN0>V`KX:"d΅:uEw 7YJZ`p;G ՜y6ËC S5GK.. m(y%o) MڶI4F܈s8SǸ0; B*RxC)FŖj](ame N;.,imDKo7e#ROkJn>W_PK7$PK Y[Metadata/model_settings.config]sF 1CI/zכN;dd65  `k[$y$〤=^} bnnM=V2\M|uq^OFj5_}erd)mnSliMyq* -ze6˻fnyfb[_竦fŢC>.v63q͡ ^_Ue/*USxGӲh6c?>1FZ(\t8)Z~:[R]m7rLu?þzϧOk.b,{RfuٌO*qBFw<}w sj\TV[#AI?ޔ2n&+WeMWNJȦU*7EC礘M{vxS.ÆSm[>r\ r\ M.~₸ . ₸ .} @. @. b<=/Y%@Ѕ/Yq u]@&`. ] L.}₸ . ₸ .[\₸ . ₸ . ~3A. @. Ʌ . ₸ . b;E X.|bꂺǭt]肹`.Lt\ M"z @. @.rTгXٽ9=KtQtQMngŢ.l\obx6J|zj_]7_=TWY}k{瓛s/ojZ."Iy&Þp5\5>c2=_jЖ1蠘vA;=c$gІ)4WC1j)as5FIc AJ6[bk5l&X;@$kRas5I96\MRְٚ$(iٚ$(iٚ$(iٚ$(ru\YGI+v:J]kQ]ludrלz}4fGE]>cZ[f]ػk$u)VVS:K26ZȤ6XѺ~Jn8Ȇ/ziAzծ:KhJorчCXDOل8} DVQh0]L )i-׹TWjptJ }L շ& )擄>Sڥwtm"CބO31>" 4!j+H>v$%C0I5!DwF !pRhѻ&BN <4E*Ո\E-uJH~O 1mO )?I?IT&3؎FYKoրt:'ahvDRp( DZGJ=aLZꉄT:sk|(NO螝κPS ZuR*'Tu^Qʜ)cL1BSM6;)#BV)Z%)'-EO)8$ *ksZK V< )m]9ӝv.]p6x)S2is&oL 1x[9Һ< :g?.cDCctTrWg"u**S.Xm6Δ?/^GIͯ>}PK;>!d7PK Y[Metadata/slice_info.config@{byYA%;C7{@"o b2Cŧ 0r%fc#:/2]d/PKy_~PK Y[ _rels/.relsMK0+B[m"/R4I`HRoE .{ ɼ3IvOȇAZS2/(Y.Pӷ)f{ c:FI=&t=6 !NT `[wgfCE?XSry4ug%%Z2ocά>6zdqi/+(S(u1"T2wf8+}bqҝAy͊Qv}]%0;AgZrQsl|PKiC PK Y[[Content_Types].xmlPK Y[)[QazazMetadata/plate_1.pngPK Y[ͷk'k'{Metadata/plate_1_small.pngPK Y[XFVFVtMetadata/plate_no_light_1.pngPK Y[>Metadata/top_1.pngPK Y[ & &?Metadata/pick_1.pngPK Y[[Metadata/plate_1.jsonPK Y[ 1,[+3D/3dmodel.modelPK Y[V3o9K3D/_rels/3dmodel.model.relsPK Y[+3D/Objects/AppArmorRed_29.modelPK Y[+U3D/Objects/AppArmorRed_4.modelPK Y[+3D/Objects/AppArmorRed_27.modelPK Y[+3D/Objects/AppArmorRed_8.modelPK Y[+I3D/Objects/AppArmorRed_28.modelPK Y[+3D/Objects/AppArmorRed_3.modelPK Y[+3D/Objects/AppArmorRed_18.modelPK Y[+>3D/Objects/AppArmorRed_17.modelPK Y[+3D/Objects/AppArmorRed_24.modelPK Y[+3D/Objects/AppArmorRed_6.modelPK Y[+33D/Objects/AppArmorRed_31.modelPK Y[+3D/Objects/AppArmorRed_15.modelPK Y[+3D/Objects/AppArmorRed_32.modelPK Y[+)3D/Objects/AppArmorRed_13.modelPK Y[+{3D/Objects/AppArmorRed_16.modelPK Y[+3D/Objects/AppArmorRed_12.modelPK Y[+3D/Objects/AppArmorRed_30.modelPK Y[+q3D/Objects/AppArmorRed_20.modelPK Y[+3D/Objects/AppArmorRed_19.modelPK Y[+3D/Objects/AppArmorRed_2.modelPK Y[+f3D/Objects/AppArmorRed_10.modelPK Y[+3D/Objects/AppArmorRed_11.modelPK Y[+ 3D/Objects/AppArmorRed_7.modelPK Y[+[3D/Objects/AppArmorRed_21.modelPK Y[+3D/Objects/AppArmorRed_23.modelPK Y[+3D/Objects/AppArmorRed_14.modelPK Y[+Q3D/Objects/AppArmorRed_5.modelPK Y[qGm?3D/Objects/AppArmorRed_1.modelPK Y[ ٢?/3D/Objects/AppArmorRed_9.modelPK Y[+`i* oMetadata/project_settings.configPK Y[VO#YMetadata/filament_settings_1.configPK Y[7$#qMetadata/filament_settings_2.configPK Y[;>!d7Metadata/model_settings.configPK Y[y_~Metadata/slice_info.configPK Y[iC  ߥ_rels/.relsPK,, !apparmor-5.0.2/documentation/keychains/AppArmorLogoShadowFlat/AppArmorLogoShadowFlat.obj000066400000000000000000003476721522511161100315360ustar00rootroot00000000000000# Created by FreeCAD v 7.296506 -36.692966 0.500000 v 8.514620 -35.476959 0.500000 v 8.514620 -35.476959 1.000000 v 7.296506 -36.692966 1.000000 v 6.324892 -35.727196 0.500000 v 5.392566 -34.723518 0.500000 v 3.909492 -32.914867 0.500000 v 2.769804 -31.239882 0.500000 v 1.947016 -29.716267 0.500000 v 1.029239 -26.120234 0.500000 v 1.405360 -28.343626 0.500000 v 1.132001 -27.245932 0.500000 v 1.029240 -20.506203 0.500000 v 15.999998 -42.962337 1.000000 v 15.999998 -42.962337 0.000000 v 15.260092 -42.572731 1.000000 v 13.764388 -41.703697 0.000000 v 14.514069 -42.152603 1.000000 v 13.734939 -41.685524 1.000000 v 12.942796 -41.181595 1.000000 v 11.518011 -40.201790 0.000000 v 12.142940 -40.643219 1.000000 v 11.341071 -40.073448 1.000000 v 10.543138 -39.475895 1.000000 v 9.154169 -38.358936 0.000000 v 9.833358 -38.917732 1.000000 v 9.136038 -38.343678 1.000000 v 8.455268 -37.757084 1.000000 v 7.869088 -37.229717 1.000000 v 7.159270 -36.560928 0.000000 v 5.394211 -34.725368 0.000000 v 3.924014 -32.934235 0.000000 v 2.774636 -31.247803 0.000000 v 1.940135 -29.701513 0.000000 v 1.394559 -28.309425 0.000000 v 1.122868 -27.192978 0.000000 v 1.029239 -26.120234 0.000000 v 17.865017 -41.910923 1.000000 v 19.727621 -40.669075 1.000000 v 20.989883 -30.487076 1.000000 v 24.227337 -36.962704 1.000000 v 21.906021 -38.995846 1.000000 v 11.381945 -40.095016 1.000000 v 10.815524 -39.675224 1.000000 v 8.001699 -37.343979 1.000000 v 7.301169 -36.690411 1.000000 v 8.718463 -37.979927 1.000000 v 9.475924 -38.619247 1.000000 v 10.256423 -39.245579 1.000000 v 1.029239 -5.535439 1.000000 v 1.029240 -20.506203 1.000000 v 1.029239 -5.535439 0.000000 v 4.761098 -35.246090 0.000000 v 6.717828 -37.214886 0.000000 v 8.988346 -39.187218 0.000000 v 3.161567 -33.357559 0.000000 v 11.477942 -41.058769 0.000000 v 1.922523 -31.596481 0.000000 v 13.747661 -42.535645 0.000000 v 1.021943 -29.985332 0.000000 v 16.000000 -43.789474 0.000000 v 0.425399 -28.528395 0.000000 v 0.109168 -27.295216 0.000000 v 0.000000 -26.106907 0.000000 v 18.391722 -42.451046 0.000000 v 17.865017 -41.910923 0.000000 v 19.727621 -40.669075 0.000000 v 20.798811 -40.864109 0.000000 v 21.906021 -38.995846 0.000000 v 23.054428 -39.152580 0.000000 v 24.227337 -36.962704 0.000000 v 25.173744 -37.315845 0.000000 v 26.107744 -35.093243 0.000000 v 27.056599 -35.442902 0.000000 v 0.000000 -6.398148 0.000000 v 27.774689 -33.171108 0.000000 v 28.638821 -33.612099 0.000000 v 0.509550 -5.954788 0.000000 v 0.918158 -5.416970 0.000000 v 1.208706 -4.807224 0.000000 v 29.038111 -31.425402 0.000000 v 29.894957 -31.880430 0.000000 v 4.650217 -4.760341 0.000000 v 29.948507 -29.848698 0.000000 v 4.583811 -4.072606 0.000000 v 30.830366 -30.281687 0.000000 v 30.547186 -28.437580 0.000000 v 31.469709 -28.829988 0.000000 v 7.743419 -3.991212 0.000000 v 30.861818 -27.256960 0.000000 v 31.846458 -27.525003 0.000000 v 7.958917 -3.194899 0.000000 v 30.970758 -26.120234 0.000000 v 31.961023 -26.807238 0.000000 v 32.000000 -26.106907 0.000000 v 10.489840 -3.175107 0.000000 v 10.911841 -2.239217 0.000000 v 12.965134 -2.269320 0.000000 v 13.455921 -1.227151 0.000000 v 14.480927 -1.610794 0.000000 v 16.000000 0.000000 0.000000 v 15.999998 -0.857075 0.000000 v 18.482809 -1.200278 0.000000 v 18.435408 -2.026988 0.000000 v 20.965618 -2.194827 0.000000 v 20.863176 -2.979459 0.000000 v 24.000000 -3.182964 0.000000 v 23.694893 -3.873033 0.000000 v 27.200001 -4.021627 0.000000 v 27.326279 -4.775409 0.000000 v 30.970758 -5.535439 0.000000 v 32.000000 -5.052633 0.000000 v 30.970758 -26.120234 1.000000 v 30.861818 -27.256960 1.000000 v 30.547186 -28.437580 1.000000 v 29.948507 -29.848698 1.000000 v 29.038111 -31.425402 1.000000 v 27.774689 -33.171108 1.000000 v 26.107744 -35.093243 1.000000 v 30.970760 -20.506199 1.000000 v 30.970760 -20.506199 0.500000 v 11.381945 -40.095016 0.500000 v 9.513083 -38.649792 0.500000 v 8.385881 -37.688759 0.500000 v 7.301169 -36.690411 0.500000 v 4.650217 -4.760341 1.000000 v 7.743419 -3.991212 1.000000 v 12.257585 -13.020544 1.000000 v 4.771930 -20.506199 1.000000 v 10.385965 -24.248888 1.000000 v 8.514620 -27.991579 1.000000 v 14.930710 -18.367388 1.000000 v 11.321638 -22.377544 1.000000 v 14.128757 -16.763306 1.000000 v 5.707603 -22.377544 1.000000 v 6.643275 -24.248888 1.000000 v 32.000000 -5.052633 3.000000 v 27.200001 -4.021627 3.000000 v 24.000000 -3.182964 3.000000 v 20.965618 -2.194827 3.000000 v 18.482809 -1.200278 3.000000 v 16.000000 0.000000 3.000000 v 32.000000 -25.279251 2.500000 v 32.000000 -25.094362 2.500000 v 32.000000 -26.106907 3.000000 v 32.000000 -25.279251 2.000000 v 32.000000 -25.094362 2.000000 v 16.000000 -43.789474 3.000000 v 18.391722 -42.451046 3.000000 v 20.798811 -40.864109 3.000000 v 23.054428 -39.152580 3.000000 v 25.173744 -37.315845 3.000000 v 27.056599 -35.442902 3.000000 v 28.638821 -33.612099 3.000000 v 29.894957 -31.880430 3.000000 v 30.830366 -30.281687 3.000000 v 31.469709 -28.829988 3.000000 v 31.846458 -27.525003 3.000000 v 31.961023 -26.807238 3.000000 v 0.000000 -26.106907 3.000000 v 0.109168 -27.295216 3.000000 v 0.425399 -28.528395 3.000000 v 1.021943 -29.985332 3.000000 v 1.922523 -31.596481 3.000000 v 3.161567 -33.357559 3.000000 v 4.761098 -35.246090 3.000000 v 6.717828 -37.214886 3.000000 v 8.988346 -39.187218 3.000000 v 11.477942 -41.058769 3.000000 v 13.747661 -42.535645 3.000000 v -0.000001 -25.094362 1.400000 v 0.000000 -6.398148 3.000000 v -0.000000 -25.282578 1.400000 v -0.000001 -25.094362 0.500000 v -0.000000 -25.282560 0.500000 v 0.000000 -5.052633 0.000000 v 13.455921 -1.227151 3.000000 v 10.911841 -2.239217 3.000000 v 7.958917 -3.194899 3.000000 v 4.583811 -4.072606 3.000000 v 1.208706 -4.807224 3.000000 v 30.970758 -5.535439 1.000000 v 15.999998 -0.857075 1.000000 v 18.435408 -2.026988 1.000000 v 20.863176 -2.979459 1.000000 v 23.694893 -3.873033 1.000000 v 27.326279 -4.775409 1.000000 v 10.489840 -3.175107 1.000000 v 12.965134 -2.269320 1.000000 v 14.480927 -1.610794 1.000000 v 12.257310 -35.476959 0.500000 v 12.725145 -34.541286 0.500000 v 16.000000 -27.991579 0.500000 v 27.228071 -20.506199 0.500000 v 20.678362 -18.634853 0.500000 v 26.292398 -18.634853 0.500000 v 23.485380 -5.535439 1.000000 v 19.742691 -5.535439 1.000000 v 15.999998 -20.506203 1.000000 v 4.771930 -20.506199 0.500000 v 19.742691 -5.535439 0.500000 v 5.707603 -22.377544 0.500000 v 11.321638 -22.377544 0.500000 v 15.064327 -22.377544 1.000000 v 12.257310 -27.991579 1.000000 v 8.514620 -27.991579 0.500000 v 10.385965 -24.248888 0.500000 v 6.643275 -24.248888 0.500000 v 5.892253 -35.093243 3.000000 v 7.772661 -36.962704 3.000000 v 4.225310 -33.171108 3.000000 v 10.093978 -38.995846 3.000000 v 2.961887 -31.425402 3.000000 v 12.272378 -40.669075 3.000000 v 2.051491 -29.848698 3.000000 v 14.134980 -41.910923 3.000000 v 1.452812 -28.437580 3.000000 v 16.000000 -42.962337 3.000000 v 1.138179 -27.256960 3.000000 v 1.029240 -26.120234 3.000000 v 18.235609 -41.703697 3.000000 v 20.481987 -40.201790 3.000000 v 22.845829 -38.358936 3.000000 v 24.840727 -36.560928 3.000000 v 26.605787 -34.725368 3.000000 v 28.075985 -32.934235 3.000000 v 0.918158 -5.416970 3.000000 v 0.509550 -5.954788 3.000000 v 1.029240 -5.535439 3.000000 v 29.225363 -31.247803 3.000000 v 4.673719 -4.775409 3.000000 v 30.059864 -29.701513 3.000000 v 30.605438 -28.309425 3.000000 v 30.877131 -27.192978 3.000000 v 8.305105 -3.873033 3.000000 v 30.970758 -26.120234 3.000000 v 11.136821 -2.979459 3.000000 v 13.564590 -2.026988 3.000000 v 16.000000 -0.857075 3.000000 v 17.519070 -1.610794 3.000000 v 19.034864 -2.269320 3.000000 v 21.510159 -3.175107 3.000000 v 24.256578 -3.991212 3.000000 v 27.349781 -4.760341 3.000000 v 30.970758 -5.535439 3.000000 v 1.303771 -2.908893 3.000000 v 1.400000 -3.700000 3.000000 v 1.400000 -3.700000 0.000000 v 1.303771 -2.908893 0.000000 v 1.020696 -2.163923 0.000000 v 1.020696 -2.163923 3.000000 v 0.567285 -1.508538 3.000000 v 0.567285 -1.508538 0.000000 v -0.030020 -0.980961 3.000000 v -0.030020 -0.980961 0.000000 v -0.736383 -0.611959 3.000000 v -0.736383 -0.611959 0.000000 v -1.510609 -0.423054 3.000000 v -1.510609 -0.423054 0.000000 v -2.307544 -0.425262 3.000000 v -2.307544 -0.425262 0.000000 v -3.080712 -0.618455 0.000000 v -3.080712 -0.618455 3.000000 v -3.785019 -0.991365 3.000000 v -3.785019 -0.991365 0.000000 v -4.379391 -1.522244 3.000000 v -4.379391 -1.522244 0.000000 v -4.829164 -2.180132 3.000000 v -4.829164 -2.180132 0.000000 v -5.108106 -2.926658 3.000000 v -5.108106 -2.926658 0.000000 v -5.199949 -3.718287 3.000000 v -5.199949 -3.718287 0.000000 v -5.099338 -4.508849 3.000000 v -5.099338 -4.508849 0.000000 v -4.812140 -5.252238 0.000000 v -4.812140 -5.252238 3.000000 v -4.355103 -5.905100 3.000000 v -4.355103 -5.905100 0.000000 v -3.754884 -6.429360 3.000000 v -3.754884 -6.429360 0.000000 v -3.046487 -6.794442 0.000000 v -3.046487 -6.794442 3.000000 v -2.271226 -6.979053 3.000000 v -2.271226 -6.979053 0.000000 v -1.474315 -6.972429 0.000000 v -1.474315 -6.972429 3.000000 v -0.702230 -6.774955 0.000000 v -0.702230 -6.774955 3.000000 v -0.000001 -25.279251 0.500000 v -0.000001 -25.279251 1.400000 v -3.247319 -4.893621 0.000000 v -2.922517 -5.181371 0.000000 v -3.493821 -4.536502 0.000000 v -2.538289 -5.383029 0.000000 v -3.647695 -4.130768 0.000000 v -2.116966 -5.486876 0.000000 v -3.700000 -3.700000 0.000000 v -1.683034 -5.486876 0.000000 v -3.647695 -3.269232 0.000000 v -1.261711 -5.383029 0.000000 v -3.493821 -2.863498 0.000000 v -0.877483 -5.181371 0.000000 v -3.247319 -2.506379 0.000000 v -0.552681 -4.893621 0.000000 v -0.306179 -4.536502 0.000000 v -2.922517 -2.218629 0.000000 v -0.152305 -4.130768 0.000000 v -2.538289 -2.016971 0.000000 v 1.314518 -4.446240 0.000000 v -2.116966 -1.913124 0.000000 v 1.378560 -4.075560 0.000000 v -0.100000 -3.700000 0.000000 v -0.152305 -3.269232 0.000000 v -1.683034 -1.913124 0.000000 v -0.306179 -2.863498 0.000000 v -1.261711 -2.016971 0.000000 v -0.552681 -2.506379 0.000000 v -0.877483 -2.218629 0.000000 v 1.314518 -4.446240 3.000000 v 1.378560 -4.075560 3.000000 v 27.228071 -20.506199 1.000000 v 26.292398 -18.634853 1.000000 v 20.678362 -18.634853 1.000000 v 17.871241 -24.249100 1.000000 v 16.000000 -27.991579 1.000000 v 12.725146 -34.541286 1.000000 v 12.725145 -34.541286 1.000000 v 12.257310 -35.476959 1.000000 v 25.356726 -16.763510 1.000000 v 21.614035 -16.763510 1.000000 v 23.485380 -13.020819 1.000000 v 0.000000 -5.052633 3.000000 v 13.564590 -2.026988 2.000000 v 16.000000 -0.857075 2.000000 v 11.136821 -2.979459 2.000000 v 8.305105 -3.873033 2.000000 v 4.673719 -4.775409 2.000000 v 1.029240 -5.535439 2.000000 v 21.510159 -3.175107 2.000000 v 24.256578 -3.991212 2.000000 v 19.034864 -2.269320 2.000000 v 17.519070 -1.610794 2.000000 v 1.029240 -20.506199 2.500000 v 1.029240 -20.506199 2.000000 v 1.029240 -26.120234 2.000000 v 27.349781 -4.760341 2.000000 v 30.970758 -5.535439 2.000000 v 1.138179 -27.256960 2.000000 v 1.452812 -28.437580 2.000000 v 2.051491 -29.848698 2.000000 v 2.961887 -31.425402 2.000000 v 4.225310 -33.171108 2.000000 v 5.892253 -35.093243 2.000000 v 7.772661 -36.962704 2.000000 v 30.970758 -20.506199 2.500000 v 30.970758 -26.120234 2.500000 v 30.970758 -20.506199 2.000000 v 10.093978 -38.995846 2.000000 v 12.272378 -40.669075 2.000000 v 14.134980 -41.910923 2.000000 v 16.000000 -42.962337 2.000000 v 18.235609 -41.703697 2.000000 v 20.481987 -40.201790 2.000000 v 22.652317 -38.520687 2.000000 v 24.703493 -36.692966 2.500000 v 24.703493 -36.692966 2.000000 v 25.675106 -35.727196 2.500000 v 26.607433 -34.723518 2.500000 v 28.090506 -32.914867 2.500000 v 29.230194 -31.239882 2.500000 v 30.052982 -29.716267 2.500000 v 30.594639 -28.343626 2.500000 v 30.867996 -27.245932 2.500000 v -3.247319 -4.893621 3.000000 v -2.922517 -5.181371 3.000000 v -3.493821 -4.536502 3.000000 v -2.538289 -5.383029 3.000000 v -3.647695 -4.130768 3.000000 v -2.116966 -5.486876 3.000000 v -3.700000 -3.700000 3.000000 v -1.683034 -5.486876 3.000000 v -3.647695 -3.269232 3.000000 v -1.261711 -5.383029 3.000000 v -3.493821 -2.863498 3.000000 v -0.877483 -5.181371 3.000000 v -3.247319 -2.506379 3.000000 v -0.552681 -4.893621 3.000000 v -0.306179 -4.536502 3.000000 v -2.922517 -2.218629 3.000000 v -0.152305 -4.130768 3.000000 v -2.538289 -2.016971 3.000000 v -2.116966 -1.913124 3.000000 v -0.100000 -3.700000 3.000000 v -0.152305 -3.269232 3.000000 v -1.683034 -1.913124 3.000000 v -0.306179 -2.863498 3.000000 v -1.261711 -2.016971 3.000000 v -0.552681 -2.506379 3.000000 v -0.877483 -2.218629 3.000000 v -0.100000 -3.700000 3.000000 v -0.100000 -3.700000 0.000000 v 25.356726 -16.763510 0.500000 v 23.485380 -13.020819 0.500000 v 21.614035 -16.763510 0.500000 v 8.514620 -5.535439 2.000000 v 12.257310 -5.535439 2.000000 v 17.069290 -18.367386 2.000000 v 19.742414 -13.020543 2.000000 v 16.000000 -20.506201 2.000000 v 11.010116 -30.487076 2.000000 v 27.228071 -20.506199 2.000000 v 23.485380 -35.476959 2.500000 v 27.228071 -20.506199 2.500000 v 12.257310 -5.535439 2.500000 v 23.485380 -35.476959 2.000000 v 16.935673 -22.377544 2.000000 v 16.935673 -22.377544 2.500000 v 8.514620 -5.535439 2.500000 v 20.618055 -40.095016 2.500000 v 20.618055 -40.095016 2.000000 v 22.486917 -38.649792 2.000000 v 22.486917 -38.649792 2.500000 v 23.614119 -37.688759 2.500000 v 23.614119 -37.688759 2.000000 v 24.698830 -36.690411 2.000000 v 24.698830 -36.690411 2.500000 v 23.485380 -5.535439 0.500000 v 8.514620 -35.476959 0.000000 v 23.485380 -5.535439 0.000000 v 30.970760 -20.506199 0.000000 v 1.029241 -20.506199 0.000000 v 1.029241 -20.506199 1.000000 v 10.385965 -24.248888 0.000000 v 8.514620 -27.991579 0.000000 v 11.321638 -22.377544 0.000000 v 5.707603 -22.377544 0.000000 v 6.643275 -24.248888 0.000000 v 19.742691 -5.535439 0.000000 v 14.128757 -16.763306 0.000000 v 4.771930 -20.506199 0.000000 v 28.410444 -5.010409 0.000000 v 25.858397 -4.435955 0.000000 v 22.983425 -3.667320 0.000000 v 20.442276 -2.827936 0.000000 v 18.224457 -1.934699 0.000000 v 28.410444 -5.010409 1.000000 v 25.858397 -4.435955 1.000000 v 22.983425 -3.667320 1.000000 v 20.442276 -2.827936 1.000000 v 18.224457 -1.934699 1.000000 v 12.257585 -13.020544 1.000000 v 14.128757 -16.763306 0.500000 v 12.257585 -13.020544 0.500000 v 13.775541 -1.934699 2.000000 v 13.775541 -1.934699 3.000000 v 11.557722 -2.827936 2.000000 v 11.557722 -2.827936 3.000000 v 9.016573 -3.667320 2.000000 v 9.016573 -3.667320 3.000000 v 6.141601 -4.435955 2.000000 v 6.141601 -4.435955 3.000000 v 3.589554 -5.010409 3.000000 v 3.589554 -5.010409 2.000000 v 1.029240 -20.506199 2.000000 v 17.069290 -18.367386 2.000000 v 19.742414 -13.020543 2.000000 v 16.000000 -20.506201 2.000000 v 1.120316 -27.156445 3.000000 v 1.384263 -28.230013 3.000000 v 16.000000 -20.506201 3.000000 v 1.880613 -29.494473 3.000000 v 2.630348 -30.896257 3.000000 v 3.666219 -32.440716 3.000000 v 5.016049 -34.122734 3.000000 v 6.338480 -35.560352 3.000000 v 7.772661 -36.962704 3.000000 v 8.514620 -5.535439 3.000000 v 12.257310 -5.535439 3.000000 v 17.871243 -16.763304 3.000000 v 7.772661 -36.962704 2.000000 v 11.010116 -30.487076 2.500000 v 11.010116 -30.487076 2.000000 v 16.000000 -20.506201 2.500000 v 17.069290 -18.367386 2.500000 v 19.742414 -13.020543 2.500000 v 1.120316 -27.156445 2.000000 v 1.384263 -28.230013 2.000000 v 1.880613 -29.494473 2.000000 v 2.630348 -30.896257 2.000000 v 3.666219 -32.440716 2.000000 v 5.016049 -34.122734 2.000000 v 6.338480 -35.560352 2.000000 v 20.678362 -22.377544 3.000000 v 20.678362 -22.377544 4.000000 v 12.257310 -5.535439 4.000000 v 8.514620 -5.535439 4.000000 v 23.485380 -35.476959 4.000000 v 23.485380 -35.476959 3.000000 v 23.485380 -27.991579 3.000000 v 21.614035 -24.248888 3.000000 v 26.292398 -22.377544 3.000000 v 25.356726 -24.248888 3.000000 v 27.228071 -20.506199 3.000000 v 30.970758 -20.506199 3.000000 v 26.292398 -22.377544 4.000000 v 21.614035 -24.248888 4.000000 v 23.485380 -27.991579 4.000000 v 25.356726 -24.248888 4.000000 v 30.970758 -20.506199 4.000000 v 27.228071 -20.506199 4.000000 v 27.228071 -20.506199 0.000000 v 30.879683 -27.156445 0.000000 v 30.615736 -28.230013 0.000000 v 30.119385 -29.494473 0.000000 v 17.871241 -24.249100 0.000000 v 29.369650 -30.896257 0.000000 v 28.333778 -32.440716 0.000000 v 26.983950 -34.122734 0.000000 v 25.661518 -35.560352 0.000000 v 20.678362 -18.634853 0.000000 v 26.292398 -18.634853 0.000000 v 30.879683 -27.156445 1.000000 v 30.615736 -28.230013 1.000000 v 30.119385 -29.494473 1.000000 v 29.369650 -30.896257 1.000000 v 28.333778 -32.440716 1.000000 v 26.983950 -34.122734 1.000000 v 25.661518 -35.560352 1.000000 v 20.989883 -30.487076 1.000000 v 20.989883 -30.487076 0.500000 v 17.871241 -24.249100 0.500000 v 23.485380 -13.020819 0.000000 v 25.356726 -16.763510 0.000000 v 21.614035 -16.763510 0.000000 v 19.742691 -35.476959 3.000000 v 19.742691 -35.476959 4.000000 v 19.274855 -34.541286 3.000000 v 19.274855 -34.541286 4.000000 v 19.742691 -27.991579 3.000000 v 19.742691 -27.991579 4.000000 v 18.081884 -41.797897 3.000000 v 14.089836 -41.883003 3.000000 v 12.182858 -40.604893 3.000000 v 20.177053 -40.419483 3.000000 v 22.391109 -38.735790 3.000000 v 9.931569 -38.861954 3.000000 v 16.000000 -27.991579 3.000000 v 24.289610 -37.083168 3.000000 v 14.128759 -24.249098 3.000000 v 26.009392 -35.376877 3.000000 v 27.488701 -33.686821 3.000000 v 19.742689 -27.991579 3.000000 v 28.695377 -32.068748 3.000000 v 29.624147 -30.559818 3.000000 v 30.289021 -29.179514 3.000000 v 30.714880 -27.933231 3.000000 v 30.905458 -27.010988 3.000000 v 16.000000 -20.506201 3.000000 v 5.707602 -18.634853 4.000000 v 1.029240 -20.506199 4.000000 v 4.771930 -20.506199 4.000000 v 19.274855 -34.541286 4.000000 v 6.643275 -16.763510 4.000000 v 16.000000 -27.991579 4.000000 v 11.321638 -18.634853 4.000000 v 10.385965 -16.763510 4.000000 v 8.514620 -13.020819 4.000000 v 19.274855 -34.541286 3.000000 v 19.742689 -27.991579 4.000000 v 30.970758 -20.506199 2.000000 v 16.689169 -42.600319 2.000000 v 17.383795 -42.211704 2.000000 v 18.107697 -41.782158 2.000000 v 18.843710 -41.320274 2.000000 v 19.587627 -40.827892 2.000000 v 20.334913 -40.307335 2.000000 v 21.080801 -39.761383 2.000000 v 21.820433 -39.193192 2.000000 v 22.476688 -38.665722 2.000000 v 23.120472 -38.125786 2.000000 v 23.748573 -37.576118 2.000000 v 24.230778 -37.137691 2.000000 v 24.703493 -36.692966 2.000000 v 25.985651 -35.402092 2.500000 v 27.195189 -34.043137 2.500000 v 28.473124 -32.389240 2.500000 v 29.456257 -30.858490 2.500000 v 30.168722 -29.461512 2.500000 v 30.640276 -28.195932 2.500000 v 30.881010 -27.169754 2.500000 v 9.931569 -38.861954 2.000000 v 12.182858 -40.604893 2.000000 v 14.089836 -41.883003 2.000000 v 19.742414 -13.020543 2.500000 v 19.742414 -13.020543 2.000000 v 17.069290 -18.367386 2.500000 v 16.000000 -20.506201 2.500000 v 17.069290 -18.367386 2.000000 v 16.000000 -20.506201 2.000000 v 11.010116 -30.487076 2.500000 v 11.321638 -18.634853 3.000000 v 1.029240 -20.506199 3.000000 v 5.707602 -18.634853 3.000000 v 4.771930 -20.506199 3.000000 v 6.643275 -16.763510 3.000000 v 8.514620 -13.020819 3.000000 v 10.385965 -16.763510 3.000000 v 21.955498 -39.078640 2.000000 v 24.046656 -37.299919 2.000000 v 23.380327 -37.894127 2.000000 v 22.678232 -38.491726 2.000000 v 21.291996 -39.593796 2.000000 v 12.725146 -34.541286 0.000000 v 12.257310 -35.476959 0.000000 v 12.257310 -27.991579 0.000000 v 17.871241 -24.249100 0.000000 v 16.000000 -27.991579 0.000000 v 13.918114 -41.797897 0.000000 v 17.910162 -41.883003 0.000000 v 19.817141 -40.604893 0.000000 v 11.822945 -40.419483 0.000000 v 9.608890 -38.735790 0.000000 v 12.725145 -34.541286 0.000000 v 22.068430 -38.861954 0.000000 v 24.227337 -36.962704 0.000000 v 7.710388 -37.083168 0.000000 v 5.990606 -35.376877 0.000000 v 4.511297 -33.686821 0.000000 v 3.304621 -32.068748 0.000000 v 1.285118 -27.933231 0.000000 v 1.094540 -27.010988 0.000000 v 1.710978 -29.179514 0.000000 v 2.375852 -30.559818 0.000000 v 12.257311 -27.991579 0.000000 v 12.257311 -27.991579 1.000000 v 15.310829 -42.600319 1.000000 v 14.616204 -42.211704 1.000000 v 13.892303 -41.782158 1.000000 v 13.156288 -41.320274 1.000000 v 12.412370 -40.827892 1.000000 v 11.665086 -40.307335 1.000000 v 10.919197 -39.761383 1.000000 v 10.179564 -39.193192 1.000000 v 9.523310 -38.665722 1.000000 v 8.879525 -38.125786 1.000000 v 8.251425 -37.576118 1.000000 v 7.769221 -37.137691 1.000000 v 6.014348 -35.402092 0.500000 v 4.804810 -34.043137 0.500000 v 3.526875 -32.389240 0.500000 v 2.543740 -30.858490 0.500000 v 1.831276 -29.461512 0.500000 v 1.359722 -28.195932 0.500000 v 1.118988 -27.169754 0.500000 v 24.227337 -36.962704 1.000000 v 22.068430 -38.861954 1.000000 v 19.817141 -40.604893 1.000000 v 17.910162 -41.883003 1.000000 v 20.989883 -30.487076 1.000000 v 10.044503 -39.078640 1.000000 v 7.953344 -37.299919 1.000000 v 8.619673 -37.894127 1.000000 v 9.321768 -38.491726 1.000000 v 10.708005 -39.593796 1.000000 v 12.257585 -13.020544 0.500000 v 14.128757 -16.763306 0.000000 v 14.128757 -16.763306 0.500000 vn 0.706495 -0.707718 0.000000 vn 0.706495 -0.707718 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.465917 0.884828 0.000000 vn 0.490563 0.871095 0.023274 vn 0.514173 0.857687 -0.000445 vn 0.536753 0.843739 0.000474 vn 0.490454 0.871150 0.023485 vn 0.558301 0.829459 0.017249 vn 0.579221 0.815168 -0.002133 vn 0.555665 0.831099 0.022612 vn 0.599421 0.800427 0.003333 vn 0.618033 0.785913 0.019404 vn 0.635569 0.772044 -0.000257 vn 0.652763 0.757562 0.000276 vn 0.614614 0.788370 0.026897 vn 0.668694 0.743269 0.020013 vn 0.683911 0.729565 0.000000 vn 0.669344 0.742640 0.021558 vn 0.680855 0.732389 0.006531 vn 0.704957 0.709223 -0.006202 vn 0.732670 0.680584 -0.000107 vn 0.720016 0.692361 0.047044 vn 0.773270 0.634073 -0.002102 vn 0.772957 0.634458 0.000196 vn 0.826766 0.562545 -0.000923 vn 0.826331 0.563181 0.002184 vn 0.879896 0.475165 0.000975 vn 0.880022 0.474930 0.001902 vn 0.930194 0.367063 -0.001971 vn 0.931040 0.364885 0.004847 vn 0.970354 0.241647 -0.004432 vn 0.971617 0.236446 0.007294 vn 0.995823 0.090906 -0.008562 vn 0.996213 0.086949 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 -0.000002 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn -0.658870 0.752257 0.000000 vn -0.658870 0.752257 0.000000 vn -0.609147 0.793057 0.000000 vn -0.609147 0.793057 0.000000 vn -0.554735 0.832027 0.000000 vn -0.554735 0.832027 0.000000 vn -0.491092 0.871108 0.000000 vn -0.491092 0.871108 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.707107 -0.707107 0.000000 vn 0.707107 -0.707107 0.000000 vn 0.707107 -0.707107 0.000001 vn -0.595430 -0.803407 0.000000 vn -0.609142 -0.792681 -0.024538 vn -0.625871 -0.779925 0.001132 vn -0.611404 -0.790625 -0.033125 vn -0.644995 -0.764185 -0.001251 vn -0.648745 -0.760917 -0.011588 vn -0.663667 -0.748005 0.005856 vn -0.682173 -0.731191 0.000000 vn -0.677151 -0.735729 -0.012969 vn -0.707107 0.707106 0.000000 vn -0.707107 0.707106 0.000000 vn 0.209316 -0.977848 0.000000 vn 0.209316 -0.977848 0.000000 vn 0.241304 -0.970450 0.000000 vn 0.241304 -0.970450 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.210003 0.977701 -0.000000 vn 0.210003 0.977701 0.000000 vn 0.253520 0.967330 -0.000000 vn 0.253520 0.967330 0.000000 vn 0.309642 0.950853 0.000000 vn 0.309642 0.950853 -0.000000 vn 0.371850 0.928293 0.000000 vn 0.371850 0.928293 -0.000000 vn 0.435243 0.900313 0.000000 vn 0.435243 0.900313 -0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 0.488343 -0.872651 0.000000 vn 0.488343 -0.872651 0.000000 vn 0.550421 -0.834887 0.000000 vn 0.550421 -0.834887 0.000000 vn 0.604470 -0.796628 0.000000 vn 0.604470 -0.796628 0.000000 vn 0.654929 -0.755690 0.000000 vn 0.654929 -0.755690 0.000000 vn 0.705238 -0.708970 0.000000 vn 0.705238 -0.708970 0.000000 vn 0.756603 -0.653874 0.000000 vn 0.756603 -0.653874 0.000000 vn 0.809460 -0.587175 0.000000 vn 0.809460 -0.587175 0.000000 vn 0.863118 -0.505002 0.000000 vn 0.863118 -0.505002 0.000000 vn 0.915176 -0.403053 0.000000 vn 0.915176 -0.403053 0.000000 vn 0.960763 -0.277372 0.000000 vn 0.960763 -0.277372 0.000000 vn 0.987500 -0.157618 0.000000 vn 0.998455 -0.055569 0.000000 vn 0.987500 -0.157618 0.000000 vn 0.998455 -0.055569 0.000000 vn -0.995807 -0.091483 0.000000 vn -0.995807 -0.091483 0.000000 vn -0.968658 -0.248398 0.000000 vn -0.968658 -0.248398 0.000000 vn -0.925430 -0.378918 0.000000 vn -0.872890 -0.487917 -0.000000 vn -0.925430 -0.378918 0.000000 vn -0.872890 -0.487917 0.000000 vn -0.817857 -0.575421 0.000000 vn -0.817857 -0.575421 0.000000 vn -0.763079 -0.646306 0.000000 vn -0.763079 -0.646306 0.000000 vn -0.709277 -0.704930 0.000000 vn -0.709277 -0.704930 0.000000 vn -0.655794 -0.754940 0.000000 vn -0.655794 -0.754940 0.000000 vn -0.600895 -0.799328 0.000000 vn -0.600895 -0.799328 0.000000 vn -0.545393 -0.838181 0.000000 vn -0.486393 -0.873740 -0.000000 vn -0.545393 -0.838181 0.000000 vn -0.486393 -0.873740 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000001 vn -1.000000 -0.000005 -0.000002 vn -1.000000 0.000000 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 -0.000005 0.000008 vn -1.000000 -0.000000 0.000000 vn -1.000000 0.000000 -0.000002 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.434455 0.900694 0.000000 vn -0.434455 0.900694 0.000000 vn -0.369638 0.929176 0.000000 vn -0.369638 0.929176 0.000000 vn -0.307915 0.951414 0.000000 vn -0.307915 0.951414 0.000000 vn -0.251682 0.967810 0.000000 vn -0.251682 0.967810 0.000000 vn -0.212678 0.977122 0.000000 vn -0.212678 0.977122 0.000000 vn -0.995439 0.095400 0.000000 vn -0.995439 0.095400 0.000000 vn -0.966276 0.257510 0.000000 vn -0.966276 0.257510 0.000000 vn -0.920577 0.390562 0.000000 vn -0.920577 0.390562 0.000000 vn -0.866005 0.500035 0.000000 vn -0.866005 0.500035 0.000000 vn -0.810099 0.586294 0.000000 vn -0.810099 0.586294 0.000000 vn -0.755476 0.655176 0.000000 vn -0.755476 0.655176 0.000000 vn -0.705040 0.709168 0.000000 vn -0.705040 0.709168 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000004 vn -0.433007 -0.901391 0.000000 vn -0.433007 -0.901391 0.000000 vn -0.365222 -0.930920 0.000000 vn -0.365222 -0.930920 0.000000 vn -0.300931 -0.953646 0.000000 vn -0.300931 -0.953646 0.000000 vn -0.241159 -0.970486 0.000000 vn -0.241159 -0.970486 0.000000 vn -0.204151 -0.978939 0.000000 vn -0.204151 -0.978939 0.000000 vn 0.284842 -0.958574 0.000000 vn 0.284842 -0.958574 0.000000 vn 0.343646 -0.939099 0.000000 vn 0.343646 -0.939099 0.000000 vn 0.398464 -0.917184 0.000000 vn 0.398464 -0.917184 0.000000 vn 0.444468 -0.895795 0.000000 vn 0.444468 -0.895795 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.707107 0.707107 0.000000 vn -0.707107 0.707107 0.000000 vn -0.707107 0.707107 -0.000001 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 0.992683 0.120748 0.000000 vn 0.992683 0.120748 0.000000 vn 0.934789 0.355203 0.000000 vn 0.934789 0.355203 0.000000 vn 0.822378 0.568941 0.000000 vn 0.822378 0.568941 0.000000 vn 0.662005 0.749499 0.000000 vn 0.662005 0.749499 0.000000 vn 0.463024 0.886346 0.000000 vn 0.463024 0.886346 0.000000 vn 0.237039 0.971500 0.000000 vn 0.237039 0.971500 0.000000 vn -0.002771 0.999996 0.000000 vn -0.242419 0.970172 0.000000 vn -0.002771 0.999996 0.000000 vn -0.242419 0.970172 0.000000 vn -0.467928 0.883766 0.000000 vn -0.467928 0.883766 0.000000 vn -0.666148 0.745819 0.000000 vn -0.666148 0.745819 0.000000 vn -0.825518 0.564375 0.000000 vn -0.825518 0.564375 0.000000 vn -0.936743 0.350017 0.000000 vn -0.936743 0.350017 0.000000 vn -0.993337 0.115246 0.000000 vn -0.993337 0.115246 0.000000 vn -0.991999 -0.126247 0.000000 vn -0.991999 -0.126247 -0.000000 vn -0.932806 -0.360377 -0.000000 vn -0.932806 -0.360377 -0.000000 vn -0.819212 -0.573490 0.000000 vn -0.819212 -0.573490 -0.000000 vn -0.657842 -0.753156 0.000000 vn -0.657842 -0.753156 -0.000000 vn -0.458105 -0.888898 -0.000000 vn -0.458105 -0.888898 -0.000000 vn -0.231651 -0.972799 0.000000 vn -0.231651 -0.972799 -0.000000 vn 0.008312 -0.999965 0.000000 vn 0.008312 -0.999965 0.000000 vn 0.247791 -0.968813 0.000000 vn 0.247791 -0.968813 0.000000 vn 0.472818 -0.881160 0.000000 vn 0.472818 -0.881160 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn 0.000000 0.000000 1.000000 vn -1.000000 -0.000269 0.000000 vn -1.000000 -0.000268 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.959624 -0.281285 0.000000 vn 0.959624 -0.281285 0.000000 vn 0.985402 -0.170246 0.000000 vn 0.985402 -0.170246 0.000000 vn 0.998374 -0.056996 0.000000 vn 0.998374 -0.056996 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 0.447213 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000003 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000002 vn -0.894428 0.447213 0.000000 vn -0.894428 0.447213 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.433007 -0.901391 0.000000 vn 0.433007 -0.901391 0.000000 vn 0.365222 -0.930920 0.000000 vn 0.365222 -0.930920 0.000000 vn 0.300932 -0.953646 0.000000 vn 0.300932 -0.953646 0.000000 vn 0.241159 -0.970486 0.000000 vn 0.241159 -0.970486 0.000000 vn 0.204151 -0.978939 0.000000 vn 0.204151 -0.978939 0.000000 vn -0.284843 -0.958574 0.000000 vn -0.284843 -0.958574 0.000000 vn -0.343646 -0.939099 0.000000 vn -0.343646 -0.939099 0.000000 vn -0.398464 -0.917184 0.000000 vn -0.398464 -0.917184 0.000000 vn -0.444468 -0.895795 0.000000 vn -0.444468 -0.895795 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 0.000000 vn -0.209316 -0.977848 0.000000 vn -0.209316 -0.977848 0.000000 vn -0.241304 -0.970450 0.000000 vn -0.241304 -0.970450 0.000000 vn 0.995439 0.095399 0.000000 vn 0.995439 0.095399 -0.000000 vn 0.966275 0.257511 -0.000000 vn 0.966275 0.257511 0.000000 vn 0.920577 0.390562 -0.000000 vn 0.920577 0.390562 0.000000 vn 0.866005 0.500035 0.000000 vn 0.866005 0.500035 -0.000000 vn 0.810098 0.586294 -0.000000 vn 0.810098 0.586294 0.000000 vn 0.755476 0.655176 -0.000000 vn 0.755476 0.655176 0.000000 vn 0.705040 0.709168 -0.000000 vn 0.705040 0.709168 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn 0.658870 0.752257 0.000000 vn 0.658870 0.752257 -0.000000 vn 0.609147 0.793057 -0.000000 vn 0.609147 0.793057 0.000000 vn 0.554735 0.832027 0.000000 vn 0.554735 0.832027 -0.000000 vn 0.491092 0.871108 -0.000000 vn 0.491092 0.871108 0.000000 vn -0.490590 0.871391 0.000000 vn -0.490590 0.871391 0.000000 vn -0.555807 0.831311 0.000000 vn -0.555807 0.831311 0.000000 vn -0.614813 0.788626 -0.008587 vn -0.612366 0.790574 0.000000 vn -0.669448 0.742755 -0.012402 vn -0.665248 0.746580 0.007973 vn -0.667651 0.744474 0.000000 vn -0.704958 0.709222 0.006199 vn -0.720016 0.692361 -0.047043 vn -0.732669 0.680585 0.000106 vn -0.772957 0.634458 -0.000197 vn -0.773271 0.634073 0.002104 vn -0.826331 0.563181 -0.002183 vn -0.826766 0.562545 0.000923 vn -0.880022 0.474930 -0.001901 vn -0.879896 0.475165 -0.000974 vn -0.931040 0.364885 -0.004849 vn -0.930194 0.367063 0.001972 vn -0.971617 0.236447 -0.007291 vn -0.970354 0.241646 0.004430 vn -0.996213 0.086948 0.000000 vn -0.995823 0.090906 0.008565 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.992709 -0.120537 -0.000000 vn -0.992709 -0.120537 0.000000 vn -0.935016 -0.354605 0.000000 vn -0.935016 -0.354605 -0.000000 vn -0.822984 -0.568065 0.000000 vn -0.822984 -0.568065 -0.000000 vn -0.663122 -0.748511 0.000000 vn -0.663122 -0.748511 -0.000000 vn -0.464723 -0.885456 0.000000 vn -0.464723 -0.885456 -0.000000 vn -0.239316 -0.970942 0.000000 vn -0.239316 -0.970942 -0.000000 vn 0.000000 -1.000000 0.000000 vn -0.000000 -1.000000 -0.000000 vn 0.239316 -0.970942 0.000000 vn 0.239316 -0.970942 0.000000 vn 0.464723 -0.885456 0.000000 vn 0.663122 -0.748511 0.000000 vn 0.464723 -0.885456 0.000000 vn 0.663122 -0.748511 0.000000 vn 0.822984 -0.568065 0.000000 vn 0.822984 -0.568065 0.000000 vn 0.935016 -0.354605 0.000000 vn 0.935016 -0.354605 0.000000 vn 0.992709 -0.120537 0.000000 vn 0.992709 -0.120537 0.000000 vn 0.992709 0.120537 -0.000000 vn 0.992709 0.120537 0.000000 vn 0.935016 0.354605 0.000000 vn 0.935016 0.354605 0.000000 vn 0.822984 0.568065 0.000000 vn 0.822984 0.568065 0.000000 vn 0.663122 0.748511 0.000000 vn 0.663122 0.748511 0.000000 vn 0.464724 0.885456 0.000000 vn 0.464724 0.885456 0.000000 vn 0.239315 0.970942 0.000000 vn 0.239315 0.970942 0.000000 vn -0.000000 1.000000 0.000000 vn 0.000000 1.000000 0.000000 vn -0.239315 0.970942 0.000000 vn -0.239315 0.970942 0.000000 vn -0.464724 0.885456 0.000000 vn -0.464724 0.885456 0.000000 vn -0.663122 0.748511 0.000000 vn -0.663122 0.748511 0.000000 vn -0.822984 0.568065 0.000000 vn -0.822984 0.568065 0.000000 vn -0.935016 0.354605 0.000000 vn -0.935016 0.354605 0.000000 vn -0.992709 0.120537 0.000000 vn -0.992709 0.120537 0.000000 vn 0.000000 1.000000 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.706495 -0.707718 0.000000 vn -0.706495 -0.707718 0.000000 vn 0.000000 -0.000000 -1.000000 vn 0.707107 0.707107 -0.000000 vn 0.707107 0.707107 0.000000 vn 0.707107 0.707107 0.000000 vn -0.936329 -0.351123 0.000000 vn -0.936329 -0.351123 0.000000 vn -0.936329 -0.351123 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 -0.000002 vn 0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.707107 -0.707107 0.000000 vn -0.707107 -0.707107 0.000000 vn -0.707107 -0.707107 0.000000 vn 0.611740 -0.791059 0.000000 vn 0.611740 -0.791059 0.000000 vn 0.648789 -0.760968 0.000000 vn 0.648789 -0.760968 0.000000 vn 0.677209 -0.735791 0.000000 vn 0.677209 -0.735791 0.000000 vn 0.707107 0.707106 -0.000000 vn 0.707107 0.707106 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 -0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.894427 0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.284842 0.958574 0.000000 vn -0.284842 0.958574 0.000000 vn -0.343646 0.939099 0.000000 vn -0.343646 0.939099 0.000000 vn -0.398464 0.917184 0.000000 vn -0.398464 0.917184 0.000000 vn -0.444468 0.895795 0.000000 vn -0.444468 0.895795 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000001 vn 0.435978 0.899957 0.000000 vn 0.435978 0.899957 -0.000000 vn 0.373593 0.927593 -0.000000 vn 0.373593 0.927593 0.000000 vn 0.313648 0.949539 -0.000000 vn 0.313648 0.949539 0.000000 vn 0.258283 0.966069 -0.000000 vn 0.258283 0.966069 0.000000 vn 0.219601 0.975590 -0.000000 vn 0.219601 0.975590 0.000000 vn 0.200884 0.979615 -0.000000 vn 0.200884 0.979615 0.000000 vn 0.707107 -0.707107 0.000000 vn 0.707107 -0.707107 0.000000 vn 0.000000 0.000000 1.000000 vn -0.435978 0.899957 0.000000 vn -0.435978 0.899957 0.000000 vn -0.373593 0.927593 0.000000 vn -0.373593 0.927593 0.000000 vn -0.313648 0.949539 0.000000 vn -0.313648 0.949539 0.000000 vn -0.258283 0.966069 0.000000 vn -0.258283 0.966069 0.000000 vn -0.219601 0.975590 0.000000 vn -0.219601 0.975590 0.000000 vn -0.200884 0.979615 0.000000 vn -0.200884 0.979615 0.000000 vn 0.284843 0.958574 -0.000000 vn 0.284843 0.958574 0.000000 vn 0.343646 0.939099 0.000000 vn 0.343646 0.939099 -0.000000 vn 0.398464 0.917184 0.000000 vn 0.398464 0.917184 -0.000000 vn 0.444468 0.895795 0.000000 vn 0.444468 0.895795 -0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 -0.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000001 vn 0.894447 -0.447174 -0.000001 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 -0.000001 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.996160 -0.087556 0.000000 vn -0.996160 -0.087556 0.000000 vn -0.971081 -0.238749 0.000000 vn -0.971081 -0.238749 0.000000 vn -0.930852 -0.365396 0.000000 vn -0.930852 -0.365396 0.000000 vn -0.881799 -0.471625 0.000000 vn -0.881799 -0.471625 0.000000 vn -0.830500 -0.557018 0.000000 vn -0.830500 -0.557018 0.000000 vn -0.779914 -0.625886 0.000000 vn -0.779914 -0.625886 0.000000 vn -0.735976 -0.677007 0.000000 vn -0.735976 -0.677007 0.000000 vn -0.699128 -0.714997 0.000000 vn -0.699128 -0.714997 0.000000 vn -0.707107 -0.707107 0.000000 vn -0.707107 -0.707107 0.000000 vn 0.936329 0.351123 0.000000 vn 0.936329 0.351123 -0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000001 vn 0.894427 0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000001 vn -0.894427 -0.447214 0.000000 vn 0.707107 0.707107 0.000000 vn 0.707107 0.707107 -0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.996160 -0.087556 0.000000 vn 0.996160 -0.087556 0.000000 vn 0.971081 -0.238749 0.000000 vn 0.971081 -0.238749 0.000000 vn 0.930852 -0.365397 0.000000 vn 0.930852 -0.365397 0.000000 vn 0.881800 -0.471624 0.000000 vn 0.881800 -0.471624 0.000000 vn 0.830500 -0.557018 0.000000 vn 0.830500 -0.557018 0.000000 vn 0.779915 -0.625886 0.000000 vn 0.779915 -0.625886 0.000000 vn 0.735976 -0.677007 0.000000 vn 0.735976 -0.677007 0.000000 vn 0.699128 -0.714997 0.000000 vn 0.699128 -0.714997 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn -0.707107 0.707107 0.000000 vn -0.707107 0.707107 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 0.447213 0.000000 vn -0.894427 0.447213 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000002 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 0.000000 vn 0.000000 -0.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.209316 0.977848 -0.000000 vn 0.209316 0.977848 0.000000 vn 0.241304 0.970450 -0.000000 vn 0.241304 0.970450 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 0.465040 -0.885290 0.000000 vn 0.488050 -0.872578 0.020376 vn 0.488141 -0.872523 0.020542 vn 0.510301 -0.859996 -0.000364 vn 0.531550 -0.847027 0.000389 vn 0.549514 -0.835253 0.019698 vn 0.551868 -0.833792 0.015243 vn 0.571586 -0.820540 -0.001792 vn 0.590635 -0.806934 0.002741 vn 0.605151 -0.795773 0.023205 vn 0.609132 -0.792925 0.015067 vn 0.626478 -0.779438 -0.001001 vn 0.642604 -0.766198 0.001307 vn 0.656459 -0.754128 0.018776 vn 0.658487 -0.752449 0.014654 vn 0.672722 -0.739895 0.000764 vn 0.685574 -0.728003 -0.000641 vn 0.685215 -0.728341 0.000000 vn 0.704021 -0.709587 0.029001 vn 0.709497 -0.704706 0.001850 vn 0.752177 -0.658382 0.027637 vn 0.746977 -0.664847 -0.001939 vn 0.801344 -0.597601 0.026849 vn 0.790975 -0.611171 -0.028782 vn 0.851295 -0.523985 0.027150 vn 0.841088 -0.540193 -0.027615 vn 0.900577 -0.433795 0.027983 vn 0.890491 -0.454155 -0.027724 vn 0.945891 -0.323215 0.028683 vn 0.936685 -0.349008 -0.028524 vn 0.973259 -0.228319 -0.025259 vn 0.979178 -0.202344 0.016372 vn 0.996130 -0.085183 -0.021659 vn 0.997324 -0.073112 0.000000 vn -0.660512 -0.750815 0.000000 vn -0.660512 -0.750815 0.000000 vn -0.612175 -0.790723 0.000000 vn -0.612175 -0.790723 0.000000 vn -0.556747 -0.830682 0.000000 vn -0.556747 -0.830682 0.000000 vn -0.491945 -0.870626 0.000000 vn -0.491945 -0.870626 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 -0.000003 vn -0.894447 0.447174 -0.000002 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000001 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000001 vn -0.894427 -0.447213 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.894427 -0.447213 0.000000 vn 0.894427 -0.447213 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.706495 0.707718 0.000000 vn 0.706495 0.707718 -0.000000 vn -0.707107 -0.707107 0.000000 vn -0.707107 -0.707107 0.000000 vn 0.936329 0.351123 0.000000 vn 0.936329 0.351123 -0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.707107 0.707107 0.000000 vn 0.707107 0.707107 -0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.596768 0.802414 0.000000 vn -0.613070 0.789612 -0.025652 vn -0.611436 0.790665 -0.031547 vn -0.630388 0.776269 0.004198 vn -0.648762 0.760936 -0.009194 vn -0.648146 0.761481 -0.007272 vn -0.665555 0.746335 0.004655 vn -0.677146 0.735723 -0.013625 vn -0.682805 0.730601 0.000000 vn -0.707107 -0.707106 0.000000 vn -0.707107 -0.707106 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.894427 -0.447213 0.000000 vn 0.894427 -0.447213 0.000000 vn 0.894427 -0.447214 0.000003 vn 0.894427 -0.447214 0.000000 vn 0.894427 0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 -0.000002 vn -0.465040 -0.885290 0.000000 vn -0.488144 -0.872527 -0.020289 vn -0.510301 -0.859996 0.000364 vn -0.531549 -0.847027 -0.000388 vn -0.488050 -0.872576 -0.020460 vn -0.551868 -0.833792 -0.015244 vn -0.571587 -0.820539 0.001792 vn -0.549514 -0.835253 -0.019698 vn -0.590634 -0.806935 -0.002741 vn -0.609132 -0.792926 -0.015069 vn -0.626479 -0.779438 0.001000 vn -0.642603 -0.766198 -0.001308 vn -0.605151 -0.795773 -0.023205 vn -0.658488 -0.752449 -0.014654 vn -0.672723 -0.739894 -0.000763 vn -0.656459 -0.754128 -0.018776 vn -0.685215 -0.728341 0.000000 vn -0.685575 -0.728002 0.000642 vn -0.709497 -0.704706 -0.001849 vn -0.704021 -0.709587 -0.028998 vn -0.746977 -0.664847 0.001941 vn -0.752177 -0.658382 -0.027636 vn -0.790975 -0.611171 0.028783 vn -0.801344 -0.597601 -0.026848 vn -0.841087 -0.540194 0.027615 vn -0.851295 -0.523985 -0.027153 vn -0.890492 -0.454154 0.027721 vn -0.900577 -0.433796 -0.027983 vn -0.936685 -0.349008 0.028525 vn -0.945891 -0.323215 -0.028683 vn -0.973259 -0.228319 0.025258 vn -0.979178 -0.202344 -0.016372 vn -0.996130 -0.085183 0.021659 vn -0.997324 -0.073113 0.000000 vn 0.660513 -0.750815 0.000000 vn 0.660513 -0.750815 0.000000 vn 0.612174 -0.790723 0.000000 vn 0.612174 -0.790723 0.000000 vn 0.556747 -0.830682 0.000000 vn 0.556747 -0.830682 0.000000 vn 0.491945 -0.870626 0.000000 vn 0.491945 -0.870626 0.000000 vn 0.894447 0.447174 0.000000 vn 0.894447 0.447174 0.000000 vn 0.894447 0.447174 -0.000000 vn 0.894447 0.447174 -0.000002 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000005 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 -0.000002 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.706495 0.707718 0.000000 vn -0.706495 0.707718 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.707107 0.707107 0.000000 vn -0.707107 0.707107 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.894428 -0.447213 0.000000 vn 0.894428 -0.447213 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.707107 -0.707106 0.000000 vn 0.707107 -0.707106 0.000000 vn 0.596769 0.802413 -0.000000 vn 0.630387 0.776269 -0.004198 vn 0.613070 0.789612 0.025651 vn 0.611436 0.790665 0.031546 vn 0.648146 0.761481 0.007272 vn 0.648762 0.760936 0.009194 vn 0.665555 0.746335 -0.004655 vn 0.682804 0.730601 0.000000 vn 0.677145 0.735723 0.013624 vn 0.000000 -1.000000 0.000000 vn 0.000000 1.000000 0.000000 vn 0.894447 0.447174 0.000000 vn 0.894447 0.447174 -0.000000 vn 0.894447 0.447174 -0.000000 vn 0.894447 0.447174 -0.000001 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.707107 -0.707107 0.000000 vn 0.707107 -0.707107 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.209316 0.977848 0.000000 vn -0.209316 0.977848 0.000000 vn -0.241304 0.970450 0.000000 vn -0.241304 0.970450 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 -0.000000 -0.000002 vn -1.000000 -0.000000 0.000000 vn 0.000000 -1.000000 -0.000008 vn 0.000001 -1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000001 -1.000000 0.000000 vn 0.000001 -1.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 1.000000 f 1//1 2//1 3//1 f 1//2 3//2 4//2 f 5//3 2//3 1//3 f 6//4 2//4 5//4 f 7//5 2//5 6//5 f 8//6 2//6 7//6 f 9//7 2//7 8//7 f 10//8 11//8 12//8 f 10//9 9//9 11//9 f 10//10 2//10 9//10 f 13//11 2//11 10//11 f 14//12 15//12 16//12 f 16//13 17//13 18//13 f 18//14 17//14 19//14 f 19//15 17//15 20//15 f 15//16 17//16 16//16 f 20//17 21//17 22//17 f 22//18 21//18 23//18 f 17//19 21//19 20//19 f 21//20 24//20 23//20 f 24//21 25//21 26//21 f 26//22 25//22 27//22 f 27//23 25//23 28//23 f 21//24 25//24 24//24 f 25//25 29//25 28//25 f 29//26 1//26 4//26 f 25//27 30//27 29//27 f 29//28 30//28 1//28 f 30//29 5//29 1//29 f 5//30 31//30 6//30 f 30//31 31//31 5//31 f 6//32 32//32 7//32 f 31//33 32//33 6//33 f 7//34 33//34 8//34 f 32//35 33//35 7//35 f 33//36 9//36 8//36 f 33//37 34//37 9//37 f 34//38 11//38 9//38 f 34//39 35//39 11//39 f 35//40 12//40 11//40 f 35//41 36//41 12//41 f 36//42 10//42 12//42 f 36//43 37//43 10//43 f 16//44 38//44 14//44 f 18//45 38//45 16//45 f 19//46 39//46 38//46 f 19//47 38//47 18//47 f 20//48 39//48 19//48 f 22//49 39//49 20//49 f 40//50 41//50 42//50 f 40//51 42//51 39//51 f 40//52 39//52 22//52 f 40//53 22//53 43//53 f 23//54 43//54 22//54 f 44//55 43//55 23//55 f 29//56 45//56 28//56 f 4//57 3//57 46//57 f 4//58 46//58 29//58 f 46//59 45//59 29//59 f 45//60 47//60 28//60 f 47//61 48//61 27//61 f 48//62 49//62 26//62 f 49//63 44//63 24//63 f 23//64 24//64 44//64 f 24//65 26//65 49//65 f 26//66 27//66 48//66 f 27//67 28//67 47//67 f 37//68 13//68 10//68 f 13//69 50//69 51//69 f 13//70 52//70 50//70 f 37//71 52//71 13//71 f 2//72 13//72 3//72 f 3//73 13//73 51//73 f 53//74 30//74 54//74 f 53//75 31//75 30//75 f 54//76 25//76 55//76 f 30//77 25//77 54//77 f 56//78 32//78 53//78 f 53//79 32//79 31//79 f 55//80 21//80 57//80 f 25//81 21//81 55//81 f 58//82 33//82 56//82 f 56//83 33//83 32//83 f 21//84 17//84 57//84 f 57//85 17//85 59//85 f 60//86 34//86 58//86 f 58//87 34//87 33//87 f 17//88 15//88 59//88 f 59//89 15//89 61//89 f 60//90 35//90 34//90 f 62//91 35//91 60//91 f 62//92 36//92 35//92 f 63//93 36//93 62//93 f 63//94 37//94 36//94 f 64//95 37//95 63//95 f 15//96 65//96 61//96 f 15//97 66//97 65//97 f 66//98 67//98 65//98 f 67//99 68//99 65//99 f 67//100 69//100 68//100 f 69//101 70//101 68//101 f 69//102 71//102 70//102 f 71//103 72//103 70//103 f 71//104 73//104 72//104 f 73//105 74//105 72//105 f 64//106 75//106 37//106 f 73//107 76//107 74//107 f 76//108 77//108 74//108 f 78//109 79//109 52//109 f 79//110 80//110 52//110 f 76//111 81//111 77//111 f 81//112 82//112 77//112 f 80//113 83//113 52//113 f 81//114 84//114 82//114 f 80//115 85//115 83//115 f 84//116 86//116 82//116 f 87//117 88//117 84//117 f 84//118 88//118 86//118 f 85//119 89//119 83//119 f 90//120 91//120 87//120 f 87//121 91//121 88//121 f 85//122 92//122 89//122 f 93//123 94//123 90//123 f 90//124 94//124 91//124 f 93//125 95//125 94//125 f 92//126 96//126 89//126 f 92//127 97//127 96//127 f 97//128 98//128 96//128 f 97//129 99//129 98//129 f 99//130 100//130 98//130 f 100//131 101//131 102//131 f 99//132 101//132 100//132 f 101//133 103//133 102//133 f 102//134 103//134 104//134 f 103//135 105//135 104//135 f 104//136 105//136 106//136 f 106//137 107//137 108//137 f 105//138 107//138 106//138 f 107//139 109//139 108//139 f 108//140 109//140 110//140 f 109//141 111//141 110//141 f 93//142 111//142 95//142 f 109//143 112//143 111//143 f 111//144 112//144 95//144 f 52//145 37//145 78//145 f 78//146 37//146 75//146 f 71//147 69//147 41//147 f 41//148 69//148 42//148 f 42//149 67//149 39//149 f 69//150 67//150 42//150 f 39//151 66//151 38//151 f 67//152 66//152 39//152 f 38//153 15//153 14//153 f 66//154 15//154 38//154 f 40//155 113//155 114//155 f 40//156 114//156 115//156 f 40//157 115//157 116//157 f 40//158 116//158 117//158 f 40//159 117//159 118//159 f 40//160 118//160 119//160 f 40//161 119//161 41//161 f 120//162 113//162 40//162 f 40//163 121//163 120//163 f 122//164 40//164 43//164 f 122//165 121//165 40//165 f 44//166 122//166 43//166 f 49//167 122//167 44//167 f 123//168 49//168 48//168 f 123//169 122//169 49//169 f 47//170 123//170 48//170 f 124//171 123//171 47//171 f 45//172 124//172 47//172 f 125//173 45//173 46//173 f 125//174 124//174 45//174 f 2//175 125//175 46//175 f 2//176 46//176 3//176 f 52//177 83//177 50//177 f 50//178 83//178 126//178 f 126//179 89//179 127//179 f 83//180 89//180 126//180 f 126//181 127//181 128//181 f 50//182 128//182 129//182 f 50//183 129//183 51//183 f 50//184 126//184 128//184 f 130//185 3//185 131//185 f 132//186 3//186 130//186 f 132//187 130//187 133//187 f 134//188 132//188 133//188 f 135//189 130//189 136//189 f 135//190 133//190 130//190 f 51//191 129//191 135//191 f 51//192 136//192 131//192 f 51//193 131//193 3//193 f 51//194 135//194 136//194 f 112//195 109//195 137//195 f 137//196 109//196 138//196 f 109//197 139//197 138//197 f 109//198 107//198 139//198 f 139//199 105//199 140//199 f 107//200 105//200 139//200 f 140//201 103//201 141//201 f 105//202 103//202 140//202 f 141//203 101//203 142//203 f 103//204 101//204 141//204 f 143//205 144//205 145//205 f 145//206 146//206 143//206 f 146//207 95//207 147//207 f 145//208 95//208 146//208 f 147//209 137//209 144//209 f 144//210 137//210 145//210 f 95//211 112//211 147//211 f 147//212 112//212 137//212 f 61//213 65//213 148//213 f 148//214 65//214 149//214 f 149//215 68//215 150//215 f 65//216 68//216 149//216 f 150//217 70//217 151//217 f 68//218 70//218 150//218 f 151//219 72//219 152//219 f 70//220 72//220 151//220 f 152//221 74//221 153//221 f 72//222 74//222 152//222 f 153//223 77//223 154//223 f 74//224 77//224 153//224 f 154//225 82//225 155//225 f 77//226 82//226 154//226 f 155//227 86//227 156//227 f 82//228 86//228 155//228 f 156//229 88//229 157//229 f 86//230 88//230 156//230 f 157//231 91//231 158//231 f 88//232 91//232 157//232 f 158//233 94//233 159//233 f 159//234 94//234 145//234 f 91//235 94//235 158//235 f 94//236 95//236 145//236 f 64//237 63//237 160//237 f 160//238 63//238 161//238 f 161//239 62//239 162//239 f 63//240 62//240 161//240 f 162//241 60//241 163//241 f 163//242 60//242 164//242 f 62//243 60//243 162//243 f 60//244 58//244 164//244 f 164//245 56//245 165//245 f 58//246 56//246 164//246 f 165//247 53//247 166//247 f 56//248 53//248 165//248 f 166//249 54//249 167//249 f 53//250 54//250 166//250 f 167//251 55//251 168//251 f 54//252 55//252 167//252 f 168//253 57//253 169//253 f 55//254 57//254 168//254 f 169//255 59//255 170//255 f 170//256 59//256 148//256 f 57//257 59//257 169//257 f 59//258 61//258 148//258 f 75//259 171//259 172//259 f 172//260 171//260 160//260 f 171//261 173//261 160//261 f 75//262 174//262 171//262 f 175//263 64//263 173//263 f 174//264 64//264 175//264 f 173//265 64//265 160//265 f 75//266 64//266 174//266 f 75//267 176//267 78//267 f 176//268 79//268 78//268 f 176//269 80//269 79//269 f 101//270 99//270 142//270 f 142//271 99//271 177//271 f 177//272 97//272 178//272 f 99//273 97//273 177//273 f 97//274 179//274 178//274 f 97//275 92//275 179//275 f 179//276 85//276 180//276 f 92//277 85//277 179//277 f 180//278 80//278 181//278 f 85//279 80//279 180//279 f 93//280 90//280 113//280 f 113//281 90//281 114//281 f 114//282 87//282 115//282 f 90//283 87//283 114//283 f 115//284 84//284 116//284 f 87//285 84//285 115//285 f 116//286 81//286 117//286 f 84//287 81//287 116//287 f 117//288 76//288 118//288 f 81//289 76//289 117//289 f 118//290 73//290 119//290 f 76//291 73//291 118//291 f 119//292 71//292 41//292 f 73//293 71//293 119//293 f 111//294 121//294 182//294 f 182//295 121//295 120//295 f 121//296 113//296 120//296 f 121//297 93//297 113//297 f 111//298 93//298 121//298 f 102//299 104//299 183//299 f 183//300 104//300 184//300 f 184//301 106//301 185//301 f 104//302 106//302 184//302 f 106//303 186//303 185//303 f 106//304 108//304 186//304 f 186//305 110//305 187//305 f 108//306 110//306 186//306 f 187//307 111//307 182//307 f 110//308 111//308 187//308 f 89//309 96//309 127//309 f 127//310 96//310 188//310 f 188//311 98//311 189//311 f 96//312 98//312 188//312 f 189//313 100//313 190//313 f 98//314 100//314 189//314 f 190//315 102//315 183//315 f 100//316 102//316 190//316 f 191//317 122//317 123//317 f 192//318 121//318 122//318 f 192//319 122//319 191//319 f 193//320 194//320 121//320 f 193//321 121//321 192//321 f 2//322 123//322 124//322 f 2//323 124//323 125//323 f 2//324 191//324 123//324 f 195//325 196//325 194//325 f 195//326 194//326 193//326 f 187//327 182//327 120//327 f 197//328 187//328 120//328 f 186//329 187//329 197//329 f 198//330 197//330 132//330 f 185//331 186//331 197//331 f 185//332 197//332 198//332 f 184//333 185//333 198//333 f 190//334 183//334 184//334 f 190//335 184//335 198//335 f 189//336 198//336 128//336 f 189//337 190//337 198//337 f 188//338 189//338 128//338 f 127//339 188//339 128//339 f 197//340 199//340 132//340 f 128//341 200//341 129//341 f 201//342 128//342 198//342 f 201//343 200//343 128//343 f 202//344 203//344 135//344 f 135//345 203//345 133//345 f 200//346 202//346 129//346 f 129//347 202//347 135//347 f 132//348 199//348 204//348 f 132//349 204//349 205//349 f 3//350 132//350 205//350 f 198//351 132//351 134//351 f 203//352 134//352 133//352 f 134//353 201//353 198//353 f 203//354 201//354 134//354 f 206//355 207//355 131//355 f 131//356 207//356 130//356 f 208//357 206//357 136//357 f 136//358 206//358 131//358 f 207//359 208//359 130//359 f 130//360 208//360 136//360 f 209//361 166//361 167//361 f 210//362 167//362 168//362 f 210//363 209//363 167//363 f 211//364 165//364 166//364 f 211//365 166//365 209//365 f 212//366 168//366 169//366 f 212//367 210//367 168//367 f 213//368 164//368 165//368 f 213//369 165//369 211//369 f 214//370 169//370 170//370 f 214//371 212//371 169//371 f 215//372 163//372 164//372 f 215//373 164//373 213//373 f 216//374 214//374 170//374 f 217//375 163//375 215//375 f 217//376 162//376 163//376 f 218//377 216//377 170//377 f 218//378 170//378 148//378 f 219//379 162//379 217//379 f 219//380 161//380 162//380 f 220//381 161//381 219//381 f 220//382 160//382 161//382 f 149//383 218//383 148//383 f 221//384 218//384 149//384 f 150//385 221//385 149//385 f 222//386 221//386 150//386 f 151//387 222//387 150//387 f 223//388 222//388 151//388 f 152//389 223//389 151//389 f 224//390 223//390 152//390 f 153//391 224//391 152//391 f 225//392 224//392 153//392 f 172//393 160//393 220//393 f 154//394 225//394 153//394 f 226//395 225//395 154//395 f 227//396 228//396 229//396 f 181//397 227//397 229//397 f 155//398 230//398 226//398 f 155//399 226//399 154//399 f 231//400 181//400 229//400 f 180//401 181//401 231//401 f 156//402 232//402 230//402 f 156//403 230//403 155//403 f 157//404 233//404 232//404 f 157//405 232//405 156//405 f 158//406 234//406 233//406 f 158//407 233//407 157//407 f 179//408 231//408 235//408 f 179//409 180//409 231//409 f 159//410 236//410 234//410 f 159//411 234//411 158//411 f 145//412 236//412 159//412 f 178//413 235//413 237//413 f 178//414 179//414 235//414 f 238//415 178//415 237//415 f 177//416 178//416 238//416 f 239//417 177//417 238//417 f 142//418 239//418 240//418 f 142//419 177//419 239//419 f 141//420 240//420 241//420 f 141//421 142//421 240//421 f 140//422 241//422 242//422 f 140//423 141//423 241//423 f 139//424 242//424 243//424 f 139//425 140//425 242//425 f 138//426 139//426 243//426 f 138//427 243//427 244//427 f 245//428 138//428 244//428 f 245//429 236//429 145//429 f 137//430 245//430 145//430 f 137//431 138//431 245//431 f 220//432 229//432 228//432 f 220//433 228//433 172//433 f 146//434 147//434 143//434 f 143//435 147//435 144//435 f 246//436 247//436 248//436 f 246//437 248//437 249//437 f 246//438 249//438 250//438 f 251//439 246//439 250//439 f 252//440 250//440 253//440 f 252//441 251//441 250//441 f 254//442 253//442 255//442 f 254//443 252//443 253//443 f 256//444 255//444 257//444 f 256//445 254//445 255//445 f 258//446 257//446 259//446 f 258//447 256//447 257//447 f 260//448 259//448 261//448 f 260//449 261//449 262//449 f 260//450 258//450 259//450 f 263//451 260//451 262//451 f 264//452 262//452 265//452 f 264//453 263//453 262//453 f 266//454 265//454 267//454 f 266//455 264//455 265//455 f 268//456 267//456 269//456 f 268//457 266//457 267//457 f 270//458 269//458 271//458 f 270//459 268//459 269//459 f 272//460 270//460 271//460 f 272//461 271//461 273//461 f 274//462 272//462 273//462 f 274//463 273//463 275//463 f 274//464 275//464 276//464 f 277//465 274//465 276//465 f 278//466 277//466 276//466 f 278//467 276//467 279//467 f 280//468 278//468 279//468 f 280//469 279//469 281//469 f 280//470 281//470 282//470 f 283//471 280//471 282//471 f 284//472 283//472 282//472 f 284//473 282//473 285//473 f 284//474 285//474 286//474 f 287//475 284//475 286//475 f 287//476 286//476 288//476 f 289//477 287//477 288//477 f 172//478 289//478 288//478 f 172//479 288//479 75//479 f 174//480 290//480 291//480 f 174//481 291//481 171//481 f 173//482 171//482 291//482 f 175//483 173//483 291//483 f 290//484 175//484 291//484 f 174//485 175//485 290//485 f 276//486 292//486 279//486 f 279//487 293//487 281//487 f 292//488 293//488 279//488 f 275//489 294//489 276//489 f 276//490 294//490 292//490 f 281//491 295//491 282//491 f 293//492 295//492 281//492 f 273//493 296//493 275//493 f 275//494 296//494 294//494 f 282//495 297//495 285//495 f 295//496 297//496 282//496 f 271//497 298//497 273//497 f 273//498 298//498 296//498 f 285//499 299//499 286//499 f 297//500 299//500 285//500 f 269//501 300//501 271//501 f 271//502 300//502 298//502 f 286//503 301//503 288//503 f 299//504 301//504 286//504 f 269//505 302//505 300//505 f 288//506 303//506 75//506 f 301//507 303//507 288//507 f 269//508 267//508 302//508 f 267//509 304//509 302//509 f 305//510 176//510 303//510 f 303//511 176//511 75//511 f 305//512 306//512 176//512 f 267//513 265//513 304//513 f 304//514 265//514 307//514 f 306//515 308//515 176//515 f 265//516 262//516 307//516 f 307//517 262//517 309//517 f 308//518 80//518 176//518 f 308//519 310//519 80//519 f 262//520 261//520 309//520 f 309//521 261//521 311//521 f 308//522 312//522 310//522 f 313//523 312//523 308//523 f 313//524 248//524 312//524 f 314//525 248//525 313//525 f 311//526 259//526 315//526 f 261//527 259//527 311//527 f 316//528 249//528 314//528 f 314//529 249//529 248//529 f 315//530 257//530 317//530 f 259//531 257//531 315//531 f 318//532 250//532 316//532 f 316//533 250//533 249//533 f 317//534 255//534 319//534 f 257//535 255//535 317//535 f 319//536 253//536 318//536 f 255//537 253//537 319//537 f 318//538 253//538 250//538 f 181//539 80//539 310//539 f 320//540 181//540 310//540 f 321//541 310//541 312//541 f 321//542 320//542 310//542 f 247//543 312//543 248//543 f 247//544 321//544 312//544 f 121//545 194//545 120//545 f 120//546 194//546 322//546 f 194//547 196//547 322//547 f 322//548 196//548 323//548 f 196//549 195//549 323//549 f 323//550 195//550 324//550 f 195//551 325//551 324//551 f 325//552 193//552 326//552 f 195//553 193//553 325//553 f 193//554 192//554 326//554 f 326//555 192//555 327//555 f 192//556 191//556 328//556 f 328//557 191//557 329//557 f 191//558 2//558 329//558 f 329//559 2//559 3//559 f 323//560 120//560 322//560 f 330//561 120//561 323//561 f 324//562 330//562 323//562 f 331//563 330//563 324//563 f 199//564 324//564 325//564 f 199//565 331//565 324//565 f 332//566 120//566 330//566 f 332//567 331//567 199//567 f 197//568 120//568 332//568 f 197//569 332//569 199//569 f 200//570 201//570 203//570 f 200//571 203//571 202//571 f 3//572 327//572 329//572 f 205//573 326//573 327//573 f 205//574 327//574 3//574 f 204//575 325//575 326//575 f 204//576 326//576 205//576 f 199//577 325//577 204//577 f 208//578 207//578 206//578 f 333//579 172//579 228//579 f 227//580 333//580 228//580 f 181//581 333//581 227//581 f 334//582 335//582 239//582 f 334//583 239//583 238//583 f 336//584 238//584 237//584 f 336//585 334//585 238//585 f 337//586 237//586 235//586 f 337//587 336//587 237//587 f 338//588 235//588 231//588 f 338//589 337//589 235//589 f 339//590 231//590 229//590 f 339//591 338//591 231//591 f 340//592 341//592 243//592 f 340//593 243//593 242//593 f 342//594 242//594 241//594 f 342//595 340//595 242//595 f 343//596 241//596 240//596 f 343//597 342//597 241//597 f 335//598 240//598 239//598 f 335//599 343//599 240//599 f 344//600 339//600 229//600 f 345//601 339//601 344//601 f 220//602 344//602 229//602 f 346//603 345//603 344//603 f 346//604 344//604 220//604 f 347//605 348//605 245//605 f 347//606 245//606 244//606 f 243//607 347//607 244//607 f 341//608 347//608 243//608 f 349//609 346//609 220//609 f 349//610 220//610 219//610 f 350//611 219//611 217//611 f 350//612 349//612 219//612 f 351//613 217//613 215//613 f 351//614 350//614 217//614 f 213//615 351//615 215//615 f 352//616 351//616 213//616 f 353//617 213//617 211//617 f 353//618 352//618 213//618 f 354//619 211//619 209//619 f 354//620 353//620 211//620 f 355//621 209//621 210//621 f 355//622 354//622 209//622 f 356//623 357//623 236//623 f 245//624 356//624 236//624 f 348//625 358//625 356//625 f 348//626 356//626 245//626 f 359//627 355//627 210//627 f 359//628 210//628 212//628 f 360//629 212//629 214//629 f 360//630 359//630 212//630 f 216//631 360//631 214//631 f 361//632 360//632 216//632 f 362//633 216//633 218//633 f 362//634 361//634 216//634 f 363//635 362//635 218//635 f 363//636 218//636 221//636 f 364//637 221//637 222//637 f 364//638 363//638 221//638 f 365//639 222//639 223//639 f 365//640 364//640 222//640 f 366//641 223//641 224//641 f 367//642 365//642 223//642 f 367//643 223//643 366//643 f 368//644 366//644 224//644 f 225//645 368//645 224//645 f 369//646 368//646 225//646 f 226//647 369//647 225//647 f 370//648 369//648 226//648 f 230//649 370//649 226//649 f 371//650 370//650 230//650 f 372//651 230//651 232//651 f 372//652 371//652 230//652 f 373//653 232//653 233//653 f 373//654 372//654 232//654 f 374//655 233//655 234//655 f 374//656 373//656 233//656 f 357//657 234//657 236//657 f 357//658 374//658 234//658 f 375//659 277//659 278//659 f 376//660 278//660 280//660 f 376//661 375//661 278//661 f 377//662 274//662 277//662 f 377//663 277//663 375//663 f 378//664 280//664 283//664 f 378//665 376//665 280//665 f 379//666 272//666 274//666 f 379//667 274//667 377//667 f 380//668 283//668 284//668 f 380//669 378//669 283//669 f 381//670 270//670 272//670 f 381//671 272//671 379//671 f 382//672 284//672 287//672 f 382//673 380//673 284//673 f 383//674 268//674 270//674 f 383//675 270//675 381//675 f 384//676 287//676 289//676 f 384//677 382//677 287//677 f 385//678 268//678 383//678 f 386//679 289//679 172//679 f 386//680 384//680 289//680 f 266//681 268//681 385//681 f 387//682 266//682 385//682 f 333//683 388//683 386//683 f 333//684 386//684 172//684 f 389//685 388//685 333//685 f 264//686 266//686 387//686 f 264//687 387//687 390//687 f 391//688 389//688 333//688 f 263//689 264//689 390//689 f 263//690 390//690 392//690 f 181//691 391//691 333//691 f 320//692 391//692 181//692 f 260//693 263//693 392//693 f 260//694 392//694 393//694 f 321//695 391//695 320//695 f 321//696 394//696 391//696 f 247//697 394//697 321//697 f 247//698 395//698 394//698 f 258//699 393//699 396//699 f 258//700 260//700 393//700 f 246//701 397//701 395//701 f 246//702 395//702 247//702 f 256//703 396//703 398//703 f 256//704 258//704 396//704 f 251//705 399//705 397//705 f 251//706 397//706 246//706 f 254//707 398//707 400//707 f 254//708 256//708 398//708 f 252//709 400//709 399//709 f 252//710 254//710 400//710 f 252//711 399//711 251//711 f 401//712 395//712 402//712 f 402//713 395//713 314//713 f 314//714 397//714 316//714 f 395//715 397//715 314//715 f 316//716 399//716 318//716 f 397//717 399//717 316//717 f 318//718 400//718 319//718 f 399//719 400//719 318//719 f 319//720 398//720 317//720 f 400//721 398//721 319//721 f 317//722 396//722 315//722 f 398//723 396//723 317//723 f 315//724 393//724 311//724 f 396//725 393//725 315//725 f 311//726 392//726 309//726 f 393//727 392//727 311//727 f 309//728 390//728 307//728 f 307//729 390//729 304//729 f 392//730 390//730 309//730 f 390//731 387//731 304//731 f 387//732 385//732 304//732 f 304//733 385//733 302//733 f 385//734 383//734 302//734 f 302//735 383//735 300//735 f 300//736 383//736 298//736 f 383//737 381//737 298//737 f 298//738 381//738 296//738 f 381//739 379//739 296//739 f 379//740 377//740 296//740 f 296//741 377//741 294//741 f 377//742 375//742 294//742 f 294//743 375//743 292//743 f 375//744 376//744 292//744 f 292//745 376//745 293//745 f 376//746 378//746 293//746 f 293//747 378//747 295//747 f 378//748 380//748 295//748 f 295//749 380//749 297//749 f 380//750 382//750 297//750 f 297//751 382//751 299//751 f 299//752 382//752 301//752 f 382//753 384//753 301//753 f 384//754 386//754 301//754 f 301//755 386//755 303//755 f 303//756 386//756 305//756 f 386//757 388//757 305//757 f 305//758 389//758 306//758 f 388//759 389//759 305//759 f 306//760 391//760 308//760 f 389//761 391//761 306//761 f 391//762 401//762 308//762 f 308//763 401//763 402//763 f 327//764 192//764 328//764 f 403//765 404//765 330//765 f 330//766 404//766 332//766 f 405//767 403//767 331//767 f 331//768 403//768 330//768 f 404//769 405//769 332//769 f 332//770 405//770 331//770 f 338//771 339//771 345//771 f 406//772 338//772 345//772 f 337//773 338//773 406//773 f 407//774 406//774 408//774 f 336//775 337//775 406//775 f 336//776 406//776 407//776 f 334//777 336//777 407//777 f 343//778 335//778 334//778 f 343//779 334//779 407//779 f 342//780 407//780 409//780 f 342//781 343//781 407//781 f 340//782 342//782 409//782 f 341//783 340//783 409//783 f 406//784 410//784 408//784 f 411//785 346//785 349//785 f 411//786 349//786 350//786 f 411//787 350//787 351//787 f 411//788 351//788 352//788 f 411//789 352//789 353//789 f 411//790 353//790 354//790 f 411//791 354//791 355//791 f 345//792 346//792 411//792 f 347//793 341//793 409//793 f 348//794 409//794 412//794 f 348//795 412//795 358//795 f 348//796 347//796 409//796 f 368//797 413//797 366//797 f 369//798 413//798 368//798 f 370//799 413//799 369//799 f 371//800 413//800 370//800 f 414//801 415//801 413//801 f 414//802 374//802 357//802 f 414//803 373//803 374//803 f 414//804 372//804 373//804 f 414//805 371//805 372//805 f 414//806 413//806 371//806 f 356//807 414//807 357//807 f 358//808 412//808 356//808 f 356//809 412//809 414//809 f 363//810 360//810 361//810 f 363//811 361//811 362//811 f 364//812 360//812 363//812 f 411//813 355//813 359//813 f 416//814 367//814 413//814 f 413//815 367//815 366//815 f 404//816 403//816 405//816 f 412//817 409//817 414//817 f 409//818 415//818 414//818 f 409//819 407//819 415//819 f 407//820 408//820 415//820 f 408//821 413//821 415//821 f 408//822 416//822 413//822 f 408//823 410//823 417//823 f 408//824 417//824 416//824 f 417//825 410//825 418//825 f 410//826 419//826 418//826 f 410//827 406//827 419//827 f 406//828 345//828 419//828 f 419//829 345//829 344//829 f 345//830 411//830 344//830 f 411//831 420//831 344//831 f 411//832 421//832 420//832 f 421//833 422//833 420//833 f 420//834 422//834 423//834 f 422//835 424//835 423//835 f 422//836 425//836 424//836 f 424//837 426//837 427//837 f 425//838 426//838 424//838 f 426//839 416//839 427//839 f 427//840 416//840 413//840 f 416//841 418//841 413//841 f 416//842 417//842 418//842 f 413//843 420//843 423//843 f 413//844 423//844 424//844 f 413//845 424//845 427//845 f 418//846 344//846 420//846 f 418//847 420//847 413//847 f 419//848 344//848 418//848 f 3//849 428//849 197//849 f 429//850 430//850 428//850 f 429//851 428//851 3//851 f 430//852 431//852 428//852 f 428//853 431//853 121//853 f 428//854 121//854 197//854 f 197//855 121//855 120//855 f 432//856 429//856 3//856 f 432//857 3//857 433//857 f 434//858 429//858 435//858 f 436//859 429//859 434//859 f 437//860 434//860 438//860 f 437//861 436//861 434//861 f 439//862 430//862 440//862 f 432//863 441//863 437//863 f 432//864 438//864 435//864 f 432//865 437//865 438//865 f 432//866 435//866 429//866 f 430//867 429//867 436//867 f 430//868 436//868 440//868 f 3//869 130//869 131//869 f 3//870 133//870 130//870 f 130//871 135//871 136//871 f 133//872 135//872 130//872 f 197//873 198//873 133//873 f 129//874 433//874 135//874 f 136//875 433//875 131//875 f 131//876 433//876 3//876 f 135//877 433//877 136//877 f 3//878 197//878 133//878 f 442//879 111//879 431//879 f 430//880 443//880 442//880 f 430//881 442//881 431//881 f 444//882 443//882 430//882 f 439//883 444//883 430//883 f 445//884 444//884 439//884 f 446//885 445//885 439//885 f 100//886 102//886 446//886 f 100//887 446//887 439//887 f 98//888 100//888 439//888 f 96//889 439//889 440//889 f 96//890 98//890 439//890 f 89//891 96//891 440//891 f 121//892 111//892 182//892 f 121//893 182//893 120//893 f 431//894 111//894 121//894 f 182//895 447//895 120//895 f 448//896 197//896 447//896 f 447//897 197//897 120//897 f 448//898 449//898 197//898 f 449//899 198//899 197//899 f 449//900 450//900 198//900 f 450//901 451//901 198//901 f 183//902 190//902 451//902 f 451//903 190//903 198//903 f 198//904 189//904 452//904 f 190//905 189//905 198//905 f 189//906 188//906 452//906 f 188//907 127//907 452//907 f 441//908 432//908 433//908 f 441//909 433//909 129//909 f 453//910 436//910 133//910 f 440//911 436//911 453//911 f 198//912 453//912 133//912 f 201//913 453//913 198//913 f 436//914 437//914 135//914 f 436//915 135//915 133//915 f 437//916 441//916 129//916 f 437//917 129//917 135//917 f 434//918 435//918 131//918 f 434//919 131//919 130//919 f 435//920 438//920 136//920 f 435//921 136//921 131//921 f 438//922 434//922 130//922 f 438//923 130//923 136//923 f 96//924 89//924 127//924 f 96//925 127//925 188//925 f 98//926 188//926 189//926 f 98//927 96//927 188//927 f 100//928 189//928 190//928 f 100//929 98//929 189//929 f 102//930 190//930 183//930 f 102//931 100//931 190//931 f 454//932 440//932 453//932 f 127//933 454//933 452//933 f 89//934 454//934 127//934 f 89//935 440//935 454//935 f 446//936 102//936 183//936 f 446//937 183//937 451//937 f 445//938 451//938 450//938 f 445//939 446//939 451//939 f 444//940 450//940 449//940 f 444//941 445//941 450//941 f 443//942 449//942 448//942 f 443//943 444//943 449//943 f 442//944 448//944 447//944 f 442//945 443//945 448//945 f 111//946 447//946 182//946 f 111//947 442//947 447//947 f 454//948 198//948 452//948 f 454//949 201//949 198//949 f 201//950 454//950 453//950 f 335//951 455//951 239//951 f 239//952 455//952 456//952 f 456//953 457//953 458//953 f 455//954 457//954 456//954 f 458//955 459//955 460//955 f 457//956 459//956 458//956 f 460//957 461//957 462//957 f 459//958 461//958 460//958 f 461//959 463//959 462//959 f 461//960 464//960 463//960 f 463//961 339//961 229//961 f 464//962 339//962 463//962 f 341//963 340//963 243//963 f 243//964 340//964 242//964 f 242//965 342//965 241//965 f 340//966 342//966 242//966 f 241//967 343//967 240//967 f 342//968 343//968 241//968 f 240//969 335//969 239//969 f 343//970 335//970 240//970 f 339//971 344//971 229//971 f 339//972 465//972 344//972 f 344//973 220//973 229//973 f 465//974 346//974 344//974 f 344//975 346//975 220//975 f 339//976 464//976 465//976 f 461//977 406//977 464//977 f 464//978 406//978 465//978 f 461//979 459//979 406//979 f 406//980 407//980 466//980 f 459//981 407//981 406//981 f 459//982 457//982 407//982 f 457//983 455//983 407//983 f 335//984 343//984 455//984 f 455//985 343//985 407//985 f 407//986 342//986 467//986 f 343//987 342//987 407//987 f 342//988 340//988 467//988 f 340//989 341//989 467//989 f 468//990 406//990 466//990 f 220//991 469//991 470//991 f 471//992 470//992 472//992 f 471//993 472//993 473//993 f 471//994 473//994 474//994 f 471//995 474//995 475//995 f 471//996 475//996 476//996 f 471//997 476//997 477//997 f 471//998 220//998 470//998 f 463//999 229//999 220//999 f 478//1000 462//1000 463//1000 f 478//1001 220//1001 471//1001 f 478//1002 463//1002 220//1002 f 460//1003 462//1003 478//1003 f 479//1004 460//1004 478//1004 f 458//1005 460//1005 479//1005 f 456//1006 458//1006 479//1006 f 240//1007 239//1007 456//1007 f 240//1008 456//1008 479//1008 f 241//1009 240//1009 479//1009 f 242//1010 479//1010 480//1010 f 242//1011 241//1011 479//1011 f 243//1012 242//1012 480//1012 f 481//1013 482//1013 477//1013 f 481//1014 483//1014 482//1014 f 482//1015 471//1015 477//1015 f 482//1016 484//1016 471//1016 f 484//1017 485//1017 471//1017 f 468//1018 466//1018 484//1018 f 484//1019 466//1019 485//1019 f 485//1020 480//1020 471//1020 f 486//1021 243//1021 480//1021 f 467//1022 341//1022 486//1022 f 486//1023 341//1023 243//1023 f 485//1024 486//1024 480//1024 f 346//1025 483//1025 487//1025 f 487//1026 483//1026 488//1026 f 488//1027 483//1027 489//1027 f 489//1028 483//1028 490//1028 f 490//1029 483//1029 491//1029 f 491//1030 483//1030 492//1030 f 492//1031 483//1031 493//1031 f 493//1032 483//1032 481//1032 f 346//1033 465//1033 483//1033 f 346//1034 487//1034 220//1034 f 220//1035 487//1035 469//1035 f 469//1036 488//1036 470//1036 f 487//1037 488//1037 469//1037 f 470//1038 489//1038 472//1038 f 488//1039 489//1039 470//1039 f 472//1040 490//1040 473//1040 f 489//1041 490//1041 472//1041 f 473//1042 491//1042 474//1042 f 490//1043 491//1043 473//1043 f 474//1044 492//1044 475//1044 f 491//1045 492//1045 474//1045 f 475//1046 493//1046 476//1046 f 492//1047 493//1047 475//1047 f 476//1048 481//1048 477//1048 f 493//1049 481//1049 476//1049 f 407//1050 467//1050 486//1050 f 407//1051 486//1051 415//1051 f 466//1052 407//1052 415//1052 f 466//1053 415//1053 485//1053 f 406//1054 468//1054 484//1054 f 406//1055 484//1055 419//1055 f 465//1056 406//1056 419//1056 f 465//1057 419//1057 344//1057 f 494//1058 480//1058 495//1058 f 480//1059 496//1059 495//1059 f 480//1060 479//1060 496//1060 f 479//1061 478//1061 496//1061 f 496//1062 478//1062 497//1062 f 478//1063 471//1063 497//1063 f 471//1064 498//1064 497//1064 f 471//1065 499//1065 498//1065 f 482//1066 465//1066 344//1066 f 483//1067 465//1067 482//1067 f 344//1068 484//1068 482//1068 f 344//1069 419//1069 484//1069 f 415//1070 486//1070 485//1070 f 471//1071 500//1071 499//1071 f 471//1072 501//1072 500//1072 f 471//1073 494//1073 501//1073 f 471//1074 480//1074 494//1074 f 501//1075 502//1075 503//1075 f 494//1076 502//1076 501//1076 f 504//1077 505//1077 502//1077 f 503//1078 505//1078 500//1078 f 500//1079 505//1079 499//1079 f 502//1080 505//1080 503//1080 f 502//1081 494//1081 506//1081 f 506//1082 494//1082 495//1082 f 507//1083 498//1083 508//1083 f 495//1084 498//1084 507//1084 f 506//1085 507//1085 509//1085 f 506//1086 495//1086 507//1086 f 496//1087 497//1087 495//1087 f 510//1088 511//1088 506//1088 f 510//1089 509//1089 508//1089 f 510//1090 508//1090 498//1090 f 510//1091 506//1091 509//1091 f 497//1092 498//1092 495//1092 f 499//1093 505//1093 498//1093 f 498//1094 505//1094 510//1094 f 504//1095 502//1095 511//1095 f 511//1096 502//1096 506//1096 f 505//1097 504//1097 510//1097 f 510//1098 504//1098 511//1098 f 500//1099 501//1099 508//1099 f 508//1100 501//1100 507//1100 f 503//1101 500//1101 509//1101 f 509//1102 500//1102 508//1102 f 501//1103 503//1103 507//1103 f 507//1104 503//1104 509//1104 f 113//1105 121//1105 120//1105 f 93//1106 431//1106 121//1106 f 93//1107 121//1107 113//1107 f 512//1108 431//1108 93//1108 f 512//1109 93//1109 513//1109 f 512//1110 513//1110 514//1110 f 512//1111 514//1111 515//1111 f 516//1112 515//1112 517//1112 f 516//1113 517//1113 518//1113 f 516//1114 518//1114 519//1114 f 516//1115 519//1115 520//1115 f 516//1116 520//1116 71//1116 f 516//1117 512//1117 515//1117 f 521//1118 522//1118 512//1118 f 521//1119 512//1119 516//1119 f 513//1120 93//1120 113//1120 f 513//1121 113//1121 523//1121 f 514//1122 523//1122 524//1122 f 514//1123 513//1123 523//1123 f 515//1124 524//1124 525//1124 f 515//1125 514//1125 524//1125 f 517//1126 525//1126 526//1126 f 517//1127 515//1127 525//1127 f 518//1128 526//1128 527//1128 f 518//1129 517//1129 526//1129 f 519//1130 527//1130 528//1130 f 519//1131 518//1131 527//1131 f 520//1132 528//1132 529//1132 f 520//1133 519//1133 528//1133 f 41//1134 520//1134 529//1134 f 71//1135 520//1135 41//1135 f 113//1136 530//1136 523//1136 f 523//1137 530//1137 524//1137 f 524//1138 530//1138 525//1138 f 525//1139 530//1139 526//1139 f 526//1140 530//1140 527//1140 f 527//1141 530//1141 528//1141 f 528//1142 530//1142 529//1142 f 529//1143 530//1143 41//1143 f 113//1144 120//1144 530//1144 f 121//1145 530//1145 120//1145 f 121//1146 531//1146 530//1146 f 431//1147 512//1147 121//1147 f 121//1148 512//1148 194//1148 f 512//1149 522//1149 194//1149 f 194//1150 522//1150 196//1150 f 522//1151 521//1151 196//1151 f 196//1152 521//1152 195//1152 f 521//1153 516//1153 195//1153 f 195//1154 516//1154 532//1154 f 531//1155 71//1155 41//1155 f 531//1156 41//1156 530//1156 f 516//1157 531//1157 532//1157 f 516//1158 71//1158 531//1158 f 121//1159 194//1159 531//1159 f 194//1160 532//1160 531//1160 f 196//1161 195//1161 194//1161 f 194//1162 195//1162 532//1162 f 533//1163 534//1163 535//1163 f 534//1164 533//1164 403//1164 f 403//1165 533//1165 404//1165 f 533//1166 535//1166 404//1166 f 404//1167 535//1167 405//1167 f 535//1168 534//1168 405//1168 f 405//1169 534//1169 403//1169 f 403//1170 404//1170 405//1170 f 536//1171 499//1171 537//1171 f 537//1172 499//1172 498//1172 f 538//1173 536//1173 539//1173 f 539//1174 536//1174 537//1174 f 499//1175 540//1175 498//1175 f 498//1176 540//1176 541//1176 f 542//1177 543//1177 218//1177 f 536//1178 544//1178 543//1178 f 536//1179 542//1179 545//1179 f 536//1180 545//1180 546//1180 f 536//1181 543//1181 542//1181 f 538//1182 547//1182 544//1182 f 538//1183 544//1183 536//1183 f 548//1184 210//1184 547//1184 f 548//1185 547//1185 538//1185 f 499//1186 546//1186 549//1186 f 499//1187 536//1187 546//1187 f 550//1188 210//1188 548//1188 f 551//1189 499//1189 549//1189 f 552//1190 553//1190 499//1190 f 552//1191 499//1191 551//1191 f 554//1192 553//1192 552//1192 f 555//1193 553//1193 554//1193 f 556//1194 553//1194 555//1194 f 557//1195 553//1195 556//1195 f 558//1196 553//1196 557//1196 f 236//1197 559//1197 553//1197 f 236//1198 553//1198 558//1198 f 244//1199 243//1199 559//1199 f 244//1200 559//1200 236//1200 f 245//1201 244//1201 236//1201 f 560//1202 561//1202 562//1202 f 498//1203 563//1203 537//1203 f 564//1204 561//1204 560//1204 f 541//1205 565//1205 563//1205 f 541//1206 563//1206 498//1206 f 566//1207 565//1207 541//1207 f 566//1208 564//1208 560//1208 f 567//1209 564//1209 566//1209 f 568//1210 561//1210 564//1210 f 497//1211 568//1211 567//1211 f 497//1212 567//1212 566//1212 f 497//1213 566//1213 541//1213 f 497//1214 561//1214 568//1214 f 569//1215 538//1215 563//1215 f 563//1216 538//1216 539//1216 f 540//1217 553//1217 541//1217 f 541//1218 553//1218 570//1218 f 348//1219 347//1219 245//1219 f 245//1220 347//1220 244//1220 f 347//1221 243//1221 244//1221 f 347//1222 341//1222 243//1222 f 357//1223 356//1223 236//1223 f 356//1224 245//1224 236//1224 f 571//1225 348//1225 356//1225 f 356//1226 348//1226 245//1226 f 362//1227 572//1227 218//1227 f 218//1228 573//1228 542//1228 f 572//1229 573//1229 218//1229 f 573//1230 574//1230 542//1230 f 574//1231 575//1231 542//1231 f 575//1232 545//1232 542//1232 f 575//1233 576//1233 545//1233 f 576//1234 577//1234 545//1234 f 577//1235 578//1235 545//1235 f 578//1236 546//1236 545//1236 f 578//1237 579//1237 546//1237 f 579//1238 580//1238 546//1238 f 580//1239 581//1239 546//1239 f 581//1240 549//1240 546//1240 f 581//1241 582//1241 549//1241 f 582//1242 583//1242 549//1242 f 583//1243 366//1243 549//1243 f 583//1244 584//1244 366//1244 f 366//1245 551//1245 549//1245 f 366//1246 585//1246 551//1246 f 551//1247 586//1247 552//1247 f 585//1248 586//1248 551//1248 f 552//1249 587//1249 554//1249 f 586//1250 587//1250 552//1250 f 554//1251 588//1251 555//1251 f 587//1252 588//1252 554//1252 f 555//1253 589//1253 556//1253 f 588//1254 589//1254 555//1254 f 556//1255 590//1255 557//1255 f 589//1256 590//1256 556//1256 f 590//1257 591//1257 557//1257 f 557//1258 591//1258 558//1258 f 591//1259 357//1259 558//1259 f 558//1260 357//1260 236//1260 f 355//1261 592//1261 210//1261 f 210//1262 592//1262 547//1262 f 547//1263 593//1263 544//1263 f 592//1264 593//1264 547//1264 f 544//1265 594//1265 543//1265 f 593//1266 594//1266 544//1266 f 543//1267 362//1267 218//1267 f 594//1268 362//1268 543//1268 f 341//1269 595//1269 243//1269 f 341//1270 596//1270 595//1270 f 597//1271 559//1271 595//1271 f 595//1272 559//1272 243//1272 f 597//1273 598//1273 559//1273 f 599//1274 600//1274 597//1274 f 597//1275 600//1275 598//1275 f 598//1276 550//1276 559//1276 f 601//1277 210//1277 550//1277 f 483//1278 355//1278 601//1278 f 601//1279 355//1279 210//1279 f 598//1280 601//1280 550//1280 f 602//1281 550//1281 566//1281 f 550//1282 565//1282 566//1282 f 550//1283 548//1283 565//1283 f 548//1284 569//1284 565//1284 f 565//1285 569//1285 563//1285 f 553//1286 559//1286 570//1286 f 559//1287 497//1287 570//1287 f 559//1288 478//1288 497//1288 f 478//1289 603//1289 497//1289 f 497//1290 603//1290 561//1290 f 604//1291 602//1291 560//1291 f 560//1292 602//1292 566//1292 f 603//1293 605//1293 561//1293 f 561//1294 605//1294 562//1294 f 605//1295 604//1295 562//1295 f 562//1296 604//1296 560//1296 f 606//1297 607//1297 564//1297 f 564//1298 607//1298 568//1298 f 608//1299 606//1299 567//1299 f 567//1300 606//1300 564//1300 f 607//1301 608//1301 568//1301 f 568//1302 608//1302 567//1302 f 341//1303 347//1303 596//1303 f 596//1304 348//1304 412//1304 f 412//1305 348//1305 571//1305 f 347//1306 348//1306 596//1306 f 413//1307 585//1307 366//1307 f 413//1308 586//1308 585//1308 f 413//1309 587//1309 586//1309 f 595//1310 414//1310 597//1310 f 597//1311 414//1311 413//1311 f 591//1312 414//1312 357//1312 f 590//1313 414//1313 591//1313 f 589//1314 414//1314 590//1314 f 588//1315 414//1315 589//1315 f 587//1316 414//1316 588//1316 f 413//1317 414//1317 587//1317 f 414//1318 356//1318 357//1318 f 412//1319 571//1319 356//1319 f 412//1320 356//1320 414//1320 f 594//1321 572//1321 362//1321 f 594//1322 573//1322 572//1322 f 594//1323 574//1323 573//1323 f 593//1324 575//1324 594//1324 f 594//1325 575//1325 574//1325 f 593//1326 576//1326 575//1326 f 355//1327 483//1327 592//1327 f 592//1328 483//1328 593//1328 f 576//1329 483//1329 577//1329 f 593//1330 483//1330 576//1330 f 577//1331 483//1331 421//1331 f 609//1332 580//1332 579//1332 f 610//1333 583//1333 582//1333 f 416//1334 584//1334 426//1334 f 426//1335 584//1335 583//1335 f 610//1336 426//1336 583//1336 f 611//1337 610//1337 582//1337 f 612//1338 611//1338 581//1338 f 609//1339 612//1339 580//1339 f 613//1340 609//1340 579//1340 f 421//1341 613//1341 578//1341 f 578//1342 577//1342 421//1342 f 579//1343 578//1343 613//1343 f 581//1344 580//1344 612//1344 f 582//1345 581//1345 611//1345 f 584//1346 416//1346 413//1346 f 584//1347 413//1347 366//1347 f 596//1348 412//1348 414//1348 f 596//1349 414//1349 595//1349 f 416//1350 599//1350 597//1350 f 416//1351 597//1351 413//1351 f 600//1352 599//1352 417//1352 f 417//1353 599//1353 416//1353 f 600//1354 417//1354 418//1354 f 600//1355 418//1355 598//1355 f 601//1356 413//1356 420//1356 f 420//1357 413//1357 423//1357 f 423//1358 413//1358 424//1358 f 424//1359 413//1359 427//1359 f 601//1360 418//1360 413//1360 f 601//1361 598//1361 418//1361 f 420//1362 483//1362 601//1362 f 421//1363 483//1363 420//1363 f 603//1364 604//1364 605//1364 f 603//1365 606//1365 604//1365 f 606//1366 602//1366 604//1366 f 606//1367 608//1367 602//1367 f 602//1368 559//1368 550//1368 f 608//1369 559//1369 602//1369 f 603//1370 607//1370 606//1370 f 608//1371 607//1371 559//1371 f 603//1372 478//1372 607//1372 f 607//1373 478//1373 559//1373 f 613//1374 421//1374 420//1374 f 423//1375 609//1375 613//1375 f 423//1376 613//1376 420//1376 f 612//1377 609//1377 423//1377 f 611//1378 423//1378 424//1378 f 611//1379 612//1379 423//1379 f 610//1380 611//1380 424//1380 f 427//1381 610//1381 424//1381 f 426//1382 610//1382 427//1382 f 416//1383 426//1383 427//1383 f 416//1384 427//1384 413//1384 f 417//1385 416//1385 413//1385 f 417//1386 413//1386 418//1386 f 522//1387 431//1387 512//1387 f 429//1388 614//1388 615//1388 f 534//1389 431//1389 522//1389 f 616//1390 617//1390 618//1390 f 616//1391 618//1391 614//1391 f 616//1392 614//1392 429//1392 f 521//1393 617//1393 616//1393 f 521//1394 534//1394 522//1394 f 535//1395 534//1395 521//1395 f 533//1396 431//1396 534//1396 f 430//1397 533//1397 535//1397 f 430//1398 521//1398 616//1398 f 430//1399 535//1399 521//1399 f 430//1400 431//1400 533//1400 f 619//1401 620//1401 15//1401 f 615//1402 621//1402 620//1402 f 615//1403 619//1403 622//1403 f 615//1404 622//1404 623//1404 f 615//1405 620//1405 619//1405 f 624//1406 625//1406 621//1406 f 624//1407 621//1407 615//1407 f 618//1408 626//1408 625//1408 f 618//1409 625//1409 624//1409 f 429//1410 623//1410 627//1410 f 429//1411 615//1411 623//1411 f 617//1412 626//1412 618//1412 f 628//1413 429//1413 627//1413 f 629//1414 429//1414 628//1414 f 630//1415 429//1415 629//1415 f 37//1416 631//1416 632//1416 f 432//1417 429//1417 37//1417 f 37//1418 429//1418 631//1418 f 631//1419 429//1419 633//1419 f 633//1420 429//1420 634//1420 f 634//1421 429//1421 630//1421 f 205//1422 2//1422 3//1422 f 616//1423 429//1423 2//1423 f 616//1424 2//1424 205//1424 f 635//1425 616//1425 205//1425 f 635//1426 205//1426 636//1426 f 430//1427 635//1427 636//1427 f 430//1428 636//1428 197//1428 f 532//1429 521//1429 324//1429 f 617//1430 521//1430 532//1430 f 326//1431 532//1431 324//1431 f 193//1432 532//1432 326//1432 f 120//1433 430//1433 197//1433 f 431//1434 430//1434 120//1434 f 521//1435 522//1435 323//1435 f 521//1436 323//1436 324//1436 f 512//1437 431//1437 120//1437 f 512//1438 120//1438 322//1438 f 522//1439 512//1439 322//1439 f 522//1440 322//1440 323//1440 f 533//1441 534//1441 330//1441 f 533//1442 330//1442 332//1442 f 534//1443 535//1443 331//1443 f 534//1444 331//1444 330//1444 f 535//1445 533//1445 332//1445 f 535//1446 332//1446 331//1446 f 13//1447 37//1447 10//1447 f 432//1448 37//1448 13//1448 f 15//1449 14//1449 637//1449 f 619//1450 637//1450 638//1450 f 619//1451 638//1451 639//1451 f 619//1452 639//1452 640//1452 f 619//1453 15//1453 637//1453 f 622//1454 640//1454 641//1454 f 622//1455 641//1455 642//1455 f 622//1456 619//1456 640//1456 f 643//1457 622//1457 642//1457 f 623//1458 643//1458 644//1458 f 623//1459 644//1459 645//1459 f 623//1460 645//1460 646//1460 f 623//1461 622//1461 643//1461 f 627//1462 646//1462 647//1462 f 627//1463 647//1463 648//1463 f 627//1464 623//1464 646//1464 f 1//1465 648//1465 4//1465 f 1//1466 627//1466 648//1466 f 628//1467 1//1467 649//1467 f 628//1468 627//1468 1//1468 f 650//1469 628//1469 649//1469 f 629//1470 628//1470 650//1470 f 651//1471 629//1471 650//1471 f 630//1472 629//1472 651//1472 f 652//1473 630//1473 651//1473 f 634//1474 630//1474 652//1474 f 653//1475 634//1475 652//1475 f 633//1476 634//1476 653//1476 f 654//1477 633//1477 653//1477 f 631//1478 633//1478 654//1478 f 655//1479 631//1479 654//1479 f 632//1480 631//1480 655//1480 f 10//1481 632//1481 655//1481 f 37//1482 632//1482 10//1482 f 625//1483 626//1483 656//1483 f 625//1484 656//1484 657//1484 f 621//1485 657//1485 658//1485 f 621//1486 625//1486 657//1486 f 620//1487 658//1487 659//1487 f 620//1488 621//1488 658//1488 f 15//1489 659//1489 14//1489 f 15//1490 620//1490 659//1490 f 531//1491 617//1491 532//1491 f 656//1492 531//1492 660//1492 f 626//1493 531//1493 656//1493 f 626//1494 617//1494 531//1494 f 429//1495 432//1495 2//1495 f 2//1496 432//1496 13//1496 f 120//1497 323//1497 322//1497 f 327//1498 3//1498 329//1498 f 120//1499 330//1499 323//1499 f 326//1500 205//1500 327//1500 f 327//1501 205//1501 3//1501 f 326//1502 324//1502 205//1502 f 330//1503 324//1503 323//1503 f 330//1504 331//1504 324//1504 f 120//1505 332//1505 330//1505 f 332//1506 197//1506 331//1506 f 331//1507 197//1507 324//1507 f 324//1508 197//1508 205//1508 f 120//1509 197//1509 332//1509 f 531//1510 191//1510 122//1510 f 122//1511 191//1511 123//1511 f 531//1512 192//1512 191//1512 f 531//1513 193//1513 192//1513 f 123//1514 2//1514 124//1514 f 124//1515 2//1515 125//1515 f 191//1516 2//1516 123//1516 f 531//1517 532//1517 193//1517 f 192//1518 193//1518 326//1518 f 192//1519 326//1519 327//1519 f 2//1520 649//1520 1//1520 f 2//1521 650//1521 649//1521 f 2//1522 651//1522 650//1522 f 2//1523 652//1523 651//1523 f 2//1524 653//1524 652//1524 f 654//1525 10//1525 655//1525 f 653//1526 10//1526 654//1526 f 2//1527 10//1527 653//1527 f 2//1528 13//1528 10//1528 f 2//1529 4//1529 3//1529 f 2//1530 1//1530 4//1530 f 659//1531 637//1531 14//1531 f 659//1532 638//1532 637//1532 f 659//1533 639//1533 638//1533 f 658//1534 640//1534 659//1534 f 659//1535 640//1535 639//1535 f 658//1536 641//1536 640//1536 f 656//1537 660//1537 657//1537 f 657//1538 660//1538 658//1538 f 641//1539 660//1539 642//1539 f 658//1540 660//1540 641//1540 f 642//1541 660//1541 43//1541 f 661//1542 645//1542 644//1542 f 662//1543 648//1543 647//1543 f 3//1544 4//1544 46//1544 f 46//1545 4//1545 648//1545 f 662//1546 46//1546 648//1546 f 663//1547 662//1547 647//1547 f 664//1548 663//1548 646//1548 f 661//1549 664//1549 645//1549 f 665//1550 661//1550 644//1550 f 43//1551 665//1551 643//1551 f 643//1552 642//1552 43//1552 f 644//1553 643//1553 665//1553 f 646//1554 645//1554 664//1554 f 647//1555 646//1555 663//1555 f 531//1556 122//1556 660//1556 f 660//1557 122//1557 43//1557 f 2//1558 191//1558 329//1558 f 2//1559 329//1559 3//1559 f 191//1560 192//1560 328//1560 f 191//1561 328//1561 329//1561 f 192//1562 327//1562 328//1562 f 125//1563 2//1563 46//1563 f 46//1564 2//1564 3//1564 f 122//1565 665//1565 43//1565 f 661//1566 123//1566 664//1566 f 665//1567 123//1567 661//1567 f 122//1568 123//1568 665//1568 f 123//1569 663//1569 664//1569 f 123//1570 124//1570 663//1570 f 124//1571 662//1571 663//1571 f 662//1572 125//1572 46//1572 f 124//1573 125//1573 662//1573 f 624//1574 614//1574 192//1574 f 614//1575 624//1575 192//1575 f 666//1576 89//1576 127//1576 f 666//1577 127//1577 128//1577 f 667//1578 666//1578 668//1578 f 667//1579 89//1579 666//1579 f 441//1580 667//1580 436//1580 f 441//1581 436//1581 437//1581 f 83//1582 89//1582 667//1582 f 83//1583 667//1583 441//1583 f 52//1584 441//1584 432//1584 f 52//1585 83//1585 441//1585 f 436//1586 667//1586 203//1586 f 203//1587 667//1587 668//1587 f 666//1588 200//1588 668//1588 f 668//1589 200//1589 203//1589 f 203//1590 200//1590 202//1590 f 200//1591 128//1591 129//1591 f 200//1592 666//1592 128//1592 f 127//1593 126//1593 128//1593 f 128//1594 50//1594 129//1594 f 129//1595 50//1595 51//1595 f 126//1596 50//1596 128//1596 f 83//1597 52//1597 50//1597 f 83//1598 50//1598 126//1598 f 89//1599 126//1599 127//1599 f 89//1600 83//1600 126//1600 f 50//1601 13//1601 51//1601 f 52//1602 432//1602 13//1602 f 52//1603 13//1603 50//1603 f 432//1604 441//1604 13//1604 f 13//1605 441//1605 200//1605 f 441//1606 437//1606 200//1606 f 200//1607 437//1607 202//1607 f 437//1608 436//1608 202//1608 f 202//1609 436//1609 203//1609 f 13//1610 200//1610 51//1610 f 51//1611 200//1611 129//1611 f 438//1612 434//1612 435//1612 f 435//1613 434//1613 206//1613 f 206//1614 434//1614 207//1614 f 434//1615 438//1615 207//1615 f 207//1616 438//1616 208//1616 f 438//1617 435//1617 208//1617 f 208//1618 435//1618 206//1618 f 207//1619 208//1619 206//1619 apparmor-5.0.2/documentation/keychains/AppArmorLogoShadowFlat/AppArmorRed.obj000066400000000000000000001007311522511161100273520ustar00rootroot00000000000000# Created by FreeCAD v 19.742691 -35.476959 3.000000 v 23.485380 -35.476959 3.000000 v 19.742691 -35.476959 4.000000 v 23.485380 -35.476959 4.000000 v 19.274855 -34.541286 3.000000 v 19.274855 -34.541286 4.000000 v 19.742691 -27.991579 3.000000 v 19.742691 -27.991579 4.000000 v 18.081884 -41.797897 3.000000 v 14.089836 -41.883003 3.000000 v 16.000000 -42.962337 3.000000 v 12.182858 -40.604893 3.000000 v 20.177053 -40.419483 3.000000 v 22.391109 -38.735790 3.000000 v 9.931569 -38.861954 3.000000 v 16.000000 -27.991579 3.000000 v 7.772661 -36.962704 3.000000 v 24.289610 -37.083168 3.000000 v 14.128759 -24.249098 3.000000 v 26.009392 -35.376877 3.000000 v 27.488701 -33.686821 3.000000 v 19.742689 -27.991579 3.000000 v 28.695377 -32.068748 3.000000 v 29.624147 -30.559818 3.000000 v 30.289021 -29.179514 3.000000 v 30.714880 -27.933231 3.000000 v 30.905458 -27.010988 3.000000 v 30.970758 -26.120234 3.000000 v 16.000000 -20.506201 3.000000 v 27.349781 -4.760341 3.000000 v 24.256578 -3.991212 3.000000 v 30.970758 -5.535439 3.000000 v 5.707602 -18.634853 4.000000 v 1.029240 -20.506199 4.000000 v 4.771930 -20.506199 4.000000 v 19.274855 -34.541286 4.000000 v 6.643275 -16.763510 4.000000 v 16.000000 -27.991579 4.000000 v 11.321638 -18.634853 4.000000 v 10.385965 -16.763510 4.000000 v 8.514620 -13.020819 4.000000 v 8.514620 -5.535439 4.000000 v 19.274855 -34.541286 3.000000 v 19.742689 -27.991579 4.000000 v 30.970758 -5.535439 2.000000 v 27.349781 -4.760341 2.000000 v 24.256578 -3.991212 2.000000 v 30.970758 -26.120234 2.500000 v 30.970758 -20.506199 2.500000 v 30.970758 -20.506199 2.000000 v 16.000000 -42.962337 2.000000 v 16.689169 -42.600319 2.000000 v 17.383795 -42.211704 2.000000 v 18.107697 -41.782158 2.000000 v 18.843710 -41.320274 2.000000 v 19.587627 -40.827892 2.000000 v 20.334913 -40.307335 2.000000 v 21.080801 -39.761383 2.000000 v 21.820433 -39.193192 2.000000 v 22.476688 -38.665722 2.000000 v 23.120472 -38.125786 2.000000 v 23.748573 -37.576118 2.000000 v 24.230778 -37.137691 2.000000 v 24.703493 -36.692966 2.500000 v 24.703493 -36.692966 2.000000 v 25.985651 -35.402092 2.500000 v 27.195189 -34.043137 2.500000 v 28.473124 -32.389240 2.500000 v 29.456257 -30.858490 2.500000 v 30.168722 -29.461512 2.500000 v 30.640276 -28.195932 2.500000 v 30.881010 -27.169754 2.500000 v 7.772661 -36.962704 2.000000 v 9.931569 -38.861954 2.000000 v 12.182858 -40.604893 2.000000 v 14.089836 -41.883003 2.000000 v 19.742414 -13.020543 2.500000 v 19.742414 -13.020543 2.000000 v 17.069290 -18.367386 2.500000 v 16.000000 -20.506201 2.500000 v 17.069290 -18.367386 2.000000 v 16.000000 -20.506201 2.000000 v 11.010116 -30.487076 2.500000 v 11.010116 -30.487076 2.000000 v 11.321638 -18.634853 3.000000 v 8.514620 -5.535439 3.000000 v 1.029240 -20.506199 3.000000 v 5.707602 -18.634853 3.000000 v 4.771930 -20.506199 3.000000 v 6.643275 -16.763510 3.000000 v 8.514620 -13.020819 3.000000 v 10.385965 -16.763510 3.000000 v 27.228071 -20.506199 2.000000 v 23.485380 -35.476959 2.500000 v 27.228071 -20.506199 2.500000 v 20.618055 -40.095016 2.000000 v 21.955498 -39.078640 2.000000 v 24.046656 -37.299919 2.000000 v 23.485380 -35.476959 2.000000 v 24.698830 -36.690411 2.000000 v 23.380327 -37.894127 2.000000 v 22.678232 -38.491726 2.000000 v 21.291996 -39.593796 2.000000 v 16.935673 -22.377544 2.000000 v 16.935673 -22.377544 2.500000 v 20.618055 -40.095016 2.500000 v 22.486917 -38.649792 2.500000 v 23.614119 -37.688759 2.500000 v 24.698830 -36.690411 2.500000 v 26.292398 -18.634853 0.000000 v 30.970760 -20.506199 0.000000 v 27.228071 -20.506199 0.000000 v 8.514620 -35.476959 0.000000 v 12.725146 -34.541286 0.000000 v 12.257310 -35.476959 0.000000 v 25.356726 -16.763510 0.000000 v 12.257310 -27.991579 0.000000 v 17.871241 -24.249100 0.000000 v 16.000000 -27.991579 0.000000 v 20.678362 -18.634853 0.000000 v 21.614035 -16.763510 0.000000 v 23.485380 -13.020819 0.000000 v 23.485380 -5.535439 0.000000 v 13.918114 -41.797897 0.000000 v 17.910162 -41.883003 0.000000 v 15.999998 -42.962337 0.000000 v 19.817141 -40.604893 0.000000 v 11.822945 -40.419483 0.000000 v 9.608890 -38.735790 0.000000 v 12.725145 -34.541286 0.000000 v 22.068430 -38.861954 0.000000 v 24.227337 -36.962704 0.000000 v 7.710388 -37.083168 0.000000 v 5.990606 -35.376877 0.000000 v 4.511297 -33.686821 0.000000 v 3.304621 -32.068748 0.000000 v 1.029239 -26.120234 0.000000 v 1.285118 -27.933231 0.000000 v 1.094540 -27.010988 0.000000 v 1.029241 -20.506199 0.000000 v 1.710978 -29.179514 0.000000 v 2.375852 -30.559818 0.000000 v 12.257310 -27.991579 1.000000 v 8.514620 -35.476959 0.500000 v 8.514620 -35.476959 1.000000 v 12.257311 -27.991579 0.000000 v 12.257311 -27.991579 1.000000 v 23.485380 -5.535439 1.000000 v 17.871241 -24.249100 0.500000 v 20.678362 -18.634853 1.000000 v 16.000000 -27.991579 1.000000 v 16.000000 -27.991579 0.500000 v 30.970760 -20.506199 1.000000 v 26.292398 -18.634853 1.000000 v 27.228071 -20.506199 1.000000 v 25.356726 -16.763510 1.000000 v 23.485380 -13.020819 1.000000 v 21.614035 -16.763510 1.000000 v 1.029240 -20.506203 0.500000 v 1.029239 -26.120234 0.500000 v 15.999998 -42.962337 1.000000 v 15.310829 -42.600319 1.000000 v 14.616204 -42.211704 1.000000 v 13.892303 -41.782158 1.000000 v 13.156288 -41.320274 1.000000 v 12.412370 -40.827892 1.000000 v 11.665086 -40.307335 1.000000 v 10.919197 -39.761383 1.000000 v 10.179564 -39.193192 1.000000 v 9.523310 -38.665722 1.000000 v 8.879525 -38.125786 1.000000 v 8.251425 -37.576118 1.000000 v 7.769221 -37.137691 1.000000 v 7.296506 -36.692966 0.500000 v 7.296506 -36.692966 1.000000 v 6.014348 -35.402092 0.500000 v 4.804810 -34.043137 0.500000 v 3.526875 -32.389240 0.500000 v 2.543740 -30.858490 0.500000 v 1.831276 -29.461512 0.500000 v 1.359722 -28.195932 0.500000 v 1.118988 -27.169754 0.500000 v 24.227337 -36.962704 1.000000 v 22.068430 -38.861954 1.000000 v 19.817141 -40.604893 1.000000 v 17.910162 -41.883003 1.000000 v 20.989883 -30.487076 0.500000 v 20.989883 -30.487076 1.000000 v 12.725146 -34.541286 1.000000 v 12.257310 -35.476959 1.000000 v 12.257310 -35.476959 0.500000 v 11.381945 -40.095016 0.500000 v 9.513083 -38.649792 0.500000 v 12.725145 -34.541286 0.500000 v 8.385881 -37.688759 0.500000 v 7.301169 -36.690411 0.500000 v 11.381945 -40.095016 1.000000 v 10.044503 -39.078640 1.000000 v 7.953344 -37.299919 1.000000 v 7.301169 -36.690411 1.000000 v 8.619673 -37.894127 1.000000 v 9.321768 -38.491726 1.000000 v 10.708005 -39.593796 1.000000 v 12.725145 -34.541286 1.000000 v 12.257585 -13.020544 0.500000 v 7.743419 -3.991212 0.000000 v 7.743419 -3.991212 1.000000 v 12.257585 -13.020544 1.000000 v 14.128757 -16.763306 0.000000 v 14.128757 -16.763306 0.500000 v 4.771930 -20.506199 0.000000 v 11.321638 -22.377544 0.000000 v 5.707603 -22.377544 0.000000 v 4.650217 -4.760341 0.000000 v 1.029239 -5.535439 0.000000 v 11.321638 -22.377544 0.500000 v 4.771930 -20.506199 0.500000 v 5.707603 -22.377544 0.500000 v 4.771930 -20.506199 1.000000 v 4.650217 -4.760341 1.000000 v 1.029239 -5.535439 1.000000 v 1.029240 -20.506203 1.000000 v 6.643275 -24.248888 0.000000 v 10.385965 -24.248888 0.000000 v 8.514620 -27.991579 0.000000 v 8.514620 -27.991579 0.500000 v 10.385965 -24.248888 0.500000 v 6.643275 -24.248888 0.500000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 0.000000 vn 0.000000 -0.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.209316 0.977848 -0.000000 vn 0.209316 0.977848 0.000000 vn 0.241304 0.970450 -0.000000 vn 0.241304 0.970450 0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 0.465040 -0.885290 0.000000 vn 0.488050 -0.872578 0.020376 vn 0.488141 -0.872523 0.020542 vn 0.510301 -0.859996 -0.000364 vn 0.531550 -0.847027 0.000389 vn 0.549514 -0.835253 0.019698 vn 0.551868 -0.833792 0.015243 vn 0.571586 -0.820540 -0.001792 vn 0.590635 -0.806934 0.002741 vn 0.605151 -0.795773 0.023205 vn 0.609132 -0.792925 0.015067 vn 0.626478 -0.779438 -0.001001 vn 0.642604 -0.766198 0.001307 vn 0.656459 -0.754128 0.018776 vn 0.658487 -0.752449 0.014654 vn 0.672722 -0.739895 0.000764 vn 0.685574 -0.728003 -0.000641 vn 0.685215 -0.728341 0.000000 vn 0.704021 -0.709587 0.029001 vn 0.709497 -0.704706 0.001850 vn 0.752177 -0.658382 0.027637 vn 0.746977 -0.664847 -0.001939 vn 0.801344 -0.597601 0.026849 vn 0.790975 -0.611171 -0.028782 vn 0.851295 -0.523985 0.027150 vn 0.841088 -0.540193 -0.027615 vn 0.900577 -0.433795 0.027983 vn 0.890491 -0.454155 -0.027724 vn 0.945891 -0.323215 0.028683 vn 0.936685 -0.349008 -0.028524 vn 0.973259 -0.228319 -0.025259 vn 0.979178 -0.202344 0.016372 vn 0.996130 -0.085183 -0.021659 vn 0.997324 -0.073112 0.000000 vn -0.660512 -0.750815 0.000000 vn -0.660512 -0.750815 0.000000 vn -0.612175 -0.790723 0.000000 vn -0.612175 -0.790723 0.000000 vn -0.556747 -0.830682 0.000000 vn -0.556747 -0.830682 0.000000 vn -0.491945 -0.870626 0.000000 vn -0.491945 -0.870626 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 -0.000003 vn -0.894447 0.447174 -0.000002 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000001 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000000 vn -0.894447 0.447174 0.000001 vn -0.894427 -0.447213 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.894427 -0.447213 0.000000 vn 0.894427 -0.447213 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.706495 0.707718 0.000000 vn 0.706495 0.707718 -0.000000 vn -0.707107 -0.707107 0.000000 vn -0.707107 -0.707107 0.000000 vn 0.936329 0.351123 0.000000 vn 0.936329 0.351123 -0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.707107 0.707107 0.000000 vn 0.707107 0.707107 -0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.596768 0.802414 0.000000 vn -0.613070 0.789612 -0.025652 vn -0.611436 0.790665 -0.031547 vn -0.630388 0.776269 0.004198 vn -0.648762 0.760936 -0.009194 vn -0.648146 0.761481 -0.007272 vn -0.665555 0.746335 0.004655 vn -0.677146 0.735723 -0.013625 vn -0.682805 0.730601 0.000000 vn -0.707107 -0.707106 0.000000 vn -0.707107 -0.707106 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.894427 -0.447213 0.000000 vn 0.894427 -0.447213 0.000000 vn 0.894427 -0.447214 0.000003 vn 0.894427 -0.447214 0.000000 vn 0.894427 0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 -0.000002 vn -0.465040 -0.885290 0.000000 vn -0.488144 -0.872527 -0.020289 vn -0.510301 -0.859996 0.000364 vn -0.531549 -0.847027 -0.000388 vn -0.488050 -0.872576 -0.020460 vn -0.551868 -0.833792 -0.015244 vn -0.571587 -0.820539 0.001792 vn -0.549514 -0.835253 -0.019698 vn -0.590634 -0.806935 -0.002741 vn -0.609132 -0.792926 -0.015069 vn -0.626479 -0.779438 0.001000 vn -0.642603 -0.766198 -0.001308 vn -0.605151 -0.795773 -0.023205 vn -0.658488 -0.752449 -0.014654 vn -0.672723 -0.739894 -0.000763 vn -0.656459 -0.754128 -0.018776 vn -0.685215 -0.728341 0.000000 vn -0.685575 -0.728002 0.000642 vn -0.709497 -0.704706 -0.001849 vn -0.704021 -0.709587 -0.028998 vn -0.746977 -0.664847 0.001941 vn -0.752177 -0.658382 -0.027636 vn -0.790975 -0.611171 0.028783 vn -0.801344 -0.597601 -0.026848 vn -0.841087 -0.540194 0.027615 vn -0.851295 -0.523985 -0.027153 vn -0.890492 -0.454154 0.027721 vn -0.900577 -0.433796 -0.027983 vn -0.936685 -0.349008 0.028525 vn -0.945891 -0.323215 -0.028683 vn -0.973259 -0.228319 0.025258 vn -0.979178 -0.202344 -0.016372 vn -0.996130 -0.085183 0.021659 vn -0.997324 -0.073113 0.000000 vn 0.660513 -0.750815 0.000000 vn 0.660513 -0.750815 0.000000 vn 0.612174 -0.790723 0.000000 vn 0.612174 -0.790723 0.000000 vn 0.556747 -0.830682 0.000000 vn 0.556747 -0.830682 0.000000 vn 0.491945 -0.870626 0.000000 vn 0.491945 -0.870626 0.000000 vn 0.894447 0.447174 0.000000 vn 0.894447 0.447174 0.000000 vn 0.894447 0.447174 -0.000000 vn 0.894447 0.447174 -0.000002 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000005 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 -0.000002 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.706495 0.707718 0.000000 vn -0.706495 0.707718 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.707107 0.707107 0.000000 vn -0.707107 0.707107 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.894428 -0.447213 0.000000 vn 0.894428 -0.447213 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.707107 -0.707106 0.000000 vn 0.707107 -0.707106 0.000000 vn 0.596769 0.802413 -0.000000 vn 0.630387 0.776269 -0.004198 vn 0.613070 0.789612 0.025651 vn 0.611436 0.790665 0.031546 vn 0.648146 0.761481 0.007272 vn 0.648762 0.760936 0.009194 vn 0.665555 0.746335 -0.004655 vn 0.682804 0.730601 0.000000 vn 0.677145 0.735723 0.013624 vn 0.000000 -1.000000 0.000000 vn 0.000000 1.000000 0.000000 vn 0.894447 0.447174 0.000000 vn 0.894447 0.447174 -0.000000 vn 0.894447 0.447174 -0.000000 vn 0.894447 0.447174 -0.000001 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.707107 -0.707107 0.000000 vn 0.707107 -0.707107 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn -0.209316 0.977848 0.000000 vn -0.209316 0.977848 0.000000 vn -0.241304 0.970450 0.000000 vn -0.241304 0.970450 0.000000 vn -1.000000 -0.000000 0.000000 vn -1.000000 -0.000000 -0.000002 vn -1.000000 -0.000000 0.000000 vn 0.000000 -1.000000 -0.000008 vn 0.000001 -1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000001 -1.000000 0.000000 vn 0.000001 -1.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 1.000000 f 1//1 2//1 3//1 f 3//2 2//2 4//2 f 5//3 1//3 6//3 f 6//4 1//4 3//4 f 2//5 7//5 4//5 f 4//6 7//6 8//6 f 9//7 10//7 11//7 f 1//8 12//8 10//8 f 1//9 9//9 13//9 f 1//10 13//10 14//10 f 1//11 10//11 9//11 f 5//12 15//12 12//12 f 5//13 12//13 1//13 f 16//14 17//14 15//14 f 16//15 15//15 5//15 f 2//16 14//16 18//16 f 2//17 1//17 14//17 f 19//18 17//18 16//18 f 20//19 2//19 18//19 f 21//20 22//20 2//20 f 21//21 2//21 20//21 f 23//22 22//22 21//22 f 24//23 22//23 23//23 f 25//24 22//24 24//24 f 26//25 22//25 25//25 f 27//26 22//26 26//26 f 28//27 29//27 22//27 f 28//28 22//28 27//28 f 30//29 31//29 29//29 f 30//30 29//30 28//30 f 32//31 30//31 28//31 f 33//32 34//32 35//32 f 4//33 36//33 3//33 f 37//34 34//34 33//34 f 8//35 38//35 36//35 f 8//36 36//36 4//36 f 39//37 38//37 8//37 f 39//38 37//38 33//38 f 40//39 37//39 39//39 f 41//40 34//40 37//40 f 42//41 41//41 40//41 f 42//42 40//42 39//42 f 42//43 39//43 8//43 f 42//44 34//44 41//44 f 43//45 5//45 36//45 f 36//46 5//46 6//46 f 7//47 22//47 8//47 f 8//48 22//48 44//48 f 45//49 46//49 32//49 f 32//50 46//50 30//50 f 46//51 31//51 30//51 f 46//52 47//52 31//52 f 48//53 49//53 28//53 f 49//54 32//54 28//54 f 50//55 45//55 49//55 f 49//56 45//56 32//56 f 51//57 52//57 11//57 f 11//58 53//58 9//58 f 52//59 53//59 11//59 f 53//60 54//60 9//60 f 54//61 55//61 9//61 f 55//62 13//62 9//62 f 55//63 56//63 13//63 f 56//64 57//64 13//64 f 57//65 58//65 13//65 f 58//66 14//66 13//66 f 58//67 59//67 14//67 f 59//68 60//68 14//68 f 60//69 61//69 14//69 f 61//70 18//70 14//70 f 61//71 62//71 18//71 f 62//72 63//72 18//72 f 63//73 64//73 18//73 f 63//74 65//74 64//74 f 64//75 20//75 18//75 f 64//76 66//76 20//76 f 20//77 67//77 21//77 f 66//78 67//78 20//78 f 21//79 68//79 23//79 f 67//80 68//80 21//80 f 23//81 69//81 24//81 f 68//82 69//82 23//82 f 24//83 70//83 25//83 f 69//84 70//84 24//84 f 25//85 71//85 26//85 f 70//86 71//86 25//86 f 71//87 72//87 26//87 f 26//88 72//88 27//88 f 72//89 48//89 27//89 f 27//90 48//90 28//90 f 73//91 74//91 17//91 f 17//92 74//92 15//92 f 15//93 75//93 12//93 f 74//94 75//94 15//94 f 12//95 76//95 10//95 f 75//96 76//96 12//96 f 10//97 51//97 11//97 f 76//98 51//98 10//98 f 47//99 77//99 31//99 f 47//100 78//100 77//100 f 79//101 29//101 77//101 f 77//102 29//102 31//102 f 79//103 80//103 29//103 f 81//104 82//104 79//104 f 79//105 82//105 80//105 f 80//106 19//106 29//106 f 83//107 17//107 19//107 f 84//108 73//108 83//108 f 83//109 73//109 17//109 f 80//110 83//110 19//110 f 85//111 19//111 39//111 f 19//112 38//112 39//112 f 19//113 16//113 38//113 f 16//114 43//114 38//114 f 38//115 43//115 36//115 f 22//116 29//116 44//116 f 29//117 42//117 44//117 f 29//118 86//118 42//118 f 86//119 87//119 42//119 f 42//120 87//120 34//120 f 88//121 85//121 33//121 f 33//122 85//122 39//122 f 87//123 89//123 34//123 f 34//124 89//124 35//124 f 89//125 88//125 35//125 f 35//126 88//126 33//126 f 90//127 91//127 37//127 f 37//128 91//128 41//128 f 92//129 90//129 40//129 f 40//130 90//130 37//130 f 91//131 92//131 41//131 f 41//132 92//132 40//132 f 47//133 46//133 78//133 f 78//134 45//134 93//134 f 93//135 45//135 50//135 f 46//136 45//136 78//136 f 94//137 66//137 64//137 f 94//138 67//138 66//138 f 94//139 68//139 67//139 f 77//140 95//140 79//140 f 79//141 95//141 94//141 f 72//142 95//142 48//142 f 71//143 95//143 72//143 f 70//144 95//144 71//144 f 69//145 95//145 70//145 f 68//146 95//146 69//146 f 94//147 95//147 68//147 f 95//148 49//148 48//148 f 93//149 50//149 49//149 f 93//150 49//150 95//150 f 76//151 52//151 51//151 f 76//152 53//152 52//152 f 76//153 54//153 53//153 f 75//154 55//154 76//154 f 76//155 55//155 54//155 f 75//156 56//156 55//156 f 73//157 84//157 74//157 f 74//158 84//158 75//158 f 56//159 84//159 57//159 f 75//160 84//160 56//160 f 57//161 84//161 96//161 f 97//162 60//162 59//162 f 98//163 63//163 62//163 f 99//164 65//164 100//164 f 100//165 65//165 63//165 f 98//166 100//166 63//166 f 101//167 98//167 62//167 f 102//168 101//168 61//168 f 97//169 102//169 60//169 f 103//170 97//170 59//170 f 96//171 103//171 58//171 f 58//172 57//172 96//172 f 59//173 58//173 103//173 f 61//174 60//174 102//174 f 62//175 61//175 101//175 f 65//176 99//176 94//176 f 65//177 94//177 64//177 f 78//178 93//178 95//178 f 78//179 95//179 77//179 f 99//180 81//180 79//180 f 99//181 79//181 94//181 f 82//182 81//182 104//182 f 104//183 81//183 99//183 f 82//184 104//184 105//184 f 82//185 105//185 80//185 f 83//186 94//186 106//186 f 106//187 94//187 107//187 f 107//188 94//188 108//188 f 108//189 94//189 109//189 f 83//190 105//190 94//190 f 83//191 80//191 105//191 f 106//192 84//192 83//192 f 96//193 84//193 106//193 f 87//194 88//194 89//194 f 87//195 90//195 88//195 f 90//196 85//196 88//196 f 90//197 92//197 85//197 f 85//198 29//198 19//198 f 92//199 29//199 85//199 f 87//200 91//200 90//200 f 92//201 91//201 29//201 f 87//202 86//202 91//202 f 91//203 86//203 29//203 f 103//204 96//204 106//204 f 107//205 97//205 103//205 f 107//206 103//206 106//206 f 102//207 97//207 107//207 f 101//208 107//208 108//208 f 101//209 102//209 107//209 f 98//210 101//210 108//210 f 109//211 98//211 108//211 f 100//212 98//212 109//212 f 99//213 100//213 109//213 f 99//214 109//214 94//214 f 104//215 99//215 94//215 f 104//216 94//216 105//216 f 110//217 111//217 112//217 f 113//218 114//218 115//218 f 116//219 111//219 110//219 f 117//220 118//220 119//220 f 117//221 119//221 114//221 f 117//222 114//222 113//222 f 120//223 118//223 117//223 f 120//224 116//224 110//224 f 121//225 116//225 120//225 f 122//226 111//226 116//226 f 123//227 122//227 121//227 f 123//228 120//228 117//228 f 123//229 121//229 120//229 f 123//230 111//230 122//230 f 124//231 125//231 126//231 f 115//232 127//232 125//232 f 115//233 124//233 128//233 f 115//234 128//234 129//234 f 115//235 125//235 124//235 f 130//236 131//236 127//236 f 130//237 127//237 115//237 f 119//238 132//238 131//238 f 119//239 131//239 130//239 f 113//240 129//240 133//240 f 113//241 115//241 129//241 f 118//242 132//242 119//242 f 134//243 113//243 133//243 f 135//244 113//244 134//244 f 136//245 113//245 135//245 f 137//246 138//246 139//246 f 140//247 113//247 137//247 f 137//248 113//248 138//248 f 138//249 113//249 141//249 f 141//250 113//250 142//250 f 142//251 113//251 136//251 f 143//252 144//252 145//252 f 117//253 113//253 144//253 f 117//254 144//254 143//254 f 146//255 117//255 143//255 f 146//256 143//256 147//256 f 123//257 146//257 147//257 f 123//258 147//258 148//258 f 149//259 120//259 150//259 f 118//260 120//260 149//260 f 151//261 149//261 150//261 f 152//262 149//262 151//262 f 153//263 123//263 148//263 f 111//264 123//264 153//264 f 120//265 110//265 154//265 f 120//266 154//266 150//266 f 112//267 111//267 153//267 f 112//268 153//268 155//268 f 110//269 112//269 155//269 f 110//270 155//270 154//270 f 122//271 116//271 156//271 f 122//272 156//272 157//272 f 116//273 121//273 158//273 f 116//274 158//274 156//274 f 121//275 122//275 157//275 f 121//276 157//276 158//276 f 159//277 137//277 160//277 f 140//278 137//278 159//278 f 126//279 161//279 162//279 f 124//280 162//280 163//280 f 124//281 163//281 164//281 f 124//282 164//282 165//282 f 124//283 126//283 162//283 f 128//284 165//284 166//284 f 128//285 166//285 167//285 f 128//286 124//286 165//286 f 168//287 128//287 167//287 f 129//288 168//288 169//288 f 129//289 169//289 170//289 f 129//290 170//290 171//290 f 129//291 128//291 168//291 f 133//292 171//292 172//292 f 133//293 172//293 173//293 f 133//294 129//294 171//294 f 174//295 173//295 175//295 f 174//296 133//296 173//296 f 134//297 174//297 176//297 f 134//298 133//298 174//298 f 177//299 134//299 176//299 f 135//300 134//300 177//300 f 178//301 135//301 177//301 f 136//302 135//302 178//302 f 179//303 136//303 178//303 f 142//304 136//304 179//304 f 180//305 142//305 179//305 f 141//306 142//306 180//306 f 181//307 141//307 180//307 f 138//308 141//308 181//308 f 182//309 138//309 181//309 f 139//310 138//310 182//310 f 160//311 139//311 182//311 f 137//312 139//312 160//312 f 131//313 132//313 183//313 f 131//314 183//314 184//314 f 127//315 184//315 185//315 f 127//316 131//316 184//316 f 125//317 185//317 186//317 f 125//318 127//318 185//318 f 126//319 186//319 161//319 f 126//320 125//320 186//320 f 187//321 118//321 149//321 f 183//322 187//322 188//322 f 132//323 187//323 183//323 f 132//324 118//324 187//324 f 113//325 140//325 144//325 f 144//326 140//326 159//326 f 153//327 154//327 155//327 f 189//328 145//328 190//328 f 153//329 156//329 154//329 f 151//330 143//330 189//330 f 189//331 143//331 145//331 f 151//332 150//332 143//332 f 156//333 150//333 154//333 f 156//334 158//334 150//334 f 153//335 157//335 156//335 f 157//336 148//336 158//336 f 158//337 148//337 150//337 f 150//338 148//338 143//338 f 153//339 148//339 157//339 f 187//340 191//340 192//340 f 192//341 191//341 193//341 f 187//342 194//342 191//342 f 187//343 152//343 194//343 f 193//344 144//344 195//344 f 195//345 144//345 196//345 f 191//346 144//346 193//346 f 187//347 149//347 152//347 f 194//348 152//348 151//348 f 194//349 151//349 189//349 f 144//350 176//350 174//350 f 144//351 177//351 176//351 f 144//352 178//352 177//352 f 144//353 179//353 178//353 f 144//354 180//354 179//354 f 181//355 160//355 182//355 f 180//356 160//356 181//356 f 144//357 160//357 180//357 f 144//358 159//358 160//358 f 144//359 175//359 145//359 f 144//360 174//360 175//360 f 186//361 162//361 161//361 f 186//362 163//362 162//362 f 186//363 164//363 163//363 f 185//364 165//364 186//364 f 186//365 165//365 164//365 f 185//366 166//366 165//366 f 183//367 188//367 184//367 f 184//368 188//368 185//368 f 166//369 188//369 167//369 f 185//370 188//370 166//370 f 167//371 188//371 197//371 f 198//372 170//372 169//372 f 199//373 173//373 172//373 f 145//374 175//374 200//374 f 200//375 175//375 173//375 f 199//376 200//376 173//376 f 201//377 199//377 172//377 f 202//378 201//378 171//378 f 198//379 202//379 170//379 f 203//380 198//380 169//380 f 197//381 203//381 168//381 f 168//382 167//382 197//382 f 169//383 168//383 203//383 f 171//384 170//384 202//384 f 172//385 171//385 201//385 f 187//386 192//386 188//386 f 188//387 192//387 197//387 f 144//388 191//388 190//388 f 144//389 190//389 145//389 f 191//390 194//390 204//390 f 191//391 204//391 190//391 f 194//392 189//392 204//392 f 196//393 144//393 200//393 f 200//394 144//394 145//394 f 192//395 203//395 197//395 f 198//396 193//396 202//396 f 203//397 193//397 198//397 f 192//398 193//398 203//398 f 193//399 201//399 202//399 f 193//400 195//400 201//400 f 195//401 199//401 201//401 f 199//402 196//402 200//402 f 195//403 196//403 199//403 f 130//404 114//404 194//404 f 114//405 130//405 194//405 f 205//406 206//406 207//406 f 205//407 207//407 208//407 f 209//408 205//408 210//408 f 209//409 206//409 205//409 f 211//410 209//410 212//410 f 211//411 212//411 213//411 f 214//412 206//412 209//412 f 214//413 209//413 211//413 f 215//414 211//414 140//414 f 215//415 214//415 211//415 f 212//416 209//416 216//416 f 216//417 209//417 210//417 f 205//418 217//418 210//418 f 210//419 217//419 216//419 f 216//420 217//420 218//420 f 217//421 208//421 219//421 f 217//422 205//422 208//422 f 207//423 220//423 208//423 f 208//424 221//424 219//424 f 219//425 221//425 222//425 f 220//426 221//426 208//426 f 214//427 215//427 221//427 f 214//428 221//428 220//428 f 206//429 220//429 207//429 f 206//430 214//430 220//430 f 221//431 159//431 222//431 f 215//432 140//432 159//432 f 215//433 159//433 221//433 f 140//434 211//434 159//434 f 159//435 211//435 217//435 f 211//436 213//436 217//436 f 217//437 213//437 218//437 f 213//438 212//438 218//438 f 218//439 212//439 216//439 f 159//440 217//440 222//440 f 222//441 217//441 219//441 f 223//442 224//442 225//442 f 225//443 224//443 226//443 f 226//444 224//444 227//444 f 224//445 223//445 227//445 f 227//446 223//446 228//446 f 223//447 225//447 228//447 f 228//448 225//448 226//448 f 227//449 228//449 226//449 apparmor-5.0.2/documentation/keychains/AppArmorLogoShadowFlat/AppArmorWhite.obj000066400000000000000000000554721522511161100277330ustar00rootroot00000000000000# Created by FreeCAD v 8.514620 -35.476959 1.000000 v 23.485380 -5.535439 0.500000 v 23.485380 -5.535439 1.000000 v 8.514620 -35.476959 0.000000 v 23.485380 -5.535439 0.000000 v 30.970760 -20.506199 0.000000 v 30.970760 -20.506199 0.500000 v 30.970760 -20.506199 1.000000 v 1.029241 -20.506199 0.000000 v 1.029241 -20.506199 1.000000 v 10.385965 -24.248888 0.000000 v 8.514620 -27.991579 0.000000 v 11.321638 -22.377544 0.000000 v 5.707603 -22.377544 0.000000 v 6.643275 -24.248888 0.000000 v 19.742691 -5.535439 0.000000 v 14.128757 -16.763306 0.000000 v 4.771930 -20.506199 0.000000 v 10.385965 -24.248888 1.000000 v 8.514620 -27.991579 1.000000 v 11.321638 -22.377544 1.000000 v 5.707603 -22.377544 1.000000 v 6.643275 -24.248888 1.000000 v 19.742691 -5.535439 1.000000 v 4.771930 -20.506199 1.000000 v 28.410444 -5.010409 0.000000 v 30.970758 -5.535439 0.000000 v 25.858397 -4.435955 0.000000 v 22.983425 -3.667320 0.000000 v 20.442276 -2.827936 0.000000 v 18.224457 -1.934699 0.000000 v 14.480927 -1.610794 0.000000 v 15.999998 -0.857075 0.000000 v 12.965134 -2.269320 0.000000 v 10.489840 -3.175107 0.000000 v 7.743419 -3.991212 0.000000 v 30.970758 -5.535439 1.000000 v 28.410444 -5.010409 1.000000 v 25.858397 -4.435955 1.000000 v 22.983425 -3.667320 1.000000 v 20.442276 -2.827936 1.000000 v 18.224457 -1.934699 1.000000 v 15.999998 -0.857075 1.000000 v 14.480927 -1.610794 1.000000 v 12.965134 -2.269320 1.000000 v 12.257585 -13.020544 1.000000 v 10.489840 -3.175107 1.000000 v 7.743419 -3.991212 1.000000 v 14.128757 -16.763306 0.500000 v 19.742691 -5.535439 0.500000 v 12.257585 -13.020544 0.500000 v 16.000000 -0.857075 2.000000 v 13.775541 -1.934699 2.000000 v 16.000000 -0.857075 3.000000 v 13.775541 -1.934699 3.000000 v 11.557722 -2.827936 2.000000 v 11.557722 -2.827936 3.000000 v 9.016573 -3.667320 2.000000 v 9.016573 -3.667320 3.000000 v 6.141601 -4.435955 2.000000 v 6.141601 -4.435955 3.000000 v 3.589554 -5.010409 3.000000 v 3.589554 -5.010409 2.000000 v 1.029240 -5.535439 2.000000 v 1.029240 -5.535439 3.000000 v 24.256578 -3.991212 2.000000 v 21.510159 -3.175107 2.000000 v 24.256578 -3.991212 3.000000 v 21.510159 -3.175107 3.000000 v 19.034864 -2.269320 2.000000 v 19.034864 -2.269320 3.000000 v 17.519070 -1.610794 2.000000 v 17.519070 -1.610794 3.000000 v 1.029240 -20.506199 2.500000 v 1.029240 -20.506199 2.000000 v 1.029240 -26.120234 3.000000 v 1.029240 -26.120234 2.000000 v 8.514620 -5.535439 2.000000 v 12.257310 -5.535439 2.000000 v 17.069290 -18.367386 2.000000 v 19.742414 -13.020543 2.000000 v 16.000000 -20.506201 2.000000 v 1.120316 -27.156445 3.000000 v 1.384263 -28.230013 3.000000 v 16.000000 -20.506201 3.000000 v 1.880613 -29.494473 3.000000 v 2.630348 -30.896257 3.000000 v 3.666219 -32.440716 3.000000 v 5.016049 -34.122734 3.000000 v 6.338480 -35.560352 3.000000 v 7.772661 -36.962704 3.000000 v 8.514620 -5.535439 3.000000 v 12.257310 -5.535439 3.000000 v 17.871243 -16.763304 3.000000 v 7.772661 -36.962704 2.000000 v 11.010116 -30.487076 2.500000 v 11.010116 -30.487076 2.000000 v 16.000000 -20.506201 2.500000 v 17.069290 -18.367386 2.500000 v 19.742414 -13.020543 2.500000 v 1.120316 -27.156445 2.000000 v 1.384263 -28.230013 2.000000 v 1.880613 -29.494473 2.000000 v 2.630348 -30.896257 2.000000 v 3.666219 -32.440716 2.000000 v 5.016049 -34.122734 2.000000 v 6.338480 -35.560352 2.000000 v 12.257310 -5.535439 2.500000 v 8.514620 -5.535439 2.500000 v 20.678362 -22.377544 3.000000 v 20.678362 -22.377544 4.000000 v 12.257310 -5.535439 4.000000 v 8.514620 -5.535439 4.000000 v 23.485380 -35.476959 4.000000 v 23.485380 -35.476959 3.000000 v 23.485380 -27.991579 3.000000 v 21.614035 -24.248888 3.000000 v 26.292398 -22.377544 3.000000 v 25.356726 -24.248888 3.000000 v 27.228071 -20.506199 3.000000 v 30.970758 -20.506199 3.000000 v 26.292398 -22.377544 4.000000 v 21.614035 -24.248888 4.000000 v 23.485380 -27.991579 4.000000 v 25.356726 -24.248888 4.000000 v 30.970758 -20.506199 4.000000 v 27.228071 -20.506199 4.000000 v 30.970758 -26.120234 1.000000 v 30.970758 -26.120234 0.000000 v 27.228071 -20.506199 0.000000 v 30.879683 -27.156445 0.000000 v 30.615736 -28.230013 0.000000 v 30.119385 -29.494473 0.000000 v 17.871241 -24.249100 0.000000 v 29.369650 -30.896257 0.000000 v 28.333778 -32.440716 0.000000 v 26.983950 -34.122734 0.000000 v 25.661518 -35.560352 0.000000 v 24.227337 -36.962704 0.000000 v 20.678362 -18.634853 0.000000 v 26.292398 -18.634853 0.000000 v 30.879683 -27.156445 1.000000 v 30.615736 -28.230013 1.000000 v 30.119385 -29.494473 1.000000 v 29.369650 -30.896257 1.000000 v 28.333778 -32.440716 1.000000 v 26.983950 -34.122734 1.000000 v 25.661518 -35.560352 1.000000 v 24.227337 -36.962704 1.000000 v 20.989883 -30.487076 1.000000 v 20.989883 -30.487076 0.500000 v 27.228071 -20.506199 0.500000 v 26.292398 -18.634853 0.500000 v 20.678362 -18.634853 0.500000 v 17.871241 -24.249100 0.500000 v 23.485380 -13.020819 0.000000 v 25.356726 -16.763510 0.000000 v 21.614035 -16.763510 0.000000 v 25.356726 -16.763510 0.500000 v 23.485380 -13.020819 0.500000 v 21.614035 -16.763510 0.500000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 1.000000 0.000000 0.000000 vn 1.000000 0.000000 -0.000000 vn 1.000000 0.000000 -0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.894427 0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn -0.284842 0.958574 0.000000 vn -0.284842 0.958574 0.000000 vn -0.343646 0.939099 0.000000 vn -0.343646 0.939099 0.000000 vn -0.398464 0.917184 0.000000 vn -0.398464 0.917184 0.000000 vn -0.444468 0.895795 0.000000 vn -0.444468 0.895795 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000001 vn 0.435978 0.899957 0.000000 vn 0.435978 0.899957 -0.000000 vn 0.373593 0.927593 -0.000000 vn 0.373593 0.927593 0.000000 vn 0.313648 0.949539 -0.000000 vn 0.313648 0.949539 0.000000 vn 0.258283 0.966069 -0.000000 vn 0.258283 0.966069 0.000000 vn 0.219601 0.975590 -0.000000 vn 0.219601 0.975590 0.000000 vn 0.200884 0.979615 -0.000000 vn 0.200884 0.979615 0.000000 vn 0.707107 -0.707107 0.000000 vn 0.707107 -0.707107 0.000000 vn 0.000000 0.000000 1.000000 vn -0.435978 0.899957 0.000000 vn -0.435978 0.899957 0.000000 vn -0.373593 0.927593 0.000000 vn -0.373593 0.927593 0.000000 vn -0.313648 0.949539 0.000000 vn -0.313648 0.949539 0.000000 vn -0.258283 0.966069 0.000000 vn -0.258283 0.966069 0.000000 vn -0.219601 0.975590 0.000000 vn -0.219601 0.975590 0.000000 vn -0.200884 0.979615 0.000000 vn -0.200884 0.979615 0.000000 vn 0.284843 0.958574 -0.000000 vn 0.284843 0.958574 0.000000 vn 0.343646 0.939099 0.000000 vn 0.343646 0.939099 -0.000000 vn 0.398464 0.917184 0.000000 vn 0.398464 0.917184 -0.000000 vn 0.444468 0.895795 0.000000 vn 0.444468 0.895795 -0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 0.000000 0.000000 vn -1.000000 -0.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000001 vn 0.894447 -0.447174 -0.000001 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 0.000000 vn 0.894447 -0.447174 -0.000001 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn -0.996160 -0.087556 0.000000 vn -0.996160 -0.087556 0.000000 vn -0.971081 -0.238749 0.000000 vn -0.971081 -0.238749 0.000000 vn -0.930852 -0.365396 0.000000 vn -0.930852 -0.365396 0.000000 vn -0.881799 -0.471625 0.000000 vn -0.881799 -0.471625 0.000000 vn -0.830500 -0.557018 0.000000 vn -0.830500 -0.557018 0.000000 vn -0.779914 -0.625886 0.000000 vn -0.779914 -0.625886 0.000000 vn -0.735976 -0.677007 0.000000 vn -0.735976 -0.677007 0.000000 vn -0.699128 -0.714997 0.000000 vn -0.699128 -0.714997 0.000000 vn -0.707107 -0.707107 0.000000 vn -0.707107 -0.707107 0.000000 vn 0.936329 0.351123 0.000000 vn 0.936329 0.351123 -0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000001 vn 0.894427 0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 -0.447214 0.000000 vn -0.894427 -0.447214 0.000001 vn -0.894427 -0.447214 0.000000 vn 0.707107 0.707107 0.000000 vn 0.707107 0.707107 -0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.894427 -0.447214 0.000000 vn 0.894427 -0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 1.000000 -0.000000 0.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 0.000000 -1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.996160 -0.087556 0.000000 vn 0.996160 -0.087556 0.000000 vn 0.971081 -0.238749 0.000000 vn 0.971081 -0.238749 0.000000 vn 0.930852 -0.365397 0.000000 vn 0.930852 -0.365397 0.000000 vn 0.881800 -0.471624 0.000000 vn 0.881800 -0.471624 0.000000 vn 0.830500 -0.557018 0.000000 vn 0.830500 -0.557018 0.000000 vn 0.779915 -0.625886 0.000000 vn 0.779915 -0.625886 0.000000 vn 0.735976 -0.677007 0.000000 vn 0.735976 -0.677007 0.000000 vn 0.699128 -0.714997 0.000000 vn 0.699128 -0.714997 0.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 1.000000 vn -0.707107 0.707107 0.000000 vn -0.707107 0.707107 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn 0.000000 1.000000 -0.000000 vn 0.000000 1.000000 0.000000 vn -0.894427 0.447213 0.000000 vn -0.894427 0.447213 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000000 vn -0.894447 -0.447174 0.000002 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 0.000000 1.000000 vn 0.000000 -0.000000 -1.000000 vn 0.894427 0.447214 -0.000000 vn 0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn -0.894427 0.447214 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 0.000000 1.000000 f 1//1 2//1 3//1 f 4//2 5//2 2//2 f 4//3 2//3 1//3 f 5//4 6//4 2//4 f 2//5 6//5 7//5 f 2//6 7//6 3//6 f 3//7 7//7 8//7 f 9//8 4//8 1//8 f 9//9 1//9 10//9 f 11//10 4//10 12//10 f 13//11 4//11 11//11 f 14//12 11//12 15//12 f 14//13 13//13 11//13 f 16//14 5//14 17//14 f 9//15 18//15 14//15 f 9//16 15//16 12//16 f 9//17 14//17 15//17 f 9//18 12//18 4//18 f 5//19 4//19 13//19 f 5//20 13//20 17//20 f 1//21 19//21 20//21 f 1//22 21//22 19//22 f 19//23 22//23 23//23 f 21//24 22//24 19//24 f 3//25 24//25 21//25 f 25//26 10//26 22//26 f 23//27 10//27 20//27 f 20//28 10//28 1//28 f 22//29 10//29 23//29 f 1//30 3//30 21//30 f 26//31 27//31 6//31 f 5//32 28//32 26//32 f 5//33 26//33 6//33 f 29//34 28//34 5//34 f 16//35 29//35 5//35 f 30//36 29//36 16//36 f 31//37 30//37 16//37 f 32//38 33//38 31//38 f 32//39 31//39 16//39 f 34//40 32//40 16//40 f 35//41 16//41 17//41 f 35//42 34//42 16//42 f 36//43 35//43 17//43 f 7//44 27//44 37//44 f 7//45 37//45 8//45 f 6//46 27//46 7//46 f 37//47 38//47 8//47 f 39//48 3//48 38//48 f 38//49 3//49 8//49 f 39//50 40//50 3//50 f 40//51 24//51 3//51 f 40//52 41//52 24//52 f 41//53 42//53 24//53 f 43//54 44//54 42//54 f 42//55 44//55 24//55 f 24//56 45//56 46//56 f 44//57 45//57 24//57 f 45//58 47//58 46//58 f 47//59 48//59 46//59 f 18//60 9//60 10//60 f 18//61 10//61 25//61 f 49//62 13//62 21//62 f 17//63 13//63 49//63 f 24//64 49//64 21//64 f 50//65 49//65 24//65 f 13//66 14//66 22//66 f 13//67 22//67 21//67 f 14//68 18//68 25//68 f 14//69 25//69 22//69 f 11//70 12//70 20//70 f 11//71 20//71 19//71 f 12//72 15//72 23//72 f 12//73 23//73 20//73 f 15//74 11//74 19//74 f 15//75 19//75 23//75 f 35//76 36//76 48//76 f 35//77 48//77 47//77 f 34//78 47//78 45//78 f 34//79 35//79 47//79 f 32//80 45//80 44//80 f 32//81 34//81 45//81 f 33//82 44//82 43//82 f 33//83 32//83 44//83 f 51//84 17//84 49//84 f 48//85 51//85 46//85 f 36//86 51//86 48//86 f 36//87 17//87 51//87 f 31//88 33//88 43//88 f 31//89 43//89 42//89 f 30//90 42//90 41//90 f 30//91 31//91 42//91 f 29//92 41//92 40//92 f 29//93 30//93 41//93 f 28//94 40//94 39//94 f 28//95 29//95 40//95 f 26//96 39//96 38//96 f 26//97 28//97 39//97 f 27//98 38//98 37//98 f 27//99 26//99 38//99 f 51//100 24//100 46//100 f 51//101 50//101 24//101 f 50//102 51//102 49//102 f 52//103 53//103 54//103 f 54//104 53//104 55//104 f 55//105 56//105 57//105 f 53//106 56//106 55//106 f 57//107 58//107 59//107 f 56//108 58//108 57//108 f 59//109 60//109 61//109 f 58//110 60//110 59//110 f 60//111 62//111 61//111 f 60//112 63//112 62//112 f 62//113 64//113 65//113 f 63//114 64//114 62//114 f 66//115 67//115 68//115 f 68//116 67//116 69//116 f 69//117 70//117 71//117 f 67//118 70//118 69//118 f 71//119 72//119 73//119 f 70//120 72//120 71//120 f 73//121 52//121 54//121 f 72//122 52//122 73//122 f 64//123 74//123 65//123 f 64//124 75//124 74//124 f 74//125 76//125 65//125 f 75//126 77//126 74//126 f 74//127 77//127 76//127 f 64//128 63//128 75//128 f 60//129 78//129 63//129 f 63//130 78//130 75//130 f 60//131 58//131 78//131 f 78//132 79//132 80//132 f 58//133 79//133 78//133 f 58//134 56//134 79//134 f 56//135 53//135 79//135 f 52//136 72//136 53//136 f 53//137 72//137 79//137 f 79//138 70//138 81//138 f 72//139 70//139 79//139 f 70//140 67//140 81//140 f 67//141 66//141 81//141 f 82//142 78//142 80//142 f 76//143 83//143 84//143 f 85//144 84//144 86//144 f 85//145 86//145 87//145 f 85//146 87//146 88//146 f 85//147 88//147 89//147 f 85//148 89//148 90//148 f 85//149 90//149 91//149 f 85//150 76//150 84//150 f 62//151 65//151 76//151 f 92//152 61//152 62//152 f 92//153 76//153 85//153 f 92//154 62//154 76//154 f 59//155 61//155 92//155 f 93//156 59//156 92//156 f 57//157 59//157 93//157 f 55//158 57//158 93//158 f 73//159 54//159 55//159 f 73//160 55//160 93//160 f 71//161 73//161 93//161 f 69//162 93//162 94//162 f 69//163 71//163 93//163 f 68//164 69//164 94//164 f 95//165 96//165 91//165 f 95//166 97//166 96//166 f 96//167 85//167 91//167 f 96//168 98//168 85//168 f 98//169 99//169 85//169 f 82//170 80//170 98//170 f 98//171 80//171 99//171 f 99//172 94//172 85//172 f 100//173 68//173 94//173 f 81//174 66//174 100//174 f 100//175 66//175 68//175 f 99//176 100//176 94//176 f 77//177 97//177 101//177 f 101//178 97//178 102//178 f 102//179 97//179 103//179 f 103//180 97//180 104//180 f 104//181 97//181 105//181 f 105//182 97//182 106//182 f 106//183 97//183 107//183 f 107//184 97//184 95//184 f 77//185 75//185 97//185 f 77//186 101//186 76//186 f 76//187 101//187 83//187 f 83//188 102//188 84//188 f 101//189 102//189 83//189 f 84//190 103//190 86//190 f 102//191 103//191 84//191 f 86//192 104//192 87//192 f 103//193 104//193 86//193 f 87//194 105//194 88//194 f 104//195 105//195 87//195 f 88//196 106//196 89//196 f 105//197 106//197 88//197 f 89//198 107//198 90//198 f 106//199 107//199 89//199 f 90//200 95//200 91//200 f 107//201 95//201 90//201 f 79//202 81//202 100//202 f 79//203 100//203 108//203 f 80//204 79//204 108//204 f 80//205 108//205 99//205 f 78//206 82//206 98//206 f 78//207 98//207 109//207 f 75//208 78//208 109//208 f 75//209 109//209 74//209 f 110//210 94//210 111//210 f 94//211 112//211 111//211 f 94//212 93//212 112//212 f 93//213 92//213 112//213 f 112//214 92//214 113//214 f 92//215 85//215 113//215 f 85//216 114//216 113//216 f 85//217 115//217 114//217 f 96//218 75//218 74//218 f 97//219 75//219 96//219 f 74//220 98//220 96//220 f 74//221 109//221 98//221 f 108//222 100//222 99//222 f 85//223 116//223 115//223 f 85//224 117//224 116//224 f 85//225 110//225 117//225 f 85//226 94//226 110//226 f 117//227 118//227 119//227 f 110//228 118//228 117//228 f 120//229 121//229 118//229 f 119//230 121//230 116//230 f 116//231 121//231 115//231 f 118//232 121//232 119//232 f 118//233 110//233 122//233 f 122//234 110//234 111//234 f 123//235 114//235 124//235 f 111//236 114//236 123//236 f 122//237 123//237 125//237 f 122//238 111//238 123//238 f 112//239 113//239 111//239 f 126//240 127//240 122//240 f 126//241 125//241 124//241 f 126//242 124//242 114//242 f 126//243 122//243 125//243 f 113//244 114//244 111//244 f 115//245 121//245 114//245 f 114//246 121//246 126//246 f 120//247 118//247 127//247 f 127//248 118//248 122//248 f 121//249 120//249 126//249 f 126//250 120//250 127//250 f 116//251 117//251 124//251 f 124//252 117//252 123//252 f 119//253 116//253 125//253 f 125//254 116//254 124//254 f 117//255 119//255 123//255 f 123//256 119//256 125//256 f 128//257 7//257 8//257 f 129//258 6//258 7//258 f 129//259 7//259 128//259 f 130//260 6//260 129//260 f 130//261 129//261 131//261 f 130//262 131//262 132//262 f 130//263 132//263 133//263 f 134//264 133//264 135//264 f 134//265 135//265 136//265 f 134//266 136//266 137//266 f 134//267 137//267 138//267 f 134//268 138//268 139//268 f 134//269 130//269 133//269 f 140//270 141//270 130//270 f 140//271 130//271 134//271 f 131//272 129//272 128//272 f 131//273 128//273 142//273 f 132//274 142//274 143//274 f 132//275 131//275 142//275 f 133//276 143//276 144//276 f 133//277 132//277 143//277 f 135//278 144//278 145//278 f 135//279 133//279 144//279 f 136//280 145//280 146//280 f 136//281 135//281 145//281 f 137//282 146//282 147//282 f 137//283 136//283 146//283 f 138//284 147//284 148//284 f 138//285 137//285 147//285 f 149//286 138//286 148//286 f 139//287 138//287 149//287 f 128//288 150//288 142//288 f 142//289 150//289 143//289 f 143//290 150//290 144//290 f 144//291 150//291 145//291 f 145//292 150//292 146//292 f 146//293 150//293 147//293 f 147//294 150//294 148//294 f 148//295 150//295 149//295 f 128//296 8//296 150//296 f 7//297 150//297 8//297 f 7//298 151//298 150//298 f 6//299 130//299 7//299 f 7//300 130//300 152//300 f 130//301 141//301 152//301 f 152//302 141//302 153//302 f 141//303 140//303 153//303 f 153//304 140//304 154//304 f 140//305 134//305 154//305 f 154//306 134//306 155//306 f 151//307 139//307 149//307 f 151//308 149//308 150//308 f 134//309 151//309 155//309 f 134//310 139//310 151//310 f 7//311 152//311 151//311 f 152//312 155//312 151//312 f 153//313 154//313 152//313 f 152//314 154//314 155//314 f 156//315 157//315 158//315 f 157//316 156//316 159//316 f 159//317 156//317 160//317 f 156//318 158//318 160//318 f 160//319 158//319 161//319 f 158//320 157//320 161//320 f 161//321 157//321 159//321 f 159//322 160//322 161//322 apparmor-5.0.2/documentation/keychains/README.md000066400000000000000000000044301522511161100214410ustar00rootroot00000000000000This is a set of file of creating keychains of the AppArmor logo. There are multiple variants of the keychain, and for a given variant it may have multiple files to support different programs or uses. If the base name of the file is the same then the files are just different format variants of the same file. Eg. - AppArmorLogoShadowFlat.3mf - AppArmorLogoShadowFlat.FCStd - AppArmorLogShadowFlat.obj are all variants of the AppArmor keychain that embedds a shadow. 3D Printing Files .FCStd - Free Cad model file .3mf - 3d manufacturing format. Contains model and material/color information. .obj - 3d object format, general higher quality than .stl .stl - sterio lithograthy file Laser Cutting/Engraving Files .lighburn - lightburn layout file, includes the model .svg - scalable vector graphics file # Key Chain Descriptions Overview of Naming Flat - backside flat FlatFlat - backside and front flat Shadow - a shadow element has been incorporated. Relies on translucency to be shown. ## 3D Printed key chains ### Filament swap/tool changer 3D printer #### AppArmorLogoFlat - variant of the key chain with the backside flat, designed to easily 3D printed with a filament swapping 3d printer #### AppAromrLogoShadowFlat - variant of the key chain with the backside flat, and a shadow element that is done using a black/dark layer internally and relies on the translucency of filament for the shadow effect to appear. Designed to be easily 3D printed with a filament swapping 3D printer. ### Single Filament 3D printer prints Single Filament models are designed to be printed in single color parts and assembled afterwards. They share a lot in common with the laser cut files, but have been extruded into 3d parts. Some tuning of filament settings will be needed to get these models to assemble correctly. The use of an assembly jib is recommended. #### todo #### AssemblyJig-D1,D2 - an assembly jig to help with multiple part print or laser cut keychains. The X, and Y specify the expect depths of different elements that are assembled together to make the keychain and are responsible for the raised effects. ## Laser Cut key chains # Assemblying For non-flat models an assembly jig is recommended. It will help with the alignment of the various elements of the model. apparmor-5.0.2/documentation/logo/000077500000000000000000000000001522511161100171435ustar00rootroot00000000000000apparmor-5.0.2/documentation/logo/README000066400000000000000000000016721522511161100200310ustar00rootroot00000000000000The apparmor logo (logo-default-red.svg) was created by Noah Davis and released under the LGPL (licence included below). Logo variants and uses: logo-default-red.svg - default logo and coloration used for the apparmor project. Created for larger (64x64) uses. Not optimized for small 16x16 tiles. License * Copyright (c) 2018 Noah Davis * * The appaprmor logo is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This logo file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . apparmor-5.0.2/documentation/logo/logo-default-red.svg000066400000000000000000000065461522511161100230310ustar00rootroot00000000000000apparmor-5.0.2/documentation/logo/logo-large.png000066400000000000000000001026321522511161100217050ustar00rootroot00000000000000PNG  IHDR lCbKGD pHYs  tIME; ' IDATxw[ׁ*,1HRmKr%yڶn9}fwΜ=;:g9;3==dJ")ي bZL"YU@E*x( (.G6Y p#_]'PO CqwhsI2e请? x-O?$-H}ccEM_^{gx/[I'#}gQ/xի[{qANKLn]Q/xg/~LߒNne_~vȚ5*x;t?tF)IG#rotn˚g w7u봶?4_+pQeP}1FPv_ϧ6۟IfqES_-#|Q/69gHnvv㔤#qy x`Aq5'y[Cy?{\{x`V/q;PRi%  Z~Ͼ\wjyIzNVr #\zuK412$黏X@[~O8Efך"|my@[v6/4K&T:Ci'|`;GսF.{WY|A8冀kP._1sI!t_Sz^Aǫ"/dD "(xW}@3&i9#2.:_d8x#mݒI0FϦZߗ^k{F,&%UHY^%v;6mZ?9 vq^'~d,0F:/E^`^(KMn~D靵 իJ} |,/_Ev;G?x/^({#ƈq8?`Bp֙5o8s2&ajI?Sj=BnI̳<_Sxv4Yʘ[%mIKڦNr74C_~m~K: el=2z]r/7ţϱ@V^}?>mU*aX8w~nEM> z~V=ouTGˊD^n3,@X~ן,6uc%*i6RK?[?mڵ;^wXk>sHOq "vZY{k*^E"pe/WSQ 淯k+`!{9.*;<իӯ7଻^ܬD6mܦc9K[v^ѡL[+i6_: BV̓]"I_K]ݬsO_}kM_֖֔zl9m6|'nHoJzk w2 ([n3Wȸ[ NQLFfZuۗn-L}5=}i8͊ؗ|d͚ xI FfXPw|5kFc^Gc(깍oj[oZˉ1yFz+ El]aROo_6FKf]#i #SMM媕m7K/1<텺?Doܪ^ި]= H HfpN[6ga@ød=CEtIWIjadJ묥[woAsgQ:3nk;Pi˨Sf'gѽ2 5n٦5Ƙk$]+.PVctե7_[YUw+-Tӡ֝zav;ŀ'9GLdph Tիi:U텒"Ntn*}+C鹍[7w,I[!N4xg! es]?Y4]VɹUNA,Fr͛\}n oCz7Җg5 bvKڭݴه30UEPr#wUΘUbmU̟=K^J_j]v9!s^޶[[(HengfD@^|'HZ1k$筚U+t뵫tKF  im=:#sUI\^gXYg~}ah0_ϗ4ʤ&HF|FfϘ[\^s"5751(~qm^ؽOcPsIoK.>; @Xy=^jVɘ땼,"FW\_ ]qDx_I^ݾ[ڧ? oTN1[\jw֮9 xo=uJtqZ%in/<˒t˕+uۗԊ eNnGq>{?|T9 bYKV>M+-Ky(hUuӪ:gb~kvWxI mf}`x2?ܕ.ny'Mu+.M뺕j)607oӖ=Fz_244~AJl05'=t<|JݴjtI9tSwն>TuqQ[3`[$]뢖RA2K4me/`,yKKg5[zmMqP\jчmG@-[`Y~.|]qLQ@]cjVEnЬwf4 0@h4 \/<_+/qM:hu0h;Y}@I6SnѼ9ܳt9g֥督ӦM`㐜rM?萮ImYM0hR%85Ai:Zyyugiެ 0I-s" I蛩W~gt%[(S!/P]MXw{β% Pb]Z}SӅ& #b#A۬wMz6@iM <}DMe@e\^#swa.X],.IR??E Ď&,Z~A c"ZqKKk|n91Vs2;I6?%q9/Ùvprݣ}+[6C*±+HZ  ^` YGelSNX͞gԱZl)Mo b$"o@&LJG$ke6u≸;hzku h+'ziz/?~Tn϶߇vnVDw`|;E>_U x4 N>]--:u[ hw Boz ?:+%SGxԹ'[Qczmnv`%ppodFsRs^unזwܹcP,Sg Bu;[ wO<\:BdeB/@c n+;7iǖw܂awdezxtֲ/j8<벾%[އ{3fLWKKsJ b0B/IfܹۖkG-J xT]t坡sys0]B/@J!?[^i9Eod˅v>Co\@ͩTӧjyt#@[7H -nwCɯK^;v7*C-oa7DF jwC> aQv7=^iXINtu5l@j![^I;g΄nF DޜVv7S-osZj]+5a͍|__L'Xn#۸cN99Rp3q5 ۏ)سǀ-9Uf7|mziwxx_KXާn|uyv3C BIԪT@uKJvmE@'jse#{z +3 zkwC> XUC/./oߺa[ΰa7&ĄY&^U~.NOWlsR@7$ ('Q-w WWWB/swy~O Ξ5"a740Я^]^R0 v7TC+bhwxxQM޷elz;Ntk]^hv7 ziwxx{5BL޷ ġD~E/nv7ZbCS+60 Yvmn6_Wl5kfpPOް/kv7s˛oņ2]I22\@ wV2Ϗ ގZwLe/@]-5|JӦOWSsdL2j3zX{zd+zO vmyG_}seM/WU@ݑU:=misy+vCE^ nn\w̙U Xv7zxHxN]ߊ #[ۛ3&Zvwl~ sTM@P}vyYi˛\a7_e./PuWbCunM8nͫ C/~!9k=8g^>Y[FVM(90~P. +f^¤q?\ww5}:{wgΘK)uM~\$o]ݽu}OxTWϓݝ=OUr˯3߯}v/H6Рu4zxjj_<\#wm?LPӫE6B/* Ô[~ܜ~-oKKKUî$Iz+@a*~ΨjTk zxt IDATH _olwÔ[~~k`-o*vT2o˼ V 9?M,x D>XZJݮ^-޻yTm@yyU_ɹP-o%®lw;Ț19w7a2ndņb-onwI-޷~AB/ϏL@9JqU<x뇷Mɫ2J39Zޢ[L=E{7UF#@xIݵ)xXs.6Zq+6a;T`&≂?@qq@jUe ]r^nP` @v*a7D[nwk9J!z ؽջ!s$saiba7s\0ξ@jm]weRe f9;k9&}cl0X}MѢ@nqgE_DؕƷ/y1c~9Jc\ނN^įuzw>οVM]l}7cH3ҰLq4E)"'%S_뉇sI˯nHxuLsyt,Y&&z.LI`'9>Z@@q6%|cDx҅+9c {n lN3gg\aWR?ƫs* n]қ} [C'_fVf׌1%ӌ~ .P~TǤ/0.x:::WDH+e-s" _~z9Uxuϻ6uTrQaWZ.;~$zuL3Mᗷ@6UՒ%KFح+%Wl-~tu]~둈2 wV#)? SǕfBi]w{v8wnI>% z >p[yյS8wF&~\K{M1.\7~ͻv_!7z/0лxsؕ¹tuÖ7sH?~D;6ӈԊ 魌cN/&"N"M66r.Zͮ&zŗa;7׊ uJa{w!yU 9ncwؕ'N L%vx.P~YW_+I+3n\ڰzNF /E pCZ-o]]gmްk&vGDBr 5x(vvdsI[乜cؕnwvGAOk41@eB]b]On(zŗe^{AWMyuW4+_׼|v_awD rjЉ~p6 ɦKؕH4Z^xTQl\0Rt$Bsz +6\庼>Ee|A)#xV$f^zA0лH])9?R+|'hy٩Gճus|p)pzpՆy^qq AB… 5A 7^e?[iyUf[5e߭% `-vvT5UUm (M%V/|myO6=(谫r~\jzDCXWd#V&JɖÖ7m(aSM[ΰkFL@en/|aW w fon&w ݻ':a{ZF'wkuތ:dzßwc3ǡaC /P\܌͛)S[5: FEWF/~k1ҥ>MdIE"u/*s4sko;|Z^pxWh^DsnwУkd; sL3WkLd ¦wJٕR+3|md`cC b ֩bx1|ZreMcFA19L.@:N6r`ó2N8JɹV-F*춤]zA9k  Fҹ|7hܹánre&vC>iy'v%*v3=kL925ٻ eb񊟯 z0J Itؕ!Zޢe ^yUk$ 5Vk#̸Y6 TupD]i:N-|qz =WUd -K nzoXؕ!]h /Pj6:8q+s5 vDaWb.x[=v2aW"aW9b eXN N5İ v{zn nЊ ]cׄNOU1 JKp49& >ov%%DĨFm pVǖwN ܲhsw 2fzɌ82g%b9vdZ[I6&uG5F/xsϿh'2Bo=-JfOXo6Vl([_ݞm&욬;V;'T0@x+,{d 1FsFZtgWf(Cؕ![̖ۺhxwУWٰ+'}좕 J_ e c~'snww e hy:Lq ^^=aJqCG{7yq îjwG]I[ybCذQc۲p AP+IGhEîduu IDATV(=V7;zk)nw+vMjǫfn®$}䢓r3&9eʸpwgψ  L5|}9*4*N2&®ݭlؕ!hyJ5V1IG\nRH72k@d?tuWk;vG!Z"ϯHD2lT;NhnNVgBx2Fۺ1CaWnwv%Z[^]qj]I6V0JRq^lTB+z} vU/ns+znT+Iږ qx(ۋ;$IU:<7٪v%d+%[^ϤӜnM֠aݠra)22^\*$٠䧟k߇9cٳsOoVռzvӡ/k/u^E^va+BCE® ?kM ^\\$8C]IJ]clxV&kE)0]WYbw­s5t=_9 8 16u~9g\FnkF ?F^049ηwR3T2HDyc@oN _*7w&/qJ򘱳+ۨm zx.ف>}EN6zk3^w$T~ZÖ ,/U-uؕ~ټOaWbsy8ˡ$ Zkv#3m3fפnF+I%J >iZ0I履i9yG7G˛!qL'|#vpybu#6r&rN)ʅ᭓_Y/Pf3-iPL'/Ad-Gؕ߿ݭhk a:욑ώxKn >~3y- x2۽{wIo5}?2 ==9vO y8K3WJ&I[ʆ]9kEj IPڜcWkH${y#`aWBؕ|K{n5aۤlaW2%"ol/P!QgK o"o_S"p &zKvsvC/58[&I֊]E$1̔gx&UҩB Uz}?r &zdމ]I:uwk-JɖuyaÏ&K؍9iWYqfBMwIɄu7}ًo͜d侘E+< TݻFڒ@޼Kr &z6ӣ;^~8-7j]`SK툰K[2ۉ4J.G l۽{wg0x r6}"W!nk@L"Μ93z'v%OZSǹytCu6- (vD3@QnRVr'@ NimPE V__1nMؕE6p.sYRaW _;p\5͜.P[$|$EY ^>U ettJͼ1̙34eJK _ux~}-|06E:79o7i?49y8lwIZgI/se8hveFS-W;V _9xh \u^J\kߟ|0ØjzXvӣy;_+g(k ?f>^ uɭo$<7l3donor&rKa$Et6W46Q ^JkF Q}xQ[(Jɹ\ii f뺼 .G6<;rţ&9({Ć]IM]I*z?R zqz?B*?!V(ammm3: >u~\[*v&sRٰ+# 1m|=@\d+\k(a蝒zC/3@4$N? @Ȋ % 3fz{zz5wǫuvCqkS?s>}~h^nڙhhؕ~prh,g%x*wti[٪\nx+ޡ8 %/<-y+H /P܆LC˻LCrܸi <6kηÏ/Re # W}inԲ :\yndonhl$RٰLjG)/C#N*,QfyqFCT5ƨGsow-̛\~ZXGzܘ5YaWov%m>Zٰk$+9V TQ'sr8|֥¶6yRښlw0SaWZr;v%Dlnd5Ɯlt< x /V6Q}O?}R"?Xrektfm+%[^scG G[#1awPFO%V>eð+#@3zIXR(JxG9(yKl14)&cq4 fy/GB>%|my_yo<|I嚄ڐB e $v~j PHv(v: Hudve[5cf4Fmy^Nl陙GO\no?юc}k*[ד\R7¬"V;LJ\~R/0۱3~m?/].7%mH_B]^n8Z/&Sʮ"NVeRV;6L*ƊdTv2َPٕgc s[_(f];Zۀn[zZG</_|G>nn2F[m6D~wPbḡ]Iqa  1 =kM=_/qe<g`k/^c={/;f z{ؕ۱a*Uyݬc~65 AT,dmjk;^` /_ʴBc2Je՚ ʍ0g }xKx: 粃1+5oDs5S*N[Vt}vĞ򖫻Y =;nr}A\ &[_ #_*;[y~y3$s>^`YOb mضk.G<9pw:]I:c&nW*.5yJZ)TٵRF*V V^4DߓͮY\}@>n#ba7UGdžU],+vͽkR@84>;mfUf jPW*mؽ3Q,[s*ߛTmk]HX,Vyۯ-VwVvm1ʮ JӍJҀ,7\}@^};iQZ}.Sruw`.}vhw_tm֮2v݂sdKIRYi6>+-vi98RX[Ҹt+v+=ﮃOqvw~YdPhPIC{b 0Q݁!rNrtv%hwatgEO{]ѡ ʮ\2 v40ٖY{|W\mvsJ&[-6M[Fwhn6OtcZknPh+\}@'7krڰѿeҌVJU#I*pS=gD-3rdžBګ~kP߲ӌ>)ب;gAҷo@XVeeW&7'fMbߦg~ht|1–gw)ӫ_|7[mWXz'7`J8Lr襲S٭2_=WsYOotmV$Uvm7u-R.~PVn}lǙ&jZîF|4z3Ave,oOWv̢6 *5?:YmiUKy:ЀkRەaW$hC/0lh[-z۵aێ]/~S T>'S7\nJo}+5)oLk_{<ؤWv/`~oԘY®mhu9W~xȼMwcaRHC}J_v73hCT eiG6=wn#i~^Ә(KaW9W\1qf<`a[7 {fN<8p)nokƧ3~MfLN+I]ImJǿCUܴ~; Nt./P .WH X~[ewsLF3^{On{}<g}[޺m̯$ʇvmTy{:}wv:R%^vԶr EzciAaR>ܫӽ_S˔f4F׶:ޕSj˕ީT٭*o]5@6gCcSr>uԬV]eYîTfqDTy7U#vtvl`ᾛ'W+ z¤ ׺_\lRtgu}+1+TLN1n_>`M{7w]wyƄ]BL]=оj٧^`xx :$sV&ƞJK˷p ۤt@LK˖C~yy{ݺ+402*@-9K^pGjOM3(xmu烿iھc1JJoٲvjb3_9uujݫ]F fCnKiU:_b-a;ɬb5ʮ<*gRPuISxӞ^FVn4|8fʧB7=̗q˶?A%r VfGү XݭG][u{aW$-ּC ppoYoTI)]3}@/iǞ} ,]۟ձohBonW2ئ-~AUOWvww]qni`-N.v%Wn xIaA:,m/ڻ@_~$szvʑٲJGG͐ IDATNf},hi~^v4Ǫa,#g. LBnzH>$i߬?%n}vCo#nw={ںEݿ~ov7xNSlhM%OWvv&{ Yt\a.6zizzcS_ҡ#uy'~%i/HЛ \YG`߾o [_qaW1*ɿlg8/Pлq}EU!Fy4tmdQa6`&9$&!j{4.ʋc 1`M@ߺ&3nW.ivՈްK;~ńN:UK5o#ݚCozX٭iEM3ȍaܕ+Am"n>_XBk} v+>َTgî[~;9k/p6ݳӟ`͠.s3P}rFܱS Twz]Ny[U{MjS!׸RG>f Yg @z;֮]p2ې]ƞv殧׮׳wOH襺;;v˚)FvKZoF0<\ص4 ve`~k8K/л|YC;Z'"ҙabBov~\Ekؽ4@OM 1T:ZN{@vC/Y|IPjoS?>|Rnx> îv'h=Xl7B=!îVwGںOȰM-:斖Y ț*BݻgjTwJ/uZ~G>@g~Ε/2rFwv=qڳK_"[땕^딘@J>5\ 1?x+s# S޾5?]T0fls\Qo~kL<|Rݺ\Bosm%EZ>îyaǪÑL&#|Tφ^"*[P5G_uPءcVάau:d>SX ry>_%~^ Gm_ z-j}?Oj'~%i/7LP❮aw}*6ߓ6fC,ve9G@}0-ydZ8Dܔ2s#Ν_e-Q_cn.}D jTv nQ -M/zahLr ̋C-Nh[zcTCꝮa+"dFɣf6SM;eOp$u. ۻicO=A)\,SSX yT(4ja1Tzk1ڧaWa 9otϲG9/0sBoos174T-ul! .zk}*"p+moC&1Z [U޼W3^ 8:֮]KCDZӛJ),R g`} K ;Lv!n2V;\7EO#Z X{>>35C' /vnHW18K,jI(u;]}'7:d-v%<|K9Z/0uwydђ3桃@;cz-F[$I/ tWR蝮aִU+1G }YBe+ss^%{7o3[%~@.^tfe+3gG6$ȧe؍I맅A2]uXI+{c#]y\ x }z٫`1j7irzU8Ea٦Z="Wto:{aP6Tٵ+;W-:gtp7K,FuyNkYgۑׇS-ѓy®U:Y?8laja {r<{fdbdliYQ)TS-np6Ħomaa f7v,=@ l C?iM¡䙙b>%z[bk!zsaPjaآi }î1ZaߧWAL- dN7-f)3ˤY_z$9ٲvWs8Oy1]<裴+Z`iS^r c+%1t,MYOi,ҨL5Ϣ^@\}j]ts:W+S:ka%G &S5\5y`a o=iomͿV 3ecxآ.@z3v(,i렠ڈ+IIZ(SÄ]; x׎Oyi?Na++Y+y:=W#zzMNσ>?F-.Ϗv=IrµG@0Av[>/QIႅyZjY9=zerD-^w"f%vĜ>SX-ޔk ֖;:G@0 Q5ӆ !AFzxl4|fekXB}$ ߓL]s)%V= 0G0uE`*:cs{\ISi\o/4$iW>qoO؍n9O sJMv]!sc= jCn\KzpF Uz2g+gҙPo'QeB<%e*5hǠʮn^.@Pл} N=}d/.y/oh`w@uIOfm^r1wq 0\>w,5#-(&Ʈ1R}nNcCݥNP1ְVtb?DnHS®v^C$ /0Mteᩧ5Ive^azTz+8[,j13UX!7}+]TL"#a]!2E0fW5G@15o-5[5yM?4P%F~gi,Q`/?n>W8VOf)#aتPZaî+ ȨMk[] M=+[+IIO{^aG¸ފJXl}p(LؕBZa:W/:k,ꝲl$sw'z$KB ;6@ץ Tv=ve1џm^ѽi K%S7R^tElg^yQc8 BԘdn[r{_'4)\t/s‘'\{Cٹ>V37M^F?f~O]Ha*1bB_*운Rw/)y#-LSoڣEzU4RIy)Q:Ç]I]1۷ܻ14@0 Dҋj }ѧz5<Xsδ^>Wt}a t@!{aU n!#]VĞy{@K 6|ٿt4~3[^I:-7^O>ڤr/ ݼC]—;W/^ E@UwϚ-#_+t|dWv{qcNŒ޷y,P-dص(%/1҉ol9 =HjY>ZNh*pHtk mcluxNnd d'vo벱9@r@'=S}0OYBi®4_l?VK aBζ[@Pw>HzS%S5J}$qSdlo*tn(ӡ*T5]l]T VC{6>l-ɻ;vKg/1ۻx\}#O@={ȵ$2x\XlvcA!-v݃]%qD8鼷HѯZw3T!J)ׇntVMi],ek f˪Wsv N=m/'7^bHa$ԖJ'ӹگMi(K 5]7fkM@9 _qyf_粷zuqo蕊7797'= tl5|uxhju)(b:x݆Zh]M2}m9w\"_dJsM~Y+Us~tѠk*symae]o./sZHn9I0Q$z_ &waVVWΕQ![kn8kxL9KS#7JگLA1i2D^8S{zS88aUqnLi"ynӚîd6J޳e-W9{XC\&䞽z%8Kp@!=?[tmoav+xˎvͮ޲/$Ͽb 8anh+IK,nF:= ) {a]BrMazem@0 C/xF^^Bi MRg^] .8{g^neKc {Krk캒B*Ya7ȞLsv-{gqp dŖV2M: iZ'.;CԽ.[*Bma7l3]YB@&p;)2%-D>S׵қ}uڑ®!5e/r @w;^b}rtWR %f \~݀Gfz\Iͫn-G-,5u}Yp傦 T0,d1J7$,ݘۍ 0lص*-{翧r**νʼtIg&e icxF]wR_8kf+vA@S ^S ]xyuo-TmP-LJыvXNemWq @z{Wߑ_!swTaץ$rţvZ?Ҿ&:0 @x?l/|h/nb2joMa7bU] ݒ۾Q s[y*|oIFi69욻B, v{✦?sYG/d9 >Kc{G;S[[ǰkVp"v+?=4;=~g]ydF߼UlaVށv=7-pa$Zf[0Q0|c|f QMQ|ҫ:ݴ8|)z`O9JLgTxL{giKftQMVl奙j+1]d1{ɚîY*܅>1^3Io5qYcyD1j!ʹv%IiTB|]s؍d8Tx(l~o/hْGU%pWc14LDc_}a\aף7Nw4a7XR_`& `IDAT:ᜋNQZ3$y|,ˮ śJӵ]3cn_^@׽|XfnlQ6ea$Y,H>o-a,l%[^01s_r^I?k6 ۘnd *paףbi>L{[.zhůf:*Pw^֗$\!׏!+վWՠk^v{®'rw</ .ؼN5D QV 3)mOvw{Ckxn,̵&=9HdT-6=Zk|=*IR-Zx &;b|CsP \tT5릕*1$Gp ym0,RSo^|-{ 0n'?𠯺ec8]X2손{Ƥm~+024@7k=כjIsf[-r{w@Z6q/ ]^(T &~b[P7's)g$}@0+KujgISd.xN|ݻ^!;%0QL!J>ml] @f< E9<끕?ek2{m=ܷEO/1]n[s=_U[[M^>NabKGD pHYs B(xtIME26mN{IDATx]ip\Օ}Rkkm} Clf'd& PϤդO'US3aR cw/mlٲdceYRֶ~^oRVG[=~;{HHB$$! IHB$$! IHB$$!,4V<e/(pb #;sf|>yN08e˒g,#/^GJTqcwF2^x +N=3 %z~&,@pH?;Y{_' 'if56i-+W|XxVExLO9Λ]\INu7>=vDEa @p=eո⛿̄Xg/ (aq:7 Bhڼ6 '6ɝMmy>iˆ O⣏Dy,[޹"q\S].\?gs|,?͚wފqvWh` ʉb݉ <;O "Lp =y3aY3Z!bJ0p t\q7.Iyr\:Q84jlIڲ04̟1 fLg ۝1jb[" Bbex )jUJCtr.{_eУEK$%%!uA!55{iI&T~''mikUѶe<ߵ=pB:ڷz9,c`-R\>U Y[_iyN.°j+CGػ.yӻ^shx8lo[74BH XAHK*[TM|%ǭdb|EI?z7Xv] l[ː6f\n.^o >Q p`@P %%zhjntPQ|yRG"f˶U4m^m{mNNx<[B88П[t\[ fzr{HM5P AR(Ptڶ鳸Ml #mi5 w1(JM|-HIq!%%D ٶ䲒Rx25m\e[Vnn.` hcM>#0*8\WV5& DT>`K0.0'mٺnM۸>.|"x=_sY:bGy nYY]㖧$\p}ȘA{;^/ƌ5EW yݲvЭ۴ dgg{>8%ގlY˧}K0HP9["̂:#0']HNvB`%G.G0bz޷g|Pw<`{N_\ ;+;m{CWf h/*igʹKm.W2R\^ʯ 0]ƥdZjlzV F` "Zn@`@@h*\WtiBiBrt "*+qU]&FJ|D>*.vx3,yY=6Gpu׃4n) nzf6`}D_5cp~>jKpmcOl^UgPRr2qUu9$(y}BDž{s$г=bZ[ETw?d{m&@ۍ="`ߨ'Ƭѩˑm)>USi{:|ծ[WWrrArR2f4|6ufX?1|`ڨͭ8.32)v!ا' D.)Cq^ j+.{=Rs0̼0D|C.J@M 㸰aL^tPk0pC!kTj20 z瑌Uo!%,|d݀֒O!@-k XKf{Ow?8 ={}NZU KJ=@o k68|炦ϱ,6:|#``@#܋BT@P`609mKʌ^ӺڒGlnhA<{{B Jnx /){yz|_=?<8D3f̱..U{+r>nyh3" !c2CDl~Y00?_| Ǿ~ 0\H`t Ƹq]2ˏAc4&2ȳ޹mu&BnnndTDL>SWݐ>gyϣ+t/l^eeFOd0@v`Ŷ̚B o ,67[|_O3B9&: _' {QǷ7l*,@{UYP !)4AhF;nܰ)?sed8vû2?|ӯ`+1aS>8f\}~$b3xJơ 4DNNnێ"Nmodž^ { 1 @1İdf0<^{>-=$dJSGм}.=kt"W{Q+z9Qω="ܜ!+vlFgqƍ\`SDU>L3֬_St 2  pK B kuMr+_).vyn=s3 #h,O?ʏؿ@vNz =9@= n@l#*؀{2?x@eĘD |hޱD }Aʓ&7`l**3BoXĝ(ND3itdQ]mCoz1~Q:ŏvj׮ Ml[YJ: Oɸ?2; ċYa tbX[Sܪ&SD& #uY٬DhA;o/ LOaXr$ANI@{t=ZOY~t|C mLxuxH!}S  :tW'Q@Eñ+ Jhqn_Wxu@Ue%rOD^p(W*,(:*Eb"@xdYaӺ7e(Xm|cirL. !3"wN[\:F/`+Vi2Iqs\bjÐ[+J+0DpuN8s2XiY(+Nc b?M0Ɍ ΟmkB?L*< NV`(xAUa'b4A߽JM,KO @g!@/ )YVWVbh㪲>Dpc}6z~Pv{Kz՚,/DGy #ax%ӝn2\K@_lxJ@In{ ^ڊAͺ{)%I08}5_2 }WPNp/ b0"KRZ+eM{ׂz`*' y3ғH?9HD񇇅(N)f D|=^ 'LF/ f^!{*a,,9\.F,!D )nHqd|qJ!#a?- l8Х#.г HLdk HUㅑÈ鏷2). }gyYr NAHaA t ))[?i9~e'{>Aߍoì |! rO9">#Ikihjo$AHц׭ `?e;BOBb>upFRHV9w sD(| GJ}eHڜ  @Bh0ʡ4t|8]Yz|FGB{dquomek` $Q"~3KO%6*{%Ueh;S(Vӣ* R@(q0sP ?wKnYa%'Ki>,J.bhs~R1w)l|Sw m-2̝/ɠT9Zs%}Fd5N(YJ!$];|خ]LI#| w7Z{dꝏLW4EiNK$'/7fLCU l| ݍ*$ cك;bcFXsZ.P֌y?F,8A kM6Sk>y>VXehlɞ:o'VY!ݤ3dŒpVAYvX(_AS D|JsDzc2iɚ{AX4'E @H?Ng֩ x1U 3h`qmŋUP3 9z$D#2'|3L֔3)џ,X|l|)So[=)hXsFPFA-O@RB@A g/mœ+<=M4`ApMj-O=,6+}9l 7@FhB[ f%vׇvū*Μ;RA VnD&]{>bi ڣQ}gaS"@x?b `"H ``B 9?whךxom,@[}WY3^ɚKM ͘~@)D7nXO Bی >2s8@qq1ܹsPT "hZj( "005Ç … "xyyBvލv`0鸵Zre 6 7n* 32^:v;H222UJD8~8V+rssq5}:d+AVux饗pIA~ԨQC͛xkZj2Ojuk4ښ5k`a2kӧcܹXf xШQ#h4hZz6m |21{l߿$͟;v IIIU4 j5'JR)#IFj쯗}p.]d!J*`0jժj0  ͛ł+W`Ȑ!|j5j5B!NLBDDuԡ'NZZjb%Kжmĉt""d6HRXXz6l5k֌V+ݽ{ t:sl6&VKjN>MO7oޤÇSzz:%&&ɓ'p N:Ptt4Sݺu),,i͚5sNx"Q>}hͤT*IЗ_~Iݻwח=2339={T A-Nꫯo߾P(fR*T*nQ (,, 䐏ё#Gd2N#BDD:oN!ANϖߘ !{f#Bߗn-P(HPb!^cp/zh4p8LJV+YV#""RIC`8t&M$u:T*1o<ڵ 999܊s:8u 1L&dggcӧj,.rZ҂zefVży>aʕx7f гgOzn ?V@@n^^^rJ\pAFVžTJHVvǏGRRv*+'{W2DzHa6 Nݻwqa˖-cX,<˿}vx{{YEff&<ݨ/L@_~%ڵkIGVnݺؿ?~N" R8NaqFFHNNFv'(Qjv(..Ƙ1cPV-,\k֬ի1n8ԩS͛7%5ޭ[7"""`F~c 4l,ra CH7OQRR"#O.v/xѲ]ɕ=g>|/SiÈTJǨw^@zz:5jN-f///t:ԩS5jm܎$.VaÆaժU|W5kp788X?s̑RĂ dHl6s/..=#Wjz"f<{ .DPP޼S,7ߕPY bl6#;;'OvѲVF &\<"BFFzFBgU@5k, n޼)Ĥ$}v̟?̥&Z(n*UBвeK$''#==X,.-%B>>];~2WZ""YfXhbVN./#@y}iΔY;vәh T*'SͅIY)qP'b̘1n$HM6I[luֹ{eU"Pj]tQXXȭc) HIC}hٕZ"X{-//Æ yfpWjl60؍BcbbX?%8Ο?cǎ1X@@aÆP*ܵf>0aޏT*x$`kRrsnmdd$ "#@b{M|gxꩧ@D0naF?,QPP*7 &Ԯ]o֫W'K;y2(HWZp |W Yn<O'RTqqq2e ݻ'#;&t:q),Y2z:toV(R&`ɒ%Uoߞ(JԨQ6m* wGll, |8o5kΝ[fi,WJťeIjհn:rsuoܸ'OgϞVbcceۮSdY#ko`0պ>*nZjcT0NF. ڮ]dR0m4u%q\hI_tČ3p YSj械YYY5j*V薭oٲ%ݻw#//v]?쳏u,U_pA?箼JjVO e&.][Y111n5PVbGS\AZ-6liӦ!##ãիW|r :kז}SO=6m`РA)@z۷ou# O@ժU-[pޒΝ;ʕ+CbĈ2e ʗ/-[O>pN:l=A=JE}zǢ*U࣏>t:qI{(_C}(..+~0Vբm۶k{vOe_x8z(2k2kԨ4ddd2xkQn]OxRw$ ʗ/;ȑ#tzaX蔒ZL_|6m3w\a !Ӈw]t +W  KFUXh444mw8pk֬AݚFTui(BPbŊ3g+zTʐpK0aTXQZ wG^^^}$/BCCѡCe+ wFi-$J%OΓ3ǎI>}ݒZ 3gΠGk%B@ڵe_:Z[@@ i\^pO?[thѢ#GDVVN'u-/((@NN5]r%5k&>lVԂ dYW_}UFNQZp H`ٲewkZtOXT*y-B`@NG R) 1EEE|42#[[h[yRįŢE`ϨQGRum;3f Ξ=;v`ɜɥZR)VKڵk$nl6Y/ 4JYZ'Oƹs ,&JI&qؽ{,+ODHHH@PPz=222ddfs~Pre\~ݍ<%H5AU:E*{bȐ!Pղ4۹sg\|YRS]vqBezÆ R ///DEEd2fIyf:ұ*+WgϞXp!t%<xO){"l6Ez~LHHRRRpnz:Nf$''c֬YlޔvlBB/U@3ؾ};ׯ_c|,nj9]w= e-sqDEEa?ʘ'O4rH\xTgU2;w.zK԰0 W' Һ;v}Μ9|\`` >DH0AQQ\'NKdU"g꣬G#]Ν;5km;::ZT<)It3gDѧOCӡjժo,zT^aa!F!##+ #]4YOL$DGGcݺunf!{s J%^xܽ{W_ eV* &LJ/ܹ^zq9i+qz"чUX… ؿjQi'>2cDTi41~x|g2+UjJ%oݺe˖Ul޼Y_l~֨QD'SNPT4hRzغu+Z-4i . 4s]ɓnjP,\߸2be-mڴAjdD-U@/n<~GaϞ=2kI*{Ӳ`ym۶uJ8$yґ'OQqZO3xg0e YfHIIZoΜ9P`?jZFnm͛ "TPP!LOv=z[!''V˗|||d4Sp"/QgX8=u{=j?zKKSU*L/v\[nq W 7c,'uADxRн{w7%, V>p88)TV 0m4TRţzkڵ+ʕ+oO_21cY60СCV)ti"" ?l̕NLL.ɓ'BPtYr_D d%?ӦMÏ?2V*++ Ν;|8JJJ/\"B`` q jYyYU "BLL -[5kT\` SOAR!..N6ۛf(z( ,T `Fᵦ? IDATDDtYJLL4:z(_-VtRLL EF!RZhDD<¬RB'BA{!JE/z\2ߏ'NB={rk%v֢y,O֫t]i['KLJ|||HPХKwTXXH T*?~h4T~}&AAAAdIՒnIXҥA `Qtt4v;f3jԨ, "BV<<9yfܼy6mBnӧˬ[ǎ "BhՊaÆ!++ 5”)Sd#?ȃ:YVaz+, ^: x I0m4t\̥f͚/,6mTqZJ"))ޝ;wu* WeAAAjT*qFDGGCTrj%K|2ҋbIgςPfM\v>sL=z3a4GnK:E:< ƍK5 J%Vp-ݎ5k裏dsÆ xyyi 5JgˬChҤ Yf?~< ~Yud™3g0k,T\6m©Sd"̢e"(}Waz8;rvVJĚ]G$XWRYqֲ,WQ.] 7ۛ41gV*֭[s*Ca1o T'lBjZQNYdQQZh!Sg3!#Oo2`Xp=Nm۶JU=BCCw^tԉsd5h4 DN0uT?~qnݺ˗/ѣQ~}NvR{Dj̙ҥ j5޽xAAAصkf3?gfV۷oGPPlIRv-DFFP N_б2W`0/"""8ISSfYFB=}4 N [pAԬY7nhZ.Z !,, ժUCpp0|||-[b{YVo*=yU駟P\\ڵkd ? h4!??|MAO f3v;LjYm>Ϟ=F[6lfڵytP5j ТE D*X\W^:u**VC[n+Y2l|9n3\t *TIMdV;w'C:)kt`~:֭[h4P*0a S' -]999p:(WGqظq#'J*ի0L(..ɈҥKضmڶm㛭ZYfׯg;*m7[ne_YYYx{=梨HTTv]Vx/LO* KhsEj0h ^?zh!ӉT( 8^QXXfrss>A3d3ʑEGիWS\\]x222hC*U(ZEݣSNNl6@aM6ѢEpԩS)**>C>j>U?J:vH~~~a2rr2}d6)==xZZMjF#S`` EDDP:uh4Rr,-UVњ5kh޼y|#& *T@{˗/Q6mwi׮]ODX=h4ұcHP?+WN>A_[Æ L>>>T\\<ȠqN!FBXiZ~Q߾}nݺb ڼy3QݺuiTbE]6n6IՒn')ǶjX5jPpp0UZ)..t:#Z|k׮ѥKbPzz:fX"?7ׯOj_'FC6j13"%&z饗/^z^1cY&OLkצΝ;ӆ hԨQꫯҢE跮|ڰa 7n܀鄷w%B+VÇ-QfqR ___4l3f@nn.]Ν;sw`0ܹs͛\{[-}΄0|pl߾Fff&eRu[VV+Μ9?͛7GrP|yT*+WDaa!BCCJȃb;m۶r,ˎm۶8{,< "lٲڵkիxҰj*YMgFFVkA*U_ѣ1yd,]ǠAЪU+TPAMLL{Ӊ{N&B6m0c ,\/_FAAl:4Ȓ_R|*ckL>DӧFi[8Ǝ ^Ϗ"""PjU(JADQ5k1w\+-9. .ԩSTvm {\r%Ǝ?uխ[W6쎩3wvԼysjҤIv-fnyϞ=iӦԠA9s&egg˗7nݺр(&&*Wd1Md2ڵkraa!]p;F? JOO_jN#,K`IH,+"e ZTNjذ!1:SxbW_}*WLG}۶\ڵkiժUDDAz'j5ݛ;ooܸA6N'?N>͵c۷oONDTlk.ܽ{D:i$>wV PV˦.JBŊqIԫWO V>) h4hZtYZTGGz RxB^4QiU6mдiS >III8t222pmܸqwl=xGJJ V^%K 22k._ܭb?3=vlz؀X, : ,h4bӦM3f :}ɓ,\EEE`b _d… 1h 4kLvAGDDw޽lxTV5~pnzz:,ǙG퇆5?!F _|O5g|T H?GբUV7oniLu%%%|JJJdvZԮ]'N$pmsڻw/w;̔5 m9r^yPn˗,Y"U@?Evv6RSSѶm[~7شijԨ{)2۷/p$''.lvC@ݻwgΜVJB:upaܹsǭߟٺ͚5C޽1}t̚5 qqqDqKI=22[ƦMv믿qvލ[nErr2n݊m۶aΝسg;ɬdׄՙ3gp1N,).XnÞ={o0a@HH^ugCt·M҆7o* {m64hmRBa]EjON]x|W_}wo Ʉ HO?lvIv 5T*ND___tϟGnnۈeߏ~ڵ+/t :`5It:/_>>>[.֭IZ-|}}nݺڵ+zΝ;SN Az-cvW[";;_I;X.,,DLLlIxx8=ʿ#F .DǎFH|:tݻ <.:` +(('OƋ/ɕjf*N9ܹZFBBޤjFR$?˖- ei2 E.],s]ESL&Ξ=-3g`ĉ<.ʬP5Z,P~};裏\niZVy,j 0߾}{Ahh(4 Cۆ4 _sV|y̘1/^V(|Abb4! 8;v,e.ׯݻwۼLJ1yoԨ0F#֯_ωõ~3d?yyyug$eTÆ qN`l?rrrd3؆ P~}hZ$$$pa0ڄ-WڷoÇcHII7HM'-**Baa!N8Çt\˚fH >|ꀟyE@hԨJ%z=|||#@:󉈰k.8 5 =gXT^=h4{I_Y̅F׭[VZ駟eإKӉo*UF0VX׮]ᅬvˋNZ-/-r7ٳ'?zLЕO-Rt:?N< ՊWrҮPnO?"B{/?vv\qqq 3j5jԨ˗/l6ҥKF[= < mڴ)s)y/ , , wWj 6l"rK@1e.%SD 6l_'L&RRRhh"ܺu `ͨ^Le@뇟~7sLY '*c /?WBdV=?SHB˗AD8q".]+Wh4e7-zYYYhҤ """ϷnM4AndΝ7g}Wd* 8ׯSz">7,, ?#/f'k,//ݻw[aСC9s&7n3͛7͛7y;x`j|t:QPPf믿͏'"SN83^ Z-e!1cƀc8NtڕT*ۗX`* (8qL(5n*ˊ-[ [la2Be0p@hZxbaE$f;v͎;Ю];YlaÆ3gMHH۷GNN4h "##CceBs?D]YIL}*UVZ| .  Ξ=={"::)))r W׮]bđ#GCJ(ӧ JB@@W0rxiQQwե˰}N-[Lrr2L Jǣ'AϜ9#KժU 'OD~~>NF/3r5j233QTT_jcǎ0%(1nݺ֭ky>lGꫯBV具zع7oV+o˼~2;ɢADŋ HqU̜9SF8UTkbРAjGv0e%K@ -kѼys!!!1118qV+JJJлwo;v C bbҌxۃIIجz?sعs'6l؀ *paZjARaĈ0L| #̦MtKj5z1RXncǎHLLāp8qF~nR?NTPmL&, SWdff"11Q6km۶8tfϞ "۷y\4套^Baa![jNRMLLhDTTUSNqիcDʕ=~Ι3GևJ&}}}qtooo^D^N<)gųԽ `~q^yPXXooox{{CZm6Ə"‰']t2M LXa\ F"??ffbΝ;Ѯ];\rVr9EGGԩSp:Xjtr6{ΒP5@b̂2LaڵlX`LT2d_իWJ*HJJ,iJ4R U@S/Ax3dEҮURp7nC9 zO=RSSk.\4" EXX:va1ǎ鄿?] _"B-˗/zx /** p8x7ylSVw-(111qL"_M6мys]f222x]+ Fh4/_/ŤRҼ}6+''r饗ܖ^:t:,ӧŌ  f͚DM$-=z44 5kVfR?GJx¨I& KQi_| {ɶ[XX?I?O?+))ǡVuV @TTN:ARm۶8<'+)1Z,IR4`L^ IDATJYG˸ ,aA&X@-n:L&et:㏸r T۷o\YWQB 7`YRbY&'bRjԨ~aԨQ=z4FbȐ!ׯ4i5kiѲN^xjIIuuTZWgϞX|9ۇt\x}嚩$)Sd* 6qqgW"7n`РAX,V~i7Kq Rۺuk / +a2(**‘#GX&޺u SN~zhZ̝;cLp@V#++Kf!ݻM#GEEE|=FLVՍ̪V*Zw|*U*H2,^¬Yc>}())AII , /G3ױ~z̛77QH+:t0\Fj]K6[^Z=tʕ333ɓ'#**Jk4TRL#f3'W/MtI -b YK;P(3`ĉ2fߥkz-U@,NDYfm=իu1KRJ_>EƖcf)z.zB.+F }A`` p+(Y++7ȈTjزe80[JEYr+((SNq%|VVu놠 ՋL+K.8}yJSM?>:uꄚ5k" FDDШQ#to&<ٳgCP`p:0 |>yUSrrʾ Z-UxjM!JZsi=-Q:$S1kժ111"xh"ɥ7n ;;͓W1~:zo߾M6qFӨQ#ܾ}=KܹvvBQ\9CֽP(;_~i].{59rgF=ϓh2J?֭QFS"s=n VG6Jմ\O=U:GL@@a ▋o^f_^_|Y/%Ȋ+N:_>n޼b UJJ 0^z nƍѼysqիWYt:P(0i$K˖-f͚jx!!!<>ڦ,һw")) ۷/V\d HblN8 `ҤI|*#@i7n݊#""*T*%1c֭[ "\a0`wY.U~}L6 /^tٱ#ǏugΜLjxӱʌp5RRRթzx|@(JL8Q֚ JXԪU[|{.fT*ŋ_bƌ?~<4hŹy 5H,4 BBB,HR@τ4a0 N?~7}W>ECij HZt)N:ŧiQUVz9\b=#xʏ'T[.Z-:w̅V4e+w=lył7nSNhР`d(]-%R*: Dtt4>Cŋy"-_L! gµgɒ%"_٣GFh/2-[?1c 7q8"u)uFRO?-Sr%%%ӧe-D',OuVR֫W/|Gn z3㒩kt"77ǁ8w.\}᫯M0|DEEaHII4E׮],GSOS af={]vE`` Z-'f C_|{h֬ T?ի'tVoِEaYi֭ڵk'q:8~8/ EddGNG3VP`0phF<+Vܹs@Vdψ ē, ¶3tP>uQhJVOM8[L\>wΟ?+Wĉ8s <7C6m8ib5,{nAV?rxG |ݺuҥ Я_?L8cǎEϞ=e?/[ԽJԩSQN'%ղوfZ8xgV0zh_fmn޼-rK,b^5 0C BuNvM8 8-ZѣtR,XC AΝ$d7+Z_? P:O잦민V ! aȓJ՜Cb!778vy`c˺9> $$?;<2{Ν>)$$t@@iQJsqA6aW\QwWQTRT@`%tC30f&,$Gtaί^:Y,JHHƍʕ+y""3gF"Knf^:L<336mTkM@@z$ij-β 4dew0ު륜ׯуa~y=TfxsrrAUL&M>];P>c^=X~ '@۷ЫnJ~;%'EEETPPPjkWeڨ,=Vp7}t>O>T8׏Yf+08N <!2L&^/E{ʉ~. 6bQTThڴ)<F#oߎ;wԪU ^$ I$I*[Nbij> ||plذ˗/LJ~Hԭ[Ws<& ǎ߿3gD OFFFnnn._X,cѨU|I~8x<Ȳobhի8s .];v̙3k׮PN\.\t 111@ӦMѠAԨQw}7v;ݺu| .`ǎB^|嗚5W`7oQ\4:rɓ'z@bb"կ_{͟BWRg4i޽ԧO_>M:j֬IǎǏs/555,;Nɓ'6lܹPsŊSeubʟӴixnu; } & Owc1bRnڤIھ};)Ersǎc$Ym6 dI&qNXHКf:wmۖSKgΜ5jЉ'hذa; ƎKC-Ac76%11jժrx6ly7ޠ;S[f۵X,jժQÆ i4qDzh„ 3{޽{S5xho>?g" 7nRSSoVt:a6a2xXۍ"<ؽ{7 U^/Ν;yxݰaCddd`65˲_@)5ؾ\.@Q\t ^v+Wv#,, ~~)0`p:4p8p<`0. ^fÇcÆ (((I>\.n#11?84i&M0nnx^8p;vm۰uV  n$"?~\\׋Ydٸ(ܦ7P~<1b>}Zj?PN4^Ц=Xرc=n7n+sF^Mb]F-[={pO}ɒ%|$J\RNjB>֭[Gp\=u֍^}U Pleq\ΝIL&lRx7ڵD1|bȣGzꩧ8 /N- IMq _XXHk׮z!>U+ec4 _RUd3e/)22RS2e ޽RRRxxhҤId+aP_}Ջ͛wPސ3&é~?}֯_ؤ7tkt:zj ۇOn q޼y3?voT32ԍ1%^vMzk[n/i{Oӱv)))EO?5cQ6nܨ1f}jժō!Sʲ\_KRtt4?OGHDGRXXH}5&L_>B3_}07~!L&6"۷/|>jذ! 4w->^y WՇ~Z.zF#p:huaPS˘qx"}G}&o( ֭KӦMӴ2VZ); Ϻz7KCn6ZnN~~fڀȡ ڵ+7D(h;[ona{衇xUF+bׯ_O}ݛ"##h~:z(?<$I\Q_DxIouRj]xZs%I^ghժUDDdZy { iv+T[ѫ(MQQcVZQϞ=iA[[nM4So. ŋ1ٳ'I.**"ǣ'CvݻAN;6`iyII獵~lB#FEu+̫f˶mۖΞ=GvӨQh̙zj2?w@{t3f&glXu0f:v… F6M67QsZTG jEQQmۖ7!x^ڱcFz. p#1}t2Ld4W^Pa;O7oN~ߖjԂQ=׏z);wn@t)hڴi',di4gnBLL EDD+:g̘A999%w5Ν-[-BSN3,,D]vUDDQQQ?g0111  `G":ȑ#d2Q{>l6իK+Jb{ڵkGqqqezzZfM^\@?3:tmڴC}˗/S6mwޔB)))%iƬʕ+z~-5jԈFy?%%{1NAN~СԨQ#_~B `:*wI 4:믿N5k֤'xvz)55Ucz];"""Y>͹`C|I뮻ΣzrsΔ5rK-.^H}rll,EDD111gc2xzOQmDкuҔ)ShĈhԟmƗ . F3шdYuѣyŋ\πHEު0ŕV j@-frJi5L'MDN^/=4nܸ).lB;v젼SVbb"=nɡFI.^?~O ԦM yQtB.Ҹ"V^͍uF*[eeeQAAܹfs1G:tIL[,?~<qՋ ȲL 6իWjɓ'b:Gl2*>|0'X2>>[Mޮsbݻw/-Z̆zOso]v<~!\UFG)fϰe!eMvڑ?P_^3Æk7{z+by+Vh4M;wLt۷S)++K3[\֭[ „* {-֭KDD,n2(&&֯__n:T0#qF^Pb2d&}еkWDҦN 59,ԣdY*سjb0Tvmr@mF}SNN7, Y,tؘ!0ܹsk׮1+DlFQrr2edd* ;իW?g~KQQQdN3-j4iQ)u<ޛ8qb@ʌ 7Џ?P%55ZhATB*((1IjpvS6m* {o 8SeQmڴ`0Ptt4St!2L|X{l(r۷'ÕEٳg1Fbxj+WR^^=z׿jz9'|DWeiSEEEte?f[oqǙz;_|WKU:žtRzGכ3g2TXXpZSjjfwukݻw4IFFu4h :}4?S7.uaѣe޼A  Qh@֭[aÆdx<<`EQhʕ|~w}mdO'xh2o6;w{fs\taޗ^zKQG{Sff&ÇiС4bZh۷TO[oѢEhӦM%8?uQ#j, ]!\ A@6MF={㠙شiWRWKK&tR휾_ѣg3q[=3%fnn.M>q߿?M8ir<2De )[{WQ:$$2Y,TcjM[nvdH ܛ3Kcƌٳgڵk)++~?5i҄okϞ=ԬY3?5ޯᠵkjxꁆ̘կ٨#Gj]큻n:~8טVLL KI8qH?#tҥyR7R fѮ]s+z-v)%%&NH]vc֭[^Ą \RC5jf y~_,7>oKo߾b:t-**{KF10*$i >))K=\ eYJNNuwS_ ̘lCAeXj/w?~< Ad6@9 OQW0aV_ڌ!f٬YT* lR #̌LPe^~eۿFrO=rr|ƾo>7n &PVV]vӽز~@@dR - 0$2k׎1BT>o]WlMm6X,+eҽ{w> z7s}U@#L&JNNni+/h% h!餤#0WEEEk.:u*uЁHG4 ]v5̐oߞԿ_qoM򋑉TT$P6,w}Gq)~zP!rifie՞3˟QRRedd┺ń6m9V6ZjuV""шTI'< &NQ/?]pSX.S$;vTTi˫ί@h:yʊ_jfs:upnݺ+Po߾=T6aWZ恀z$I:wFŨ"Ӳd9sh;ZtRfxv;FjӦ ͚5x/~]z|r?IZD!ɲL{Qjjjg}D'TE}.+hJ"No .P޽aZ1`ԯ_VN~,%<$ $l6`0O?`0ЪU+dgg#22:Fռ_XX"Brr2:5k֠aÆF||<]ۍ'N ''$ҥKHKKC^^dYS~D={b:thc;|0E]W/?3կ__t=|ˋSs)t=Sz4v 4,4?z T0{Ν4h ~l 3t*v5+$A| ԱcG:yd oYx$8@-[T 6ۈ逢ΩKjg>L޿?uM}1OgшHW+g[:֍l8`\uz8zzɒ%ԧO~k֬)*YEEEPkƩSW<:w,/U합_̙3 "|>H8^u7 뿣RCN0W|Ew0f~;٦z+W`̘1ܦ~ e4ߧkrw7BQJFС 0 (i&ޙtRK#t}SenbݻwSӦMyqKBJJ "":u™3gx"~9%2|>~y111P^+Pcϗ*ʿ%"tb>|8VBbȑKgΜX\Pʶ9WWotR[z|l T/O 0T"";:u$ bӦM52))>裀sEFPbb"}7z6ߌ!{E h~]ƅqb4Gy-[9$љ3gQ!ȑ#p\F!))醆ͬ^Xjoda?[ppJ>P"P(z<47ٌ/> ·i&qA\6l[Ϝ9C111@}j4iDD|^u%&&U`"+k֬ 8/R?caqP*˗/Ce$$$`…v&IC+ Q1/hذaX~}ȑ#|*f0s<翬A<,, /6n܈;vQFpݘ;w0  TW^B|>YrxH^?o2** /Uefv~wUV9HJJpOjcPҰi&t ^Z\Ν;yHyA?bnhE}V`9x =33La3L&m۶- |>x<`#S GرRʋ\}jyZ6͍lDDP}{+7Z\b7lt, sOqȐ!jժ  1o̙3|_D.^RQeVYzPdY_h4jε<oAW\gyV+=Kcƌ˗+\(߽{wʢ~Lvc(((bR(:_~FDD999|٦M *B~p%ШQ#9sB)-ܴlի֯_کNz<dggW@}rt`0`0੧Bǎ5Է?s(**fC||N<=R*P={DڵW_UROXvc̘1Xf 222x-˾Yw03!Qn&$%%qPSuQS!;uѬl6݀_Dif͚? 4@&M@Dx# @i`!fvp82rl`@tt4z!lܸ1(Wmq#k׮}i5rJOmHyzV> Pb]L7@`!¶m Iw l&EQ())D7dl•B@ Wv)Cݻw/ɭH_Z5zwd2Q~h̘1%ZMK 7Q^^ٓf3="(aFוc3#""]‚~]^jժVT2h޼y5sL0`"غF#YV[{DD|zGQ!CC(y^ԩ;lܸV/NUʮ,UڹS+{p} Pu^=5G8wp뭷hWٶm^/ظqxAc׮]v8Zc|L& ɡM:5hN>%$$T">L u ӏEq! =4o<2G;wzsrPz7]X=z^P233Brݸ|r$̻/g_/;bNJ$:t)ݧ玲s"'x?#<~?dYdCyP"T,_< >l"."aPJJRЫW/|'xK∈hŊ!뜪#L111DD /*n/Wd\\?~"N>M 40%Bi={< mٲ{[>~GԩSrwĩ&{Šɓ*Ps v3CCҥKn,˼<֭[T)zP|||P҄ӱc8E#G)DU̷ƍ#M}Q.9~9@4gP%$$`DakӦM=LU /l 4;Fv;?^UV==}>oOT{B$a…O?T&T[gƍY% IR 4ԉ ~M4A˖-uV=EDS5_+VX9w t 5kDQQyqq ZPZ5nX K\jy8Eg49>,˜wʌBydYFBBE!Ohot:qwBlFqY\.XVX,cQ㿲Tlٳ &`XPL:ذaix5CԊ̙C[n(2Ld04ӧ!_:~?=%8eIM;Xf =%:~g!cSK`49=YfDDd2He6V@xU)qqq@&(0aO\gL<'UwHII`"ݡI;vN:bh<&˗1d΢恪=d@XTr;wvBTT\^zIUaPNl!/Fw^|>ԨQaaa|@PjU2\+:̠ÁtMݺu8|0z-MկmrS޸q#ڶmA`¨ T ̙3|:iYQQQP 2 4w\;fа!YrUG)`0j (,[M&hѢ;w{=θ((( &$ F.smm黧H&34Wŕ&PM2 t2 $Id2+WPϞ=y.##z'$]%{ի]( zԩCvszqfи#"OeQ$lNs,Srr2͞=WGaT?ODa6jjjGAAS㵠<#F媒finDe!3gҁdӧOL _/_~yH~z"" J5k$j+*l߾DLs=_ IDAT]p\(**BѱcG<,k Oʶx4}U LA-ڭ?GH[nYYY\ܛ6l(pݸx"vcƌp\~_ǃn֭Czpe 4H\l V,8}teUm"-- 6lT~}>v;*2J] hRPXI c|߰0ԫWmǗ4i{9x<ڵ cǎwfސ`Ŵ@9r$`Ř8q"Lӧ@6bZlۋ-BRR ѣGh4bʕػw*'yr8p8h޽\].Iuؑrss]VVǍMG͈:wLZYLϽ^/Y,R+2*~dŋ_n.=)BTK}S;jY^=2) wnnNJ+о}{~!Eľ}7n,s~N_D~\TߏTUJTf޿lF^^f8> V.ԨQ!Y_{EQtRW\ѣGՏҎS_7둑!TaP+񓓓Qzu(V;uꄽ{zɿB -dYFzz:V6i &V/,,,Ce`0@Qrj`РA1b7x'OF&MK/!""BSt fFx^lٲƍC.];w@‘#Gvd6yl|tܙ\.ծ]P˖-г>K&PQ$ь3 vІr(\.;[nESy/"..s)Y9Po.\wo6'6QwWVRBJJ *p^{M9r$v;)))CLLL@`ql#8{lԪU ׇ¤IE( jBll,mZyKetEEE0LC=@}iyDٲY,(. \+#⇇`0`ҫ Ia0 Xjڵk2J$x^{]w8y*{}wsΝHȑ#*/Ŝ>}ԩSK~(jWP_<5 3%%H驗X,4|pTN2 4iҤ _{5:c2e )BF1[iaEԢ"zĉ"|Q(z*w}# 'Зf3ɲLC/5%Inh*+رy<""Ր˚/P͚55z=u^{ffsr֭[SxxFBw\JJ ?!n$"|8z(u۶mt`ӧQ 5Rϟ+ԛ1cVW }Ioo4hԨ]SLz-&L!2ϟ\x<Ǩ?fX`rs]`MgϞǘr?xu3SN4w\X1)XU+.~?o|%Be~Q/JeZd eeeQ-(i$ !ehڴiDDb (B^^7ooвeˠc"<:eϯVbapED(-gPԦ}bPڵk|z(z}{Gjݺ5~aaaXt)ėaaǹw^dffA$ _~*F 6бcѣGZ-[h={҅ m۶! v i1O%;;ҪMX)c%$$1cƐ`ZjQ1WA5kFW^%"l>W=% c^ah޽bE͚5a0prєVZiӦADǃBvX,9\NcU[I"5ei#|>NgQlU n!%%Æ `XpBPSe^O?[oG6ϟ07?ƍ#//-L0rP;~pۺuk롿UjQ^ 6 & ׮]}OLLRSSy戊ˆ#vdF~(›>I& ",_\\]v%Yiȑ_j1K.$Izt)jڴ)F^\wyʍ^z@r|i~ .f#"Uw4v,:t(l&EQ`0/LG%"7|?kXtI #Foz~Xv-Ν; QgT^i*w#d:{M6#VoO?TZlFjƌѣ<˗/K/8@FFq]wv͏m۶i"ny-Z "3gVƽ[& @QԼ_Vs\F-Z YiĈǷxbzG4 WDD'O[lQ/+ P9Rz,Ls=֭KV\YyDP/ccc\H~sYez@eeeѪU(!!AsCvѢEDDEL~(hݺ56oRϪ,f.X%+T;$ F8tX,%شi=<{bذa0`D/P9EM6 ;(0""駟4^Sy Rkz^z: `$TsQ\!"Fz?wIz>nEQM6C͏ٳgaԨQ!-o6( n 'T0ѪU^[e{V{j1ۍH-={w5ӧi߾}?߿_\ p8̔ù-p>f(XEǏu27#"##qe:aҎ]eޫV>Of8 6_X o><ƠAuU^3x\Y4[fD|z92H\r\-ر+N'5j;'N@VVMs%q TaPoP],njiD1/6nz+ډUσ} |||<+LH`vc֭hӦ |>_R 66{U,s Ъɟ. 5 ur^ryQ[iۉ!Ǖ+Waaa&@ P^=x^tro5@eȲ"F4Qסu ŋp2-ԩڵkz$I7UQJ@*3ʺr"##Tjӧ =@Ʋ4g7PzL> nD/X("R  $EQ`2r1Q^i0xx(Ȩs@A-~5k@PCfhJ;v&/aXZ@2# "i"E%xiŊɁ$Iaf|@ꊢ <<͛7G-ФIL&!!!V7棓^/v;^/EܙaTϬbU 7j֭'-SmcP3ӧOcٲeعs'~\.L&|riF8ggϞGXX zm۶Xb8޽{YfHIIp=VŚf$&&b߾}VY2˗/#;;7oƥK`X˗/رc ,G(D222`2xwXtt4:t֭[jnݺEdd$4h=vXl֬YUٱvp@_ k yaP興^ɓ'@@<`jV,|Z8p z^zP9778~8ߏC.\+Wzɷg6yHɍKzͧx~\.^BJJ V\|DFF"""hӦ ѳgOvmذa._Ad2!)) kƑ#GPPPPJc.S㣏>"L߿f4G e`lfjըC4o<ڳg%''SQFp8J(.LU눪9$`(1UP/&{BŦ|VV7oN 4 DtEZd 5k֬TA`S"BI@x7 n7: 5j^z :u|q|_Yb |`b6 ͆"AYpa@ ԩS5b&{@@V?@gϞÇFzU{۵k܎9C[gߟ@%KժU+GlZl6$IԨQ#޽;=sԽ{w #˅J?о-[PAA_CY uhҤIXCe^>}())V\IժU+axf3ۗ<OejРhтosW^d@{ݻP;8`|*{,uPn]z饗諯8>`0j oy^RE[ *K4(N^TTT a>K0<_z%`РA@nk=H?Soos(I,˰l6h4rqѣGXW^h׮oU,C6- 7rC unBBM4bcc[nPbb"^>[5 _sF#լY=JVwaQޭ,M`iRQ@TaC#"**?lDET1oh4Ɩ%klDŖ  `4 H[w>ϳr޹3{̙3*d_sApj mڴ!DBnnnDDT*+ E-oI$קI&sQ||<͛73AػwnMҍ7FFFtJEёPLL %&&2"]R FK,!JE-[|}Y6lHDD4p@zE:t dooOsΥ0W]VQ^PX̙3r8cDz[TTDqeaE=;wRJJ SDDoߞ޽{IB$B JEhv핟)tj8@͛7gVD"w^viNN?-_YSLaufnnN^^^dddS)CCCakӦMN|,nYqRMo>ڵ+ocjU*K VKiժU"mӦ(F:-ﯔdnnNӦM#JxW>'333@~!駟* 1tV1cpApj euD)1@]P\.g^^^TkQ\()^ɉ"##لSR:r Ν;,kLmmmiʕ$)J۷\.nݺUpB~kPPT0y ,qaz\|}TuVAQn\RQxx8 {J4xTOiĉ:\L$IFk׎|}}i…lI+QR)8qH.SΝr8de:D[PI.… IOOr9>}R!"@:B'ZDDdCe˖281ӧM4KVؓRpb!f5 }'-;`ǎ̊.Gs8ڂh%~lfIb?p?=zzxUVÙЮ[ܹs$HΎ~M6[n!rZlKVrr2uޝƍsRE]vO??>3+PeZe*O ]~"߇\.S[.,--fp""c˖-pwwǨQ^*r6mVdDDDϏ7(CӦMѻw;;;;!88-]f  \(J_MQn]dJ3Tq88?rH=z3lV,חY&LxU@2Eh/Q IDATFItIÇ5}MMJJ NUQVPPaa!skO\}:<<}Xzzz:{I UP@ioF(SHNN͛L&/ҥK8jժ 4PEWhтJ% 6,2OOO6?gΜj)ΈxJ)qʕ+lPP9Ռ6lwppiyOh腮SSS$&&ի(,,ogΝh`nnj?4 R)bbbBdlmY]]]a7n\%ej۶-޽3opé𙙦PŋdiiIr\\)BI5ƈ#IU!&7קYt^WV,Y͝;˺sNJuV.οݻw>$}}}'DH$HDĶM|+lFp!7\.j<*ʖ?~\ ߿/puRԨQ#ŋSLL øPzz:]v,,,֭[:8{,رGn9999э7XW($Hhҥ,*zNhmmM qA!!!$Hߟh@-\SNs r6mJ˖-gҮ]hܸq$VPJC 6mڐL4|zwi̘1˶V(\M6?X7qssm۶~)uڕ\]]iذaO)))\9WW!e 996l^# #ZzEBYɓ'*P;uDݻwU@__RN7rvvPٳgWŗKiQĎݩ|[lIiii۷5koF:mB̓+?@ \T9ԩS$ u҅>s8\J,Q߾}YWiho\*K)))T*+ߏe˖m۶.%"(JEaff}YfbZX[[C"??_֏bŸ,j5{vg=oʊU"8pvUfu%K4h;] "Bll, Q\\ ,G .]BLL siMa̙}„ 4l0tЁ͛aJEQN,Xcƌ(())D"Azz:vڅK.߿===d|v~@(..FI ҎjVwu7<9~߼Gaa!$ mۆѣG8vE;v2220`9rŤ/_3g`۶m "ric022B`` zꅷz rOl;EFFbѢE*>4i"rh4:oҤ ڵÁ@D066/-[vIJjjh4P(())A:upcyBƉۭ$o 6m<84iqqq\^څ H׳4 N> ===@.J999صk= T GGG;wADAff&222i& 27o34Ѻukbbb(** oߦÇԩS"q%K`NFyp{Ĵy{PPPɓ'̙3Z:B@aa!Ç:h1Tр0gJW?4O\cϐ/|PBQQT*?ݻwcĈƞ={_~\T)44p $''#$$R2 (((R)CwVq}2F J:F>* /lڴ /=J}E^^^1*oe ""_k\* =bbj**)===k^qff =BFh (`Ŋ;w.Ν;GGGܸqk7СC J5k;wf'S )) ̯*v"R ===<|R_]Cg̍hTddd ##...J077F?7" @ق̘1EEE8@DpwwGll,ѢE qüǾ3僑w?SAƵ^_Ǹ{nT* իѷo_ɵ ÇGheVL&#t,OA!>|,{Pa|XblxSfu[¬ {xwFaa!b6$֪Ef !:XA?uȐ!ؽ{7@D\Y$$$кu_toooO?EII L( e˖ظq#FsU, t/R7%M9~v=(((T*e~JAꊮ]BRa…Xd ª޿5j o[3翯 |~(ݢЯWpzWZۥ#[[[=zn݂\.lmmqihZ\^6UCٶm) C9PwwwIb6ogg-[`ʔ)8w $KTw#;{W8Fܮ{e@$իW|r[n:}6m޼YYY]v1w{dҾb*]vk.$%%(T ֭[Vɉw@nL2/^ V B\&2<ɘ?3<9h\Jʦ)`E!ud: DT933jZz^hmժ"*Jk`{+'$$$ c ߬xU>83WD}{9JƱ~1 [l*_. Č3аaC B,{oSR4z}.e5) J' 6:6k򂣣#:D-_^bq6oL&MVa=}:rssq5ѣv}5&f̘A9J%Z-z a_KY233^ffqoKFΝ;'' GX3ݸ&JJJаaC\|7&&e^斺[uuÆ 044,s 6MS;wȨ@݅TJNNF\\ WWWaѢEq 33lmmMB2m7qIdp;$%%? sжm[|;~ggg!nA渲#3ٌyń wUKܱc?`n)/j*]K=^BZ&{Nh-@___y_IkvPXXsssHRhݺ5Ο?-PwILL?$ \\\0 999t oޠruCb˖- Sc~!!!@QQ@DBHHp=s 99 /tܹ$&H$G /tE="\q5!cI^^d`7HKKcJ%FŔ[oL8 &cs7rAϵƓ( Uj-R)°n:8::K:o߾=Ξ=[]]~qʕ+pwwĉC JOgJRAP@ 駂W>ljk4{? ._,ԟz} ϟIgϞšC`ff777Yiiix :wmӏ?,og ,?~5BFFNU>ecgwŬYx0ͼ aɒ%jLuEGGcԨQ(,,dyr|;>| GJBJJ xb 8q"u7Vǡgcꀞm+$@9s hf{ܳGqcG0vy vލٳgcܹ5=կ_n.U'G@%E{J d=ǟ A=RCL gٔmBSz|>nXCOO999044dYXXJbI6lXiZ6555EvvvhKSN @__J&q9AFc#}h*4>n3gμd`Q,fŋ1i$wk |+]IZu7** ReC?zbC\ o߆D"AFFݱuWڵkH$hѢN`||xrW$,, R,\v qEbk>n OZfff@sw z\\\F ;VXXXڵfϞ- AF 0fsάnr9Z|wO˗qUDGGcޗn޼ T&(9\Pk=^^^ \z Mh8CKU ;;;; @ zj̛7_T8qo CNô-pGX@vܕ+W`mmۿߣnݺ¡C`hhȆػw/sRIIIGi "WY/[ Z"Kwns}y- cp2 ?3nܸ?J~I*R\\͟?_&!^@@@8P(t[[[@#G|PStOGMQա9Bu!4{lh4lȍmu^O7IOO!W+5hЀ̙꾠t\SNB-7SSS@Vcڵ̂ñ6~Or= PTT׿rAOO۷oqhժ{?}t)T#3=zt]Gjj* HP(@DJyyy4hL,,,?\_v YYY3gVZV RcDzxcBb 9o-[d3*$ 5mڔl͜Lв/3+ȈڴiCǏСC թSh͕GP@@@MÃH*LXNA CCC@NNNr?'O&ccc~uQR=z4ݻw‚YBY yxxЇ~H 0lVW &&&/6VRSSo߾<==)nT(k׮LP% "DBkln۶-)JqNZ3`!򘙙8q"iZ5js5mڔ *"..uM(,,O>>>h":x ݾ}uRd)JiĈԻwo3P7oNeQ$ooooI. tJIO),,>!Xbj]ݍ,-- [DD4}t@uDbcc^zl)'Dhuɓk[G&L&^+7o$Բe˧ eu* L&qQ^^VZqApj*7oޤSNԩSΎhԩzjWVud{{{lҮ];@^^^ir k駟_~:n ###V ;wԉ~Sٳg`RHVNhݺu:=p-!CpA!4_~Ν;SDGGѣGQXXsss899M6zj=]u{p8p8p8p8p8p8p8p8p8p8p8p8p8p8p8pT[% EIENDB`apparmor-5.0.2/init/000077500000000000000000000000001522511161100142755ustar00rootroot00000000000000apparmor-5.0.2/init/COPYING.GPL000066400000000000000000000433721522511161100157620ustar00rootroot00000000000000This license applies to all source files within the AppArmor parser package. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. apparmor-5.0.2/init/Makefile000066400000000000000000000064771522511161100157530ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 # NOVELL (All rights reserved) # # Copyright (c) Christian Boltz 2018 # Copyright (c) Canonical 2025 # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- all: COMMONDIR=../common/ include $(COMMONDIR)/Make.rules DESTDIR=/ APPARMOR_BIN_PREFIX=${DESTDIR}/lib/apparmor SBINDIR=${DESTDIR}/sbin USR_SBINDIR=${DESTDIR}/usr/sbin SYSTEMD_UNIT_DIR=${DESTDIR}/usr/lib/systemd/system LOCALEDIR=/usr/share/locale MANPAGES=aa-teardown.8 manpages: $(MANPAGES) htmlmanpages: $(HTMLMANPAGES) docs: manpages htmlmanpages indep: docs all: indep .SILENT: check .PHONY: check check: check_pod_files tests .SILENT: tests tests: .PHONY: install-redhat install-redhat: install-systemd .PHONY: install-suse install-suse: install-systemd install -m 755 -d $(SBINDIR) ln -sf service $(SBINDIR)/rcapparmor .PHONY: install-slackware install-slackware: install -m 755 -d $(APPARMOR_BIN_PREFIX)/install install -m 755 frob_slack_rc $(APPARMOR_BIN_PREFIX)/install install -m 755 -d $(DESTDIR)/etc/rc.d install -m 755 rc.apparmor.$(subst install-,,$(@)) $(DESTDIR)/etc/rc.d/rc.apparmor .PHONY: install-debian install-debian: .PHONY: install-unknown install-unknown: ifndef DISTRO DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \ echo slackware ; \ elif [ -f /etc/debian_version ] ; then \ echo debian ;\ elif which rpm > /dev/null ; then \ if [ "$$(rpm --eval '0%{?suse_version}')" != "0" ] ; then \ echo suse ;\ elif [ "$$(rpm --eval '%{_host_vendor}')" = redhat ] ; then \ echo redhat ;\ elif [ "$$(rpm --eval '0%{?fedora}')" != "0" ] ; then \ echo redhat ;\ else \ echo unknown ;\ fi ;\ else \ echo unknown ;\ fi) endif ifdef DISTRO INSTALLDEPS+=install-$(DISTRO) endif .PHONY: install install: $(MAKE) install-indep $(MAKE) install-arch .PHONY: install-arch install-arch: $(INSTALLDEPS) install -m 755 -d $(SBINDIR) .PHONY: install-indep install-indep: indep install -m 755 -d $(APPARMOR_BIN_PREFIX) install -m 755 rc.apparmor.functions $(APPARMOR_BIN_PREFIX) install -m 755 profile-load $(APPARMOR_BIN_PREFIX) $(MAKE) install_manpages DESTDIR=${DESTDIR} .PHONY: install-systemd install-systemd: install -m 755 -d $(SYSTEMD_UNIT_DIR) install -m 644 apparmor.service $(SYSTEMD_UNIT_DIR) install -m 755 apparmor.systemd $(APPARMOR_BIN_PREFIX) install -m 755 -d $(USR_SBINDIR) install -m 755 aa-teardown $(USR_SBINDIR) ifndef VERBOSE .SILENT: clean endif .PHONY: clean clean: pod_clean rm -f core core.* *.o *.s *.a *~ *.gcda *.gcno rm -f gmon.out FORCE: apparmor-5.0.2/init/aa-teardown000066400000000000000000000002111522511161100164140ustar00rootroot00000000000000#!/bin/sh test $# = 0 || { echo "Usage: $0" echo echo "Unloads all AppArmor profiles" exit 1 } /lib/apparmor/apparmor.systemd stop apparmor-5.0.2/init/aa-teardown.pod000066400000000000000000000021421522511161100172020ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 2018 Christian Boltz # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- =pod =head1 NAME aa-teardown - unload all AppArmor profiles =head1 SYNOPSIS B =head1 DESCRIPTION aa-teardown unloads all AppArmor profiles =head1 BUGS If you find any bugs, please report them at L. =head1 SEE ALSO apparmor(7), apparmor.d(5), and L. =cut apparmor-5.0.2/init/apparmor.service000066400000000000000000000022471522511161100175050ustar00rootroot00000000000000[Unit] Description=Load AppArmor profiles DefaultDependencies=no Before=sysinit.target After=local-fs.target After=systemd-journald-audit.socket After=systemd-sysctl.service RequiresMountsFor=/var/cache/apparmor AssertPathIsReadWrite=/sys/kernel/security/apparmor/.load ConditionSecurity=apparmor Documentation=man:apparmor(7) Documentation=https://gitlab.com/apparmor/apparmor/wikis/home/ # Don't start this unit on the Ubuntu Live CD ConditionPathExists=!/rofs/etc/apparmor.d # Don't start this unit on the Debian Live CD when using overlayfs ConditionPathExists=!/run/live/overlay/work [Service] Type=oneshot ExecStart=/lib/apparmor/apparmor.systemd reload ExecReload=/lib/apparmor/apparmor.systemd reload # systemd maps 'restart' to 'stop; start' which means removing AppArmor confinement # from running processes (and not being able to re-apply it later). # Upstream systemd developers refused to implement an option that allows overriding # this behaviour, therefore we have to make ExecStop a no-op to error out on the # safe side. # # If you really want to unload all AppArmor profiles, run aa-teardown ExecStop=/bin/true RemainAfterExit=yes [Install] WantedBy=sysinit.target apparmor-5.0.2/init/apparmor.systemd000066400000000000000000000055021522511161100175320ustar00rootroot00000000000000#!/bin/sh # ---------------------------------------------------------------------- # Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions # This function is used in rc.apparmor.functions # shellcheck disable=SC2317,SC2329 aa_action() { echo "$1" shift "$@" return $? } # This function is used in rc.apparmor.functions # shellcheck disable=SC2317,SC2329 aa_log_warning_msg() { echo "Warning: $*" } # This function is used in rc.apparmor.functions # shellcheck disable=SC2317 aa_log_failure_msg() { echo "Error: $*" } # This function is used in rc.apparmor.functions # shellcheck disable=SC2317,SC2329 aa_log_action_start() { echo "$@" } # This function is used in rc.apparmor.functions # shellcheck disable=SC2317,SC2329 aa_log_action_end() { printf "" } # This function is used in rc.apparmor.functions # shellcheck disable=SC2317 aa_log_daemon_msg() { echo "$@" } # This function is used in rc.apparmor.functions # shellcheck disable=SC2317,SC2329 aa_log_skipped_msg() { echo "Skipped: $*" } # This function is used in rc.apparmor.functions # shellcheck disable=SC2317 aa_log_end_msg() { printf "" } # source apparmor function library if [ -f "${APPARMOR_FUNCTIONS}" ]; then # shellcheck source=rc.apparmor.functions . "${APPARMOR_FUNCTIONS}" else aa_log_failure_msg "Unable to find AppArmor initscript functions" exit 1 fi case "$1" in start) if [ -x /usr/bin/systemd-detect-virt ] && \ systemd-detect-virt --quiet --container && \ ! is_container_with_internal_policy; then aa_log_daemon_msg "Not starting AppArmor in container" aa_log_end_msg 0 exit 0 fi apparmor_start rc=$? ;; stop) apparmor_stop rc=$? ;; restart|reload|force-reload) if [ -x /usr/bin/systemd-detect-virt ] && \ systemd-detect-virt --quiet --container && \ ! is_container_with_internal_policy; then aa_log_daemon_msg "Not starting AppArmor in container" aa_log_end_msg 0 exit 0 fi apparmor_restart rc=$? ;; try-restart) apparmor_try_restart rc=$? ;; kill) apparmor_kill rc=$? ;; status) apparmor_status rc=$? ;; *) exit 1 ;; esac exit "$rc" apparmor-5.0.2/init/frob_slack_rc000077500000000000000000000060511522511161100170160ustar00rootroot00000000000000#!/usr/bin/perl -w # ---------------------------------------------------------------------- # Copyright (c) 2004, 2005 NOVELL (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- use Getopt::Long; use File::Temp qw/ :mktemp /; use File::stat; sub usage { print "$0\t(--init|--shutdown) \tmodify startup or shutdown script\n"; print "\t\t\t--file= \tmodify \n"; print "\t\t\t--remove \tremove option from config file\n"; print "\t\t\t--help \t\tthis help\n"; print '$Revision: 1.1 $', "\n"; exit(0); } my ($conffile,$help,$remove,$init,$shutdown); GetOptions( "file=s" => \$conffile, "init" => \$init, "shutdown" => \$shutdown, "remove" => \$remove, "help!" => \$help ) or usage(); usage() if ($help); my ($my_regexp, $my_command); if (defined $init) { $my_regexp = '^echo "Going multiuser..."$'; $my_command = "start"; } elsif (defined $shutdown) { $my_regexp = '^# Kill all processes.$'; $my_command = "kill"; } else { usage(); } if (defined $conffile) { $old = $conffile; chomp($old); } elsif (defined $init) { $old="/etc/rc.d/rc.M"; } elsif (defined $shutdown) { $old="/etc/rc.d/rc.K"; } open(MENU,"<$old") or die "Fatal: can't open $old: $!"; ($fh, $file) = mkstemp($old . "XXXXXX" ); my $skip = FALSE; while () { # ok, we rely on the '="' to site the changes ; if (! defined $remove) { if ( /$my_regexp/ ) { print $fh $_; print $fh "# BEGIN rc.apparmor INSERTION\n"; print $fh "if [ -x /etc/rc.d/rc.apparmor ] ; then\n"; print $fh " /etc/rc.d/rc.apparmor $my_command\n"; print $fh "fi\n"; print $fh "# END rc.apparmor INSERTION\n"; } elsif ( /^# BEGIN rc.subdomain INSERTION$/ ) { $skip = TRUE; } elsif ( $skip eq TRUE ) { if ( /^# END rc.subdomain INSERTION$/ ) { $skip = FALSE; } } else { print $fh $_; } } elsif ( /^# BEGIN rc.(apparmor\|subdomain) INSERTION$/ ) { $skip = TRUE; } elsif ( $skip eq TRUE ) { if ( /^# END rc.(apparmor\|subdomain) INSERTION$/ ) { $skip = FALSE; } } else { print $fh $_; } } my $sb = stat($old) || die "Could not get permission bits from $old"; my $oldmode = $sb->mode & 07777; rename $old, "$old.orig" || system("/bin/mv", $old, "$old.orig") && die "$old could not be renamed to $old.orig ($!); see $file for modifications"; rename $file, "$old" || system("/bin/mv", $file, "$old") && die "$file could not be renamed to $old ($!); see $file for modifications"; chmod $oldmode, $old || die "COuld not restore permission bits on $old"; apparmor-5.0.2/init/profile-load000077500000000000000000000037001522511161100166000ustar00rootroot00000000000000#!/bin/sh # profile-load # # ---------------------------------------------------------------------- # Copyright (c) 2010-2015 Canonical, Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Canonical, Ltd. # ---------------------------------------------------------------------- # # Helper for loading an AppArmor profile in pre-start scripts. [ -z "$1" ] && exit 1 # require a profile name . /lib/apparmor/rc.apparmor.functions # do not load in a container if [ -x /usr/bin/systemd-detect-virt ] && \ systemd-detect-virt --quiet --container && \ ! is_container_with_internal_policy; then exit 0 fi [ -d /rofs/etc/apparmor.d ] && exit 0 # do not load if running liveCD profile=/etc/apparmor.d/"$1" [ -e "$profile" ] || exit 0 # skip when missing profile module=/sys/module/apparmor [ -d $module ] || exit 0 # do not load without AppArmor in kernel [ -x /sbin/apparmor_parser ] || exit 0 # do not load without parser aafs=/sys/kernel/security/apparmor [ -d $aafs ] || exit 0 # do not load if unmounted [ -w $aafs/.load ] || exit 1 # fail if cannot load profiles params=$module/parameters [ -r $params/enabled ] || exit 0 # do not load if missing read -r enabled < $params/enabled || exit 1 # if this fails, something went wrong [ "$enabled" = "Y" ] || exit 0 # do not load if disabled /sbin/apparmor_parser -r -W "$profile" || exit 0 # LP: #1058356 apparmor-5.0.2/init/rc.apparmor.functions000066400000000000000000000233761522511161100204660ustar00rootroot00000000000000#!/bin/sh # ---------------------------------------------------------------------- # Copyright (c) 1999-2008 NOVELL (All rights reserved) # Copyright (c) 2009-2018 Canonical Ltd. (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- # rc.apparmor.functions by Steve Beattie # # NOTE: rc.apparmor initscripts that source this file need to implement # the following set of functions: # aa_action # aa_log_action_start # aa_log_action_end # aa_log_success_msg # aa_log_warning_msg # aa_log_failure_msg # aa_log_skipped_msg # aa_log_daemon_msg # aa_log_end_msg # Some nice defines that we use PARSER=/sbin/apparmor_parser PARSER_OPTS= # Suppress warnings when booting in quiet mode if [ "${QUIET:-no}" = yes ] || [ "${quiet:-n}" = y ]; then PARSER_OPTS="$PARSER_OPTS --quiet" fi if [ -d /etc/apparmor.d ] ; then PROFILE_DIRS=/etc/apparmor.d else aa_log_warning_msg "Unable to find profiles directory, installation problem?" fi # Eg. snapd policy might need this on some systems if loading policy # during early boot if not using the snapd unit file ADDITIONAL_PROFILE_DIR= if [ -n "$ADDITIONAL_PROFILE_DIR" ] && [ -d "$ADDITIONAL_PROFILE_DIR" ]; then PROFILE_DIRS="$PROFILE_DIRS $ADDITIONAL_PROFILE_DIR" fi AA_STATUS=/usr/sbin/aa-status SECURITYFS=/sys/kernel/security SFS_MOUNTPOINT="${SECURITYFS}/apparmor" # keep exit status from parser during profile load. 0 is good, 1 is bad STATUS=0 # Test if the apparmor "module" is present. is_apparmor_present() { [ -d /sys/module/apparmor ] } # Checks to see if the current container is capable of having internal AppArmor # profiles that should be loaded. Callers of this function should have already # verified that they're running inside of a container environment with # something like `systemd-detect-virt --container`. # # The only known container environments capable of supporting internal policy # are LXD and LXC environments, and Windows Subsystem for Linux. # # Returns 0 if the container environment is capable of having its own internal # policy and non-zero otherwise. # # IMPORTANT: This function will return 0 in the case of a non-LXD/non-LXC # system container technology being nested inside of a LXD/LXC container that # utilized an AppArmor namespace and profile stacking. The reason 0 will be # returned is because .ns_stacked will be "yes" and .ns_name will still match # "lx[dc]-*" since the nested system container technology will not have set up # a new AppArmor profile namespace. This will result in the nested system # container's boot process to experience failed policy loads but the boot # process should continue without any loss of functionality. This is an # unsupported configuration that cannot be properly handled by this function. is_container_with_internal_policy() { # this function is sometimes called independently of # is_apparmor_loaded(), so also define this here. local ns_stacked_path="${SFS_MOUNTPOINT}/.ns_stacked" local ns_name_path="${SFS_MOUNTPOINT}/.ns_name" local ns_stacked local ns_name # WSL needs to be detected explicitly if [ -x /usr/bin/systemd-detect-virt ] && \ [ "$(systemd-detect-virt --container)" = "wsl" ]; then return 0 fi if ! [ -f "$ns_stacked_path" ] || ! [ -f "$ns_name_path" ]; then return 1 fi read -r ns_stacked < "$ns_stacked_path" if [ "$ns_stacked" != "yes" ]; then return 1 fi # LXD, Incus and LXC set up AppArmor namespaces starting with "lxd-", # "incus-" and "lxc-", respectively. Return non-zero for all other # namespace identifiers. read -r ns_name < "$ns_name_path" if [ "${ns_name#lxd-*}" = "$ns_name" ] && \ [ "${ns_name#incus-*}" = "$ns_name" ] && \ [ "${ns_name#lxc-*}" = "$ns_name" ]; then return 1 fi return 0 } __parse_profiles_dir() { local parser_cmd="$1" local profile_dir="$2" local status=0 if [ ! -d "$profile_dir" ]; then aa_log_failure_msg "Profile directory not found: $profile_dir" return 1 fi if [ -z "$(ls "$profile_dir"/)" ]; then aa_log_failure_msg "No profiles found in $profile_dir" return 1 fi # shellcheck disable=SC2086 if ! "$PARSER" $PARSER_OPTS "$parser_cmd" -- "$profile_dir"; then status=1 aa_log_failure_msg "At least one profile failed to load" fi return "$status" } check_and_set_userns() { userns_restricted=$(sysctl -e -n kernel.apparmor_restrict_unprivileged_userns) unconfined_userns=$([ -f /sys/kernel/security/apparmor/features/policy/unconfined_restrictions/userns ] && cat /sys/kernel/security/apparmor/features/policy/unconfined_restrictions/userns || echo 0) if [ -n "$userns_restricted" ] && [ "$userns_restricted" -eq 1 ]; then if [ "$unconfined_userns" = "0" ] || [ "$unconfined_userns" = "no" ]; then # userns restrictions rely on unconfined userns to be supported aa_action "disabling unprivileged userns restrictions since unconfined userns is not supported / enabled" \ sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 fi fi } parse_profiles() { check_and_set_userns # get parser arg case "$1" in load) PARSER_CMD="--add" PARSER_MSG="Loading AppArmor profiles " ;; reload) PARSER_CMD="--replace" PARSER_MSG="Reloading AppArmor profiles " ;; *) aa_log_failure_msg "required 'load' or 'reload'" exit 1 ;; esac aa_log_action_start "$PARSER_MSG" # run the parser on all of the apparmor profiles if [ ! -f "$PARSER" ]; then aa_log_failure_msg "AppArmor parser not found" exit 1 fi for profile_dir in $PROFILE_DIRS; do __parse_profiles_dir "$PARSER_CMD" "$profile_dir" || STATUS=$? done aa_log_action_end "$STATUS" return "$STATUS" } is_apparmor_loaded() { if ! is_securityfs_mounted ; then mount_securityfs fi if [ -f "${SFS_MOUNTPOINT}/profiles" ]; then return 0 fi is_apparmor_present return $? } is_securityfs_mounted() { test -d "$SECURITYFS" -a -d /sys/fs/cgroup/systemd || grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts return $? } mount_securityfs() { if grep -q securityfs /proc/filesystems ; then aa_action "Mounting securityfs on $SECURITYFS" \ mount -t securityfs securityfs "$SECURITYFS" return $? fi return 0 } apparmor_start() { aa_log_daemon_msg "Starting AppArmor" if ! is_apparmor_present ; then aa_log_failure_msg "Starting AppArmor - failed, To enable AppArmor, ensure your kernel is configured with CONFIG_SECURITY_APPARMOR=y then add 'security=apparmor apparmor=1' to the kernel command line" aa_log_end_msg 1 return 1 elif ! is_apparmor_loaded ; then aa_log_failure_msg "Starting AppArmor - AppArmor control files aren't available under /sys/kernel/security/, please make sure securityfs is mounted." aa_log_end_msg 1 return 1 fi if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?" aa_log_end_msg 1 return 1 fi # if there is anything in the profiles file don't load if ! read -r _ < "$SFS_MOUNTPOINT/profiles"; then parse_profiles load else aa_log_skipped_msg ": already loaded with profiles." return 0 fi aa_log_end_msg 0 return 0 } remove_profiles() { # removing profiles as we directly read from apparmorfs # doesn't work, since we are removing entries which screws up # our position. Lets hope there are never enough profiles to # overflow the variable if ! is_apparmor_loaded ; then aa_log_failure_msg "AppArmor module is not loaded" return 1 fi if [ ! -w "$SFS_MOUNTPOINT/.remove" ] ; then aa_log_failure_msg "Root privileges not available" return 1 fi if [ ! -x "$PARSER" ] ; then aa_log_failure_msg "Unable to execute AppArmor parser" return 1 fi retval=0 # We filter child profiles as removing the parent will remove # the children sed -e "s/ (\(enforce\|complain\|prompt\|kill\|unconfined\))$//" "$SFS_MOUNTPOINT/profiles" | \ LC_COLLATE=C sort | grep -v // | { while read -r profile ; do printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove" rc=$? if [ "$rc" -ne 0 ] ; then retval=$rc aa_log_failure_msg "Unloading profile '$profile' failed" fi done return "$retval" } } apparmor_stop() { aa_log_daemon_msg "Unloading AppArmor profiles" remove_profiles rc=$? aa_log_end_msg "$rc" return "$rc" } apparmor_kill() { if ! is_apparmor_loaded ; then aa_log_failure_msg "AppArmor module is not loaded" return 1 fi aa_log_failure_msg "apparmor_kill() is no longer supported because AppArmor can't be built as a module" return 1 } __apparmor_restart() { if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?" return 4 fi aa_log_daemon_msg "Restarting AppArmor" parse_profiles reload rc=$? aa_log_end_msg "$rc" return "$rc" } apparmor_restart() { if ! is_apparmor_loaded ; then apparmor_start rc=$? return "$rc" fi __apparmor_restart return $? } apparmor_try_restart() { if ! is_apparmor_loaded ; then return 0 fi __apparmor_restart return $? } apparmor_status () { if test -x "$AA_STATUS" ; then "$AA_STATUS" --verbose return $? fi if ! is_apparmor_loaded ; then echo "AppArmor is not loaded." rc=1 else echo "AppArmor is enabled." rc=0 fi echo "Install the apparmor-utils package to receive more detailed" echo "status information here (or examine $SFS_MOUNTPOINT directly)." return "$rc" } apparmor-5.0.2/init/rc.apparmor.slackware000066400000000000000000000045141522511161100204230ustar00rootroot00000000000000#!/bin/sh # ---------------------------------------------------------------------- # Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 # NOVELL (All rights reserved) # Copyright (c) 2025 Christian Boltz # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- # rc.apparmor by Steve Beattie # shellcheck disable=SC2317,SC2329 # Don't warn about unreachable commands in this file # since most functions get called via rc.apparmor.functions # /etc/rc.d/rc.apparmor APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions aa_action() { STRING=$1 shift "$@" rc=$? if [ "$rc" -eq 0 ] ; then aa_log_success_msg "$STRING" else aa_log_failure_msg "$STRING" fi return "$rc" } aa_log_warning_msg() { [ -n "$1" ] && echo -n "$1" echo ": Warning." } aa_log_success_msg() { [ -n "$1" ] && echo -n "$1" echo ": OK." } aa_log_failure_msg() { [ -n "$1" ] && echo -n "$1" echo ": Failed." } aa_log_skipped_msg() { [ -n "$1" ] && echo -n "$1" echo ": Skipped." } aa_log_action_start() { echo "$@" } aa_log_action_end() { printf "" } aa_log_daemon_msg() { echo "$@" } aa_log_end_msg() { printf "" } usage() { echo "Usage: $0 {start|stop|restart|try-restart|reload|force-reload|status|kill}" } # source function library if [ -f "${APPARMOR_FUNCTIONS}" ] ; then . ${APPARMOR_FUNCTIONS} else aa_log_failure_msg "Unable to find AppArmor initscript functions" exit 1 fi case "$1" in start) apparmor_start rc=$? ;; stop) apparmor_stop rc=$? ;; restart|reload|force-reload) apparmor_restart rc=$? ;; try-restart) apparmor_try_restart rc=$? ;; kill) apparmor_kill rc=$? ;; status) apparmor_status rc=$? ;; *) usage exit 1 ;; esac exit $rc apparmor-5.0.2/kernel-patches/000077500000000000000000000000001522511161100162375ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/2.6.36.2/000077500000000000000000000000001522511161100171335ustar00rootroot000000000000000001-AppArmor-compatibility-patch-for-v5-network-controll.patch000066400000000000000000000366721522511161100326130ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/2.6.36.2From 729ccc6e522199ace96d9344b941e4530b7a0e64 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll Add compatibility for v5 network rules. Signed-off-by: John Johansen --- include/linux/lsm_audit.h | 4 + security/apparmor/Makefile | 6 +- security/apparmor/include/net.h | 40 +++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++ security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 48 ++++++++++- 8 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 112a550..d5f3dd7 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -123,6 +123,10 @@ struct common_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; } apparmor_audit_data; #endif diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index f204869..a9a1db0 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,17 +4,21 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o clean-files: capability_names.h af_names.h quiet_cmd_make-caps = GEN $@ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; 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 >> $@ ; echo "};" >> $@ +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ; 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 >> $@ ; echo "};" >> $@ + quiet_cmd_make-rlim = GEN $@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/af_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..3c7d599 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,40 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf..6776929 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; @@ -145,6 +146,7 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -181,6 +183,7 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index cf1de44..324ab91 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -31,6 +31,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -619,6 +620,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -650,6 +749,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..1765901 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "af_names.h" + +static const char *sock_type_names[] = { + "unknown(0)", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "dccp", + "unknown(7)", + "unknown(8)", + "unknown(9)", + "packet", +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net.family]) { + audit_log_string(ab, address_family_names[sa->u.net.family]); + } else { + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); + } + + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad.net.type]) { + audit_log_string(ab, sock_type_names[sa->aad.net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); + } + + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + + sa.aad.op = op, + sa.u.net.family = family; + sa.u.net.sk = sk; + sa.aad.net.type = type; + sa.aad.net.protocol = protocol; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u16 audit_mask = profile->net.audit[sa.u.net.family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad.net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 52cc865..3b5da44 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index eb3700e..c2b6225 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -190,6 +190,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + size_t size = 0; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i > AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + } + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- 1.7.1 apparmor-5.0.2/kernel-patches/2.6.36.2/0002-AppArmor-compatibility-patch-for-v5-interface.patch000066400000000000000000000257361522511161100313070ustar00rootroot00000000000000From 287eaf29269e7692c0fe510fe3838f286a2984e1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface Signed-off-by: John Johansen --- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 2 + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 18 ++- security/apparmor/include/apparmorfs.h | 6 + 5 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/apparmorfs-24.c diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index a9a1db0..e5e8968 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -6,6 +6,8 @@ apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o sid.o file.o net.o +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o + clean-files: capability_names.h af_names.h quiet_cmd_make-caps = GEN $@ diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c new file mode 100644 index 0000000..dc8c744 --- /dev/null +++ b/security/apparmor/apparmorfs-24.c @@ -0,0 +1,287 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + * + * + * This file contain functions providing an interface for <= AppArmor 2.4 + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 + * being set (see Makefile). + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + + +/* 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=crwxamlk/ " + "user::other"; + + return simple_read_from_buffer(buf, size, ppos, matching, + sizeof(matching) - 1); +} + +const struct file_operations aa_fs_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.1 capability=2.0 network=1.0 " + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; + + return simple_read_from_buffer(buf, size, ppos, features, + sizeof(features) - 1); +} + +const struct file_operations aa_fs_features_fops = { + .read = aa_features_read, +}; + +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 7320331..0e27449 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,7 +182,11 @@ void __init aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + aafs_remove("profiles"); + aafs_remove("matching"); + aafs_remove("features"); +#endif securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -213,7 +217,17 @@ int __init aa_create_aafs(void) aa_fs_dentry = NULL; goto error; } - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + error = aafs_create("matching", 0444, &aa_fs_matching_fops); + if (error) + goto error; + error = aafs_create("features", 0444, &aa_fs_features_fops); + if (error) + goto error; +#endif + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); + if (error) + goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a..14f955c 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,4 +17,10 @@ extern void __init aa_destroy_aafs(void); +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +extern const struct file_operations aa_fs_matching_fops; +extern const struct file_operations aa_fs_features_fops; +extern const struct file_operations aa_fs_profiles_fops; +#endif + #endif /* __AA_APPARMORFS_H */ -- 1.7.1 0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch000066400000000000000000000050701522511161100325000ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/2.6.36.2From 484d99eaaa89c3bfd707128b4c508d4a70a18eea Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 20 Jul 2010 06:57:08 -0700 Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace The apparmor_parser when compiling policy could generate invalid dfas that did not have sufficient padding to avoid invalid references, when used by the kernel. The kernels check to verify the next/check table size was broken meaning invalid dfas were being created by userspace and not caught. To remain compatible with old tools that are not fixed, pad the loaded dfas next/check table. The dfa's themselves are valid except for the high padding for potentially invalid transitions (high bounds error), which have a maximimum is 256 entries. So just allocate an extra null filled 256 entries for the next/check tables. This will guarentee all bounds are good and invalid transitions go to the null (0) state. Signed-off-by: John Johansen --- security/apparmor/match.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 5cb4dc1..0248bb3 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; + /* Pad table allocation for next/check by 256 entries to remain + * backwards compatible with old (buggy) tools and remain safe without + * run time checks + */ + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) + tsize += 256 * th.td_flags; + table = kvmalloc(tsize); if (table) { + /* ensure the pad is clear, else there will be errors */ + memset(table, 0, tsize); *table = th; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, @@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) goto out; if (flags & DFA_FLAG_VERIFY_STATES) { + int warning = 0; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (warning) + continue; + printk(KERN_WARNING "AppArmor DFA next/check " + "upper bounds error fixed, upgrade " + "user space tools \n"); + warning = 1; + } else if (BASE_TABLE(dfa)[i] >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; -- 1.7.1 apparmor-5.0.2/kernel-patches/2.6.36/000077500000000000000000000000001522511161100167735ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/2.6.36/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch000066400000000000000000000366721522511161100325320ustar00rootroot00000000000000From 6ab924a333c81d552eb92900509113bdf2fccb2e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll Add compatibility for v5 network rules. Signed-off-by: John Johansen --- include/linux/lsm_audit.h | 4 + security/apparmor/Makefile | 6 +- security/apparmor/include/net.h | 40 +++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++ security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 48 ++++++++++- 8 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 112a550..d5f3dd7 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -123,6 +123,10 @@ struct common_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; } apparmor_audit_data; #endif diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index f204869..a9a1db0 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,17 +4,21 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o clean-files: capability_names.h af_names.h quiet_cmd_make-caps = GEN $@ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; 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 >> $@ ; echo "};" >> $@ +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ; 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 >> $@ ; echo "};" >> $@ + quiet_cmd_make-rlim = GEN $@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/af_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..3c7d599 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,40 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf..6776929 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; @@ -145,6 +146,7 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -181,6 +183,7 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index cf1de44..324ab91 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -31,6 +31,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -619,6 +620,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -650,6 +749,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..1765901 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "af_names.h" + +static const char *sock_type_names[] = { + "unknown(0)", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "dccp", + "unknown(7)", + "unknown(8)", + "unknown(9)", + "packet", +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net.family]) { + audit_log_string(ab, address_family_names[sa->u.net.family]); + } else { + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); + } + + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad.net.type]) { + audit_log_string(ab, sock_type_names[sa->aad.net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); + } + + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + + sa.aad.op = op, + sa.u.net.family = family; + sa.u.net.sk = sk; + sa.aad.net.type = type; + sa.aad.net.protocol = protocol; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u16 audit_mask = profile->net.audit[sa.u.net.family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad.net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 52cc865..3b5da44 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index eb3700e..c2b6225 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -190,6 +190,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + size_t size = 0; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i > AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + } + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- 1.7.1 apparmor-5.0.2/kernel-patches/2.6.36/0002-AppArmor-compatibility-patch-for-v5-interface.patch000066400000000000000000000257361522511161100311470ustar00rootroot00000000000000From 5f034900aa447abea213c434d6d262d28fd168e7 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface Signed-off-by: John Johansen --- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 2 + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 18 ++- security/apparmor/include/apparmorfs.h | 6 + 5 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/apparmorfs-24.c diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index a9a1db0..e5e8968 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -6,6 +6,8 @@ apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o sid.o file.o net.o +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o + clean-files: capability_names.h af_names.h quiet_cmd_make-caps = GEN $@ diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c new file mode 100644 index 0000000..dc8c744 --- /dev/null +++ b/security/apparmor/apparmorfs-24.c @@ -0,0 +1,287 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + * + * + * This file contain functions providing an interface for <= AppArmor 2.4 + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 + * being set (see Makefile). + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + + +/* 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=crwxamlk/ " + "user::other"; + + return simple_read_from_buffer(buf, size, ppos, matching, + sizeof(matching) - 1); +} + +const struct file_operations aa_fs_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.1 capability=2.0 network=1.0 " + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; + + return simple_read_from_buffer(buf, size, ppos, features, + sizeof(features) - 1); +} + +const struct file_operations aa_fs_features_fops = { + .read = aa_features_read, +}; + +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 7320331..0e27449 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,7 +182,11 @@ void __init aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + aafs_remove("profiles"); + aafs_remove("matching"); + aafs_remove("features"); +#endif securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -213,7 +217,17 @@ int __init aa_create_aafs(void) aa_fs_dentry = NULL; goto error; } - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + error = aafs_create("matching", 0444, &aa_fs_matching_fops); + if (error) + goto error; + error = aafs_create("features", 0444, &aa_fs_features_fops); + if (error) + goto error; +#endif + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); + if (error) + goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a..14f955c 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,4 +17,10 @@ extern void __init aa_destroy_aafs(void); +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +extern const struct file_operations aa_fs_matching_fops; +extern const struct file_operations aa_fs_features_fops; +extern const struct file_operations aa_fs_profiles_fops; +#endif + #endif /* __AA_APPARMORFS_H */ -- 1.7.1 apparmor-5.0.2/kernel-patches/2.6.36/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch000066400000000000000000000050701522511161100324170ustar00rootroot00000000000000From 5d5b58edb77c2e2746395a3818239c6b7d17315d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 20 Jul 2010 06:57:08 -0700 Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace The apparmor_parser when compiling policy could generate invalid dfas that did not have sufficient padding to avoid invalid references, when used by the kernel. The kernels check to verify the next/check table size was broken meaning invalid dfas were being created by userspace and not caught. To remain compatible with old tools that are not fixed, pad the loaded dfas next/check table. The dfa's themselves are valid except for the high padding for potentially invalid transitions (high bounds error), which have a maximimum is 256 entries. So just allocate an extra null filled 256 entries for the next/check tables. This will guarentee all bounds are good and invalid transitions go to the null (0) state. Signed-off-by: John Johansen --- security/apparmor/match.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 5cb4dc1..0248bb3 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; + /* Pad table allocation for next/check by 256 entries to remain + * backwards compatible with old (buggy) tools and remain safe without + * run time checks + */ + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) + tsize += 256 * th.td_flags; + table = kvmalloc(tsize); if (table) { + /* ensure the pad is clear, else there will be errors */ + memset(table, 0, tsize); *table = th; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, @@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) goto out; if (flags & DFA_FLAG_VERIFY_STATES) { + int warning = 0; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (warning) + continue; + printk(KERN_WARNING "AppArmor DFA next/check " + "upper bounds error fixed, upgrade " + "user space tools \n"); + warning = 1; + } else if (BASE_TABLE(dfa)[i] >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; -- 1.7.1 apparmor-5.0.2/kernel-patches/2.6.37/000077500000000000000000000000001522511161100167745ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/2.6.37/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch000066400000000000000000000366721522511161100325330ustar00rootroot00000000000000From 333f21a5004bc386f217801549514b689f3430cd Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll Add compatibility for v5 network rules. Signed-off-by: John Johansen --- include/linux/lsm_audit.h | 4 + security/apparmor/Makefile | 6 +- security/apparmor/include/net.h | 40 +++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++ security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 48 ++++++++++- 8 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 112a550..d5f3dd7 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -123,6 +123,10 @@ struct common_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; } apparmor_audit_data; #endif diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index f204869..a9a1db0 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,17 +4,21 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o clean-files: capability_names.h af_names.h quiet_cmd_make-caps = GEN $@ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; 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 >> $@ ; echo "};" >> $@ +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ; 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 >> $@ ; echo "};" >> $@ + quiet_cmd_make-rlim = GEN $@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/af_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..3c7d599 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,40 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf..6776929 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; @@ -145,6 +146,7 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -181,6 +183,7 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b7106f1..fa778a7 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -31,6 +31,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -619,6 +620,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -650,6 +749,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..1765901 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "af_names.h" + +static const char *sock_type_names[] = { + "unknown(0)", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "dccp", + "unknown(7)", + "unknown(8)", + "unknown(9)", + "packet", +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net.family]) { + audit_log_string(ab, address_family_names[sa->u.net.family]); + } else { + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); + } + + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad.net.type]) { + audit_log_string(ab, sock_type_names[sa->aad.net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); + } + + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + + sa.aad.op = op, + sa.u.net.family = family; + sa.u.net.sk = sk; + sa.aad.net.type = type; + sa.aad.net.protocol = protocol; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u16 audit_mask = profile->net.audit[sa.u.net.family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad.net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4f0eade..4d5ce13 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index eb3700e..c2b6225 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -190,6 +190,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + size_t size = 0; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i > AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + } + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- 1.7.1 apparmor-5.0.2/kernel-patches/2.6.37/0002-AppArmor-compatibility-patch-for-v5-interface.patch000066400000000000000000000257361522511161100311500ustar00rootroot00000000000000From a89f0bb2dfd82445e58f5f6bfceb40ab8bee543f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface Signed-off-by: John Johansen --- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 2 + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 18 ++- security/apparmor/include/apparmorfs.h | 6 + 5 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/apparmorfs-24.c diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index a9a1db0..e5e8968 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -6,6 +6,8 @@ apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o sid.o file.o net.o +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o + clean-files: capability_names.h af_names.h quiet_cmd_make-caps = GEN $@ diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c new file mode 100644 index 0000000..dc8c744 --- /dev/null +++ b/security/apparmor/apparmorfs-24.c @@ -0,0 +1,287 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + * + * + * This file contain functions providing an interface for <= AppArmor 2.4 + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 + * being set (see Makefile). + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + + +/* 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=crwxamlk/ " + "user::other"; + + return simple_read_from_buffer(buf, size, ppos, matching, + sizeof(matching) - 1); +} + +const struct file_operations aa_fs_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.1 capability=2.0 network=1.0 " + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; + + return simple_read_from_buffer(buf, size, ppos, features, + sizeof(features) - 1); +} + +const struct file_operations aa_fs_features_fops = { + .read = aa_features_read, +}; + +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0848292..28c52ac 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + aafs_remove("profiles"); + aafs_remove("matching"); + aafs_remove("features"); +#endif securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -218,7 +222,17 @@ int __init aa_create_aafs(void) aa_fs_dentry = NULL; goto error; } - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + error = aafs_create("matching", 0444, &aa_fs_matching_fops); + if (error) + goto error; + error = aafs_create("features", 0444, &aa_fs_features_fops); + if (error) + goto error; +#endif + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); + if (error) + goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a..14f955c 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,4 +17,10 @@ extern void __init aa_destroy_aafs(void); +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +extern const struct file_operations aa_fs_matching_fops; +extern const struct file_operations aa_fs_features_fops; +extern const struct file_operations aa_fs_profiles_fops; +#endif + #endif /* __AA_APPARMORFS_H */ -- 1.7.1 apparmor-5.0.2/kernel-patches/2.6.37/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch000066400000000000000000000050701522511161100324200ustar00rootroot00000000000000From dd6eaf697f4deb510ecbfba12ac0d5221e4c6829 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 20 Jul 2010 06:57:08 -0700 Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace The apparmor_parser when compiling policy could generate invalid dfas that did not have sufficient padding to avoid invalid references, when used by the kernel. The kernels check to verify the next/check table size was broken meaning invalid dfas were being created by userspace and not caught. To remain compatible with old tools that are not fixed, pad the loaded dfas next/check table. The dfa's themselves are valid except for the high padding for potentially invalid transitions (high bounds error), which have a maximimum is 256 entries. So just allocate an extra null filled 256 entries for the next/check tables. This will guarentee all bounds are good and invalid transitions go to the null (0) state. Signed-off-by: John Johansen --- security/apparmor/match.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 5cb4dc1..0248bb3 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; + /* Pad table allocation for next/check by 256 entries to remain + * backwards compatible with old (buggy) tools and remain safe without + * run time checks + */ + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) + tsize += 256 * th.td_flags; + table = kvmalloc(tsize); if (table) { + /* ensure the pad is clear, else there will be errors */ + memset(table, 0, tsize); *table = th; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, @@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) goto out; if (flags & DFA_FLAG_VERIFY_STATES) { + int warning = 0; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (warning) + continue; + printk(KERN_WARNING "AppArmor DFA next/check " + "upper bounds error fixed, upgrade " + "user space tools \n"); + warning = 1; + } else if (BASE_TABLE(dfa)[i] >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; -- 1.7.1 apparmor-5.0.2/kernel-patches/2.6.39/000077500000000000000000000000001522511161100167765ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/2.6.39/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch000066400000000000000000000365111522511161100325250ustar00rootroot00000000000000From 0ae314bc92d8b22250f04f85e4bd36ee9ed30890 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll Add compatibility for v5 network rules. Signed-off-by: John Johansen --- include/linux/lsm_audit.h | 4 + security/apparmor/Makefile | 19 ++++- security/apparmor/include/net.h | 40 +++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++ security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 48 ++++++++++- 8 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 112a550..d5f3dd7 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -123,6 +123,10 @@ struct common_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; } apparmor_audit_data; #endif diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 2dafe50..7cefef9 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h af_names.h # Build a lower case string table of capability names @@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\ sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\ echo "};" >> $@ +# Build a lower case string table of address family names. +# Transform lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [2] = "inet", +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\ + echo "};" >> $@ + + $(obj)/capability.o : $(obj)/capability_names.h $(obj)/resource.o : $(obj)/rlim_names.h +$(obj)/net.o : $(obj)/af_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h $(call cmd,make-rlim) +$(obj)/af_names.h : $(srctree)/include/linux/socket.h + $(call cmd,make-af) \ No newline at end of file diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..3c7d599 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,40 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf..6776929 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; @@ -145,6 +146,7 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -181,6 +183,7 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ae3a698..05c018b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -620,6 +621,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -651,6 +750,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..1765901 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "af_names.h" + +static const char *sock_type_names[] = { + "unknown(0)", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "dccp", + "unknown(7)", + "unknown(8)", + "unknown(9)", + "packet", +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net.family]) { + audit_log_string(ab, address_family_names[sa->u.net.family]); + } else { + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); + } + + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad.net.type]) { + audit_log_string(ab, sock_type_names[sa->aad.net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); + } + + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + + sa.aad.op = op, + sa.u.net.family = family; + sa.u.net.sk = sk; + sa.aad.net.type = type; + sa.aad.net.protocol = protocol; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u16 audit_mask = profile->net.audit[sa.u.net.family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad.net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4f0eade..4d5ce13 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index e33aaf7..fa3f1b4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -190,6 +190,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + size_t size = 0; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i > AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + } + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- 1.7.0.4 apparmor-5.0.2/kernel-patches/2.6.39/0002-AppArmor-compatibility-patch-for-v5-interface.patch000066400000000000000000000257721522511161100311520ustar00rootroot00000000000000From cdc6b35345e5bcfe92bb2b52ef003f94ceedd40d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface Signed-off-by: John Johansen --- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 1 + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 18 ++- security/apparmor/include/apparmorfs.h | 6 + 5 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/apparmorfs-24.c diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 7cefef9..0bb604b 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o sid.o file.o net.o +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o clean-files := capability_names.h rlim_names.h af_names.h diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c new file mode 100644 index 0000000..dc8c744 --- /dev/null +++ b/security/apparmor/apparmorfs-24.c @@ -0,0 +1,287 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + * + * + * This file contain functions providing an interface for <= AppArmor 2.4 + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 + * being set (see Makefile). + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + + +/* 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=crwxamlk/ " + "user::other"; + + return simple_read_from_buffer(buf, size, ppos, matching, + sizeof(matching) - 1); +} + +const struct file_operations aa_fs_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.1 capability=2.0 network=1.0 " + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; + + return simple_read_from_buffer(buf, size, ppos, features, + sizeof(features) - 1); +} + +const struct file_operations aa_fs_features_fops = { + .read = aa_features_read, +}; + +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0848292..28c52ac 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + aafs_remove("profiles"); + aafs_remove("matching"); + aafs_remove("features"); +#endif securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -218,7 +222,17 @@ int __init aa_create_aafs(void) aa_fs_dentry = NULL; goto error; } - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + error = aafs_create("matching", 0444, &aa_fs_matching_fops); + if (error) + goto error; + error = aafs_create("features", 0444, &aa_fs_features_fops); + if (error) + goto error; +#endif + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); + if (error) + goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a..14f955c 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,4 +17,10 @@ extern void __init aa_destroy_aafs(void); +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +extern const struct file_operations aa_fs_matching_fops; +extern const struct file_operations aa_fs_features_fops; +extern const struct file_operations aa_fs_profiles_fops; +#endif + #endif /* __AA_APPARMORFS_H */ -- 1.7.0.4 apparmor-5.0.2/kernel-patches/2.6.39/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch000066400000000000000000000050721522511161100324240ustar00rootroot00000000000000From f17b28f64b963c47e76737f7bb7f58ce3a7c5249 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 20 Jul 2010 06:57:08 -0700 Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace The apparmor_parser when compiling policy could generate invalid dfas that did not have sufficient padding to avoid invalid references, when used by the kernel. The kernels check to verify the next/check table size was broken meaning invalid dfas were being created by userspace and not caught. To remain compatible with old tools that are not fixed, pad the loaded dfas next/check table. The dfa's themselves are valid except for the high padding for potentially invalid transitions (high bounds error), which have a maximimum is 256 entries. So just allocate an extra null filled 256 entries for the next/check tables. This will guarentee all bounds are good and invalid transitions go to the null (0) state. Signed-off-by: John Johansen --- security/apparmor/match.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 06d764c..cf92856 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; + /* Pad table allocation for next/check by 256 entries to remain + * backwards compatible with old (buggy) tools and remain safe without + * run time checks + */ + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) + tsize += 256 * th.td_flags; + table = kvmalloc(tsize); if (table) { + /* ensure the pad is clear, else there will be errors */ + memset(table, 0, tsize); *table = th; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, @@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) goto out; if (flags & DFA_FLAG_VERIFY_STATES) { + int warning = 0; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (warning) + continue; + printk(KERN_WARNING "AppArmor DFA next/check " + "upper bounds error fixed, upgrade " + "user space tools \n"); + warning = 1; + } else if (BASE_TABLE(dfa)[i] >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; -- 1.7.0.4 apparmor-5.0.2/kernel-patches/3.0/000077500000000000000000000000001522511161100165375ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.0/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch000066400000000000000000000365131522511161100322700ustar00rootroot00000000000000From dc13dec93dbd04bfa7a9ba67df1b8ed3431d8d48 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:39 -0700 Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll Add compatibility for v5 network rules. Signed-off-by: John Johansen --- include/linux/lsm_audit.h | 4 + security/apparmor/Makefile | 19 ++++- security/apparmor/include/net.h | 40 +++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++ security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 48 ++++++++++- 8 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 88e78de..c63979a 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -124,6 +124,10 @@ struct common_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; } apparmor_audit_data; #endif diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 2dafe50..7cefef9 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h af_names.h # Build a lower case string table of capability names @@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\ sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\ echo "};" >> $@ +# Build a lower case string table of address family names. +# Transform lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [2] = "inet", +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\ + echo "};" >> $@ + + $(obj)/capability.o : $(obj)/capability_names.h $(obj)/resource.o : $(obj)/rlim_names.h +$(obj)/net.o : $(obj)/af_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h $(call cmd,make-rlim) +$(obj)/af_names.h : $(srctree)/include/linux/socket.h + $(call cmd,make-af) \ No newline at end of file diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..3c7d599 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,40 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf..6776929 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; @@ -145,6 +146,7 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -181,6 +183,7 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3d2fd14..aa293ae 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -621,6 +622,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -652,6 +751,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..1765901 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "af_names.h" + +static const char *sock_type_names[] = { + "unknown(0)", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "dccp", + "unknown(7)", + "unknown(8)", + "unknown(9)", + "packet", +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net.family]) { + audit_log_string(ab, address_family_names[sa->u.net.family]); + } else { + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); + } + + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad.net.type]) { + audit_log_string(ab, sock_type_names[sa->aad.net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); + } + + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + + sa.aad.op = op, + sa.u.net.family = family; + sa.u.net.sk = sk; + sa.aad.net.type = type; + sa.aad.net.protocol = protocol; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u16 audit_mask = profile->net.audit[sa.u.net.family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad.net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4f0eade..4d5ce13 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index d6d9a57..f4874c4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -190,6 +190,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + size_t size = 0; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + } + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- 1.7.5.4 apparmor-5.0.2/kernel-patches/3.0/0002-AppArmor-compatibility-patch-for-v5-interface.patch000066400000000000000000000257721522511161100307130ustar00rootroot00000000000000From a2515f25ad5a7833ddc5a032d34eee6a5ddee3a2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:40 -0700 Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface Signed-off-by: John Johansen --- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 1 + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 18 ++- security/apparmor/include/apparmorfs.h | 6 + 5 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/apparmorfs-24.c diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 7cefef9..0bb604b 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o sid.o file.o net.o +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o clean-files := capability_names.h rlim_names.h af_names.h diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c new file mode 100644 index 0000000..dc8c744 --- /dev/null +++ b/security/apparmor/apparmorfs-24.c @@ -0,0 +1,287 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + * + * + * This file contain functions providing an interface for <= AppArmor 2.4 + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 + * being set (see Makefile). + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + + +/* 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=crwxamlk/ " + "user::other"; + + return simple_read_from_buffer(buf, size, ppos, matching, + sizeof(matching) - 1); +} + +const struct file_operations aa_fs_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.1 capability=2.0 network=1.0 " + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; + + return simple_read_from_buffer(buf, size, ppos, features, + sizeof(features) - 1); +} + +const struct file_operations aa_fs_features_fops = { + .read = aa_features_read, +}; + +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0848292..28c52ac 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + aafs_remove("profiles"); + aafs_remove("matching"); + aafs_remove("features"); +#endif securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -218,7 +222,17 @@ int __init aa_create_aafs(void) aa_fs_dentry = NULL; goto error; } - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + error = aafs_create("matching", 0444, &aa_fs_matching_fops); + if (error) + goto error; + error = aafs_create("features", 0444, &aa_fs_features_fops); + if (error) + goto error; +#endif + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); + if (error) + goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a..14f955c 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,4 +17,10 @@ extern void __init aa_destroy_aafs(void); +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +extern const struct file_operations aa_fs_matching_fops; +extern const struct file_operations aa_fs_features_fops; +extern const struct file_operations aa_fs_profiles_fops; +#endif + #endif /* __AA_APPARMORFS_H */ -- 1.7.5.4 apparmor-5.0.2/kernel-patches/3.0/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch000066400000000000000000000050731522511161100321660ustar00rootroot00000000000000From 7a10d093f9779f42cb8d6affcb6a4436d3ebd6d3 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:41 -0700 Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace The apparmor_parser when compiling policy could generate invalid dfas that did not have sufficient padding to avoid invalid references, when used by the kernel. The kernels check to verify the next/check table size was broken meaning invalid dfas were being created by userspace and not caught. To remain compatible with old tools that are not fixed, pad the loaded dfas next/check table. The dfa's themselves are valid except for the high padding for potentially invalid transitions (high bounds error), which have a maximimum is 256 entries. So just allocate an extra null filled 256 entries for the next/check tables. This will guarentee all bounds are good and invalid transitions go to the null (0) state. Signed-off-by: John Johansen --- security/apparmor/match.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 94de6b4..081491e 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; + /* Pad table allocation for next/check by 256 entries to remain + * backwards compatible with old (buggy) tools and remain safe without + * run time checks + */ + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) + tsize += 256 * th.td_flags; + table = kvmalloc(tsize); if (table) { + /* ensure the pad is clear, else there will be errors */ + memset(table, 0, tsize); *table = th; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, @@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) goto out; if (flags & DFA_FLAG_VERIFY_STATES) { + int warning = 0; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (warning) + continue; + printk(KERN_WARNING "AppArmor DFA next/check " + "upper bounds error fixed, upgrade " + "user space tools \n"); + warning = 1; + } else if (BASE_TABLE(dfa)[i] >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; -- 1.7.5.4 apparmor-5.0.2/kernel-patches/3.1/000077500000000000000000000000001522511161100165405ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.1/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch000066400000000000000000000365131522511161100322710ustar00rootroot00000000000000From dc13dec93dbd04bfa7a9ba67df1b8ed3431d8d48 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:39 -0700 Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll Add compatibility for v5 network rules. Signed-off-by: John Johansen --- include/linux/lsm_audit.h | 4 + security/apparmor/Makefile | 19 ++++- security/apparmor/include/net.h | 40 +++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++ security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 48 ++++++++++- 8 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 88e78de..c63979a 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -124,6 +124,10 @@ struct common_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; } apparmor_audit_data; #endif diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 2dafe50..7cefef9 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h af_names.h # Build a lower case string table of capability names @@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\ sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\ echo "};" >> $@ +# Build a lower case string table of address family names. +# Transform lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [2] = "inet", +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\ + echo "};" >> $@ + + $(obj)/capability.o : $(obj)/capability_names.h $(obj)/resource.o : $(obj)/rlim_names.h +$(obj)/net.o : $(obj)/af_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h $(call cmd,make-rlim) +$(obj)/af_names.h : $(srctree)/include/linux/socket.h + $(call cmd,make-af) \ No newline at end of file diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..3c7d599 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,40 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf..6776929 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; @@ -145,6 +146,7 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -181,6 +183,7 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3d2fd14..aa293ae 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -621,6 +622,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -652,6 +751,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..1765901 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "af_names.h" + +static const char *sock_type_names[] = { + "unknown(0)", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "dccp", + "unknown(7)", + "unknown(8)", + "unknown(9)", + "packet", +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net.family]) { + audit_log_string(ab, address_family_names[sa->u.net.family]); + } else { + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); + } + + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad.net.type]) { + audit_log_string(ab, sock_type_names[sa->aad.net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); + } + + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + + sa.aad.op = op, + sa.u.net.family = family; + sa.u.net.sk = sk; + sa.aad.net.type = type; + sa.aad.net.protocol = protocol; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u16 audit_mask = profile->net.audit[sa.u.net.family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad.net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4f0eade..4d5ce13 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index d6d9a57..f4874c4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -190,6 +190,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + size_t size = 0; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + } + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- 1.7.5.4 apparmor-5.0.2/kernel-patches/3.1/0002-AppArmor-compatibility-patch-for-v5-interface.patch000066400000000000000000000257721522511161100307140ustar00rootroot00000000000000From a2515f25ad5a7833ddc5a032d34eee6a5ddee3a2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:40 -0700 Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface Signed-off-by: John Johansen --- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 1 + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 18 ++- security/apparmor/include/apparmorfs.h | 6 + 5 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/apparmorfs-24.c diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 7cefef9..0bb604b 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o sid.o file.o net.o +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o clean-files := capability_names.h rlim_names.h af_names.h diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c new file mode 100644 index 0000000..dc8c744 --- /dev/null +++ b/security/apparmor/apparmorfs-24.c @@ -0,0 +1,287 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + * + * + * This file contain functions providing an interface for <= AppArmor 2.4 + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 + * being set (see Makefile). + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + + +/* 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=crwxamlk/ " + "user::other"; + + return simple_read_from_buffer(buf, size, ppos, matching, + sizeof(matching) - 1); +} + +const struct file_operations aa_fs_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.1 capability=2.0 network=1.0 " + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; + + return simple_read_from_buffer(buf, size, ppos, features, + sizeof(features) - 1); +} + +const struct file_operations aa_fs_features_fops = { + .read = aa_features_read, +}; + +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0848292..28c52ac 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + aafs_remove("profiles"); + aafs_remove("matching"); + aafs_remove("features"); +#endif securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -218,7 +222,17 @@ int __init aa_create_aafs(void) aa_fs_dentry = NULL; goto error; } - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + error = aafs_create("matching", 0444, &aa_fs_matching_fops); + if (error) + goto error; + error = aafs_create("features", 0444, &aa_fs_features_fops); + if (error) + goto error; +#endif + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); + if (error) + goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a..14f955c 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,4 +17,10 @@ extern void __init aa_destroy_aafs(void); +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +extern const struct file_operations aa_fs_matching_fops; +extern const struct file_operations aa_fs_features_fops; +extern const struct file_operations aa_fs_profiles_fops; +#endif + #endif /* __AA_APPARMORFS_H */ -- 1.7.5.4 apparmor-5.0.2/kernel-patches/3.1/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch000066400000000000000000000050731522511161100321670ustar00rootroot00000000000000From 7a10d093f9779f42cb8d6affcb6a4436d3ebd6d3 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:41 -0700 Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace The apparmor_parser when compiling policy could generate invalid dfas that did not have sufficient padding to avoid invalid references, when used by the kernel. The kernels check to verify the next/check table size was broken meaning invalid dfas were being created by userspace and not caught. To remain compatible with old tools that are not fixed, pad the loaded dfas next/check table. The dfa's themselves are valid except for the high padding for potentially invalid transitions (high bounds error), which have a maximimum is 256 entries. So just allocate an extra null filled 256 entries for the next/check tables. This will guarentee all bounds are good and invalid transitions go to the null (0) state. Signed-off-by: John Johansen --- security/apparmor/match.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 94de6b4..081491e 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; + /* Pad table allocation for next/check by 256 entries to remain + * backwards compatible with old (buggy) tools and remain safe without + * run time checks + */ + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) + tsize += 256 * th.td_flags; + table = kvmalloc(tsize); if (table) { + /* ensure the pad is clear, else there will be errors */ + memset(table, 0, tsize); *table = th; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, @@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) goto out; if (flags & DFA_FLAG_VERIFY_STATES) { + int warning = 0; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (warning) + continue; + printk(KERN_WARNING "AppArmor DFA next/check " + "upper bounds error fixed, upgrade " + "user space tools \n"); + warning = 1; + } else if (BASE_TABLE(dfa)[i] >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; -- 1.7.5.4 apparmor-5.0.2/kernel-patches/3.10/000077500000000000000000000000001522511161100166205ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.10/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch000066400000000000000000000174441522511161100315670ustar00rootroot00000000000000From 1b5e29dcf1c18938e62e23f24e9a19c01b861561 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 1/4] UBUNTU: SAUCE: AppArmor: Add profile introspection file to interface Add the dynamic profiles file to the interace, to allow load policy introspection. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Kconfig | 9 ++ security/apparmor/apparmorfs.c | 231 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..42b7c9f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; +#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ + /** Base file system setup **/ static struct aa_fs_entry aa_fs_entry_file[] = { @@ -210,6 +438,9 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), +#endif AA_FS_DIR("features", aa_fs_entry_features), { } }; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.10/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000430511522511161100302160ustar00rootroot00000000000000From e83b058f391e96a2a640fb2e2812d50ef67e0f43 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 2/4] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5706b74..e270692 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 42b7c9f..114fb23 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 69d8cae..4af6523 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569..eb13a73 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const profile_mode_names[]; @@ -157,6 +158,7 @@ struct aa_policydb { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -194,6 +196,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b21830e..1bce440 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -614,6 +615,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -646,6 +745,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 8132003..56e5304 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -747,6 +747,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 329b1fd..1b90dfa 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.10/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322660ustar00rootroot00000000000000From ee0073a1e7b0ec172273a6211a3b117d024e5949 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 3/4] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.10/0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000647401522511161100314570ustar00rootroot00000000000000From 7a445944525ad3b2a3f292ddf0d491ae6ed947c1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 4/4] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ 10 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index e270692..9b44e1a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 114fb23..ee77ec9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3ae28db..e267963 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 859abda..3fee1fe 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9..e243d96 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -29,8 +29,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4af6523..ada004d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -73,6 +73,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -122,6 +126,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 1bce440..6750673 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -504,6 +505,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -721,6 +776,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..478aa4d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.11/000077500000000000000000000000001522511161100166215ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.11/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch000066400000000000000000000174441522511161100315700ustar00rootroot00000000000000From b69a30ebeca7c4a021a2465a2c66eb422609c65d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 1/4] UBUNTU: SAUCE: AppArmor: Add profile introspection file to interface Add the dynamic profiles file to the interace, to allow load policy introspection. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Kconfig | 9 ++ security/apparmor/apparmorfs.c | 231 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..42b7c9f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; +#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ + /** Base file system setup **/ static struct aa_fs_entry aa_fs_entry_file[] = { @@ -210,6 +438,9 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), +#endif AA_FS_DIR("features", aa_fs_entry_features), { } }; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.11/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000430531522511161100302210ustar00rootroot00000000000000From 187d55e41a0c81061500334c0493b7a7bf560eb1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 2/4] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5706b74..e270692 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 42b7c9f..114fb23 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 69d8cae..4af6523 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b25491a..2aed6de 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const profile_mode_names[]; @@ -158,6 +159,7 @@ struct aa_policydb { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -194,6 +196,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 2e2a0dd..8610d6f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -613,6 +614,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -645,6 +744,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 0f345c4..6e5853a 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -669,6 +669,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_put_dfa(profile->xmatch); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 6dac7d7..c6380c0 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -192,6 +192,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -473,6 +486,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -566,6 +580,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.11/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322670ustar00rootroot00000000000000From 920dd3917d665e57fb1c317bcf0b07b5cb8b7640 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 3/4] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.11/0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000647401522511161100314600ustar00rootroot00000000000000From 3dce616a157eafb2963bbd684dd556deb5cea6c6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 4/4] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ 10 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index e270692..9b44e1a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 114fb23..ee77ec9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 031d2d9..02d804c 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 01b7bd6..abd613c 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -238,7 +238,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 1ba2ca5..d1b145b 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -30,8 +30,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4af6523..ada004d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -73,6 +73,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -122,6 +126,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8610d6f..daab74e 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -502,6 +503,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -720,6 +775,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..478aa4d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.12/000077500000000000000000000000001522511161100166225ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.12/0001-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000433441522511161100302240ustar00rootroot00000000000000From d29d73fa5d7b5d016f9c17236fff2a741acea247 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen Conflicts: security/apparmor/Makefile security/apparmor/policy.c --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df8..5dbb72f 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 7db9954..18fc02c 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -806,6 +806,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 30e8d76..61abec5 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -126,6 +126,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index c28b0f2..b524d88 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const aa_profile_mode_names[]; @@ -176,6 +177,7 @@ struct aa_replacedby { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -217,6 +219,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; unsigned char *hash; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index fb99e18..de55a7f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -615,6 +616,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -647,6 +746,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 705c287..e2afe29 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a689f10..1a35e6b 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.12/0002-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322670ustar00rootroot00000000000000From b452a37e97af826ba6c7548230e07c95bd13d9c4 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 2/3] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.12/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000654621522511161100314620ustar00rootroot00000000000000From 0f113c1f052be315f5097d8b7294a620b0adda87 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook Conflicts: security/apparmor/Makefile security/apparmor/apparmorfs.c --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 15 +- security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ 10 files changed, 768 insertions(+), 4 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5dbb72f..89b3445 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 18fc02c..e709030 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -799,7 +799,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_policy[] = { AA_FS_FILE_BOOLEAN("set_load", 1), - {} + { } +}; + +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } }; static struct aa_fs_entry aa_fs_entry_features[] = { @@ -807,6 +818,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 031d2d9..02d804c 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 26c607c..23936c5 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -238,7 +238,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 8fb1488..22b172c 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -30,8 +30,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 61abec5..a9835c3 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -72,6 +72,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -121,6 +125,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index de55a7f..e0dd95f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -502,6 +503,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -722,6 +777,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..478aa4d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.2/000077500000000000000000000000001522511161100165415ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.2/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch000066400000000000000000000365121522511161100322710ustar00rootroot00000000000000From 125fccb600288968aa3395883c0a394c47176fcd Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:39 -0700 Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll Add compatibility for v5 network rules. Signed-off-by: John Johansen --- include/linux/lsm_audit.h | 4 + security/apparmor/Makefile | 19 +++- security/apparmor/include/net.h | 40 +++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 ++++++++++++++++++++++++ security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 48 +++++++++- 8 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 88e78de..c63979a 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -124,6 +124,10 @@ struct common_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; } apparmor_audit_data; #endif diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 2dafe50..7cefef9 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h af_names.h # Build a lower case string table of capability names @@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\ sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\ echo "};" >> $@ +# Build a lower case string table of address family names. +# Transform lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [2] = "inet", +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\ + echo "};" >> $@ + + $(obj)/capability.o : $(obj)/capability_names.h $(obj)/resource.o : $(obj)/rlim_names.h +$(obj)/net.o : $(obj)/af_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h $(call cmd,make-rlim) +$(obj)/af_names.h : $(srctree)/include/linux/socket.h + $(call cmd,make-af) \ No newline at end of file diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..3c7d599 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,40 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf..6776929 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; @@ -145,6 +146,7 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -181,6 +183,7 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3783202..7459547 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -621,6 +622,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -652,6 +751,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..1765901 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "af_names.h" + +static const char *sock_type_names[] = { + "unknown(0)", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "dccp", + "unknown(7)", + "unknown(8)", + "unknown(9)", + "packet", +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net.family]) { + audit_log_string(ab, address_family_names[sa->u.net.family]); + } else { + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); + } + + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad.net.type]) { + audit_log_string(ab, sock_type_names[sa->aad.net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); + } + + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + + sa.aad.op = op, + sa.u.net.family = family; + sa.u.net.sk = sk; + sa.aad.net.type = type; + sa.aad.net.protocol = protocol; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u16 audit_mask = profile->net.audit[sa.u.net.family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad.net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4f0eade..4d5ce13 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 741dd13..ee8043e 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -190,6 +190,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + size_t size = 0; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + } + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.2/0002-AppArmor-compatibility-patch-for-v5-interface.patch000066400000000000000000000260001522511161100306760ustar00rootroot00000000000000From 004192fb5223c7b81a949e36a080a5da56132826 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:40 -0700 Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface Signed-off-by: John Johansen --- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 1 + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 18 +- security/apparmor/include/apparmorfs.h | 6 + 5 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/apparmorfs-24.c diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 7cefef9..0bb604b 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o sid.o file.o net.o +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o clean-files := capability_names.h rlim_names.h af_names.h diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c new file mode 100644 index 0000000..dc8c744 --- /dev/null +++ b/security/apparmor/apparmorfs-24.c @@ -0,0 +1,287 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + * + * + * This file contain functions providing an interface for <= AppArmor 2.4 + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 + * being set (see Makefile). + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + + +/* 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=crwxamlk/ " + "user::other"; + + return simple_read_from_buffer(buf, size, ppos, matching, + sizeof(matching) - 1); +} + +const struct file_operations aa_fs_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.1 capability=2.0 network=1.0 " + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; + + return simple_read_from_buffer(buf, size, ppos, features, + sizeof(features) - 1); +} + +const struct file_operations aa_fs_features_fops = { + .read = aa_features_read, +}; + +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 69ddb47..867995c 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + aafs_remove("profiles"); + aafs_remove("matching"); + aafs_remove("features"); +#endif securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -218,7 +222,17 @@ static int __init aa_create_aafs(void) aa_fs_dentry = NULL; goto error; } - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + error = aafs_create("matching", 0444, &aa_fs_matching_fops); + if (error) + goto error; + error = aafs_create("features", 0444, &aa_fs_features_fops); + if (error) + goto error; +#endif + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); + if (error) + goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a..14f955c 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,4 +17,10 @@ extern void __init aa_destroy_aafs(void); +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +extern const struct file_operations aa_fs_matching_fops; +extern const struct file_operations aa_fs_features_fops; +extern const struct file_operations aa_fs_profiles_fops; +#endif + #endif /* __AA_APPARMORFS_H */ -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.2/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch000066400000000000000000000050521522511161100321650ustar00rootroot00000000000000From e5d90918aa31f948ecec2f3c088567dbab30c90b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:41 -0700 Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace The apparmor_parser when compiling policy could generate invalid dfas that did not have sufficient padding to avoid invalid references, when used by the kernel. The kernels check to verify the next/check table size was broken meaning invalid dfas were being created by userspace and not caught. To remain compatible with old tools that are not fixed, pad the loaded dfas next/check table. The dfa's themselves are valid except for the high padding for potentially invalid transitions (high bounds error), which have a maximimum is 256 entries. So just allocate an extra null filled 256 entries for the next/check tables. This will guarentee all bounds are good and invalid transitions go to the null (0) state. Signed-off-by: John Johansen --- security/apparmor/match.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 94de6b4..081491e 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; + /* Pad table allocation for next/check by 256 entries to remain + * backwards compatible with old (buggy) tools and remain safe without + * run time checks + */ + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) + tsize += 256 * th.td_flags; + table = kvmalloc(tsize); if (table) { + /* ensure the pad is clear, else there will be errors */ + memset(table, 0, tsize); *table = th; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, @@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) goto out; if (flags & DFA_FLAG_VERIFY_STATES) { + int warning = 0; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (warning) + continue; + printk(KERN_WARNING "AppArmor DFA next/check " + "upper bounds error fixed, upgrade " + "user space tools \n"); + warning = 1; + } else if (BASE_TABLE(dfa)[i] >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.3/000077500000000000000000000000001522511161100165425ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.3/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch000066400000000000000000000365121522511161100322720ustar00rootroot00000000000000From 1023c7c2f9d9c5707147479104312c4c3d1a2c2b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:39 -0700 Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll Add compatibility for v5 network rules. Signed-off-by: John Johansen --- include/linux/lsm_audit.h | 4 + security/apparmor/Makefile | 19 +++- security/apparmor/include/net.h | 40 +++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 ++++++++++++++++++++++++ security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 48 +++++++++- 8 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 88e78de..c63979a 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -124,6 +124,10 @@ struct common_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; } apparmor_audit_data; #endif diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 2dafe50..7cefef9 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h af_names.h # Build a lower case string table of capability names @@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\ sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\ echo "};" >> $@ +# Build a lower case string table of address family names. +# Transform lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [2] = "inet", +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\ + echo "};" >> $@ + + $(obj)/capability.o : $(obj)/capability_names.h $(obj)/resource.o : $(obj)/rlim_names.h +$(obj)/net.o : $(obj)/af_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h $(call cmd,make-rlim) +$(obj)/af_names.h : $(srctree)/include/linux/socket.h + $(call cmd,make-af) \ No newline at end of file diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..3c7d599 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,40 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf..6776929 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; @@ -145,6 +146,7 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -181,6 +183,7 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 97ce8fa..a54adbc 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -620,6 +621,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -651,6 +750,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..1765901 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "af_names.h" + +static const char *sock_type_names[] = { + "unknown(0)", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "dccp", + "unknown(7)", + "unknown(8)", + "unknown(9)", + "packet", +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net.family]) { + audit_log_string(ab, address_family_names[sa->u.net.family]); + } else { + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); + } + + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad.net.type]) { + audit_log_string(ab, sock_type_names[sa->aad.net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); + } + + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + + sa.aad.op = op, + sa.u.net.family = family; + sa.u.net.sk = sk; + sa.aad.net.type = type; + sa.aad.net.protocol = protocol; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u16 audit_mask = profile->net.audit[sa.u.net.family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad.net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4f0eade..4d5ce13 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 741dd13..ee8043e 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -190,6 +190,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + size_t size = 0; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + } + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.3/0002-AppArmor-compatibility-patch-for-v5-interface.patch000066400000000000000000000260001522511161100306770ustar00rootroot00000000000000From da1ce2265ebb70860b9c137a542e48b170e4606b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:40 -0700 Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface Signed-off-by: John Johansen --- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 1 + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 18 +- security/apparmor/include/apparmorfs.h | 6 + 5 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/apparmorfs-24.c diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 7cefef9..0bb604b 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o sid.o file.o net.o +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o clean-files := capability_names.h rlim_names.h af_names.h diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c new file mode 100644 index 0000000..dc8c744 --- /dev/null +++ b/security/apparmor/apparmorfs-24.c @@ -0,0 +1,287 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * 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. + * + * + * This file contain functions providing an interface for <= AppArmor 2.4 + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 + * being set (see Makefile). + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + + +/* 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=crwxamlk/ " + "user::other"; + + return simple_read_from_buffer(buf, size, ppos, matching, + sizeof(matching) - 1); +} + +const struct file_operations aa_fs_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.1 capability=2.0 network=1.0 " + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; + + return simple_read_from_buffer(buf, size, ppos, features, + sizeof(features) - 1); +} + +const struct file_operations aa_fs_features_fops = { + .read = aa_features_read, +}; + +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index e39df6d..235e9fa 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + aafs_remove("profiles"); + aafs_remove("matching"); + aafs_remove("features"); +#endif securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -218,7 +222,17 @@ static int __init aa_create_aafs(void) aa_fs_dentry = NULL; goto error; } - +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + error = aafs_create("matching", 0444, &aa_fs_matching_fops); + if (error) + goto error; + error = aafs_create("features", 0444, &aa_fs_features_fops); + if (error) + goto error; +#endif + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); + if (error) + goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a..14f955c 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,4 +17,10 @@ extern void __init aa_destroy_aafs(void); +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +extern const struct file_operations aa_fs_matching_fops; +extern const struct file_operations aa_fs_features_fops; +extern const struct file_operations aa_fs_profiles_fops; +#endif + #endif /* __AA_APPARMORFS_H */ -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.3/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch000066400000000000000000000050521522511161100321660ustar00rootroot00000000000000From 5d05f2909c12f6f03581bca9c1fa52dafa10fb0f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Aug 2011 22:02:41 -0700 Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace The apparmor_parser when compiling policy could generate invalid dfas that did not have sufficient padding to avoid invalid references, when used by the kernel. The kernels check to verify the next/check table size was broken meaning invalid dfas were being created by userspace and not caught. To remain compatible with old tools that are not fixed, pad the loaded dfas next/check table. The dfa's themselves are valid except for the high padding for potentially invalid transitions (high bounds error), which have a maximimum is 256 entries. So just allocate an extra null filled 256 entries for the next/check tables. This will guarentee all bounds are good and invalid transitions go to the null (0) state. Signed-off-by: John Johansen --- security/apparmor/match.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 94de6b4..081491e 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; + /* Pad table allocation for next/check by 256 entries to remain + * backwards compatible with old (buggy) tools and remain safe without + * run time checks + */ + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) + tsize += 256 * th.td_flags; + table = kvmalloc(tsize); if (table) { + /* ensure the pad is clear, else there will be errors */ + memset(table, 0, tsize); *table = th; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, @@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) goto out; if (flags & DFA_FLAG_VERIFY_STATES) { + int warning = 0; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (warning) + continue; + printk(KERN_WARNING "AppArmor DFA next/check " + "upper bounds error fixed, upgrade " + "user space tools \n"); + warning = 1; + } else if (BASE_TABLE(dfa)[i] >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.4/000077500000000000000000000000001522511161100165435ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.4/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch000066400000000000000000000161321522511161100315030ustar00rootroot00000000000000From 8de755e4dfdbc40bfcaca848ae6b5aeaf0ede0e8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: Add profile introspection file to interface Add the dynamic profiles file to the interace, to allow load policy introspection. Signed-off-by: John Johansen Acked-by: Kees Cook Signed-off-by: Tim Gardner --- security/apparmor/apparmorfs.c | 227 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..89bdc62 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,6 +182,232 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; + /** Base file system setup **/ static struct aa_fs_entry aa_fs_entry_file[] = { @@ -210,6 +436,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), AA_FS_DIR("features", aa_fs_entry_features), { } }; -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.4/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000430661522511161100301470ustar00rootroot00000000000000From 423e2cb454d75d6185eecd0c1b5cf6ccc2d8482d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 2/3] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 2 +- security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 ++++++++++ 10 files changed, 414 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 4d995ae..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,6 +1,6 @@ # # Generated include files # -af_names.h +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 806bd19..19daa85 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ $(src)/Makefile @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 89bdc62..c66315d 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -427,6 +427,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 3868b1e..c1ff09c 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -126,6 +126,10 @@ struct apparmor_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569..eb13a73 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const profile_mode_names[]; @@ -157,6 +158,7 @@ struct aa_policydb { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -194,6 +196,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ad05d39..3cde194 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -622,6 +623,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -653,6 +752,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..084232b --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + COMMON_AUDIT_DATA_INIT(&sa, NET); + } else { + COMMON_AUDIT_DATA_INIT(&sa, NONE); + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f1f7506..b8100a7 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index deab7c7..8f8e9c1 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.4/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000647511522511161100314030ustar00rootroot00000000000000From a94d5e11c0484af59e5feebf144cc48c186892ad Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 ++++++++++++++++++++++++++++++++++ 10 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 19daa85..63e0a4c 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index c66315d..ff19009 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -424,10 +424,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index cc3520d..b9f5ee9 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6327685..dfdc47b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9..e243d96 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -29,8 +29,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index c1ff09c..7b90900c 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -73,6 +73,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -121,6 +125,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3cde194..4512cc6 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -512,6 +513,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -729,6 +784,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..63d8493 --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.7.9.5 apparmor-5.0.2/kernel-patches/3.5/000077500000000000000000000000001522511161100165445ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.5/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch000066400000000000000000000174461522511161100315150ustar00rootroot00000000000000From 05bf1eb7276886a3eda0588a8e012b558b693e96 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 1/6] UBUNTU: SAUCE: AppArmor: Add profile introspection file to interface Add the dynamic profiles file to the interace, to allow load policy introspection. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Kconfig | 9 ++ security/apparmor/apparmorfs.c | 231 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..42b7c9f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; +#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ + /** Base file system setup **/ static struct aa_fs_entry aa_fs_entry_file[] = { @@ -210,6 +438,9 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), +#endif AA_FS_DIR("features", aa_fs_entry_features), { } }; -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.5/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000430571522511161100301500ustar00rootroot00000000000000From 4facdf9db37c12ff655c91270d9030e2ed805ca2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 2/6] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 2 +- security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 ++++++++++ 10 files changed, 414 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 4d995ae..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,6 +1,6 @@ # # Generated include files # -af_names.h +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 806bd19..19daa85 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ $(src)/Makefile @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 42b7c9f..114fb23 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4b7e189..17734f9 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569..eb13a73 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const profile_mode_names[]; @@ -157,6 +158,7 @@ struct aa_policydb { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -194,6 +196,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8ea39aa..f628734 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -614,6 +615,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -646,6 +745,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index cf5fd22..27c8161 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 329b1fd..1b90dfa 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.5/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000030001522511161100321710ustar00rootroot00000000000000From 4b25e62dc1e8d81d80f778e1e57b7c38ba4fd901 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 3/6] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.5/0004-apparmor-Ensure-apparmor-does-not-mediate-kernel-bas.patch000066400000000000000000000064331522511161100322300ustar00rootroot00000000000000From e2d745442133f625e715f713c0441f0f2a7ea6ad Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:01 -0700 Subject: [PATCH 4/6] apparmor: Ensure apparmor does not mediate kernel based sockets Currently apparmor makes the assumption that kernel sockets are unmediated because mediation is only done against tasks that have a profile attached. Ensure we never get in a situation where a kernel socket is being mediated by tagging the sk_security field for kernel sockets. Signed-off-by: John Johansen --- security/apparmor/include/net.h | 2 ++ security/apparmor/lsm.c | 18 ++++++++++++++++++ security/apparmor/net.c | 3 +++ 3 files changed, 23 insertions(+) diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index cb8a121..bc8198b 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -19,6 +19,8 @@ #include "apparmorfs.h" +#define AA_SOCK_KERN 0xAA + /* struct aa_net - network confinement data * @allowed: basic network families permissions * @audit_network: which network permissions to force audit diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f628734..a172d01 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -630,6 +630,16 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern) return error; } +static int apparmor_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) +{ + if (kern) + /* tag kernel sockets so we don't mediate them later */ + sock->sk->sk_security = (void *) AA_SOCK_KERN; + + return 0; +} + static int apparmor_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { @@ -713,6 +723,12 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); } +static void apparmor_sk_clone_security(const struct sock *sk, + struct sock *newsk) +{ + newsk->sk_security = sk->sk_security; +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -746,6 +762,7 @@ static struct security_operations apparmor_ops = { .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, @@ -757,6 +774,7 @@ static struct security_operations apparmor_ops = { .socket_getsockopt = apparmor_socket_getsockopt, .socket_setsockopt = apparmor_socket_setsockopt, .socket_shutdown = apparmor_socket_shutdown, + .sk_clone_security = apparmor_sk_clone_security, .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 6e6e5c9..baa4df1 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -153,6 +153,9 @@ int aa_revalidate_sk(int op, struct sock *sk) if (in_interrupt()) return 0; + if (sk->sk_security == (void *) AA_SOCK_KERN) + return 0; + profile = __aa_current_profile(); if (!unconfined(profile)) error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.5/0005-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000647531522511161100314100ustar00rootroot00000000000000From 272431fc90fab50ea9593d969d3ab8d98f03627c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 5/6] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 ++++++++++++++++++++++++++++++++++ 10 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 19daa85..63e0a4c 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 114fb23..ee77ec9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3ae28db..e267963 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index b81ea10..afa8671 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9..e243d96 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -29,8 +29,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 17734f9..66a738c 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -73,6 +73,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -122,6 +126,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index a172d01..5da8af9 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -504,6 +505,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -737,6 +792,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..478aa4d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.5/0006-apparmor-fix-IRQ-stack-overflow-during-free_profile.patch000066400000000000000000000045771522511161100321140ustar00rootroot00000000000000From f58c91bc1871d604f88d0056099dc34f8ce3ae21 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 24 Oct 2012 06:27:32 -0700 Subject: [PATCH 6/6] apparmor: fix IRQ stack overflow during free_profile BugLink: http://bugs.launchpad.net/bugs/1056078 Profile replacement can cause long chains of profiles to build up when the profile being replaced is pinned. When the pinned profile is finally freed, it puts the reference to its replacement, which may in turn nest another call to free_profile on the stack. Because this may happen for each profile in the replacedby chain this can result in a recusion that causes the stack to overflow. Break this nesting by directly walking the chain of replacedby profiles (ie. use iteration instead of recursion to free the list). This results in at most 2 levels of free_profile being called, while freeing a replacedby chain. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/policy.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 27c8161..56e5304 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -724,6 +724,8 @@ fail: */ static void free_profile(struct aa_profile *profile) { + struct aa_profile *p; + AA_DEBUG("%s(%p)\n", __func__, profile); if (!profile) @@ -752,7 +754,27 @@ static void free_profile(struct aa_profile *profile) aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); - aa_put_profile(profile->replacedby); + /* put the profile reference for replacedby, but not via + * put_profile(kref_put). + * replacedby can form a long chain that can result in cascading + * frees that blows the stack because kref_put makes a nested fn + * call (it looks like recursion, with free_profile calling + * free_profile) for each profile in the chain lp#1056078. + */ + for (p = profile->replacedby; p; ) { + if (atomic_dec_and_test(&p->base.count.refcount)) { + /* no more refs on p, grab its replacedby */ + struct aa_profile *next = p->replacedby; + /* break the chain */ + p->replacedby = NULL; + /* now free p, chain is broken */ + free_profile(p); + + /* follow up with next profile in the chain */ + p = next; + } else + break; + } kzfree(profile); } -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.6/000077500000000000000000000000001522511161100165455ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.6/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch000066400000000000000000000174461522511161100315160ustar00rootroot00000000000000From 259cf7251194d81a4a3c4e6d76c2cf9e38d5647d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 1/6] UBUNTU: SAUCE: AppArmor: Add profile introspection file to interface Add the dynamic profiles file to the interace, to allow load policy introspection. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Kconfig | 9 ++ security/apparmor/apparmorfs.c | 231 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..42b7c9f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; +#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ + /** Base file system setup **/ static struct aa_fs_entry aa_fs_entry_file[] = { @@ -210,6 +438,9 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), +#endif AA_FS_DIR("features", aa_fs_entry_features), { } }; -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.6/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000430571522511161100301510ustar00rootroot00000000000000From 0317e6ba6aa4adc71f645b7da5318f4caa69267e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 2/6] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 2 +- security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 ++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 ++++++++++ 10 files changed, 414 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 4d995ae..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,6 +1,6 @@ # # Generated include files # -af_names.h +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 806bd19..19daa85 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ $(src)/Makefile @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 42b7c9f..114fb23 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4b7e189..17734f9 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { u32 denied; uid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569..eb13a73 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const profile_mode_names[]; @@ -157,6 +158,7 @@ struct aa_policydb { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -194,6 +196,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8ea39aa..f628734 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -614,6 +615,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -646,6 +745,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index cf5fd22..27c8161 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 329b1fd..1b90dfa 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.6/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000030001522511161100321720ustar00rootroot00000000000000From b1cb9d1b4f0d585c271c584da954d9eb2e347b40 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 3/6] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.6/0004-apparmor-Ensure-apparmor-does-not-mediate-kernel-bas.patch000066400000000000000000000064331522511161100322310ustar00rootroot00000000000000From f284c9554341aded2d599e9355574cac36c2dd23 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:01 -0700 Subject: [PATCH 4/6] apparmor: Ensure apparmor does not mediate kernel based sockets Currently apparmor makes the assumption that kernel sockets are unmediated because mediation is only done against tasks that have a profile attached. Ensure we never get in a situation where a kernel socket is being mediated by tagging the sk_security field for kernel sockets. Signed-off-by: John Johansen --- security/apparmor/include/net.h | 2 ++ security/apparmor/lsm.c | 18 ++++++++++++++++++ security/apparmor/net.c | 3 +++ 3 files changed, 23 insertions(+) diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index cb8a121..bc8198b 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -19,6 +19,8 @@ #include "apparmorfs.h" +#define AA_SOCK_KERN 0xAA + /* struct aa_net - network confinement data * @allowed: basic network families permissions * @audit_network: which network permissions to force audit diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f628734..a172d01 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -630,6 +630,16 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern) return error; } +static int apparmor_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) +{ + if (kern) + /* tag kernel sockets so we don't mediate them later */ + sock->sk->sk_security = (void *) AA_SOCK_KERN; + + return 0; +} + static int apparmor_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { @@ -713,6 +723,12 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); } +static void apparmor_sk_clone_security(const struct sock *sk, + struct sock *newsk) +{ + newsk->sk_security = sk->sk_security; +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -746,6 +762,7 @@ static struct security_operations apparmor_ops = { .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, @@ -757,6 +774,7 @@ static struct security_operations apparmor_ops = { .socket_getsockopt = apparmor_socket_getsockopt, .socket_setsockopt = apparmor_socket_setsockopt, .socket_shutdown = apparmor_socket_shutdown, + .sk_clone_security = apparmor_sk_clone_security, .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 6e6e5c9..baa4df1 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -153,6 +153,9 @@ int aa_revalidate_sk(int op, struct sock *sk) if (in_interrupt()) return 0; + if (sk->sk_security == (void *) AA_SOCK_KERN) + return 0; + profile = __aa_current_profile(); if (!unconfined(profile)) error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.6/0005-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000647531522511161100314110ustar00rootroot00000000000000From f5e962d77f98deab3461404567abd4759f5445a7 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 5/6] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 ++++++++++++++++++++++++++++++++++ 10 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 19daa85..63e0a4c 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 114fb23..ee77ec9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3ae28db..e267963 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index b81ea10..afa8671 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9..e243d96 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -29,8 +29,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 17734f9..66a738c 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -73,6 +73,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -122,6 +126,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index a172d01..5da8af9 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -504,6 +505,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -737,6 +792,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..478aa4d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.6/0006-apparmor-fix-IRQ-stack-overflow-during-free_profile.patch000066400000000000000000000045771522511161100321150ustar00rootroot00000000000000From 663d5bbe6197bf990721c37ec877ea8ba5840202 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 24 Oct 2012 06:27:32 -0700 Subject: [PATCH 6/6] apparmor: fix IRQ stack overflow during free_profile BugLink: http://bugs.launchpad.net/bugs/1056078 Profile replacement can cause long chains of profiles to build up when the profile being replaced is pinned. When the pinned profile is finally freed, it puts the reference to its replacement, which may in turn nest another call to free_profile on the stack. Because this may happen for each profile in the replacedby chain this can result in a recusion that causes the stack to overflow. Break this nesting by directly walking the chain of replacedby profiles (ie. use iteration instead of recursion to free the list). This results in at most 2 levels of free_profile being called, while freeing a replacedby chain. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/policy.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 27c8161..56e5304 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -724,6 +724,8 @@ fail: */ static void free_profile(struct aa_profile *profile) { + struct aa_profile *p; + AA_DEBUG("%s(%p)\n", __func__, profile); if (!profile) @@ -752,7 +754,27 @@ static void free_profile(struct aa_profile *profile) aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); - aa_put_profile(profile->replacedby); + /* put the profile reference for replacedby, but not via + * put_profile(kref_put). + * replacedby can form a long chain that can result in cascading + * frees that blows the stack because kref_put makes a nested fn + * call (it looks like recursion, with free_profile calling + * free_profile) for each profile in the chain lp#1056078. + */ + for (p = profile->replacedby; p; ) { + if (atomic_dec_and_test(&p->base.count.refcount)) { + /* no more refs on p, grab its replacedby */ + struct aa_profile *next = p->replacedby; + /* break the chain */ + p->replacedby = NULL; + /* now free p, chain is broken */ + free_profile(p); + + /* follow up with next profile in the chain */ + p = next; + } else + break; + } kzfree(profile); } -- 1.7.10.4 apparmor-5.0.2/kernel-patches/3.7/000077500000000000000000000000001522511161100165465ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.7/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch000066400000000000000000000174441522511161100315150ustar00rootroot00000000000000From f799dd0857774850de17901bf2f1bacd823036c4 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 1/4] UBUNTU: SAUCE: AppArmor: Add profile introspection file to interface Add the dynamic profiles file to the interace, to allow load policy introspection. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Kconfig | 9 ++ security/apparmor/apparmorfs.c | 231 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..42b7c9f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; +#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ + /** Base file system setup **/ static struct aa_fs_entry aa_fs_entry_file[] = { @@ -210,6 +438,9 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), +#endif AA_FS_DIR("features", aa_fs_entry_features), { } }; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.7/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000430511522511161100301440ustar00rootroot00000000000000From a1bfb457f0b8a5f5783bfc8efe127830f13a4c70 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 2/4] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5706b74..e270692 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 42b7c9f..114fb23 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 69d8cae..4af6523 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569..eb13a73 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const profile_mode_names[]; @@ -157,6 +158,7 @@ struct aa_policydb { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -194,6 +196,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8c2a7f6..dcb578e 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -614,6 +615,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -646,6 +745,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 8132003..56e5304 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -747,6 +747,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 329b1fd..1b90dfa 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.7/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322140ustar00rootroot00000000000000From 3ffe7266aea607c1cdad484b141c5b244e384de0 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 3/4] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.7/0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000647401522511161100314050ustar00rootroot00000000000000From 4c06a31907ae49bc33adb983946456cc2f9409ab Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 4/4] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ 10 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index e270692..9b44e1a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 114fb23..ee77ec9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3ae28db..e267963 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 60f0c76..4625a28 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9..e243d96 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -29,8 +29,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4af6523..ada004d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -73,6 +73,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -122,6 +126,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index dcb578e..1989066 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -504,6 +505,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -721,6 +776,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..478aa4d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.8/000077500000000000000000000000001522511161100165475ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.8/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch000066400000000000000000000174441522511161100315160ustar00rootroot00000000000000From 301639739c56c36bcbe90000934fedb416a65019 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 1/4] UBUNTU: SAUCE: AppArmor: Add profile introspection file to interface Add the dynamic profiles file to the interace, to allow load policy introspection. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Kconfig | 9 ++ security/apparmor/apparmorfs.c | 231 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..42b7c9f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; +#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ + /** Base file system setup **/ static struct aa_fs_entry aa_fs_entry_file[] = { @@ -210,6 +438,9 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), +#endif AA_FS_DIR("features", aa_fs_entry_features), { } }; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.8/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000430511522511161100301450ustar00rootroot00000000000000From 7e975d9b537f9aeadbf13bede10c6b5e5fb47a1d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 2/4] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5706b74..e270692 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 42b7c9f..114fb23 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 69d8cae..4af6523 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569..eb13a73 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const profile_mode_names[]; @@ -157,6 +158,7 @@ struct aa_policydb { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -194,6 +196,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8c2a7f6..dcb578e 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -614,6 +615,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -646,6 +745,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 8132003..56e5304 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -747,6 +747,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 329b1fd..1b90dfa 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.8/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322150ustar00rootroot00000000000000From c7208a008f899194674c77b424ea8386ec2fb413 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 3/4] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.8/0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000647401522511161100314060ustar00rootroot00000000000000From ab9e3accebaaf0a6d123d73bd7387d4b81df92ef Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 4/4] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ 10 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index e270692..9b44e1a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 114fb23..ee77ec9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3ae28db..e267963 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 60f0c76..4625a28 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9..e243d96 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -29,8 +29,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4af6523..ada004d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -73,6 +73,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -122,6 +126,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index dcb578e..1989066 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -504,6 +505,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -721,6 +776,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..478aa4d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.9/000077500000000000000000000000001522511161100165505ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/3.9/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch000066400000000000000000000174441522511161100315170ustar00rootroot00000000000000From 9b9e0d69288c5f83f1d8b3799d6163a7d97f8e5c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 22 Jul 2010 02:32:02 -0700 Subject: [PATCH 1/4] UBUNTU: SAUCE: AppArmor: Add profile introspection file to interface Add the dynamic profiles file to the interace, to allow load policy introspection. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Kconfig | 9 ++ security/apparmor/apparmorfs.c | 231 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b..51ebf96 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE boot. If you are unsure how to answer this question, answer 1. + +config SECURITY_APPARMOR_COMPAT_24 + bool "Enable AppArmor 2.4 compatability" + depends on SECURITY_APPARMOR + default y + help + This option enables compatability with AppArmor 2.4. It is + recommended if compatability with older versions of AppArmor + is desired. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..42b7c9f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 +/** + * __next_namespace - find the next namespace to list + * @root: root namespace to stop search at (NOT NULL) + * @ns: current ns position (NOT NULL) + * + * Find the next namespace from @ns under @root and handle all locking needed + * while switching current namespace. + * + * Returns: next namespace or NULL if at last namespace under @root + * NOTE: will not unlock root->lock + */ +static struct aa_namespace *__next_namespace(struct aa_namespace *root, + struct aa_namespace *ns) +{ + struct aa_namespace *parent; + + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + struct aa_namespace *next; + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); + read_lock(&next->lock); + return next; + } + + /* check if the next ns is a sibling, parent, gp, .. */ + parent = ns->parent; + while (parent) { + read_unlock(&ns->lock); + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { + read_lock(&ns->lock); + return ns; + } + if (parent == root) + return NULL; + ns = parent; + parent = parent->parent; + } + + return NULL; +} + +/** + * __first_profile - find the first profile in a namespace + * @root: namespace that is root of profiles being displayed (NOT NULL) + * @ns: namespace to start in (NOT NULL) + * + * Returns: unrefcounted profile or NULL if no profile + */ +static struct aa_profile *__first_profile(struct aa_namespace *root, + struct aa_namespace *ns) +{ + for ( ; ns; ns = __next_namespace(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, + struct aa_profile, base.list); + } + return NULL; +} + +/** + * __next_profile - step to the next profile in a profile tree + * @profile: current profile in tree (NOT NULL) + * + * Perform a depth first taversal on the profile tree in a namespace + * + * Returns: next profile or NULL if done + * Requires: profile->ns.lock to be held + */ +static struct aa_profile *__next_profile(struct aa_profile *p) +{ + struct aa_profile *parent; + struct aa_namespace *ns = p->ns; + + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), + base.list); + + /* is next profile a sibling, parent sibling, gp, subling, .. */ + parent = p->parent; + while (parent) { + list_for_each_entry_continue(p, &parent->base.profiles, + base.list) + return p; + p = parent; + parent = parent->parent; + } + + /* is next another profile in the namespace */ + list_for_each_entry_continue(p, &ns->base.profiles, base.list) + return p; + + return NULL; +} + +/** + * next_profile - step to the next profile in where ever it may be + * @root: root namespace (NOT NULL) + * @profile: current profile (NOT NULL) + * + * Returns: next profile or NULL if there isn't one + */ +static struct aa_profile *next_profile(struct aa_namespace *root, + struct aa_profile *profile) +{ + struct aa_profile *next = __next_profile(profile); + if (next) + return next; + + /* finished all profiles in namespace move to next namespace */ + return __first_profile(root, __next_namespace(root, profile->ns)); +} + +/** + * p_start - start a depth first traversal of profile tree + * @f: seq_file to fill + * @pos: current position + * + * Returns: first profile under current namespace or NULL if none found + * + * acquires first ns->lock + */ +static void *p_start(struct seq_file *f, loff_t *pos) + __acquires(root->lock) +{ + struct aa_profile *profile = NULL; + struct aa_namespace *root = aa_current_profile()->ns; + loff_t l = *pos; + f->private = aa_get_namespace(root); + + + /* find the first profile */ + read_lock(&root->lock); + profile = __first_profile(root, root); + + /* skip to position */ + for (; profile && l > 0; l--) + profile = next_profile(root, profile); + + return profile; +} + +/** + * p_next - read the next profile entry + * @f: seq_file to fill + * @p: profile previously returned + * @pos: current position + * + * Returns: next profile after @p or NULL if none + * + * may acquire/release locks in namespace tree as necessary + */ +static void *p_next(struct seq_file *f, void *p, loff_t *pos) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private; + (*pos)++; + + return next_profile(root, profile); +} + +/** + * p_stop - stop depth first traversal + * @f: seq_file we are filling + * @p: the last profile writen + * + * Release all locking done by p_start/p_next on namespace tree + */ +static void p_stop(struct seq_file *f, void *p) + __releases(root->lock) +{ + struct aa_profile *profile = p; + struct aa_namespace *root = f->private, *ns; + + if (profile) { + for (ns = profile->ns; ns && ns != root; ns = ns->parent) + read_unlock(&ns->lock); + } + read_unlock(&root->lock); + aa_put_namespace(root); +} + +/** + * seq_show_profile - show a profile entry + * @f: seq_file to file + * @p: current position (profile) (NOT NULL) + * + * Returns: error on failure + */ +static int seq_show_profile(struct seq_file *f, void *p) +{ + struct aa_profile *profile = (struct aa_profile *)p; + struct aa_namespace *root = f->private; + + if (profile->ns != root) + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, "%s (%s)\n", profile->base.hname, + COMPLAIN_MODE(profile) ? "complain" : "enforce"); + + return 0; +} + +static const struct seq_operations aa_fs_profiles_op = { + .start = p_start, + .next = p_next, + .stop = p_stop, + .show = seq_show_profile, +}; + +static int profiles_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &aa_fs_profiles_op); +} + +static int profiles_release(struct inode *inode, struct file *file) +{ + return seq_release(inode, file); +} + +const struct file_operations aa_fs_profiles_fops = { + .open = profiles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = profiles_release, +}; +#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ + /** Base file system setup **/ static struct aa_fs_entry aa_fs_entry_file[] = { @@ -210,6 +438,9 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), +#endif AA_FS_DIR("features", aa_fs_entry_features), { } }; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.9/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000430511522511161100301460ustar00rootroot00000000000000From b50585bdf248fa83c60cf5df33019e46b1051553 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 2/4] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5706b74..e270692 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 42b7c9f..114fb23 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 69d8cae..4af6523 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569..eb13a73 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const profile_mode_names[]; @@ -157,6 +158,7 @@ struct aa_policydb { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -194,6 +196,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b21830e..1bce440 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -614,6 +615,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -646,6 +745,19 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .socket_create = apparmor_socket_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, + .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 8132003..56e5304 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -747,6 +747,7 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 329b1fd..1b90dfa 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.9/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322160ustar00rootroot00000000000000From 0937b6c0fd45917de9debc8ec5be9cb1a447a6f9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 3/4] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 1.8.3.2 apparmor-5.0.2/kernel-patches/3.9/0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000647401522511161100314070ustar00rootroot00000000000000From 883574b10ad78766ca48a7eb11082dc21597c583 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 4/4] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ 10 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index e270692..9b44e1a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 114fb23..ee77ec9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3ae28db..e267963 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 859abda..3fee1fe 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9..e243d96 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -29,8 +29,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4af6523..ada004d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -73,6 +73,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -122,6 +126,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..bc17a53 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 1bce440..6750673 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -504,6 +505,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -721,6 +776,10 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_sb_umount, + .sb_pivotroot = apparmor_sb_pivotroot, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..478aa4d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, + struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 1.8.3.2 apparmor-5.0.2/kernel-patches/4.4/000077500000000000000000000000001522511161100165445ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/4.4/0001-apparmor-fix-oops-validate-buffer-size-in-apparmor_s.patch000066400000000000000000000066331522511161100323110ustar00rootroot00000000000000From 24b6ac149a57c2d3d5a9920e64d914e8ff00d346 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Thu, 7 Jul 2016 13:41:11 -0700 Subject: [PATCH 01/27] apparmor: fix oops, validate buffer size in apparmor_setprocattr() When proc_pid_attr_write() was changed to use memdup_user apparmor's (interface violating) assumption that the setprocattr buffer was always a single page was violated. The size test is not strictly speaking needed as proc_pid_attr_write() will reject anything larger, but for the sake of robustness we can keep it in. SMACK and SELinux look safe to me, but somebody else should probably have a look just in case. Based on original patch from Vegard Nossum modified for the case that apparmor provides null termination. Fixes: bb646cdb12e75d82258c2f2e7746d5952d3e321a Reported-by: Vegard Nossum Cc: Al Viro Cc: John Johansen Cc: Paul Moore Cc: Stephen Smalley Cc: Eric Paris Cc: Casey Schaufler Cc: stable@kernel.org Signed-off-by: John Johansen Reviewed-by: Tyler Hicks Signed-off-by: James Morris --- security/apparmor/lsm.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index dec607c..5ee8201 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -523,34 +523,34 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - char *command, *args = value; + char *command, *largs = NULL, *args = value; size_t arg_size; int error; if (size == 0) return -EINVAL; - /* args points to a PAGE_SIZE buffer, AppArmor requires that - * the buffer must be null terminated or have size <= PAGE_SIZE -1 - * so that AppArmor can null terminate them - */ - if (args[size - 1] != '\0') { - if (size == PAGE_SIZE) - return -EINVAL; - args[size] = '\0'; - } - /* task can only write its own attributes */ if (current != task) return -EACCES; - args = value; + /* AppArmor requires that the buffer must be null terminated atm */ + if (args[size - 1] != '\0') { + /* null terminate */ + largs = args = kmalloc(size + 1, GFP_KERNEL); + if (!args) + return -ENOMEM; + memcpy(args, value, size); + args[size] = '\0'; + } + + error = -EINVAL; args = strim(args); command = strsep(&args, " "); if (!args) - return -EINVAL; + goto out; args = skip_spaces(args); if (!*args) - return -EINVAL; + goto out; arg_size = size - (args - (char *) value); if (strcmp(name, "current") == 0) { @@ -576,10 +576,12 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, goto fail; } else /* only support the "current" and "exec" process attributes */ - return -EINVAL; + goto fail; if (!error) error = size; +out: + kfree(largs); return error; fail: @@ -588,9 +590,9 @@ fail: aad.profile = aa_current_profile(); aad.op = OP_SETPROCATTR; aad.info = name; - aad.error = -EINVAL; + aad.error = error = -EINVAL; aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); - return -EINVAL; + goto out; } static int apparmor_task_setrlimit(struct task_struct *task, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0002-apparmor-fix-refcount-bug-in-profile-replacement.patch000066400000000000000000000022361522511161100315110ustar00rootroot00000000000000From 444bc4f95ec283cd0fb9777f4890bd9bc307809d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:55:10 -0700 Subject: [PATCH 02/27] apparmor: fix refcount bug in profile replacement Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 705c287..222052f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1189,12 +1189,12 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) aa_get_profile(newest); aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); - } else - aa_put_profile(newest); + } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); __list_add_profile(&parent->base.profiles, ent->new); + aa_put_profile(newest); } else { /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0003-apparmor-fix-replacement-bug-that-adds-new-child-to-.patch000066400000000000000000000027261522511161100320450ustar00rootroot00000000000000From 1224a06778b89dcbf0ca85bd961c2fcdd8765a69 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:57:19 -0700 Subject: [PATCH 03/27] apparmor: fix replacement bug that adds new child to old parent When set atomic replacement is used and the parent is updated before the child, and the child did not exist in the old parent so there is no direct replacement then the new child is incorrectly added to the old parent. This results in the new parent not having the child(ren) that it should and the old parent when being destroyed asserting the following error. AppArmor: policy_destroy: internal error, policy '' still contains profiles Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 222052f..c92a9f6 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1193,7 +1193,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); - __list_add_profile(&parent->base.profiles, ent->new); + __list_add_profile(&newest->base.profiles, ent->new); aa_put_profile(newest); } else { /* aafs interface uses replacedby */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0004-apparmor-fix-uninitialized-lsm_audit-member.patch000066400000000000000000000100161522511161100306430ustar00rootroot00000000000000From 15d921647676fdc2c3ee1cf9aa8f578b1012ecff Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 8 Jun 2014 11:20:54 -0700 Subject: [PATCH 04/27] apparmor: fix uninitialized lsm_audit member BugLink: http://bugs.launchpad.net/bugs/1268727 The task field in the lsm_audit struct needs to be initialized if a change_hat fails, otherwise the following oops will occur BUG: unable to handle kernel paging request at 0000002fbead7d08 IP: [] _raw_spin_lock+0xe/0x50 PGD 1e3f35067 PUD 0 Oops: 0002 [#1] SMP Modules linked in: pppox crc_ccitt p8023 p8022 psnap llc ax25 btrfs raid6_pq xor xfs libcrc32c dm_multipath scsi_dh kvm_amd dcdbas kvm microcode amd64_edac_mod joydev edac_core psmouse edac_mce_amd serio_raw k10temp sp5100_tco i2c_piix4 ipmi_si ipmi_msghandler acpi_power_meter mac_hid lp parport hid_generic usbhid hid pata_acpi mpt2sas ahci raid_class pata_atiixp bnx2 libahci scsi_transport_sas [last unloaded: tipc] CPU: 2 PID: 699 Comm: changehat_twice Tainted: GF O 3.13.0-7-generic #25-Ubuntu Hardware name: Dell Inc. PowerEdge R415/08WNM9, BIOS 1.8.6 12/06/2011 task: ffff8802135c6000 ti: ffff880212986000 task.ti: ffff880212986000 RIP: 0010:[] [] _raw_spin_lock+0xe/0x50 RSP: 0018:ffff880212987b68 EFLAGS: 00010006 RAX: 0000000000020000 RBX: 0000002fbead7500 RCX: 0000000000000000 RDX: 0000000000000292 RSI: ffff880212987ba8 RDI: 0000002fbead7d08 RBP: ffff880212987b68 R08: 0000000000000246 R09: ffff880216e572a0 R10: ffffffff815fd677 R11: ffffea0008469580 R12: ffffffff8130966f R13: ffff880212987ba8 R14: 0000002fbead7d08 R15: ffff8800d8c6b830 FS: 00002b5e6c84e7c0(0000) GS:ffff880216e40000(0000) knlGS:0000000055731700 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000002fbead7d08 CR3: 000000021270f000 CR4: 00000000000006e0 Stack: ffff880212987b98 ffffffff81075f17 ffffffff8130966f 0000000000000009 0000000000000000 0000000000000000 ffff880212987bd0 ffffffff81075f7c 0000000000000292 ffff880212987c08 ffff8800d8c6b800 0000000000000026 Call Trace: [] __lock_task_sighand+0x47/0x80 [] ? apparmor_cred_prepare+0x2f/0x50 [] do_send_sig_info+0x2c/0x80 [] send_sig_info+0x1e/0x30 [] aa_audit+0x13d/0x190 [] aa_audit_file+0xbc/0x130 [] ? apparmor_cred_prepare+0x2f/0x50 [] aa_change_hat+0x202/0x530 [] aa_setprocattr_changehat+0x116/0x1d0 [] apparmor_setprocattr+0x25d/0x300 [] security_setprocattr+0x16/0x20 [] proc_pid_attr_write+0x107/0x130 [] vfs_write+0xb4/0x1f0 [] SyS_write+0x49/0xa0 [] tracesys+0xe1/0xe6 Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/audit.c | 3 ++- security/apparmor/file.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 89c7865..3a7f1da 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, if (sa->aad->type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, - sa->u.tsk ? sa->u.tsk : current); + sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? + sa->u.tsk : current); if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) return complain_error(sa->aad->error); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 913f377..43d6ae7 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; + sa.type = LSM_AUDIT_DATA_TASK; + sa.u.tsk = NULL; sa.aad = &aad; aad.op = op, aad.fs.request = request; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0005-apparmor-exec-should-not-be-returning-ENOENT-when-it.patch000066400000000000000000000021141522511161100317400ustar00rootroot00000000000000From c1216728b7d644443eef31e4bd9d01b4a0a51d61 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:03 -0700 Subject: [PATCH 05/27] apparmor: exec should not be returning ENOENT when it denies The current behavior is confusing as it causes exec failures to report the executable is missing instead of identifying that apparmor caused the failure. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index dc0027b..67a7418 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) new_profile = aa_get_newest_profile(ns->unconfined); info = "ux fallback"; } else { - error = -ENOENT; + error = -EACCES; info = "profile not found"; /* remove MAY_EXEC to audit as failure */ perms.allow &= ~MAY_EXEC; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0006-apparmor-fix-update-the-mtime-of-the-profile-file-on.patch000066400000000000000000000015741522511161100321000ustar00rootroot00000000000000From 2d3389de6c8ab6b3ad2cef4ea460c8fce2a226b9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:01:56 -0700 Subject: [PATCH 06/27] apparmor: fix update the mtime of the profile file on replacement Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ad4fa49..45a6199 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -379,6 +379,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, for (i = 0; i < AAFS_PROF_SIZEOF; i++) { new->dents[i] = old->dents[i]; + if (new->dents[i]) + new->dents[i]->d_inode->i_mtime = CURRENT_TIME; old->dents[i] = NULL; } } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0007-apparmor-fix-disconnected-bind-mnts-reconnection.patch000066400000000000000000000020731522511161100315750ustar00rootroot00000000000000From 9caa96e30a1b2bb191a29af872285c8d0b078c10 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:08 -0700 Subject: [PATCH 07/27] apparmor: fix disconnected bind mnts reconnection Bind mounts can fail to be properly reconnected when PATH_CONNECT is specified. Ensure that when PATH_CONNECT is specified the path has a root. BugLink: http://bugs.launchpad.net/bugs/1319984 Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/path.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 71e0e3a..bb2f2c6 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -141,7 +141,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, error = -EACCES; if (*res == '/') *name = res + 1; - } + } else if (*res != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + } out: -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0008-apparmor-internal-paths-should-be-treated-as-disconn.patch000066400000000000000000000067631522511161100323000ustar00rootroot00000000000000From 11702a732e149380e05e2ab8ae1b743ac89f892f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:10 -0700 Subject: [PATCH 08/27] apparmor: internal paths should be treated as disconnected Internal mounts are not mounted anywhere and as such should be treated as disconnected paths. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/path.c | 64 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index bb2f2c6..596f799 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -25,7 +25,6 @@ #include "include/path.h" #include "include/policy.h" - /* modified from dcache.c */ static int prepend(char **buffer, int buflen, const char *str, int namelen) { @@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) +/* If the path is not connected to the expected root, + * check if it is a sysctl and handle specially else remove any + * leading / that __d_path may have returned. + * Unless + * specifically directed to connect the path, + * OR + * if in a chroot and doing chroot relative paths and the path + * resolves to the namespace root (would be connected outside + * of chroot) and specifically directed to connect paths to + * namespace root. + */ +static int disconnect(const struct path *path, char *buf, char **name, + int flags) +{ + int error = 0; + + if (!(flags & PATH_CONNECT_PATH) && + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && + our_mnt(path->mnt))) { + /* disconnected path, don't return pathname starting + * with '/' + */ + error = -EACCES; + if (**name == '/') + *name = *name + 1; + } else if (**name != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + + return error; +} + /** * d_namespace_path - lookup a name associated with a given path * @path: path to lookup (NOT NULL) @@ -74,7 +105,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, * control instead of hard coded /proc */ return prepend(name, *name - buf, "/proc", 5); - } + } else + return disconnect(path, buf, name, flags); return 0; } @@ -120,32 +152,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, goto out; } - /* If the path is not connected to the expected root, - * check if it is a sysctl and handle specially else remove any - * leading / that __d_path may have returned. - * Unless - * specifically directed to connect the path, - * OR - * if in a chroot and doing chroot relative paths and the path - * resolves to the namespace root (would be connected outside - * of chroot) and specifically directed to connect paths to - * namespace root. - */ - if (!connected) { - if (!(flags & PATH_CONNECT_PATH) && - !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && - our_mnt(path->mnt))) { - /* disconnected path, don't return pathname starting - * with '/' - */ - error = -EACCES; - if (*res == '/') - *name = res + 1; - } else if (*res != '/') - /* CONNECT_PATH with missing root */ - error = prepend(name, *name - buf, "/", 1); - - } + if (!connected) + error = disconnect(path, buf, name, flags); out: return error; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0009-apparmor-fix-put-parent-ref-after-updating-the-activ.patch000066400000000000000000000017761522511161100322340ustar00rootroot00000000000000From c70811d9e6234c96d0ef405cd8ad78b70efb8637 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 13:59:02 -0700 Subject: [PATCH 09/27] apparmor: fix put() parent ref after updating the active ref Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c92a9f6..455c9f8 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1187,8 +1187,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* parent replaced in this atomic set? */ if (newest != parent) { aa_get_profile(newest); - aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); + aa_put_profile(parent); } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0010-apparmor-fix-log-failures-for-all-profiles-in-a-set.patch000066400000000000000000000056761522511161100317430ustar00rootroot00000000000000From f671b902943f83f0fbc8c8b7bf8bbfb817d124f1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:16:50 -0700 Subject: [PATCH 10/27] apparmor: fix log failures for all profiles in a set currently only the profile that is causing the failure is logged. This makes it more confusing than necessary about which profiles loaded and which didn't. So make sure to log success and failure messages for all profiles in the set being loaded. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 455c9f8..db31bc5 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1067,7 +1067,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, */ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) { - const char *ns_name, *name = NULL, *info = NULL; + const char *ns_name, *info = NULL; struct aa_namespace *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; @@ -1082,18 +1082,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* released below */ ns = aa_prepare_namespace(ns_name); if (!ns) { - info = "failed to prepare namespace"; - error = -ENOMEM; - name = ns_name; - goto fail; + error = audit_policy(op, GFP_KERNEL, ns_name, + "failed to prepare namespace", -ENOMEM); + goto free; } mutex_lock(&ns->lock); /* setup parent and ns info */ list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; - - name = ent->new->base.hname; error = __lookup_replace(ns, ent->new->base.hname, noreplace, &ent->old, &info); if (error) @@ -1121,7 +1118,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) if (!p) { error = -ENOENT; info = "parent does not exist"; - name = ent->new->base.hname; goto fail_lock; } rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); @@ -1214,9 +1210,22 @@ out: fail_lock: mutex_unlock(&ns->lock); -fail: - error = audit_policy(op, GFP_KERNEL, name, info, error); + /* audit cause of failure */ + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); + /* audit status that rest of profiles in the atomic set failed too */ + info = "valid profile in failed atomic policy load"; + list_for_each_entry(tmp, &lh, list) { + if (tmp == ent) { + info = "unchecked profile in failed atomic policy load"; + /* skip entry that caused failure */ + continue; + } + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); + } +free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0011-apparmor-fix-audit-full-profile-hname-on-successful-.patch000066400000000000000000000023421522511161100322060ustar00rootroot00000000000000From bc3c7d342bf53afdfdf46bc92dac5c624c89fb91 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:19:38 -0700 Subject: [PATCH 11/27] apparmor: fix audit full profile hname on successful load Currently logging of a successful profile load only logs the basename of the profile. This can result in confusion when a child profile has the same name as the another profile in the set. Logging the hname will ensure there is no confusion. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index db31bc5..ca402d0 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1159,7 +1159,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); + audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0012-apparmor-ensure-the-target-profile-name-is-always-au.patch000066400000000000000000000065351522511161100322260ustar00rootroot00000000000000From 848da0479e5b9da3dc2ae4c64e0cca77a0abf02a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 20 Apr 2016 14:18:18 -0700 Subject: [PATCH 12/27] apparmor: ensure the target profile name is always audited The target profile name was not being correctly audited in a few cases because the target variable was not being set and gotos passed the code to set it at apply: Since it is always based on new_profile just drop the target var and conditionally report based on new_profile. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 67a7418..fc3036b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) file_inode(bprm->file)->i_uid, file_inode(bprm->file)->i_mode }; - const char *name = NULL, *target = NULL, *info = NULL; + const char *name = NULL, *info = NULL; int error = 0; if (bprm->cred_prepared) @@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (cxt->onexec) { struct file_perms cp; info = "change_profile onexec"; + new_profile = aa_get_newest_profile(cxt->onexec); if (!(perms.allow & AA_MAY_ONEXEC)) goto audit; @@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!(cp.allow & AA_MAY_ONEXEC)) goto audit; - new_profile = aa_get_newest_profile(cxt->onexec); goto apply; } @@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!new_profile) { error = -ENOMEM; info = "could not create null profile"; - } else { + } else error = -EACCES; - target = new_profile->base.hname; - } perms.xindex |= AA_X_UNSAFE; } else /* fail exec */ @@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * fail the exec. */ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) { - aa_put_profile(new_profile); error = -EPERM; goto cleanup; } @@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { error = may_change_ptraced_domain(new_profile); - if (error) { - aa_put_profile(new_profile); + if (error) goto audit; - } } /* Determine if secure exec is needed. @@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) bprm->unsafe |= AA_SECURE_X_NEEDED; } apply: - target = new_profile->base.hname; /* when transitioning profiles clear unsafe personality bits */ bprm->per_clear |= PER_CLEAR_ON_SETID; @@ -506,15 +500,19 @@ x_clear: aa_put_profile(cxt->profile); /* transfer new profile reference will be released when cxt is freed */ cxt->profile = new_profile; + new_profile = NULL; /* clear out all temporary/transitional state from the context */ aa_clear_task_cxt_trans(cxt); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, - name, target, cond.uid, info, error); + name, + new_profile ? new_profile->base.hname : NULL, + cond.uid, info, error); cleanup: + aa_put_profile(new_profile); aa_put_profile(profile); kfree(buffer); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0013-apparmor-check-that-xindex-is-in-trans_table-bounds.patch000066400000000000000000000016471522511161100321060ustar00rootroot00000000000000From 706473f3ead5cdffe5ad159adfbc090e0fda81d6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 17 Mar 2016 12:02:54 -0700 Subject: [PATCH 13/27] apparmor: check that xindex is in trans_table bounds Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a689f10..c841b12 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -676,7 +676,7 @@ static bool verify_xindex(int xindex, int table_size) int index, xtype; xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; - if (xtype == AA_X_TABLE && index > table_size) + if (xtype == AA_X_TABLE && index >= table_size) return 0; return 1; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0014-apparmor-fix-ref-count-leak-when-profile-sha1-hash-i.patch000066400000000000000000000014651522511161100317720ustar00rootroot00000000000000From 05a64c434466029b298ee1e78a988cd6a7f80c0e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 18 Nov 2015 11:41:05 -0800 Subject: [PATCH 14/27] apparmor: fix ref count leak when profile sha1 hash is read Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 45a6199..0d8dd71 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) seq_printf(seq, "%.2x", profile->hash[i]); seq_puts(seq, "\n"); } + aa_put_profile(profile); return 0; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0015-apparmor-fix-refcount-race-when-finding-a-child-prof.patch000066400000000000000000000024201522511161100321240ustar00rootroot00000000000000From 6b0b8b91f454bd021e27abe0e611a6764e4806c1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Dec 2015 18:09:10 -0800 Subject: [PATCH 15/27] apparmor: fix refcount race when finding a child profile When finding a child profile via an rcu critical section, the profile may be put and scheduled for deletion after the child is found but before its refcount is incremented. Protect against this by repeating the lookup if the profiles refcount is 0 and is one its way to deletion. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ca402d0..7807125 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) struct aa_profile *profile; rcu_read_lock(); - profile = aa_get_profile(__find_child(&parent->base.profiles, name)); + do { + profile = __find_child(&parent->base.profiles, name); + } while (profile && !aa_get_profile_not0(profile)); rcu_read_unlock(); /* refcount released by caller */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0016-apparmor-use-list_next_entry-instead-of-list_entry_n.patch000066400000000000000000000036771522511161100325670ustar00rootroot00000000000000From 84acc6aa6976e62756e14d3a00c5634724cbaa59 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 16 Nov 2015 21:46:33 +0800 Subject: [PATCH 16/27] apparmor: use list_next_entry instead of list_entry_next list_next_entry has been defined in list.h, so I replace list_entry_next with it. Signed-off-by: Geliang Tang Acked-by: Serge Hallyn Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0d8dd71..729e595 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -553,8 +553,6 @@ fail2: } -#define list_entry_next(pos, member) \ - list_entry(pos->member.next, typeof(*pos), member) #define list_entry_is_head(pos, head, member) (&pos->member == (head)) /** @@ -585,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, parent = ns->parent; while (ns != root) { mutex_unlock(&ns->lock); - next = list_entry_next(ns, base.list); + next = list_next_entry(ns, base.list); if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { mutex_lock(&next->lock); return next; @@ -639,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) parent = rcu_dereference_protected(p->parent, mutex_is_locked(&p->ns->lock)); while (parent) { - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &parent->base.profiles, base.list)) return p; p = parent; @@ -648,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) } /* is next another profile in the namespace */ - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &ns->base.profiles, base.list)) return p; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0017-apparmor-allow-SYS_CAP_RESOURCE-to-be-sufficient-to-.patch000066400000000000000000000037441522511161100314230ustar00rootroot00000000000000From a3896605318b86d8cf288c122e03604e349d5dd7 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 6 Nov 2015 15:17:30 -0500 Subject: [PATCH 17/27] apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task While using AppArmor, SYS_CAP_RESOURCE is insufficient to call prlimit on another task. The only other example of a AppArmor mediating access to another, already running, task (ignoring fork+exec) is ptrace. The AppArmor model for ptrace is that one of the following must be true: 1) The tracer is unconfined 2) The tracer is in complain mode 3) The tracer and tracee are confined by the same profile 4) The tracer is confined but has SYS_CAP_PTRACE 1), 2, and 3) are already true for setrlimit. We can match the ptrace model just by allowing CAP_SYS_RESOURCE. We still test the values of the rlimit since it can always be overridden using a value that means unlimited for a particular resource. Signed-off-by: Jeff Mahoney Signed-off-by: John Johansen --- security/apparmor/resource.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 748bf0c..67a6072 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, /* TODO: extend resource control to handle other (non current) * profiles. AppArmor rules currently have the implicit assumption * that the task is setting the resource of a task confined with - * the same profile. + * the same profile or that the task setting the resource of another + * task has CAP_SYS_RESOURCE. */ - if (profile != task_profile || + if ((profile != task_profile && + aa_capable(profile, CAP_SYS_RESOURCE, 1)) || (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) error = -EACCES; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0018-apparmor-add-missing-id-bounds-check-on-dfa-verifica.patch000066400000000000000000000024741522511161100320650ustar00rootroot00000000000000From 6fdcc3cfecd4d89457036627d59ebe5154d094c5 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 2 Jun 2016 02:37:02 -0700 Subject: [PATCH 18/27] apparmor: add missing id bounds check on dfa verification Signed-off-by: John Johansen --- security/apparmor/include/match.h | 1 + security/apparmor/match.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 001c43a..a1c04fe 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -62,6 +62,7 @@ struct table_set_header { #define YYTD_ID_ACCEPT2 6 #define YYTD_ID_NXT 7 #define YYTD_ID_TSIZE 8 +#define YYTD_ID_MAX 8 #define YYTD_DATA8 1 #define YYTD_DATA16 2 diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 727eb42..f9f57c6 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize) * it every time we use td_id as an index */ th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; + if (th.td_id > YYTD_ID_MAX) + goto out; th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); blob += sizeof(struct table_header); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0019-apparmor-don-t-check-for-vmalloc_addr-if-kvzalloc-fa.patch000066400000000000000000000021421522511161100321020ustar00rootroot00000000000000From 95d203cfb59627a86483a279ba82f1aa75297e07 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 09:57:55 +0300 Subject: [PATCH 19/27] apparmor: don't check for vmalloc_addr if kvzalloc() failed Signed-off-by: John Johansen --- security/apparmor/match.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index f9f57c6..32b72eb 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -75,14 +75,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize) u32, be32_to_cpu); else goto fail; + /* if table was vmalloced make sure the page tables are synced + * before it is used, as it goes live to all cpus. + */ + if (is_vmalloc_addr(table)) + vm_unmap_aliases(); } out: - /* if table was vmalloced make sure the page tables are synced - * before it is used, as it goes live to all cpus. - */ - if (is_vmalloc_addr(table)) - vm_unmap_aliases(); return table; fail: kvfree(table); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0020-apparmor-fix-oops-in-profile_unpack-when-policy_db-i.patch000066400000000000000000000021311522511161100322560ustar00rootroot00000000000000From e925f976c7a9c85455f67c360671254bac2d9a91 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 10:00:55 +0300 Subject: [PATCH 20/27] apparmor: fix oops in profile_unpack() when policy_db is not present BugLink: http://bugs.launchpad.net/bugs/1592547 If unpack_dfa() returns NULL due to the dfa not being present, profile_unpack() is not checking if the dfa is not present (NULL). Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c841b12..dac2121 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) error = PTR_ERR(profile->policy.dfa); profile->policy.dfa = NULL; goto fail; + } else if (!profile->policy.dfa) { + error = -EPROTO; + goto fail; } if (!unpack_u32(e, &profile->policy.start[0], "start")) /* default start state */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0021-apparmor-fix-module-parameters-can-be-changed-after-.patch000066400000000000000000000112361522511161100320670ustar00rootroot00000000000000From 45774028820fe2ffbbc94667165f04749821d529 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Jun 2016 18:01:08 -0700 Subject: [PATCH 21/27] apparmor: fix module parameters can be changed after policy is locked the policy_lock parameter is a one way switch that prevents policy from being further modified. Unfortunately some of the module parameters can effectively modify policy by turning off enforcement. split policy_admin_capable into a view check and a full admin check, and update the admin check to test the policy_lock parameter. Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 ++ security/apparmor/lsm.c | 22 ++++++++++------------ security/apparmor/policy.c | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index c28b0f2..52275f0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile) return profile->audit; } +bool policy_view_capable(void); +bool policy_admin_capable(void); bool aa_may_manage_policy(int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 5ee8201..bd40b12 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -751,51 +751,49 @@ __setup("apparmor=", apparmor_enabled_setup); /* set global flag turning off the ability to load policy */ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; - if (aa_g_lock_policy) - return -EACCES; return param_set_bool(val, kp); } static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aabool(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_bool(val, kp); } static int param_get_aabool(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aauint(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_uint(val, kp); } static int param_get_aauint(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_uint(buffer, kp); } static int param_get_audit(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; if (!apparmor_enabled) @@ -807,7 +805,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) static int param_set_audit(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -828,7 +826,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -840,7 +838,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7807125..179e68d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -918,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, &sa, NULL); } +bool policy_view_capable(void) +{ + struct user_namespace *user_ns = current_user_ns(); + bool response = false; + + if (ns_capable(user_ns, CAP_MAC_ADMIN)) + response = true; + + return response; +} + +bool policy_admin_capable(void) +{ + return policy_view_capable() && !aa_g_lock_policy; +} + /** * aa_may_manage_policy - can the current task manage policy * @op: the policy manipulation operation being done @@ -932,7 +948,7 @@ bool aa_may_manage_policy(int op) return 0; } - if (!capable(CAP_MAC_ADMIN)) { + if (!policy_admin_capable()) { audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); return 0; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0022-apparmor-do-not-expose-kernel-stack.patch000066400000000000000000000017571522511161100270610ustar00rootroot00000000000000From 7fcfc22cd04261ac35a579c99bcc804db7eb3e83 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 10 Jun 2016 23:34:26 +0200 Subject: [PATCH 22/27] apparmor: do not expose kernel stack Do not copy uninitalized fields th.td_hilen, th.td_data. Signed-off-by: Heinrich Schuchardt Signed-off-by: John Johansen --- security/apparmor/match.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 32b72eb..3f900fc 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -63,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) table = kvzalloc(tsize); if (table) { - *table = th; + table->td_id = th.td_id; + table->td_flags = th.td_flags; + table->td_lolen = th.td_lolen; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, u8, byte_to_byte); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0023-apparmor-fix-arg_size-computation-for-when-setprocat.patch000066400000000000000000000016371522511161100324510ustar00rootroot00000000000000From 1b98560066c26fecb0a61aeb9249e141af2e63f9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 9 Jul 2016 23:46:33 -0700 Subject: [PATCH 23/27] apparmor: fix arg_size computation for when setprocattr is null terminated Signed-off-by: John Johansen --- security/apparmor/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index bd40b12..1bf6c53 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -552,7 +552,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, if (!*args) goto out; - arg_size = size - (args - (char *) value); + arg_size = size - (args - (largs ? largs : (char *) value)); if (strcmp(name, "current") == 0) { if (strcmp(command, "changehat") == 0) { error = aa_setprocattr_changehat(args, arg_size, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0024-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000437201522511161100301510ustar00rootroot00000000000000From 8d7c032e7798fa1c46449728874b64fff882368b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 24/27] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df8..5dbb72f 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 729e595..181d961 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -807,6 +807,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index ba3dfd1..5d3c419 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -125,6 +125,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 52275f0..4fc4dac 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const aa_profile_mode_names[]; @@ -176,6 +177,7 @@ struct aa_replacedby { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -217,6 +219,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; unsigned char *hash; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 1bf6c53..284ddda 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -607,6 +608,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -636,6 +735,19 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 179e68d..f1a8541 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index dac2121..0107bc4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0025-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322160ustar00rootroot00000000000000From aa45ba104003404efb59e6f7178045ade756035d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 25/27] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0026-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000714271522511161100314070ustar00rootroot00000000000000From da5b036d6235f44c4ccbeaaf8d46fb29ff22745f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 26/27] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 15 +- security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/include/path.h | 2 +- security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ security/apparmor/path.c | 8 +- 12 files changed, 773 insertions(+), 9 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5dbb72f..89b3445 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 181d961..5fb67f6 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -800,7 +800,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_policy[] = { AA_FS_FILE_BOOLEAN("set_load", 1), - {} + { } +}; + +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } }; static struct aa_fs_entry aa_fs_entry_features[] = { @@ -808,6 +819,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3a7f1da..c2a8b8a 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index fc3036b..f2a83b4 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -236,7 +236,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index e4ea626..ce6ff6a 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -30,8 +30,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 5d3c419..b9f1d57 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -72,6 +72,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -120,6 +124,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..a43b1d6 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 286ac75..73560f2 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -26,7 +26,7 @@ enum path_flags { PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ }; -int aa_path_name(struct path *path, int flags, char **buffer, +int aa_path_name(const struct path *path, int flags, char **buffer, const char **name, const char **info); #endif /* __AA_PATH_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 284ddda..780fec9 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -492,6 +493,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(const char *dev_name, struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -712,6 +767,10 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(capget, apparmor_capget), LSM_HOOK_INIT(capable, apparmor_capable), + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), + LSM_HOOK_INIT(path_link, apparmor_path_link), LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..9cf9170 --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, const struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 596f799..a8fc7d0 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -84,7 +84,7 @@ static int disconnect(const struct path *path, char *buf, char **name, * When no error the path name is returned in @name which points to * to a position in @buf */ -static int d_namespace_path(struct path *path, char *buf, int buflen, +static int d_namespace_path(const struct path *path, char *buf, int buflen, char **name, int flags) { char *res; @@ -169,7 +169,7 @@ out: * * Returns: %0 else error on failure */ -static int get_name_to_buffer(struct path *path, int flags, char *buffer, +static int get_name_to_buffer(const struct path *path, int flags, char *buffer, int size, char **name, const char **info) { int adjust = (flags & PATH_IS_DIR) ? 1 : 0; @@ -215,8 +215,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, * * Returns: %0 else error code if could retrieve name */ -int aa_path_name(struct path *path, int flags, char **buffer, const char **name, - const char **info) +int aa_path_name(const struct path *path, int flags, char **buffer, + const char **name, const char **info) { char *buf, *str = NULL; int size = 256; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.4/0027-UBUNTU-SAUCE-AppArmor-fix-boolreturn.cocci-warnings.patch000066400000000000000000000122451522511161100314400ustar00rootroot00000000000000From 1eff686074a6af0cf47fc24c45ebb001c570a98b Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 29 Jul 2016 12:44:43 +0800 Subject: [PATCH 27/27] UBUNTU: SAUCE: AppArmor: fix boolreturn.cocci warnings security/apparmor/policy_unpack.c:143:9-10: WARNING: return of 0/1 in function 'unpack_X' with return type bool security/apparmor/policy_unpack.c:189:9-10: WARNING: return of 0/1 in function 'unpack_nameX' with return type bool security/apparmor/policy_unpack.c:475:8-9: WARNING: return of 0/1 in function 'unpack_rlimits' with return type bool security/apparmor/policy_unpack.c:440:8-9: WARNING: return of 0/1 in function 'unpack_trans_table' with return type bool security/apparmor/policy_unpack.c:200:10-11: WARNING: return of 0/1 in function 'unpack_u16' with return type bool security/apparmor/policy_unpack.c:213:10-11: WARNING: return of 0/1 in function 'unpack_u32' with return type bool security/apparmor/policy_unpack.c:226:10-11: WARNING: return of 0/1 in function 'unpack_u64' with return type bool security/apparmor/policy_unpack.c:325:10-11: WARNING: return of 0/1 in function 'verify_accept' with return type bool security/apparmor/policy_unpack.c:739:10-11: WARNING: return of 0/1 in function 'verify_dfa_xindex' with return type bool security/apparmor/policy_unpack.c:729:9-10: WARNING: return of 0/1 in function 'verify_xindex' with return type bool Return statements in functions returning bool should use true/false instead of 1/0. Generated by: scripts/coccinelle/misc/boolreturn.cocci Signed-off-by: Fengguang Wu Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 0107bc4..af14626 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -140,11 +140,11 @@ static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) static bool unpack_X(struct aa_ext *e, enum aa_code code) { if (!inbounds(e, 1)) - return 0; + return false; if (*(u8 *) e->pos != code) - return 0; + return false; e->pos++; - return 1; + return true; } /** @@ -186,50 +186,50 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) /* now check if type code matches */ if (unpack_X(e, code)) - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) { if (unpack_nameX(e, AA_U16, name)) { if (!inbounds(e, sizeof(u16))) - return 0; + return false; if (data) *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); e->pos += sizeof(u16); - return 1; + return true; } - return 0; + return false; } static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { if (!inbounds(e, sizeof(u32))) - return 0; + return false; if (data) *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); e->pos += sizeof(u32); - return 1; + return true; } - return 0; + return false; } static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) { if (unpack_nameX(e, AA_U64, name)) { if (!inbounds(e, sizeof(u64))) - return 0; + return false; if (data) *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); e->pos += sizeof(u64); - return 1; + return true; } - return 0; + return false; } static size_t unpack_array(struct aa_ext *e, const char *name) @@ -322,12 +322,12 @@ static bool verify_accept(struct aa_dfa *dfa, int flags) int mode = ACCEPT_TABLE(dfa)[i]; if (mode & ~DFA_VALID_PERM_MASK) - return 0; + return false; if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) - return 0; + return false; } - return 1; + return true; } /** @@ -437,12 +437,12 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } - return 1; + return true; fail: aa_free_domain_entries(&profile->file.trans); e->pos = pos; - return 0; + return false; } static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) @@ -472,11 +472,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } /** @@ -726,8 +726,8 @@ static bool verify_xindex(int xindex, int table_size) xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; if (xtype == AA_X_TABLE && index >= table_size) - return 0; - return 1; + return false; + return true; } /* verify dfa xindexes are in range of transition tables */ @@ -736,11 +736,11 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) int i; for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) - return 0; + return false; if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) - return 0; + return false; } - return 1; + return true; } /** -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/000077500000000000000000000000001522511161100165455ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/4.5/0001-apparmor-fix-oops-validate-buffer-size-in-apparmor_s.patch000066400000000000000000000066331522511161100323120ustar00rootroot00000000000000From 24b6ac149a57c2d3d5a9920e64d914e8ff00d346 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Thu, 7 Jul 2016 13:41:11 -0700 Subject: [PATCH 01/27] apparmor: fix oops, validate buffer size in apparmor_setprocattr() When proc_pid_attr_write() was changed to use memdup_user apparmor's (interface violating) assumption that the setprocattr buffer was always a single page was violated. The size test is not strictly speaking needed as proc_pid_attr_write() will reject anything larger, but for the sake of robustness we can keep it in. SMACK and SELinux look safe to me, but somebody else should probably have a look just in case. Based on original patch from Vegard Nossum modified for the case that apparmor provides null termination. Fixes: bb646cdb12e75d82258c2f2e7746d5952d3e321a Reported-by: Vegard Nossum Cc: Al Viro Cc: John Johansen Cc: Paul Moore Cc: Stephen Smalley Cc: Eric Paris Cc: Casey Schaufler Cc: stable@kernel.org Signed-off-by: John Johansen Reviewed-by: Tyler Hicks Signed-off-by: James Morris --- security/apparmor/lsm.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index dec607c..5ee8201 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -523,34 +523,34 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - char *command, *args = value; + char *command, *largs = NULL, *args = value; size_t arg_size; int error; if (size == 0) return -EINVAL; - /* args points to a PAGE_SIZE buffer, AppArmor requires that - * the buffer must be null terminated or have size <= PAGE_SIZE -1 - * so that AppArmor can null terminate them - */ - if (args[size - 1] != '\0') { - if (size == PAGE_SIZE) - return -EINVAL; - args[size] = '\0'; - } - /* task can only write its own attributes */ if (current != task) return -EACCES; - args = value; + /* AppArmor requires that the buffer must be null terminated atm */ + if (args[size - 1] != '\0') { + /* null terminate */ + largs = args = kmalloc(size + 1, GFP_KERNEL); + if (!args) + return -ENOMEM; + memcpy(args, value, size); + args[size] = '\0'; + } + + error = -EINVAL; args = strim(args); command = strsep(&args, " "); if (!args) - return -EINVAL; + goto out; args = skip_spaces(args); if (!*args) - return -EINVAL; + goto out; arg_size = size - (args - (char *) value); if (strcmp(name, "current") == 0) { @@ -576,10 +576,12 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, goto fail; } else /* only support the "current" and "exec" process attributes */ - return -EINVAL; + goto fail; if (!error) error = size; +out: + kfree(largs); return error; fail: @@ -588,9 +590,9 @@ fail: aad.profile = aa_current_profile(); aad.op = OP_SETPROCATTR; aad.info = name; - aad.error = -EINVAL; + aad.error = error = -EINVAL; aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); - return -EINVAL; + goto out; } static int apparmor_task_setrlimit(struct task_struct *task, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0002-apparmor-fix-refcount-bug-in-profile-replacement.patch000066400000000000000000000022361522511161100315120ustar00rootroot00000000000000From 444bc4f95ec283cd0fb9777f4890bd9bc307809d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:55:10 -0700 Subject: [PATCH 02/27] apparmor: fix refcount bug in profile replacement Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 705c287..222052f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1189,12 +1189,12 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) aa_get_profile(newest); aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); - } else - aa_put_profile(newest); + } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); __list_add_profile(&parent->base.profiles, ent->new); + aa_put_profile(newest); } else { /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0003-apparmor-fix-replacement-bug-that-adds-new-child-to-.patch000066400000000000000000000027261522511161100320460ustar00rootroot00000000000000From 1224a06778b89dcbf0ca85bd961c2fcdd8765a69 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:57:19 -0700 Subject: [PATCH 03/27] apparmor: fix replacement bug that adds new child to old parent When set atomic replacement is used and the parent is updated before the child, and the child did not exist in the old parent so there is no direct replacement then the new child is incorrectly added to the old parent. This results in the new parent not having the child(ren) that it should and the old parent when being destroyed asserting the following error. AppArmor: policy_destroy: internal error, policy '' still contains profiles Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 222052f..c92a9f6 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1193,7 +1193,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); - __list_add_profile(&parent->base.profiles, ent->new); + __list_add_profile(&newest->base.profiles, ent->new); aa_put_profile(newest); } else { /* aafs interface uses replacedby */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0004-apparmor-fix-uninitialized-lsm_audit-member.patch000066400000000000000000000100161522511161100306440ustar00rootroot00000000000000From 15d921647676fdc2c3ee1cf9aa8f578b1012ecff Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 8 Jun 2014 11:20:54 -0700 Subject: [PATCH 04/27] apparmor: fix uninitialized lsm_audit member BugLink: http://bugs.launchpad.net/bugs/1268727 The task field in the lsm_audit struct needs to be initialized if a change_hat fails, otherwise the following oops will occur BUG: unable to handle kernel paging request at 0000002fbead7d08 IP: [] _raw_spin_lock+0xe/0x50 PGD 1e3f35067 PUD 0 Oops: 0002 [#1] SMP Modules linked in: pppox crc_ccitt p8023 p8022 psnap llc ax25 btrfs raid6_pq xor xfs libcrc32c dm_multipath scsi_dh kvm_amd dcdbas kvm microcode amd64_edac_mod joydev edac_core psmouse edac_mce_amd serio_raw k10temp sp5100_tco i2c_piix4 ipmi_si ipmi_msghandler acpi_power_meter mac_hid lp parport hid_generic usbhid hid pata_acpi mpt2sas ahci raid_class pata_atiixp bnx2 libahci scsi_transport_sas [last unloaded: tipc] CPU: 2 PID: 699 Comm: changehat_twice Tainted: GF O 3.13.0-7-generic #25-Ubuntu Hardware name: Dell Inc. PowerEdge R415/08WNM9, BIOS 1.8.6 12/06/2011 task: ffff8802135c6000 ti: ffff880212986000 task.ti: ffff880212986000 RIP: 0010:[] [] _raw_spin_lock+0xe/0x50 RSP: 0018:ffff880212987b68 EFLAGS: 00010006 RAX: 0000000000020000 RBX: 0000002fbead7500 RCX: 0000000000000000 RDX: 0000000000000292 RSI: ffff880212987ba8 RDI: 0000002fbead7d08 RBP: ffff880212987b68 R08: 0000000000000246 R09: ffff880216e572a0 R10: ffffffff815fd677 R11: ffffea0008469580 R12: ffffffff8130966f R13: ffff880212987ba8 R14: 0000002fbead7d08 R15: ffff8800d8c6b830 FS: 00002b5e6c84e7c0(0000) GS:ffff880216e40000(0000) knlGS:0000000055731700 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000002fbead7d08 CR3: 000000021270f000 CR4: 00000000000006e0 Stack: ffff880212987b98 ffffffff81075f17 ffffffff8130966f 0000000000000009 0000000000000000 0000000000000000 ffff880212987bd0 ffffffff81075f7c 0000000000000292 ffff880212987c08 ffff8800d8c6b800 0000000000000026 Call Trace: [] __lock_task_sighand+0x47/0x80 [] ? apparmor_cred_prepare+0x2f/0x50 [] do_send_sig_info+0x2c/0x80 [] send_sig_info+0x1e/0x30 [] aa_audit+0x13d/0x190 [] aa_audit_file+0xbc/0x130 [] ? apparmor_cred_prepare+0x2f/0x50 [] aa_change_hat+0x202/0x530 [] aa_setprocattr_changehat+0x116/0x1d0 [] apparmor_setprocattr+0x25d/0x300 [] security_setprocattr+0x16/0x20 [] proc_pid_attr_write+0x107/0x130 [] vfs_write+0xb4/0x1f0 [] SyS_write+0x49/0xa0 [] tracesys+0xe1/0xe6 Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/audit.c | 3 ++- security/apparmor/file.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 89c7865..3a7f1da 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, if (sa->aad->type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, - sa->u.tsk ? sa->u.tsk : current); + sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? + sa->u.tsk : current); if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) return complain_error(sa->aad->error); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 913f377..43d6ae7 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; + sa.type = LSM_AUDIT_DATA_TASK; + sa.u.tsk = NULL; sa.aad = &aad; aad.op = op, aad.fs.request = request; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0005-apparmor-exec-should-not-be-returning-ENOENT-when-it.patch000066400000000000000000000021141522511161100317410ustar00rootroot00000000000000From c1216728b7d644443eef31e4bd9d01b4a0a51d61 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:03 -0700 Subject: [PATCH 05/27] apparmor: exec should not be returning ENOENT when it denies The current behavior is confusing as it causes exec failures to report the executable is missing instead of identifying that apparmor caused the failure. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index dc0027b..67a7418 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) new_profile = aa_get_newest_profile(ns->unconfined); info = "ux fallback"; } else { - error = -ENOENT; + error = -EACCES; info = "profile not found"; /* remove MAY_EXEC to audit as failure */ perms.allow &= ~MAY_EXEC; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0006-apparmor-fix-update-the-mtime-of-the-profile-file-on.patch000066400000000000000000000015741522511161100321010ustar00rootroot00000000000000From 2d3389de6c8ab6b3ad2cef4ea460c8fce2a226b9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:01:56 -0700 Subject: [PATCH 06/27] apparmor: fix update the mtime of the profile file on replacement Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ad4fa49..45a6199 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -379,6 +379,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, for (i = 0; i < AAFS_PROF_SIZEOF; i++) { new->dents[i] = old->dents[i]; + if (new->dents[i]) + new->dents[i]->d_inode->i_mtime = CURRENT_TIME; old->dents[i] = NULL; } } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0007-apparmor-fix-disconnected-bind-mnts-reconnection.patch000066400000000000000000000020731522511161100315760ustar00rootroot00000000000000From 9caa96e30a1b2bb191a29af872285c8d0b078c10 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:08 -0700 Subject: [PATCH 07/27] apparmor: fix disconnected bind mnts reconnection Bind mounts can fail to be properly reconnected when PATH_CONNECT is specified. Ensure that when PATH_CONNECT is specified the path has a root. BugLink: http://bugs.launchpad.net/bugs/1319984 Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/path.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 71e0e3a..bb2f2c6 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -141,7 +141,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, error = -EACCES; if (*res == '/') *name = res + 1; - } + } else if (*res != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + } out: -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0008-apparmor-internal-paths-should-be-treated-as-disconn.patch000066400000000000000000000067631522511161100323010ustar00rootroot00000000000000From 11702a732e149380e05e2ab8ae1b743ac89f892f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:10 -0700 Subject: [PATCH 08/27] apparmor: internal paths should be treated as disconnected Internal mounts are not mounted anywhere and as such should be treated as disconnected paths. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/path.c | 64 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index bb2f2c6..596f799 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -25,7 +25,6 @@ #include "include/path.h" #include "include/policy.h" - /* modified from dcache.c */ static int prepend(char **buffer, int buflen, const char *str, int namelen) { @@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) +/* If the path is not connected to the expected root, + * check if it is a sysctl and handle specially else remove any + * leading / that __d_path may have returned. + * Unless + * specifically directed to connect the path, + * OR + * if in a chroot and doing chroot relative paths and the path + * resolves to the namespace root (would be connected outside + * of chroot) and specifically directed to connect paths to + * namespace root. + */ +static int disconnect(const struct path *path, char *buf, char **name, + int flags) +{ + int error = 0; + + if (!(flags & PATH_CONNECT_PATH) && + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && + our_mnt(path->mnt))) { + /* disconnected path, don't return pathname starting + * with '/' + */ + error = -EACCES; + if (**name == '/') + *name = *name + 1; + } else if (**name != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + + return error; +} + /** * d_namespace_path - lookup a name associated with a given path * @path: path to lookup (NOT NULL) @@ -74,7 +105,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, * control instead of hard coded /proc */ return prepend(name, *name - buf, "/proc", 5); - } + } else + return disconnect(path, buf, name, flags); return 0; } @@ -120,32 +152,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, goto out; } - /* If the path is not connected to the expected root, - * check if it is a sysctl and handle specially else remove any - * leading / that __d_path may have returned. - * Unless - * specifically directed to connect the path, - * OR - * if in a chroot and doing chroot relative paths and the path - * resolves to the namespace root (would be connected outside - * of chroot) and specifically directed to connect paths to - * namespace root. - */ - if (!connected) { - if (!(flags & PATH_CONNECT_PATH) && - !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && - our_mnt(path->mnt))) { - /* disconnected path, don't return pathname starting - * with '/' - */ - error = -EACCES; - if (*res == '/') - *name = res + 1; - } else if (*res != '/') - /* CONNECT_PATH with missing root */ - error = prepend(name, *name - buf, "/", 1); - - } + if (!connected) + error = disconnect(path, buf, name, flags); out: return error; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0009-apparmor-fix-put-parent-ref-after-updating-the-activ.patch000066400000000000000000000017761522511161100322350ustar00rootroot00000000000000From c70811d9e6234c96d0ef405cd8ad78b70efb8637 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 13:59:02 -0700 Subject: [PATCH 09/27] apparmor: fix put() parent ref after updating the active ref Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c92a9f6..455c9f8 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1187,8 +1187,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* parent replaced in this atomic set? */ if (newest != parent) { aa_get_profile(newest); - aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); + aa_put_profile(parent); } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0010-apparmor-fix-log-failures-for-all-profiles-in-a-set.patch000066400000000000000000000056761522511161100317440ustar00rootroot00000000000000From f671b902943f83f0fbc8c8b7bf8bbfb817d124f1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:16:50 -0700 Subject: [PATCH 10/27] apparmor: fix log failures for all profiles in a set currently only the profile that is causing the failure is logged. This makes it more confusing than necessary about which profiles loaded and which didn't. So make sure to log success and failure messages for all profiles in the set being loaded. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 455c9f8..db31bc5 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1067,7 +1067,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, */ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) { - const char *ns_name, *name = NULL, *info = NULL; + const char *ns_name, *info = NULL; struct aa_namespace *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; @@ -1082,18 +1082,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* released below */ ns = aa_prepare_namespace(ns_name); if (!ns) { - info = "failed to prepare namespace"; - error = -ENOMEM; - name = ns_name; - goto fail; + error = audit_policy(op, GFP_KERNEL, ns_name, + "failed to prepare namespace", -ENOMEM); + goto free; } mutex_lock(&ns->lock); /* setup parent and ns info */ list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; - - name = ent->new->base.hname; error = __lookup_replace(ns, ent->new->base.hname, noreplace, &ent->old, &info); if (error) @@ -1121,7 +1118,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) if (!p) { error = -ENOENT; info = "parent does not exist"; - name = ent->new->base.hname; goto fail_lock; } rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); @@ -1214,9 +1210,22 @@ out: fail_lock: mutex_unlock(&ns->lock); -fail: - error = audit_policy(op, GFP_KERNEL, name, info, error); + /* audit cause of failure */ + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); + /* audit status that rest of profiles in the atomic set failed too */ + info = "valid profile in failed atomic policy load"; + list_for_each_entry(tmp, &lh, list) { + if (tmp == ent) { + info = "unchecked profile in failed atomic policy load"; + /* skip entry that caused failure */ + continue; + } + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); + } +free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0011-apparmor-fix-audit-full-profile-hname-on-successful-.patch000066400000000000000000000023421522511161100322070ustar00rootroot00000000000000From bc3c7d342bf53afdfdf46bc92dac5c624c89fb91 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:19:38 -0700 Subject: [PATCH 11/27] apparmor: fix audit full profile hname on successful load Currently logging of a successful profile load only logs the basename of the profile. This can result in confusion when a child profile has the same name as the another profile in the set. Logging the hname will ensure there is no confusion. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index db31bc5..ca402d0 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1159,7 +1159,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); + audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0012-apparmor-ensure-the-target-profile-name-is-always-au.patch000066400000000000000000000065351522511161100322270ustar00rootroot00000000000000From 848da0479e5b9da3dc2ae4c64e0cca77a0abf02a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 20 Apr 2016 14:18:18 -0700 Subject: [PATCH 12/27] apparmor: ensure the target profile name is always audited The target profile name was not being correctly audited in a few cases because the target variable was not being set and gotos passed the code to set it at apply: Since it is always based on new_profile just drop the target var and conditionally report based on new_profile. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 67a7418..fc3036b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) file_inode(bprm->file)->i_uid, file_inode(bprm->file)->i_mode }; - const char *name = NULL, *target = NULL, *info = NULL; + const char *name = NULL, *info = NULL; int error = 0; if (bprm->cred_prepared) @@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (cxt->onexec) { struct file_perms cp; info = "change_profile onexec"; + new_profile = aa_get_newest_profile(cxt->onexec); if (!(perms.allow & AA_MAY_ONEXEC)) goto audit; @@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!(cp.allow & AA_MAY_ONEXEC)) goto audit; - new_profile = aa_get_newest_profile(cxt->onexec); goto apply; } @@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!new_profile) { error = -ENOMEM; info = "could not create null profile"; - } else { + } else error = -EACCES; - target = new_profile->base.hname; - } perms.xindex |= AA_X_UNSAFE; } else /* fail exec */ @@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * fail the exec. */ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) { - aa_put_profile(new_profile); error = -EPERM; goto cleanup; } @@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { error = may_change_ptraced_domain(new_profile); - if (error) { - aa_put_profile(new_profile); + if (error) goto audit; - } } /* Determine if secure exec is needed. @@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) bprm->unsafe |= AA_SECURE_X_NEEDED; } apply: - target = new_profile->base.hname; /* when transitioning profiles clear unsafe personality bits */ bprm->per_clear |= PER_CLEAR_ON_SETID; @@ -506,15 +500,19 @@ x_clear: aa_put_profile(cxt->profile); /* transfer new profile reference will be released when cxt is freed */ cxt->profile = new_profile; + new_profile = NULL; /* clear out all temporary/transitional state from the context */ aa_clear_task_cxt_trans(cxt); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, - name, target, cond.uid, info, error); + name, + new_profile ? new_profile->base.hname : NULL, + cond.uid, info, error); cleanup: + aa_put_profile(new_profile); aa_put_profile(profile); kfree(buffer); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0013-apparmor-check-that-xindex-is-in-trans_table-bounds.patch000066400000000000000000000016471522511161100321070ustar00rootroot00000000000000From 706473f3ead5cdffe5ad159adfbc090e0fda81d6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 17 Mar 2016 12:02:54 -0700 Subject: [PATCH 13/27] apparmor: check that xindex is in trans_table bounds Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a689f10..c841b12 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -676,7 +676,7 @@ static bool verify_xindex(int xindex, int table_size) int index, xtype; xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; - if (xtype == AA_X_TABLE && index > table_size) + if (xtype == AA_X_TABLE && index >= table_size) return 0; return 1; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0014-apparmor-fix-ref-count-leak-when-profile-sha1-hash-i.patch000066400000000000000000000014651522511161100317730ustar00rootroot00000000000000From 05a64c434466029b298ee1e78a988cd6a7f80c0e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 18 Nov 2015 11:41:05 -0800 Subject: [PATCH 14/27] apparmor: fix ref count leak when profile sha1 hash is read Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 45a6199..0d8dd71 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) seq_printf(seq, "%.2x", profile->hash[i]); seq_puts(seq, "\n"); } + aa_put_profile(profile); return 0; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0015-apparmor-fix-refcount-race-when-finding-a-child-prof.patch000066400000000000000000000024201522511161100321250ustar00rootroot00000000000000From 6b0b8b91f454bd021e27abe0e611a6764e4806c1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Dec 2015 18:09:10 -0800 Subject: [PATCH 15/27] apparmor: fix refcount race when finding a child profile When finding a child profile via an rcu critical section, the profile may be put and scheduled for deletion after the child is found but before its refcount is incremented. Protect against this by repeating the lookup if the profiles refcount is 0 and is one its way to deletion. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ca402d0..7807125 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) struct aa_profile *profile; rcu_read_lock(); - profile = aa_get_profile(__find_child(&parent->base.profiles, name)); + do { + profile = __find_child(&parent->base.profiles, name); + } while (profile && !aa_get_profile_not0(profile)); rcu_read_unlock(); /* refcount released by caller */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0016-apparmor-use-list_next_entry-instead-of-list_entry_n.patch000066400000000000000000000036771522511161100325700ustar00rootroot00000000000000From 84acc6aa6976e62756e14d3a00c5634724cbaa59 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 16 Nov 2015 21:46:33 +0800 Subject: [PATCH 16/27] apparmor: use list_next_entry instead of list_entry_next list_next_entry has been defined in list.h, so I replace list_entry_next with it. Signed-off-by: Geliang Tang Acked-by: Serge Hallyn Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0d8dd71..729e595 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -553,8 +553,6 @@ fail2: } -#define list_entry_next(pos, member) \ - list_entry(pos->member.next, typeof(*pos), member) #define list_entry_is_head(pos, head, member) (&pos->member == (head)) /** @@ -585,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, parent = ns->parent; while (ns != root) { mutex_unlock(&ns->lock); - next = list_entry_next(ns, base.list); + next = list_next_entry(ns, base.list); if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { mutex_lock(&next->lock); return next; @@ -639,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) parent = rcu_dereference_protected(p->parent, mutex_is_locked(&p->ns->lock)); while (parent) { - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &parent->base.profiles, base.list)) return p; p = parent; @@ -648,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) } /* is next another profile in the namespace */ - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &ns->base.profiles, base.list)) return p; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0017-apparmor-allow-SYS_CAP_RESOURCE-to-be-sufficient-to-.patch000066400000000000000000000037441522511161100314240ustar00rootroot00000000000000From a3896605318b86d8cf288c122e03604e349d5dd7 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 6 Nov 2015 15:17:30 -0500 Subject: [PATCH 17/27] apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task While using AppArmor, SYS_CAP_RESOURCE is insufficient to call prlimit on another task. The only other example of a AppArmor mediating access to another, already running, task (ignoring fork+exec) is ptrace. The AppArmor model for ptrace is that one of the following must be true: 1) The tracer is unconfined 2) The tracer is in complain mode 3) The tracer and tracee are confined by the same profile 4) The tracer is confined but has SYS_CAP_PTRACE 1), 2, and 3) are already true for setrlimit. We can match the ptrace model just by allowing CAP_SYS_RESOURCE. We still test the values of the rlimit since it can always be overridden using a value that means unlimited for a particular resource. Signed-off-by: Jeff Mahoney Signed-off-by: John Johansen --- security/apparmor/resource.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 748bf0c..67a6072 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, /* TODO: extend resource control to handle other (non current) * profiles. AppArmor rules currently have the implicit assumption * that the task is setting the resource of a task confined with - * the same profile. + * the same profile or that the task setting the resource of another + * task has CAP_SYS_RESOURCE. */ - if (profile != task_profile || + if ((profile != task_profile && + aa_capable(profile, CAP_SYS_RESOURCE, 1)) || (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) error = -EACCES; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0018-apparmor-add-missing-id-bounds-check-on-dfa-verifica.patch000066400000000000000000000024741522511161100320660ustar00rootroot00000000000000From 6fdcc3cfecd4d89457036627d59ebe5154d094c5 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 2 Jun 2016 02:37:02 -0700 Subject: [PATCH 18/27] apparmor: add missing id bounds check on dfa verification Signed-off-by: John Johansen --- security/apparmor/include/match.h | 1 + security/apparmor/match.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 001c43a..a1c04fe 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -62,6 +62,7 @@ struct table_set_header { #define YYTD_ID_ACCEPT2 6 #define YYTD_ID_NXT 7 #define YYTD_ID_TSIZE 8 +#define YYTD_ID_MAX 8 #define YYTD_DATA8 1 #define YYTD_DATA16 2 diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 727eb42..f9f57c6 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize) * it every time we use td_id as an index */ th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; + if (th.td_id > YYTD_ID_MAX) + goto out; th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); blob += sizeof(struct table_header); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0019-apparmor-don-t-check-for-vmalloc_addr-if-kvzalloc-fa.patch000066400000000000000000000021421522511161100321030ustar00rootroot00000000000000From 95d203cfb59627a86483a279ba82f1aa75297e07 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 09:57:55 +0300 Subject: [PATCH 19/27] apparmor: don't check for vmalloc_addr if kvzalloc() failed Signed-off-by: John Johansen --- security/apparmor/match.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index f9f57c6..32b72eb 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -75,14 +75,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize) u32, be32_to_cpu); else goto fail; + /* if table was vmalloced make sure the page tables are synced + * before it is used, as it goes live to all cpus. + */ + if (is_vmalloc_addr(table)) + vm_unmap_aliases(); } out: - /* if table was vmalloced make sure the page tables are synced - * before it is used, as it goes live to all cpus. - */ - if (is_vmalloc_addr(table)) - vm_unmap_aliases(); return table; fail: kvfree(table); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0020-apparmor-fix-oops-in-profile_unpack-when-policy_db-i.patch000066400000000000000000000021311522511161100322570ustar00rootroot00000000000000From e925f976c7a9c85455f67c360671254bac2d9a91 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 10:00:55 +0300 Subject: [PATCH 20/27] apparmor: fix oops in profile_unpack() when policy_db is not present BugLink: http://bugs.launchpad.net/bugs/1592547 If unpack_dfa() returns NULL due to the dfa not being present, profile_unpack() is not checking if the dfa is not present (NULL). Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c841b12..dac2121 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) error = PTR_ERR(profile->policy.dfa); profile->policy.dfa = NULL; goto fail; + } else if (!profile->policy.dfa) { + error = -EPROTO; + goto fail; } if (!unpack_u32(e, &profile->policy.start[0], "start")) /* default start state */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0021-apparmor-fix-module-parameters-can-be-changed-after-.patch000066400000000000000000000112361522511161100320700ustar00rootroot00000000000000From 45774028820fe2ffbbc94667165f04749821d529 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Jun 2016 18:01:08 -0700 Subject: [PATCH 21/27] apparmor: fix module parameters can be changed after policy is locked the policy_lock parameter is a one way switch that prevents policy from being further modified. Unfortunately some of the module parameters can effectively modify policy by turning off enforcement. split policy_admin_capable into a view check and a full admin check, and update the admin check to test the policy_lock parameter. Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 ++ security/apparmor/lsm.c | 22 ++++++++++------------ security/apparmor/policy.c | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index c28b0f2..52275f0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile) return profile->audit; } +bool policy_view_capable(void); +bool policy_admin_capable(void); bool aa_may_manage_policy(int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 5ee8201..bd40b12 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -751,51 +751,49 @@ __setup("apparmor=", apparmor_enabled_setup); /* set global flag turning off the ability to load policy */ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; - if (aa_g_lock_policy) - return -EACCES; return param_set_bool(val, kp); } static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aabool(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_bool(val, kp); } static int param_get_aabool(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aauint(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_uint(val, kp); } static int param_get_aauint(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_uint(buffer, kp); } static int param_get_audit(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; if (!apparmor_enabled) @@ -807,7 +805,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) static int param_set_audit(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -828,7 +826,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -840,7 +838,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7807125..179e68d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -918,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, &sa, NULL); } +bool policy_view_capable(void) +{ + struct user_namespace *user_ns = current_user_ns(); + bool response = false; + + if (ns_capable(user_ns, CAP_MAC_ADMIN)) + response = true; + + return response; +} + +bool policy_admin_capable(void) +{ + return policy_view_capable() && !aa_g_lock_policy; +} + /** * aa_may_manage_policy - can the current task manage policy * @op: the policy manipulation operation being done @@ -932,7 +948,7 @@ bool aa_may_manage_policy(int op) return 0; } - if (!capable(CAP_MAC_ADMIN)) { + if (!policy_admin_capable()) { audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); return 0; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0022-apparmor-do-not-expose-kernel-stack.patch000066400000000000000000000017571522511161100270620ustar00rootroot00000000000000From 7fcfc22cd04261ac35a579c99bcc804db7eb3e83 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 10 Jun 2016 23:34:26 +0200 Subject: [PATCH 22/27] apparmor: do not expose kernel stack Do not copy uninitalized fields th.td_hilen, th.td_data. Signed-off-by: Heinrich Schuchardt Signed-off-by: John Johansen --- security/apparmor/match.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 32b72eb..3f900fc 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -63,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) table = kvzalloc(tsize); if (table) { - *table = th; + table->td_id = th.td_id; + table->td_flags = th.td_flags; + table->td_lolen = th.td_lolen; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, u8, byte_to_byte); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0023-apparmor-fix-arg_size-computation-for-when-setprocat.patch000066400000000000000000000016371522511161100324520ustar00rootroot00000000000000From 1b98560066c26fecb0a61aeb9249e141af2e63f9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 9 Jul 2016 23:46:33 -0700 Subject: [PATCH 23/27] apparmor: fix arg_size computation for when setprocattr is null terminated Signed-off-by: John Johansen --- security/apparmor/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index bd40b12..1bf6c53 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -552,7 +552,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, if (!*args) goto out; - arg_size = size - (args - (char *) value); + arg_size = size - (args - (largs ? largs : (char *) value)); if (strcmp(name, "current") == 0) { if (strcmp(command, "changehat") == 0) { error = aa_setprocattr_changehat(args, arg_size, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0024-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000437201522511161100301520ustar00rootroot00000000000000From 8d7c032e7798fa1c46449728874b64fff882368b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 24/27] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df8..5dbb72f 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 729e595..181d961 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -807,6 +807,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index ba3dfd1..5d3c419 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -125,6 +125,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 52275f0..4fc4dac 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const aa_profile_mode_names[]; @@ -176,6 +177,7 @@ struct aa_replacedby { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -217,6 +219,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; unsigned char *hash; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 1bf6c53..284ddda 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -607,6 +608,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -636,6 +735,19 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 179e68d..f1a8541 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index dac2121..0107bc4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0025-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322170ustar00rootroot00000000000000From aa45ba104003404efb59e6f7178045ade756035d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 25/27] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0026-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000714271522511161100314100ustar00rootroot00000000000000From da5b036d6235f44c4ccbeaaf8d46fb29ff22745f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 26/27] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 15 +- security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/include/path.h | 2 +- security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ security/apparmor/path.c | 8 +- 12 files changed, 773 insertions(+), 9 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5dbb72f..89b3445 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 181d961..5fb67f6 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -800,7 +800,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_policy[] = { AA_FS_FILE_BOOLEAN("set_load", 1), - {} + { } +}; + +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } }; static struct aa_fs_entry aa_fs_entry_features[] = { @@ -808,6 +819,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3a7f1da..c2a8b8a 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index fc3036b..f2a83b4 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -236,7 +236,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index e4ea626..ce6ff6a 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -30,8 +30,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 5d3c419..b9f1d57 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -72,6 +72,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -120,6 +124,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..a43b1d6 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 286ac75..73560f2 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -26,7 +26,7 @@ enum path_flags { PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ }; -int aa_path_name(struct path *path, int flags, char **buffer, +int aa_path_name(const struct path *path, int flags, char **buffer, const char **name, const char **info); #endif /* __AA_PATH_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 284ddda..780fec9 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -492,6 +493,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(const char *dev_name, struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -712,6 +767,10 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(capget, apparmor_capget), LSM_HOOK_INIT(capable, apparmor_capable), + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), + LSM_HOOK_INIT(path_link, apparmor_path_link), LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..9cf9170 --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, const struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 596f799..a8fc7d0 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -84,7 +84,7 @@ static int disconnect(const struct path *path, char *buf, char **name, * When no error the path name is returned in @name which points to * to a position in @buf */ -static int d_namespace_path(struct path *path, char *buf, int buflen, +static int d_namespace_path(const struct path *path, char *buf, int buflen, char **name, int flags) { char *res; @@ -169,7 +169,7 @@ out: * * Returns: %0 else error on failure */ -static int get_name_to_buffer(struct path *path, int flags, char *buffer, +static int get_name_to_buffer(const struct path *path, int flags, char *buffer, int size, char **name, const char **info) { int adjust = (flags & PATH_IS_DIR) ? 1 : 0; @@ -215,8 +215,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, * * Returns: %0 else error code if could retrieve name */ -int aa_path_name(struct path *path, int flags, char **buffer, const char **name, - const char **info) +int aa_path_name(const struct path *path, int flags, char **buffer, + const char **name, const char **info) { char *buf, *str = NULL; int size = 256; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.5/0027-UBUNTU-SAUCE-AppArmor-fix-boolreturn.cocci-warnings.patch000066400000000000000000000122451522511161100314410ustar00rootroot00000000000000From 1eff686074a6af0cf47fc24c45ebb001c570a98b Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 29 Jul 2016 12:44:43 +0800 Subject: [PATCH 27/27] UBUNTU: SAUCE: AppArmor: fix boolreturn.cocci warnings security/apparmor/policy_unpack.c:143:9-10: WARNING: return of 0/1 in function 'unpack_X' with return type bool security/apparmor/policy_unpack.c:189:9-10: WARNING: return of 0/1 in function 'unpack_nameX' with return type bool security/apparmor/policy_unpack.c:475:8-9: WARNING: return of 0/1 in function 'unpack_rlimits' with return type bool security/apparmor/policy_unpack.c:440:8-9: WARNING: return of 0/1 in function 'unpack_trans_table' with return type bool security/apparmor/policy_unpack.c:200:10-11: WARNING: return of 0/1 in function 'unpack_u16' with return type bool security/apparmor/policy_unpack.c:213:10-11: WARNING: return of 0/1 in function 'unpack_u32' with return type bool security/apparmor/policy_unpack.c:226:10-11: WARNING: return of 0/1 in function 'unpack_u64' with return type bool security/apparmor/policy_unpack.c:325:10-11: WARNING: return of 0/1 in function 'verify_accept' with return type bool security/apparmor/policy_unpack.c:739:10-11: WARNING: return of 0/1 in function 'verify_dfa_xindex' with return type bool security/apparmor/policy_unpack.c:729:9-10: WARNING: return of 0/1 in function 'verify_xindex' with return type bool Return statements in functions returning bool should use true/false instead of 1/0. Generated by: scripts/coccinelle/misc/boolreturn.cocci Signed-off-by: Fengguang Wu Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 0107bc4..af14626 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -140,11 +140,11 @@ static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) static bool unpack_X(struct aa_ext *e, enum aa_code code) { if (!inbounds(e, 1)) - return 0; + return false; if (*(u8 *) e->pos != code) - return 0; + return false; e->pos++; - return 1; + return true; } /** @@ -186,50 +186,50 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) /* now check if type code matches */ if (unpack_X(e, code)) - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) { if (unpack_nameX(e, AA_U16, name)) { if (!inbounds(e, sizeof(u16))) - return 0; + return false; if (data) *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); e->pos += sizeof(u16); - return 1; + return true; } - return 0; + return false; } static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { if (!inbounds(e, sizeof(u32))) - return 0; + return false; if (data) *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); e->pos += sizeof(u32); - return 1; + return true; } - return 0; + return false; } static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) { if (unpack_nameX(e, AA_U64, name)) { if (!inbounds(e, sizeof(u64))) - return 0; + return false; if (data) *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); e->pos += sizeof(u64); - return 1; + return true; } - return 0; + return false; } static size_t unpack_array(struct aa_ext *e, const char *name) @@ -322,12 +322,12 @@ static bool verify_accept(struct aa_dfa *dfa, int flags) int mode = ACCEPT_TABLE(dfa)[i]; if (mode & ~DFA_VALID_PERM_MASK) - return 0; + return false; if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) - return 0; + return false; } - return 1; + return true; } /** @@ -437,12 +437,12 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } - return 1; + return true; fail: aa_free_domain_entries(&profile->file.trans); e->pos = pos; - return 0; + return false; } static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) @@ -472,11 +472,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } /** @@ -726,8 +726,8 @@ static bool verify_xindex(int xindex, int table_size) xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; if (xtype == AA_X_TABLE && index >= table_size) - return 0; - return 1; + return false; + return true; } /* verify dfa xindexes are in range of transition tables */ @@ -736,11 +736,11 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) int i; for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) - return 0; + return false; if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) - return 0; + return false; } - return 1; + return true; } /** -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/000077500000000000000000000000001522511161100165465ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/4.6/0001-apparmor-fix-oops-validate-buffer-size-in-apparmor_s.patch000066400000000000000000000066331522511161100323130ustar00rootroot00000000000000From 24b6ac149a57c2d3d5a9920e64d914e8ff00d346 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Thu, 7 Jul 2016 13:41:11 -0700 Subject: [PATCH 01/27] apparmor: fix oops, validate buffer size in apparmor_setprocattr() When proc_pid_attr_write() was changed to use memdup_user apparmor's (interface violating) assumption that the setprocattr buffer was always a single page was violated. The size test is not strictly speaking needed as proc_pid_attr_write() will reject anything larger, but for the sake of robustness we can keep it in. SMACK and SELinux look safe to me, but somebody else should probably have a look just in case. Based on original patch from Vegard Nossum modified for the case that apparmor provides null termination. Fixes: bb646cdb12e75d82258c2f2e7746d5952d3e321a Reported-by: Vegard Nossum Cc: Al Viro Cc: John Johansen Cc: Paul Moore Cc: Stephen Smalley Cc: Eric Paris Cc: Casey Schaufler Cc: stable@kernel.org Signed-off-by: John Johansen Reviewed-by: Tyler Hicks Signed-off-by: James Morris --- security/apparmor/lsm.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index dec607c..5ee8201 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -523,34 +523,34 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - char *command, *args = value; + char *command, *largs = NULL, *args = value; size_t arg_size; int error; if (size == 0) return -EINVAL; - /* args points to a PAGE_SIZE buffer, AppArmor requires that - * the buffer must be null terminated or have size <= PAGE_SIZE -1 - * so that AppArmor can null terminate them - */ - if (args[size - 1] != '\0') { - if (size == PAGE_SIZE) - return -EINVAL; - args[size] = '\0'; - } - /* task can only write its own attributes */ if (current != task) return -EACCES; - args = value; + /* AppArmor requires that the buffer must be null terminated atm */ + if (args[size - 1] != '\0') { + /* null terminate */ + largs = args = kmalloc(size + 1, GFP_KERNEL); + if (!args) + return -ENOMEM; + memcpy(args, value, size); + args[size] = '\0'; + } + + error = -EINVAL; args = strim(args); command = strsep(&args, " "); if (!args) - return -EINVAL; + goto out; args = skip_spaces(args); if (!*args) - return -EINVAL; + goto out; arg_size = size - (args - (char *) value); if (strcmp(name, "current") == 0) { @@ -576,10 +576,12 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, goto fail; } else /* only support the "current" and "exec" process attributes */ - return -EINVAL; + goto fail; if (!error) error = size; +out: + kfree(largs); return error; fail: @@ -588,9 +590,9 @@ fail: aad.profile = aa_current_profile(); aad.op = OP_SETPROCATTR; aad.info = name; - aad.error = -EINVAL; + aad.error = error = -EINVAL; aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); - return -EINVAL; + goto out; } static int apparmor_task_setrlimit(struct task_struct *task, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0002-apparmor-fix-refcount-bug-in-profile-replacement.patch000066400000000000000000000022361522511161100315130ustar00rootroot00000000000000From 444bc4f95ec283cd0fb9777f4890bd9bc307809d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:55:10 -0700 Subject: [PATCH 02/27] apparmor: fix refcount bug in profile replacement Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 705c287..222052f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1189,12 +1189,12 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) aa_get_profile(newest); aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); - } else - aa_put_profile(newest); + } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); __list_add_profile(&parent->base.profiles, ent->new); + aa_put_profile(newest); } else { /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0003-apparmor-fix-replacement-bug-that-adds-new-child-to-.patch000066400000000000000000000027261522511161100320470ustar00rootroot00000000000000From 1224a06778b89dcbf0ca85bd961c2fcdd8765a69 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:57:19 -0700 Subject: [PATCH 03/27] apparmor: fix replacement bug that adds new child to old parent When set atomic replacement is used and the parent is updated before the child, and the child did not exist in the old parent so there is no direct replacement then the new child is incorrectly added to the old parent. This results in the new parent not having the child(ren) that it should and the old parent when being destroyed asserting the following error. AppArmor: policy_destroy: internal error, policy '' still contains profiles Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 222052f..c92a9f6 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1193,7 +1193,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); - __list_add_profile(&parent->base.profiles, ent->new); + __list_add_profile(&newest->base.profiles, ent->new); aa_put_profile(newest); } else { /* aafs interface uses replacedby */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0004-apparmor-fix-uninitialized-lsm_audit-member.patch000066400000000000000000000100161522511161100306450ustar00rootroot00000000000000From 15d921647676fdc2c3ee1cf9aa8f578b1012ecff Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 8 Jun 2014 11:20:54 -0700 Subject: [PATCH 04/27] apparmor: fix uninitialized lsm_audit member BugLink: http://bugs.launchpad.net/bugs/1268727 The task field in the lsm_audit struct needs to be initialized if a change_hat fails, otherwise the following oops will occur BUG: unable to handle kernel paging request at 0000002fbead7d08 IP: [] _raw_spin_lock+0xe/0x50 PGD 1e3f35067 PUD 0 Oops: 0002 [#1] SMP Modules linked in: pppox crc_ccitt p8023 p8022 psnap llc ax25 btrfs raid6_pq xor xfs libcrc32c dm_multipath scsi_dh kvm_amd dcdbas kvm microcode amd64_edac_mod joydev edac_core psmouse edac_mce_amd serio_raw k10temp sp5100_tco i2c_piix4 ipmi_si ipmi_msghandler acpi_power_meter mac_hid lp parport hid_generic usbhid hid pata_acpi mpt2sas ahci raid_class pata_atiixp bnx2 libahci scsi_transport_sas [last unloaded: tipc] CPU: 2 PID: 699 Comm: changehat_twice Tainted: GF O 3.13.0-7-generic #25-Ubuntu Hardware name: Dell Inc. PowerEdge R415/08WNM9, BIOS 1.8.6 12/06/2011 task: ffff8802135c6000 ti: ffff880212986000 task.ti: ffff880212986000 RIP: 0010:[] [] _raw_spin_lock+0xe/0x50 RSP: 0018:ffff880212987b68 EFLAGS: 00010006 RAX: 0000000000020000 RBX: 0000002fbead7500 RCX: 0000000000000000 RDX: 0000000000000292 RSI: ffff880212987ba8 RDI: 0000002fbead7d08 RBP: ffff880212987b68 R08: 0000000000000246 R09: ffff880216e572a0 R10: ffffffff815fd677 R11: ffffea0008469580 R12: ffffffff8130966f R13: ffff880212987ba8 R14: 0000002fbead7d08 R15: ffff8800d8c6b830 FS: 00002b5e6c84e7c0(0000) GS:ffff880216e40000(0000) knlGS:0000000055731700 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000002fbead7d08 CR3: 000000021270f000 CR4: 00000000000006e0 Stack: ffff880212987b98 ffffffff81075f17 ffffffff8130966f 0000000000000009 0000000000000000 0000000000000000 ffff880212987bd0 ffffffff81075f7c 0000000000000292 ffff880212987c08 ffff8800d8c6b800 0000000000000026 Call Trace: [] __lock_task_sighand+0x47/0x80 [] ? apparmor_cred_prepare+0x2f/0x50 [] do_send_sig_info+0x2c/0x80 [] send_sig_info+0x1e/0x30 [] aa_audit+0x13d/0x190 [] aa_audit_file+0xbc/0x130 [] ? apparmor_cred_prepare+0x2f/0x50 [] aa_change_hat+0x202/0x530 [] aa_setprocattr_changehat+0x116/0x1d0 [] apparmor_setprocattr+0x25d/0x300 [] security_setprocattr+0x16/0x20 [] proc_pid_attr_write+0x107/0x130 [] vfs_write+0xb4/0x1f0 [] SyS_write+0x49/0xa0 [] tracesys+0xe1/0xe6 Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/audit.c | 3 ++- security/apparmor/file.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 89c7865..3a7f1da 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, if (sa->aad->type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, - sa->u.tsk ? sa->u.tsk : current); + sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? + sa->u.tsk : current); if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) return complain_error(sa->aad->error); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 913f377..43d6ae7 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; + sa.type = LSM_AUDIT_DATA_TASK; + sa.u.tsk = NULL; sa.aad = &aad; aad.op = op, aad.fs.request = request; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0005-apparmor-exec-should-not-be-returning-ENOENT-when-it.patch000066400000000000000000000021141522511161100317420ustar00rootroot00000000000000From c1216728b7d644443eef31e4bd9d01b4a0a51d61 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:03 -0700 Subject: [PATCH 05/27] apparmor: exec should not be returning ENOENT when it denies The current behavior is confusing as it causes exec failures to report the executable is missing instead of identifying that apparmor caused the failure. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index dc0027b..67a7418 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) new_profile = aa_get_newest_profile(ns->unconfined); info = "ux fallback"; } else { - error = -ENOENT; + error = -EACCES; info = "profile not found"; /* remove MAY_EXEC to audit as failure */ perms.allow &= ~MAY_EXEC; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0006-apparmor-fix-update-the-mtime-of-the-profile-file-on.patch000066400000000000000000000015741522511161100321020ustar00rootroot00000000000000From 2d3389de6c8ab6b3ad2cef4ea460c8fce2a226b9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:01:56 -0700 Subject: [PATCH 06/27] apparmor: fix update the mtime of the profile file on replacement Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ad4fa49..45a6199 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -379,6 +379,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, for (i = 0; i < AAFS_PROF_SIZEOF; i++) { new->dents[i] = old->dents[i]; + if (new->dents[i]) + new->dents[i]->d_inode->i_mtime = CURRENT_TIME; old->dents[i] = NULL; } } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0007-apparmor-fix-disconnected-bind-mnts-reconnection.patch000066400000000000000000000020731522511161100315770ustar00rootroot00000000000000From 9caa96e30a1b2bb191a29af872285c8d0b078c10 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:08 -0700 Subject: [PATCH 07/27] apparmor: fix disconnected bind mnts reconnection Bind mounts can fail to be properly reconnected when PATH_CONNECT is specified. Ensure that when PATH_CONNECT is specified the path has a root. BugLink: http://bugs.launchpad.net/bugs/1319984 Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/path.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 71e0e3a..bb2f2c6 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -141,7 +141,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, error = -EACCES; if (*res == '/') *name = res + 1; - } + } else if (*res != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + } out: -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0008-apparmor-internal-paths-should-be-treated-as-disconn.patch000066400000000000000000000067631522511161100323020ustar00rootroot00000000000000From 11702a732e149380e05e2ab8ae1b743ac89f892f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:10 -0700 Subject: [PATCH 08/27] apparmor: internal paths should be treated as disconnected Internal mounts are not mounted anywhere and as such should be treated as disconnected paths. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/path.c | 64 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index bb2f2c6..596f799 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -25,7 +25,6 @@ #include "include/path.h" #include "include/policy.h" - /* modified from dcache.c */ static int prepend(char **buffer, int buflen, const char *str, int namelen) { @@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) +/* If the path is not connected to the expected root, + * check if it is a sysctl and handle specially else remove any + * leading / that __d_path may have returned. + * Unless + * specifically directed to connect the path, + * OR + * if in a chroot and doing chroot relative paths and the path + * resolves to the namespace root (would be connected outside + * of chroot) and specifically directed to connect paths to + * namespace root. + */ +static int disconnect(const struct path *path, char *buf, char **name, + int flags) +{ + int error = 0; + + if (!(flags & PATH_CONNECT_PATH) && + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && + our_mnt(path->mnt))) { + /* disconnected path, don't return pathname starting + * with '/' + */ + error = -EACCES; + if (**name == '/') + *name = *name + 1; + } else if (**name != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + + return error; +} + /** * d_namespace_path - lookup a name associated with a given path * @path: path to lookup (NOT NULL) @@ -74,7 +105,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, * control instead of hard coded /proc */ return prepend(name, *name - buf, "/proc", 5); - } + } else + return disconnect(path, buf, name, flags); return 0; } @@ -120,32 +152,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, goto out; } - /* If the path is not connected to the expected root, - * check if it is a sysctl and handle specially else remove any - * leading / that __d_path may have returned. - * Unless - * specifically directed to connect the path, - * OR - * if in a chroot and doing chroot relative paths and the path - * resolves to the namespace root (would be connected outside - * of chroot) and specifically directed to connect paths to - * namespace root. - */ - if (!connected) { - if (!(flags & PATH_CONNECT_PATH) && - !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && - our_mnt(path->mnt))) { - /* disconnected path, don't return pathname starting - * with '/' - */ - error = -EACCES; - if (*res == '/') - *name = res + 1; - } else if (*res != '/') - /* CONNECT_PATH with missing root */ - error = prepend(name, *name - buf, "/", 1); - - } + if (!connected) + error = disconnect(path, buf, name, flags); out: return error; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0009-apparmor-fix-put-parent-ref-after-updating-the-activ.patch000066400000000000000000000017761522511161100322360ustar00rootroot00000000000000From c70811d9e6234c96d0ef405cd8ad78b70efb8637 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 13:59:02 -0700 Subject: [PATCH 09/27] apparmor: fix put() parent ref after updating the active ref Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c92a9f6..455c9f8 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1187,8 +1187,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* parent replaced in this atomic set? */ if (newest != parent) { aa_get_profile(newest); - aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); + aa_put_profile(parent); } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0010-apparmor-fix-log-failures-for-all-profiles-in-a-set.patch000066400000000000000000000056761522511161100317450ustar00rootroot00000000000000From f671b902943f83f0fbc8c8b7bf8bbfb817d124f1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:16:50 -0700 Subject: [PATCH 10/27] apparmor: fix log failures for all profiles in a set currently only the profile that is causing the failure is logged. This makes it more confusing than necessary about which profiles loaded and which didn't. So make sure to log success and failure messages for all profiles in the set being loaded. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 455c9f8..db31bc5 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1067,7 +1067,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, */ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) { - const char *ns_name, *name = NULL, *info = NULL; + const char *ns_name, *info = NULL; struct aa_namespace *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; @@ -1082,18 +1082,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* released below */ ns = aa_prepare_namespace(ns_name); if (!ns) { - info = "failed to prepare namespace"; - error = -ENOMEM; - name = ns_name; - goto fail; + error = audit_policy(op, GFP_KERNEL, ns_name, + "failed to prepare namespace", -ENOMEM); + goto free; } mutex_lock(&ns->lock); /* setup parent and ns info */ list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; - - name = ent->new->base.hname; error = __lookup_replace(ns, ent->new->base.hname, noreplace, &ent->old, &info); if (error) @@ -1121,7 +1118,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) if (!p) { error = -ENOENT; info = "parent does not exist"; - name = ent->new->base.hname; goto fail_lock; } rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); @@ -1214,9 +1210,22 @@ out: fail_lock: mutex_unlock(&ns->lock); -fail: - error = audit_policy(op, GFP_KERNEL, name, info, error); + /* audit cause of failure */ + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); + /* audit status that rest of profiles in the atomic set failed too */ + info = "valid profile in failed atomic policy load"; + list_for_each_entry(tmp, &lh, list) { + if (tmp == ent) { + info = "unchecked profile in failed atomic policy load"; + /* skip entry that caused failure */ + continue; + } + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); + } +free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0011-apparmor-fix-audit-full-profile-hname-on-successful-.patch000066400000000000000000000023421522511161100322100ustar00rootroot00000000000000From bc3c7d342bf53afdfdf46bc92dac5c624c89fb91 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:19:38 -0700 Subject: [PATCH 11/27] apparmor: fix audit full profile hname on successful load Currently logging of a successful profile load only logs the basename of the profile. This can result in confusion when a child profile has the same name as the another profile in the set. Logging the hname will ensure there is no confusion. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index db31bc5..ca402d0 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1159,7 +1159,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); + audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0012-apparmor-ensure-the-target-profile-name-is-always-au.patch000066400000000000000000000065351522511161100322300ustar00rootroot00000000000000From 848da0479e5b9da3dc2ae4c64e0cca77a0abf02a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 20 Apr 2016 14:18:18 -0700 Subject: [PATCH 12/27] apparmor: ensure the target profile name is always audited The target profile name was not being correctly audited in a few cases because the target variable was not being set and gotos passed the code to set it at apply: Since it is always based on new_profile just drop the target var and conditionally report based on new_profile. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 67a7418..fc3036b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) file_inode(bprm->file)->i_uid, file_inode(bprm->file)->i_mode }; - const char *name = NULL, *target = NULL, *info = NULL; + const char *name = NULL, *info = NULL; int error = 0; if (bprm->cred_prepared) @@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (cxt->onexec) { struct file_perms cp; info = "change_profile onexec"; + new_profile = aa_get_newest_profile(cxt->onexec); if (!(perms.allow & AA_MAY_ONEXEC)) goto audit; @@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!(cp.allow & AA_MAY_ONEXEC)) goto audit; - new_profile = aa_get_newest_profile(cxt->onexec); goto apply; } @@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!new_profile) { error = -ENOMEM; info = "could not create null profile"; - } else { + } else error = -EACCES; - target = new_profile->base.hname; - } perms.xindex |= AA_X_UNSAFE; } else /* fail exec */ @@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * fail the exec. */ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) { - aa_put_profile(new_profile); error = -EPERM; goto cleanup; } @@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { error = may_change_ptraced_domain(new_profile); - if (error) { - aa_put_profile(new_profile); + if (error) goto audit; - } } /* Determine if secure exec is needed. @@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) bprm->unsafe |= AA_SECURE_X_NEEDED; } apply: - target = new_profile->base.hname; /* when transitioning profiles clear unsafe personality bits */ bprm->per_clear |= PER_CLEAR_ON_SETID; @@ -506,15 +500,19 @@ x_clear: aa_put_profile(cxt->profile); /* transfer new profile reference will be released when cxt is freed */ cxt->profile = new_profile; + new_profile = NULL; /* clear out all temporary/transitional state from the context */ aa_clear_task_cxt_trans(cxt); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, - name, target, cond.uid, info, error); + name, + new_profile ? new_profile->base.hname : NULL, + cond.uid, info, error); cleanup: + aa_put_profile(new_profile); aa_put_profile(profile); kfree(buffer); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0013-apparmor-check-that-xindex-is-in-trans_table-bounds.patch000066400000000000000000000016471522511161100321100ustar00rootroot00000000000000From 706473f3ead5cdffe5ad159adfbc090e0fda81d6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 17 Mar 2016 12:02:54 -0700 Subject: [PATCH 13/27] apparmor: check that xindex is in trans_table bounds Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a689f10..c841b12 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -676,7 +676,7 @@ static bool verify_xindex(int xindex, int table_size) int index, xtype; xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; - if (xtype == AA_X_TABLE && index > table_size) + if (xtype == AA_X_TABLE && index >= table_size) return 0; return 1; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0014-apparmor-fix-ref-count-leak-when-profile-sha1-hash-i.patch000066400000000000000000000014651522511161100317740ustar00rootroot00000000000000From 05a64c434466029b298ee1e78a988cd6a7f80c0e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 18 Nov 2015 11:41:05 -0800 Subject: [PATCH 14/27] apparmor: fix ref count leak when profile sha1 hash is read Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 45a6199..0d8dd71 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) seq_printf(seq, "%.2x", profile->hash[i]); seq_puts(seq, "\n"); } + aa_put_profile(profile); return 0; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0015-apparmor-fix-refcount-race-when-finding-a-child-prof.patch000066400000000000000000000024201522511161100321260ustar00rootroot00000000000000From 6b0b8b91f454bd021e27abe0e611a6764e4806c1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Dec 2015 18:09:10 -0800 Subject: [PATCH 15/27] apparmor: fix refcount race when finding a child profile When finding a child profile via an rcu critical section, the profile may be put and scheduled for deletion after the child is found but before its refcount is incremented. Protect against this by repeating the lookup if the profiles refcount is 0 and is one its way to deletion. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ca402d0..7807125 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) struct aa_profile *profile; rcu_read_lock(); - profile = aa_get_profile(__find_child(&parent->base.profiles, name)); + do { + profile = __find_child(&parent->base.profiles, name); + } while (profile && !aa_get_profile_not0(profile)); rcu_read_unlock(); /* refcount released by caller */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0016-apparmor-use-list_next_entry-instead-of-list_entry_n.patch000066400000000000000000000036771522511161100325710ustar00rootroot00000000000000From 84acc6aa6976e62756e14d3a00c5634724cbaa59 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 16 Nov 2015 21:46:33 +0800 Subject: [PATCH 16/27] apparmor: use list_next_entry instead of list_entry_next list_next_entry has been defined in list.h, so I replace list_entry_next with it. Signed-off-by: Geliang Tang Acked-by: Serge Hallyn Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0d8dd71..729e595 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -553,8 +553,6 @@ fail2: } -#define list_entry_next(pos, member) \ - list_entry(pos->member.next, typeof(*pos), member) #define list_entry_is_head(pos, head, member) (&pos->member == (head)) /** @@ -585,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, parent = ns->parent; while (ns != root) { mutex_unlock(&ns->lock); - next = list_entry_next(ns, base.list); + next = list_next_entry(ns, base.list); if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { mutex_lock(&next->lock); return next; @@ -639,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) parent = rcu_dereference_protected(p->parent, mutex_is_locked(&p->ns->lock)); while (parent) { - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &parent->base.profiles, base.list)) return p; p = parent; @@ -648,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) } /* is next another profile in the namespace */ - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &ns->base.profiles, base.list)) return p; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0017-apparmor-allow-SYS_CAP_RESOURCE-to-be-sufficient-to-.patch000066400000000000000000000037441522511161100314250ustar00rootroot00000000000000From a3896605318b86d8cf288c122e03604e349d5dd7 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 6 Nov 2015 15:17:30 -0500 Subject: [PATCH 17/27] apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task While using AppArmor, SYS_CAP_RESOURCE is insufficient to call prlimit on another task. The only other example of a AppArmor mediating access to another, already running, task (ignoring fork+exec) is ptrace. The AppArmor model for ptrace is that one of the following must be true: 1) The tracer is unconfined 2) The tracer is in complain mode 3) The tracer and tracee are confined by the same profile 4) The tracer is confined but has SYS_CAP_PTRACE 1), 2, and 3) are already true for setrlimit. We can match the ptrace model just by allowing CAP_SYS_RESOURCE. We still test the values of the rlimit since it can always be overridden using a value that means unlimited for a particular resource. Signed-off-by: Jeff Mahoney Signed-off-by: John Johansen --- security/apparmor/resource.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 748bf0c..67a6072 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, /* TODO: extend resource control to handle other (non current) * profiles. AppArmor rules currently have the implicit assumption * that the task is setting the resource of a task confined with - * the same profile. + * the same profile or that the task setting the resource of another + * task has CAP_SYS_RESOURCE. */ - if (profile != task_profile || + if ((profile != task_profile && + aa_capable(profile, CAP_SYS_RESOURCE, 1)) || (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) error = -EACCES; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0018-apparmor-add-missing-id-bounds-check-on-dfa-verifica.patch000066400000000000000000000024741522511161100320670ustar00rootroot00000000000000From 6fdcc3cfecd4d89457036627d59ebe5154d094c5 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 2 Jun 2016 02:37:02 -0700 Subject: [PATCH 18/27] apparmor: add missing id bounds check on dfa verification Signed-off-by: John Johansen --- security/apparmor/include/match.h | 1 + security/apparmor/match.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 001c43a..a1c04fe 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -62,6 +62,7 @@ struct table_set_header { #define YYTD_ID_ACCEPT2 6 #define YYTD_ID_NXT 7 #define YYTD_ID_TSIZE 8 +#define YYTD_ID_MAX 8 #define YYTD_DATA8 1 #define YYTD_DATA16 2 diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 727eb42..f9f57c6 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize) * it every time we use td_id as an index */ th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; + if (th.td_id > YYTD_ID_MAX) + goto out; th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); blob += sizeof(struct table_header); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0019-apparmor-don-t-check-for-vmalloc_addr-if-kvzalloc-fa.patch000066400000000000000000000021421522511161100321040ustar00rootroot00000000000000From 95d203cfb59627a86483a279ba82f1aa75297e07 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 09:57:55 +0300 Subject: [PATCH 19/27] apparmor: don't check for vmalloc_addr if kvzalloc() failed Signed-off-by: John Johansen --- security/apparmor/match.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index f9f57c6..32b72eb 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -75,14 +75,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize) u32, be32_to_cpu); else goto fail; + /* if table was vmalloced make sure the page tables are synced + * before it is used, as it goes live to all cpus. + */ + if (is_vmalloc_addr(table)) + vm_unmap_aliases(); } out: - /* if table was vmalloced make sure the page tables are synced - * before it is used, as it goes live to all cpus. - */ - if (is_vmalloc_addr(table)) - vm_unmap_aliases(); return table; fail: kvfree(table); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0020-apparmor-fix-oops-in-profile_unpack-when-policy_db-i.patch000066400000000000000000000021311522511161100322600ustar00rootroot00000000000000From e925f976c7a9c85455f67c360671254bac2d9a91 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 10:00:55 +0300 Subject: [PATCH 20/27] apparmor: fix oops in profile_unpack() when policy_db is not present BugLink: http://bugs.launchpad.net/bugs/1592547 If unpack_dfa() returns NULL due to the dfa not being present, profile_unpack() is not checking if the dfa is not present (NULL). Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c841b12..dac2121 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) error = PTR_ERR(profile->policy.dfa); profile->policy.dfa = NULL; goto fail; + } else if (!profile->policy.dfa) { + error = -EPROTO; + goto fail; } if (!unpack_u32(e, &profile->policy.start[0], "start")) /* default start state */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0021-apparmor-fix-module-parameters-can-be-changed-after-.patch000066400000000000000000000112361522511161100320710ustar00rootroot00000000000000From 45774028820fe2ffbbc94667165f04749821d529 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Jun 2016 18:01:08 -0700 Subject: [PATCH 21/27] apparmor: fix module parameters can be changed after policy is locked the policy_lock parameter is a one way switch that prevents policy from being further modified. Unfortunately some of the module parameters can effectively modify policy by turning off enforcement. split policy_admin_capable into a view check and a full admin check, and update the admin check to test the policy_lock parameter. Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 ++ security/apparmor/lsm.c | 22 ++++++++++------------ security/apparmor/policy.c | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index c28b0f2..52275f0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile) return profile->audit; } +bool policy_view_capable(void); +bool policy_admin_capable(void); bool aa_may_manage_policy(int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 5ee8201..bd40b12 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -751,51 +751,49 @@ __setup("apparmor=", apparmor_enabled_setup); /* set global flag turning off the ability to load policy */ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; - if (aa_g_lock_policy) - return -EACCES; return param_set_bool(val, kp); } static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aabool(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_bool(val, kp); } static int param_get_aabool(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aauint(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_uint(val, kp); } static int param_get_aauint(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_uint(buffer, kp); } static int param_get_audit(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; if (!apparmor_enabled) @@ -807,7 +805,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) static int param_set_audit(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -828,7 +826,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -840,7 +838,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7807125..179e68d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -918,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, &sa, NULL); } +bool policy_view_capable(void) +{ + struct user_namespace *user_ns = current_user_ns(); + bool response = false; + + if (ns_capable(user_ns, CAP_MAC_ADMIN)) + response = true; + + return response; +} + +bool policy_admin_capable(void) +{ + return policy_view_capable() && !aa_g_lock_policy; +} + /** * aa_may_manage_policy - can the current task manage policy * @op: the policy manipulation operation being done @@ -932,7 +948,7 @@ bool aa_may_manage_policy(int op) return 0; } - if (!capable(CAP_MAC_ADMIN)) { + if (!policy_admin_capable()) { audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); return 0; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0022-apparmor-do-not-expose-kernel-stack.patch000066400000000000000000000017571522511161100270630ustar00rootroot00000000000000From 7fcfc22cd04261ac35a579c99bcc804db7eb3e83 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 10 Jun 2016 23:34:26 +0200 Subject: [PATCH 22/27] apparmor: do not expose kernel stack Do not copy uninitalized fields th.td_hilen, th.td_data. Signed-off-by: Heinrich Schuchardt Signed-off-by: John Johansen --- security/apparmor/match.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 32b72eb..3f900fc 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -63,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) table = kvzalloc(tsize); if (table) { - *table = th; + table->td_id = th.td_id; + table->td_flags = th.td_flags; + table->td_lolen = th.td_lolen; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, u8, byte_to_byte); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0023-apparmor-fix-arg_size-computation-for-when-setprocat.patch000066400000000000000000000016371522511161100324530ustar00rootroot00000000000000From 1b98560066c26fecb0a61aeb9249e141af2e63f9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 9 Jul 2016 23:46:33 -0700 Subject: [PATCH 23/27] apparmor: fix arg_size computation for when setprocattr is null terminated Signed-off-by: John Johansen --- security/apparmor/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index bd40b12..1bf6c53 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -552,7 +552,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, if (!*args) goto out; - arg_size = size - (args - (char *) value); + arg_size = size - (args - (largs ? largs : (char *) value)); if (strcmp(name, "current") == 0) { if (strcmp(command, "changehat") == 0) { error = aa_setprocattr_changehat(args, arg_size, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0024-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000437201522511161100301530ustar00rootroot00000000000000From 8d7c032e7798fa1c46449728874b64fff882368b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 24/27] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df8..5dbb72f 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 729e595..181d961 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -807,6 +807,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index ba3dfd1..5d3c419 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -125,6 +125,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 52275f0..4fc4dac 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const aa_profile_mode_names[]; @@ -176,6 +177,7 @@ struct aa_replacedby { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -217,6 +219,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; unsigned char *hash; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 1bf6c53..284ddda 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -607,6 +608,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -636,6 +735,19 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 179e68d..f1a8541 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index dac2121..0107bc4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0025-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322200ustar00rootroot00000000000000From aa45ba104003404efb59e6f7178045ade756035d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 25/27] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0026-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000714271522511161100314110ustar00rootroot00000000000000From da5b036d6235f44c4ccbeaaf8d46fb29ff22745f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 26/27] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 15 +- security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/include/path.h | 2 +- security/apparmor/lsm.c | 59 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ security/apparmor/path.c | 8 +- 12 files changed, 773 insertions(+), 9 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5dbb72f..89b3445 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 181d961..5fb67f6 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -800,7 +800,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_policy[] = { AA_FS_FILE_BOOLEAN("set_load", 1), - {} + { } +}; + +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } }; static struct aa_fs_entry aa_fs_entry_features[] = { @@ -808,6 +819,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3a7f1da..c2a8b8a 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index fc3036b..f2a83b4 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -236,7 +236,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index e4ea626..ce6ff6a 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -30,8 +30,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 5d3c419..b9f1d57 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -72,6 +72,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -120,6 +124,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..a43b1d6 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 286ac75..73560f2 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -26,7 +26,7 @@ enum path_flags { PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ }; -int aa_path_name(struct path *path, int flags, char **buffer, +int aa_path_name(const struct path *path, int flags, char **buffer, const char **name, const char **info); #endif /* __AA_PATH_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 284ddda..780fec9 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -492,6 +493,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(const char *dev_name, struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -712,6 +767,10 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(capget, apparmor_capget), LSM_HOOK_INIT(capable, apparmor_capable), + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), + LSM_HOOK_INIT(path_link, apparmor_path_link), LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..9cf9170 --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, const struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 596f799..a8fc7d0 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -84,7 +84,7 @@ static int disconnect(const struct path *path, char *buf, char **name, * When no error the path name is returned in @name which points to * to a position in @buf */ -static int d_namespace_path(struct path *path, char *buf, int buflen, +static int d_namespace_path(const struct path *path, char *buf, int buflen, char **name, int flags) { char *res; @@ -169,7 +169,7 @@ out: * * Returns: %0 else error on failure */ -static int get_name_to_buffer(struct path *path, int flags, char *buffer, +static int get_name_to_buffer(const struct path *path, int flags, char *buffer, int size, char **name, const char **info) { int adjust = (flags & PATH_IS_DIR) ? 1 : 0; @@ -215,8 +215,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, * * Returns: %0 else error code if could retrieve name */ -int aa_path_name(struct path *path, int flags, char **buffer, const char **name, - const char **info) +int aa_path_name(const struct path *path, int flags, char **buffer, + const char **name, const char **info) { char *buf, *str = NULL; int size = 256; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.6/0027-UBUNTU-SAUCE-AppArmor-fix-boolreturn.cocci-warnings.patch000066400000000000000000000122451522511161100314420ustar00rootroot00000000000000From 1eff686074a6af0cf47fc24c45ebb001c570a98b Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 29 Jul 2016 12:44:43 +0800 Subject: [PATCH 27/27] UBUNTU: SAUCE: AppArmor: fix boolreturn.cocci warnings security/apparmor/policy_unpack.c:143:9-10: WARNING: return of 0/1 in function 'unpack_X' with return type bool security/apparmor/policy_unpack.c:189:9-10: WARNING: return of 0/1 in function 'unpack_nameX' with return type bool security/apparmor/policy_unpack.c:475:8-9: WARNING: return of 0/1 in function 'unpack_rlimits' with return type bool security/apparmor/policy_unpack.c:440:8-9: WARNING: return of 0/1 in function 'unpack_trans_table' with return type bool security/apparmor/policy_unpack.c:200:10-11: WARNING: return of 0/1 in function 'unpack_u16' with return type bool security/apparmor/policy_unpack.c:213:10-11: WARNING: return of 0/1 in function 'unpack_u32' with return type bool security/apparmor/policy_unpack.c:226:10-11: WARNING: return of 0/1 in function 'unpack_u64' with return type bool security/apparmor/policy_unpack.c:325:10-11: WARNING: return of 0/1 in function 'verify_accept' with return type bool security/apparmor/policy_unpack.c:739:10-11: WARNING: return of 0/1 in function 'verify_dfa_xindex' with return type bool security/apparmor/policy_unpack.c:729:9-10: WARNING: return of 0/1 in function 'verify_xindex' with return type bool Return statements in functions returning bool should use true/false instead of 1/0. Generated by: scripts/coccinelle/misc/boolreturn.cocci Signed-off-by: Fengguang Wu Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 0107bc4..af14626 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -140,11 +140,11 @@ static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) static bool unpack_X(struct aa_ext *e, enum aa_code code) { if (!inbounds(e, 1)) - return 0; + return false; if (*(u8 *) e->pos != code) - return 0; + return false; e->pos++; - return 1; + return true; } /** @@ -186,50 +186,50 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) /* now check if type code matches */ if (unpack_X(e, code)) - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) { if (unpack_nameX(e, AA_U16, name)) { if (!inbounds(e, sizeof(u16))) - return 0; + return false; if (data) *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); e->pos += sizeof(u16); - return 1; + return true; } - return 0; + return false; } static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { if (!inbounds(e, sizeof(u32))) - return 0; + return false; if (data) *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); e->pos += sizeof(u32); - return 1; + return true; } - return 0; + return false; } static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) { if (unpack_nameX(e, AA_U64, name)) { if (!inbounds(e, sizeof(u64))) - return 0; + return false; if (data) *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); e->pos += sizeof(u64); - return 1; + return true; } - return 0; + return false; } static size_t unpack_array(struct aa_ext *e, const char *name) @@ -322,12 +322,12 @@ static bool verify_accept(struct aa_dfa *dfa, int flags) int mode = ACCEPT_TABLE(dfa)[i]; if (mode & ~DFA_VALID_PERM_MASK) - return 0; + return false; if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) - return 0; + return false; } - return 1; + return true; } /** @@ -437,12 +437,12 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } - return 1; + return true; fail: aa_free_domain_entries(&profile->file.trans); e->pos = pos; - return 0; + return false; } static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) @@ -472,11 +472,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } /** @@ -726,8 +726,8 @@ static bool verify_xindex(int xindex, int table_size) xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; if (xtype == AA_X_TABLE && index >= table_size) - return 0; - return 1; + return false; + return true; } /* verify dfa xindexes are in range of transition tables */ @@ -736,11 +736,11 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) int i; for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) - return 0; + return false; if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) - return 0; + return false; } - return 1; + return true; } /** -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/000077500000000000000000000000001522511161100165475ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/4.7/0001-apparmor-fix-refcount-bug-in-profile-replacement.patch000066400000000000000000000022361522511161100315130ustar00rootroot00000000000000From 5ea33f587f5f7324c40c5986286d0f38307923bb Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:55:10 -0700 Subject: [PATCH 01/25] apparmor: fix refcount bug in profile replacement Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 705c287..222052f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1189,12 +1189,12 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) aa_get_profile(newest); aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); - } else - aa_put_profile(newest); + } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); __list_add_profile(&parent->base.profiles, ent->new); + aa_put_profile(newest); } else { /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0002-apparmor-fix-replacement-bug-that-adds-new-child-to-.patch000066400000000000000000000027261522511161100320470ustar00rootroot00000000000000From f65b1c9b72442e6166332c04f332e4b4d4797887 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:57:19 -0700 Subject: [PATCH 02/25] apparmor: fix replacement bug that adds new child to old parent When set atomic replacement is used and the parent is updated before the child, and the child did not exist in the old parent so there is no direct replacement then the new child is incorrectly added to the old parent. This results in the new parent not having the child(ren) that it should and the old parent when being destroyed asserting the following error. AppArmor: policy_destroy: internal error, policy '' still contains profiles Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 222052f..c92a9f6 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1193,7 +1193,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); - __list_add_profile(&parent->base.profiles, ent->new); + __list_add_profile(&newest->base.profiles, ent->new); aa_put_profile(newest); } else { /* aafs interface uses replacedby */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0003-apparmor-fix-uninitialized-lsm_audit-member.patch000066400000000000000000000100161522511161100306450ustar00rootroot00000000000000From b6669bef20c9d934bc6498e79fffa220f6226518 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 8 Jun 2014 11:20:54 -0700 Subject: [PATCH 03/25] apparmor: fix uninitialized lsm_audit member BugLink: http://bugs.launchpad.net/bugs/1268727 The task field in the lsm_audit struct needs to be initialized if a change_hat fails, otherwise the following oops will occur BUG: unable to handle kernel paging request at 0000002fbead7d08 IP: [] _raw_spin_lock+0xe/0x50 PGD 1e3f35067 PUD 0 Oops: 0002 [#1] SMP Modules linked in: pppox crc_ccitt p8023 p8022 psnap llc ax25 btrfs raid6_pq xor xfs libcrc32c dm_multipath scsi_dh kvm_amd dcdbas kvm microcode amd64_edac_mod joydev edac_core psmouse edac_mce_amd serio_raw k10temp sp5100_tco i2c_piix4 ipmi_si ipmi_msghandler acpi_power_meter mac_hid lp parport hid_generic usbhid hid pata_acpi mpt2sas ahci raid_class pata_atiixp bnx2 libahci scsi_transport_sas [last unloaded: tipc] CPU: 2 PID: 699 Comm: changehat_twice Tainted: GF O 3.13.0-7-generic #25-Ubuntu Hardware name: Dell Inc. PowerEdge R415/08WNM9, BIOS 1.8.6 12/06/2011 task: ffff8802135c6000 ti: ffff880212986000 task.ti: ffff880212986000 RIP: 0010:[] [] _raw_spin_lock+0xe/0x50 RSP: 0018:ffff880212987b68 EFLAGS: 00010006 RAX: 0000000000020000 RBX: 0000002fbead7500 RCX: 0000000000000000 RDX: 0000000000000292 RSI: ffff880212987ba8 RDI: 0000002fbead7d08 RBP: ffff880212987b68 R08: 0000000000000246 R09: ffff880216e572a0 R10: ffffffff815fd677 R11: ffffea0008469580 R12: ffffffff8130966f R13: ffff880212987ba8 R14: 0000002fbead7d08 R15: ffff8800d8c6b830 FS: 00002b5e6c84e7c0(0000) GS:ffff880216e40000(0000) knlGS:0000000055731700 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000002fbead7d08 CR3: 000000021270f000 CR4: 00000000000006e0 Stack: ffff880212987b98 ffffffff81075f17 ffffffff8130966f 0000000000000009 0000000000000000 0000000000000000 ffff880212987bd0 ffffffff81075f7c 0000000000000292 ffff880212987c08 ffff8800d8c6b800 0000000000000026 Call Trace: [] __lock_task_sighand+0x47/0x80 [] ? apparmor_cred_prepare+0x2f/0x50 [] do_send_sig_info+0x2c/0x80 [] send_sig_info+0x1e/0x30 [] aa_audit+0x13d/0x190 [] aa_audit_file+0xbc/0x130 [] ? apparmor_cred_prepare+0x2f/0x50 [] aa_change_hat+0x202/0x530 [] aa_setprocattr_changehat+0x116/0x1d0 [] apparmor_setprocattr+0x25d/0x300 [] security_setprocattr+0x16/0x20 [] proc_pid_attr_write+0x107/0x130 [] vfs_write+0xb4/0x1f0 [] SyS_write+0x49/0xa0 [] tracesys+0xe1/0xe6 Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/audit.c | 3 ++- security/apparmor/file.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 89c7865..3a7f1da 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, if (sa->aad->type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, - sa->u.tsk ? sa->u.tsk : current); + sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? + sa->u.tsk : current); if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) return complain_error(sa->aad->error); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index d186674..4d2af4b 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; + sa.type = LSM_AUDIT_DATA_TASK; + sa.u.tsk = NULL; sa.aad = &aad; aad.op = op, aad.fs.request = request; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0004-apparmor-exec-should-not-be-returning-ENOENT-when-it.patch000066400000000000000000000021141522511161100317420ustar00rootroot00000000000000From aeab4cbfb86d0faeeb709e8201672e0662aa2c6f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:03 -0700 Subject: [PATCH 04/25] apparmor: exec should not be returning ENOENT when it denies The current behavior is confusing as it causes exec failures to report the executable is missing instead of identifying that apparmor caused the failure. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index dc0027b..67a7418 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) new_profile = aa_get_newest_profile(ns->unconfined); info = "ux fallback"; } else { - error = -ENOENT; + error = -EACCES; info = "profile not found"; /* remove MAY_EXEC to audit as failure */ perms.allow &= ~MAY_EXEC; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0005-apparmor-fix-update-the-mtime-of-the-profile-file-on.patch000066400000000000000000000015741522511161100321020ustar00rootroot00000000000000From 752e4263021d90cf23c262f2fd3ebfd6dbccd455 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:01:56 -0700 Subject: [PATCH 05/25] apparmor: fix update the mtime of the profile file on replacement Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ad4fa49..45a6199 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -379,6 +379,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, for (i = 0; i < AAFS_PROF_SIZEOF; i++) { new->dents[i] = old->dents[i]; + if (new->dents[i]) + new->dents[i]->d_inode->i_mtime = CURRENT_TIME; old->dents[i] = NULL; } } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0006-apparmor-fix-disconnected-bind-mnts-reconnection.patch000066400000000000000000000021011522511161100315670ustar00rootroot00000000000000From 0c67233b18406dc7a8629faf8f9452feace6fb13 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:08 -0700 Subject: [PATCH 06/25] apparmor: fix disconnected bind mnts reconnection Bind mounts can fail to be properly reconnected when PATH_CONNECT is specified. Ensure that when PATH_CONNECT is specified the path has a root. BugLink: http://bugs.launchpad.net/bugs/1319984 Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/path.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index edddc02..f261678 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -141,7 +141,10 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, error = -EACCES; if (*res == '/') *name = res + 1; - } + } else if (*res != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + } out: -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0007-apparmor-internal-paths-should-be-treated-as-disconn.patch000066400000000000000000000067771522511161100323070ustar00rootroot00000000000000From 30c2b759b4f456e97e859ca550666c8abe84ff3c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:10 -0700 Subject: [PATCH 07/25] apparmor: internal paths should be treated as disconnected Internal mounts are not mounted anywhere and as such should be treated as disconnected paths. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/path.c | 64 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index f261678..a8fc7d0 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -25,7 +25,6 @@ #include "include/path.h" #include "include/policy.h" - /* modified from dcache.c */ static int prepend(char **buffer, int buflen, const char *str, int namelen) { @@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) +/* If the path is not connected to the expected root, + * check if it is a sysctl and handle specially else remove any + * leading / that __d_path may have returned. + * Unless + * specifically directed to connect the path, + * OR + * if in a chroot and doing chroot relative paths and the path + * resolves to the namespace root (would be connected outside + * of chroot) and specifically directed to connect paths to + * namespace root. + */ +static int disconnect(const struct path *path, char *buf, char **name, + int flags) +{ + int error = 0; + + if (!(flags & PATH_CONNECT_PATH) && + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && + our_mnt(path->mnt))) { + /* disconnected path, don't return pathname starting + * with '/' + */ + error = -EACCES; + if (**name == '/') + *name = *name + 1; + } else if (**name != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + + return error; +} + /** * d_namespace_path - lookup a name associated with a given path * @path: path to lookup (NOT NULL) @@ -74,7 +105,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, * control instead of hard coded /proc */ return prepend(name, *name - buf, "/proc", 5); - } + } else + return disconnect(path, buf, name, flags); return 0; } @@ -120,32 +152,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, goto out; } - /* If the path is not connected to the expected root, - * check if it is a sysctl and handle specially else remove any - * leading / that __d_path may have returned. - * Unless - * specifically directed to connect the path, - * OR - * if in a chroot and doing chroot relative paths and the path - * resolves to the namespace root (would be connected outside - * of chroot) and specifically directed to connect paths to - * namespace root. - */ - if (!connected) { - if (!(flags & PATH_CONNECT_PATH) && - !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && - our_mnt(path->mnt))) { - /* disconnected path, don't return pathname starting - * with '/' - */ - error = -EACCES; - if (*res == '/') - *name = res + 1; - } else if (*res != '/') - /* CONNECT_PATH with missing root */ - error = prepend(name, *name - buf, "/", 1); - - } + if (!connected) + error = disconnect(path, buf, name, flags); out: return error; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0008-apparmor-fix-put-parent-ref-after-updating-the-activ.patch000066400000000000000000000017761522511161100322360ustar00rootroot00000000000000From 35f89b597a40c870f93a068bc92a7ef4f9b16a66 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 13:59:02 -0700 Subject: [PATCH 08/25] apparmor: fix put() parent ref after updating the active ref Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c92a9f6..455c9f8 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1187,8 +1187,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* parent replaced in this atomic set? */ if (newest != parent) { aa_get_profile(newest); - aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); + aa_put_profile(parent); } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0009-apparmor-fix-log-failures-for-all-profiles-in-a-set.patch000066400000000000000000000056761522511161100317560ustar00rootroot00000000000000From 7b1ec6a04ca57fabe250f1102f2803dea7fbd03b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:16:50 -0700 Subject: [PATCH 09/25] apparmor: fix log failures for all profiles in a set currently only the profile that is causing the failure is logged. This makes it more confusing than necessary about which profiles loaded and which didn't. So make sure to log success and failure messages for all profiles in the set being loaded. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 455c9f8..db31bc5 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1067,7 +1067,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, */ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) { - const char *ns_name, *name = NULL, *info = NULL; + const char *ns_name, *info = NULL; struct aa_namespace *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; @@ -1082,18 +1082,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* released below */ ns = aa_prepare_namespace(ns_name); if (!ns) { - info = "failed to prepare namespace"; - error = -ENOMEM; - name = ns_name; - goto fail; + error = audit_policy(op, GFP_KERNEL, ns_name, + "failed to prepare namespace", -ENOMEM); + goto free; } mutex_lock(&ns->lock); /* setup parent and ns info */ list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; - - name = ent->new->base.hname; error = __lookup_replace(ns, ent->new->base.hname, noreplace, &ent->old, &info); if (error) @@ -1121,7 +1118,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) if (!p) { error = -ENOENT; info = "parent does not exist"; - name = ent->new->base.hname; goto fail_lock; } rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); @@ -1214,9 +1210,22 @@ out: fail_lock: mutex_unlock(&ns->lock); -fail: - error = audit_policy(op, GFP_KERNEL, name, info, error); + /* audit cause of failure */ + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); + /* audit status that rest of profiles in the atomic set failed too */ + info = "valid profile in failed atomic policy load"; + list_for_each_entry(tmp, &lh, list) { + if (tmp == ent) { + info = "unchecked profile in failed atomic policy load"; + /* skip entry that caused failure */ + continue; + } + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); + } +free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0010-apparmor-fix-audit-full-profile-hname-on-successful-.patch000066400000000000000000000023421522511161100322100ustar00rootroot00000000000000From 4c475747a31b0637f0d47cb9bddaf2c6efb02854 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:19:38 -0700 Subject: [PATCH 10/25] apparmor: fix audit full profile hname on successful load Currently logging of a successful profile load only logs the basename of the profile. This can result in confusion when a child profile has the same name as the another profile in the set. Logging the hname will ensure there is no confusion. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index db31bc5..ca402d0 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1159,7 +1159,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); + audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0011-apparmor-ensure-the-target-profile-name-is-always-au.patch000066400000000000000000000065351522511161100322300ustar00rootroot00000000000000From 430741dd766291d2e618b04e918ee6da844c230a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 20 Apr 2016 14:18:18 -0700 Subject: [PATCH 11/25] apparmor: ensure the target profile name is always audited The target profile name was not being correctly audited in a few cases because the target variable was not being set and gotos passed the code to set it at apply: Since it is always based on new_profile just drop the target var and conditionally report based on new_profile. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 67a7418..fc3036b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) file_inode(bprm->file)->i_uid, file_inode(bprm->file)->i_mode }; - const char *name = NULL, *target = NULL, *info = NULL; + const char *name = NULL, *info = NULL; int error = 0; if (bprm->cred_prepared) @@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (cxt->onexec) { struct file_perms cp; info = "change_profile onexec"; + new_profile = aa_get_newest_profile(cxt->onexec); if (!(perms.allow & AA_MAY_ONEXEC)) goto audit; @@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!(cp.allow & AA_MAY_ONEXEC)) goto audit; - new_profile = aa_get_newest_profile(cxt->onexec); goto apply; } @@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!new_profile) { error = -ENOMEM; info = "could not create null profile"; - } else { + } else error = -EACCES; - target = new_profile->base.hname; - } perms.xindex |= AA_X_UNSAFE; } else /* fail exec */ @@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * fail the exec. */ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) { - aa_put_profile(new_profile); error = -EPERM; goto cleanup; } @@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { error = may_change_ptraced_domain(new_profile); - if (error) { - aa_put_profile(new_profile); + if (error) goto audit; - } } /* Determine if secure exec is needed. @@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) bprm->unsafe |= AA_SECURE_X_NEEDED; } apply: - target = new_profile->base.hname; /* when transitioning profiles clear unsafe personality bits */ bprm->per_clear |= PER_CLEAR_ON_SETID; @@ -506,15 +500,19 @@ x_clear: aa_put_profile(cxt->profile); /* transfer new profile reference will be released when cxt is freed */ cxt->profile = new_profile; + new_profile = NULL; /* clear out all temporary/transitional state from the context */ aa_clear_task_cxt_trans(cxt); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, - name, target, cond.uid, info, error); + name, + new_profile ? new_profile->base.hname : NULL, + cond.uid, info, error); cleanup: + aa_put_profile(new_profile); aa_put_profile(profile); kfree(buffer); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0012-apparmor-check-that-xindex-is-in-trans_table-bounds.patch000066400000000000000000000016471522511161100321100ustar00rootroot00000000000000From 06763d057300b3d5bbe1894acfe236cf193bab78 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 17 Mar 2016 12:02:54 -0700 Subject: [PATCH 12/25] apparmor: check that xindex is in trans_table bounds Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a689f10..c841b12 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -676,7 +676,7 @@ static bool verify_xindex(int xindex, int table_size) int index, xtype; xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; - if (xtype == AA_X_TABLE && index > table_size) + if (xtype == AA_X_TABLE && index >= table_size) return 0; return 1; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0013-apparmor-fix-ref-count-leak-when-profile-sha1-hash-i.patch000066400000000000000000000014651522511161100317740ustar00rootroot00000000000000From 9ad29b2e7820895339f90eb71b411d0134cf1ce9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 18 Nov 2015 11:41:05 -0800 Subject: [PATCH 13/25] apparmor: fix ref count leak when profile sha1 hash is read Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 45a6199..0d8dd71 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) seq_printf(seq, "%.2x", profile->hash[i]); seq_puts(seq, "\n"); } + aa_put_profile(profile); return 0; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0014-apparmor-fix-refcount-race-when-finding-a-child-prof.patch000066400000000000000000000024201522511161100321260ustar00rootroot00000000000000From e13f968d154ba9d6a2c4f82f33d3312a63430b54 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Dec 2015 18:09:10 -0800 Subject: [PATCH 14/25] apparmor: fix refcount race when finding a child profile When finding a child profile via an rcu critical section, the profile may be put and scheduled for deletion after the child is found but before its refcount is incremented. Protect against this by repeating the lookup if the profiles refcount is 0 and is one its way to deletion. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/policy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ca402d0..7807125 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) struct aa_profile *profile; rcu_read_lock(); - profile = aa_get_profile(__find_child(&parent->base.profiles, name)); + do { + profile = __find_child(&parent->base.profiles, name); + } while (profile && !aa_get_profile_not0(profile)); rcu_read_unlock(); /* refcount released by caller */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0015-apparmor-use-list_next_entry-instead-of-list_entry_n.patch000066400000000000000000000036771522511161100325710ustar00rootroot00000000000000From 5833ccff1227fbc8f1bab64351f6747a6c71bdeb Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 16 Nov 2015 21:46:33 +0800 Subject: [PATCH 15/25] apparmor: use list_next_entry instead of list_entry_next list_next_entry has been defined in list.h, so I replace list_entry_next with it. Signed-off-by: Geliang Tang Acked-by: Serge Hallyn Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0d8dd71..729e595 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -553,8 +553,6 @@ fail2: } -#define list_entry_next(pos, member) \ - list_entry(pos->member.next, typeof(*pos), member) #define list_entry_is_head(pos, head, member) (&pos->member == (head)) /** @@ -585,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, parent = ns->parent; while (ns != root) { mutex_unlock(&ns->lock); - next = list_entry_next(ns, base.list); + next = list_next_entry(ns, base.list); if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { mutex_lock(&next->lock); return next; @@ -639,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) parent = rcu_dereference_protected(p->parent, mutex_is_locked(&p->ns->lock)); while (parent) { - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &parent->base.profiles, base.list)) return p; p = parent; @@ -648,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) } /* is next another profile in the namespace */ - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &ns->base.profiles, base.list)) return p; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0016-apparmor-allow-SYS_CAP_RESOURCE-to-be-sufficient-to-.patch000066400000000000000000000037441522511161100314250ustar00rootroot00000000000000From 645801f1ddd183109c011e5ecee23ed3fdcae244 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 6 Nov 2015 15:17:30 -0500 Subject: [PATCH 16/25] apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task While using AppArmor, SYS_CAP_RESOURCE is insufficient to call prlimit on another task. The only other example of a AppArmor mediating access to another, already running, task (ignoring fork+exec) is ptrace. The AppArmor model for ptrace is that one of the following must be true: 1) The tracer is unconfined 2) The tracer is in complain mode 3) The tracer and tracee are confined by the same profile 4) The tracer is confined but has SYS_CAP_PTRACE 1), 2, and 3) are already true for setrlimit. We can match the ptrace model just by allowing CAP_SYS_RESOURCE. We still test the values of the rlimit since it can always be overridden using a value that means unlimited for a particular resource. Signed-off-by: Jeff Mahoney Signed-off-by: John Johansen --- security/apparmor/resource.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 748bf0c..67a6072 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, /* TODO: extend resource control to handle other (non current) * profiles. AppArmor rules currently have the implicit assumption * that the task is setting the resource of a task confined with - * the same profile. + * the same profile or that the task setting the resource of another + * task has CAP_SYS_RESOURCE. */ - if (profile != task_profile || + if ((profile != task_profile && + aa_capable(profile, CAP_SYS_RESOURCE, 1)) || (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) error = -EACCES; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0017-apparmor-add-missing-id-bounds-check-on-dfa-verifica.patch000066400000000000000000000024741522511161100320670ustar00rootroot00000000000000From 2be4aed1f3332d87273eb593944332054f3cffac Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 2 Jun 2016 02:37:02 -0700 Subject: [PATCH 17/25] apparmor: add missing id bounds check on dfa verification Signed-off-by: John Johansen --- security/apparmor/include/match.h | 1 + security/apparmor/match.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 001c43a..a1c04fe 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -62,6 +62,7 @@ struct table_set_header { #define YYTD_ID_ACCEPT2 6 #define YYTD_ID_NXT 7 #define YYTD_ID_TSIZE 8 +#define YYTD_ID_MAX 8 #define YYTD_DATA8 1 #define YYTD_DATA16 2 diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 727eb42..f9f57c6 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize) * it every time we use td_id as an index */ th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; + if (th.td_id > YYTD_ID_MAX) + goto out; th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); blob += sizeof(struct table_header); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0018-apparmor-don-t-check-for-vmalloc_addr-if-kvzalloc-fa.patch000066400000000000000000000021421522511161100321040ustar00rootroot00000000000000From c7f87d3c3363b1a0c4724e627e5c8e640a883c89 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 09:57:55 +0300 Subject: [PATCH 18/25] apparmor: don't check for vmalloc_addr if kvzalloc() failed Signed-off-by: John Johansen --- security/apparmor/match.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index f9f57c6..32b72eb 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -75,14 +75,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize) u32, be32_to_cpu); else goto fail; + /* if table was vmalloced make sure the page tables are synced + * before it is used, as it goes live to all cpus. + */ + if (is_vmalloc_addr(table)) + vm_unmap_aliases(); } out: - /* if table was vmalloced make sure the page tables are synced - * before it is used, as it goes live to all cpus. - */ - if (is_vmalloc_addr(table)) - vm_unmap_aliases(); return table; fail: kvfree(table); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0019-apparmor-fix-oops-in-profile_unpack-when-policy_db-i.patch000066400000000000000000000021311522511161100322710ustar00rootroot00000000000000From 0f7e61013dd1e67ebb54d58eee11ab009ceb5ef3 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 10:00:55 +0300 Subject: [PATCH 19/25] apparmor: fix oops in profile_unpack() when policy_db is not present BugLink: http://bugs.launchpad.net/bugs/1592547 If unpack_dfa() returns NULL due to the dfa not being present, profile_unpack() is not checking if the dfa is not present (NULL). Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c841b12..dac2121 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) error = PTR_ERR(profile->policy.dfa); profile->policy.dfa = NULL; goto fail; + } else if (!profile->policy.dfa) { + error = -EPROTO; + goto fail; } if (!unpack_u32(e, &profile->policy.start[0], "start")) /* default start state */ -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0020-apparmor-fix-module-parameters-can-be-changed-after-.patch000066400000000000000000000112361522511161100320710ustar00rootroot00000000000000From de4ca46ec035283928e8fa40797897cefcf6ec3e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Jun 2016 18:01:08 -0700 Subject: [PATCH 20/25] apparmor: fix module parameters can be changed after policy is locked the policy_lock parameter is a one way switch that prevents policy from being further modified. Unfortunately some of the module parameters can effectively modify policy by turning off enforcement. split policy_admin_capable into a view check and a full admin check, and update the admin check to test the policy_lock parameter. Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 ++ security/apparmor/lsm.c | 22 ++++++++++------------ security/apparmor/policy.c | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index c28b0f2..52275f0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile) return profile->audit; } +bool policy_view_capable(void); +bool policy_admin_capable(void); bool aa_may_manage_policy(int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 7798e16..e83eefb 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -728,51 +728,49 @@ __setup("apparmor=", apparmor_enabled_setup); /* set global flag turning off the ability to load policy */ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; - if (aa_g_lock_policy) - return -EACCES; return param_set_bool(val, kp); } static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aabool(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_bool(val, kp); } static int param_get_aabool(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aauint(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_uint(val, kp); } static int param_get_aauint(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_uint(buffer, kp); } static int param_get_audit(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; if (!apparmor_enabled) @@ -784,7 +782,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) static int param_set_audit(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -805,7 +803,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -817,7 +815,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7807125..179e68d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -918,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, &sa, NULL); } +bool policy_view_capable(void) +{ + struct user_namespace *user_ns = current_user_ns(); + bool response = false; + + if (ns_capable(user_ns, CAP_MAC_ADMIN)) + response = true; + + return response; +} + +bool policy_admin_capable(void) +{ + return policy_view_capable() && !aa_g_lock_policy; +} + /** * aa_may_manage_policy - can the current task manage policy * @op: the policy manipulation operation being done @@ -932,7 +948,7 @@ bool aa_may_manage_policy(int op) return 0; } - if (!capable(CAP_MAC_ADMIN)) { + if (!policy_admin_capable()) { audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); return 0; } -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0021-apparmor-do-not-expose-kernel-stack.patch000066400000000000000000000017571522511161100270630ustar00rootroot00000000000000From 46c339f46b83e4cf8098f599cd182e65e9d054fc Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 10 Jun 2016 23:34:26 +0200 Subject: [PATCH 21/25] apparmor: do not expose kernel stack Do not copy uninitalized fields th.td_hilen, th.td_data. Signed-off-by: Heinrich Schuchardt Signed-off-by: John Johansen --- security/apparmor/match.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 32b72eb..3f900fc 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -63,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) table = kvzalloc(tsize); if (table) { - *table = th; + table->td_id = th.td_id; + table->td_flags = th.td_flags; + table->td_lolen = th.td_lolen; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, u8, byte_to_byte); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0022-apparmor-fix-arg_size-computation-for-when-setprocat.patch000066400000000000000000000016371522511161100324530ustar00rootroot00000000000000From 7e65e8142b2ea4891581173d6e92fc337b02ff8b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 9 Jul 2016 23:46:33 -0700 Subject: [PATCH 22/25] apparmor: fix arg_size computation for when setprocattr is null terminated Signed-off-by: John Johansen --- security/apparmor/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e83eefb..ba8207b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -529,7 +529,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, if (!*args) goto out; - arg_size = size - (args - (char *) value); + arg_size = size - (args - (largs ? largs : (char *) value)); if (strcmp(name, "current") == 0) { if (strcmp(command, "changehat") == 0) { error = aa_setprocattr_changehat(args, arg_size, -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0023-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000437201522511161100301530ustar00rootroot00000000000000From b661b13237991be6b5cdf0849f137c5ec58217bf Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 23/25] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70..d5b291e 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df8..5dbb72f 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 729e595..181d961 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -807,6 +807,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index ba3dfd1..5d3c419 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -125,6 +125,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..cb8a121 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 52275f0..4fc4dac 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const aa_profile_mode_names[]; @@ -176,6 +177,7 @@ struct aa_replacedby { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -217,6 +219,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; unsigned char *hash; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ba8207b..88d3b0a 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -584,6 +585,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -613,6 +712,19 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 0000000..003dd18 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 179e68d..f1a8541 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index dac2121..0107bc4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0024-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000027741522511161100322200ustar00rootroot00000000000000From 64c5e24470a219c79c2870c63f18f6bd55648b1b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 24/25] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18..6e6e5c9 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 2.7.4 apparmor-5.0.2/kernel-patches/4.7/0025-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000657031522511161100314110ustar00rootroot00000000000000From f7cef61751a2382fb4ea26c18736d7552ffdb24a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 25/25] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 15 +- security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 60 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ 10 files changed, 769 insertions(+), 4 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5dbb72f..89b3445 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 181d961..5fb67f6 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -800,7 +800,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_policy[] = { AA_FS_FILE_BOOLEAN("set_load", 1), - {} + { } +}; + +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } }; static struct aa_fs_entry aa_fs_entry_features[] = { @@ -808,6 +819,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3a7f1da..c2a8b8a 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index fc3036b..f2a83b4 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -236,7 +236,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index e4ea626..ce6ff6a 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -30,8 +30,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 5d3c419..b9f1d57 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -72,6 +72,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -120,6 +124,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..a3f70c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 0000000..a43b1d6 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 88d3b0a..432cbd3 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -469,6 +470,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(const char *dev_name, const struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(const struct path *old_path, + const struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -689,6 +745,10 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(capget, apparmor_capget), LSM_HOOK_INIT(capable, apparmor_capable), + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), + LSM_HOOK_INIT(path_link, apparmor_path_link), LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 0000000..9cf9170 --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, const struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 2.7.4 apparmor-5.0.2/kernel-patches/v4.11/000077500000000000000000000000001522511161100170105ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/v4.11/0001-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000447341522511161100304160ustar00rootroot00000000000000From 97b3200925ba627346432edf521d49de8bb018a3 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 ++++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 59 +++++++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 ++++++++++++++++++++++++++++ security/apparmor/net.c | 148 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 47 +++++++++++- 10 files changed, 415 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70d72b8..d5b291e94264 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index ad369a7aac24..a7dc10be232d 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o + resource.o secid.o file.o policy_ns.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 41073f70eb41..4d236736cfb8 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1209,6 +1209,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index fdc4774318ba..0df708e8748b 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { int rlim; unsigned long max; } rlim; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 000000000000..55da1dad8720 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,59 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ + struct lsm_network_audit NAME ## _net = { .sk = (SK), \ + .family = (F)}; \ + DEFINE_AUDIT_DATA(NAME, \ + ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ + LSM_AUDIT_DATA_NONE, \ + OP); \ + NAME.u.net = &(NAME ## _net); \ + aad(&NAME)->net.type = (T); \ + aad(&NAME)->net.protocol = (P) + +#define DEFINE_AUDIT_SK(NAME, OP, SK) \ + DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ + (SK)->sk_protocol) + +extern int aa_net_perm(const char *op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(const char *op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 67bc96afe541..a3d18ea8d730 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -28,6 +28,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "lib.h" #include "resource.h" @@ -132,6 +133,7 @@ struct aa_data { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -174,6 +176,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; struct aa_loaddata *rawdata; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 709eacd23909..e3017129a404 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -33,6 +33,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/policy_ns.h" @@ -587,6 +588,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SHUTDOWN, sk); +} + static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -616,6 +715,19 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 000000000000..b9c8cd0e882e --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,148 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[aad(sa)->net.type]) { + audit_log_string(ab, sock_type_names[aad(sa)->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type); + } + audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, const char *op, u16 family, + int type, int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + DEFINE_AUDIT_NET(sa, op, sk, family, type, protocol); + + aad(&sa)->error = error; + + if (likely(!aad(&sa)->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << aad(&sa)->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error; + } + + return aa_audit(audit_type, profile, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(const char *op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(const char *op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index def1fbd6bdfd..9fe7b9d4500f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -237,6 +237,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 2e37c9c26bbd..bc23a5b3b113 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -217,6 +217,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -519,7 +532,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) { struct aa_profile *profile = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; - size_t ns_len; + size_t ns_len, size = 0; struct rhashtable_params params = { 0 }; char *key = NULL; struct aa_data *data; @@ -635,6 +648,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.11/0002-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000030071522511161100324430ustar00rootroot00000000000000From b866a43c2897f5469c9d787426144074a3713f6a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 2/3] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index b9c8cd0e882e..5ba19ad1d65c 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -74,7 +74,7 @@ static int audit_net(struct aa_profile *profile, const char *op, u16 family, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask; + u16 denied = (1 << aad(&sa)->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.11/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000651321522511161100316420ustar00rootroot00000000000000From 4429c3f9522b608300cfe1ae148dc6cdadf3d76c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 60 ++++ security/apparmor/mount.c | 616 +++++++++++++++++++++++++++++++++++ 9 files changed, 760 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index a7dc10be232d..01368441f230 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o net.o + resource.o secid.o file.o policy_ns.o net.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 4d236736cfb8..2e8d09e2368b 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1205,11 +1205,24 @@ static struct aa_fs_entry aa_fs_entry_policy[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 001e133a3c8c..708b7e22b9b5 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -237,7 +237,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_ns *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 1750cc0721c1..3383dc66f30f 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -27,8 +27,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 0df708e8748b..41374ad89547 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -70,6 +70,10 @@ enum audit_type { #define OP_FMMAP "file_mmap" #define OP_FMPROT "file_mprotect" +#define OP_PIVOTROOT "pivotroot" +#define OP_MOUNT "mount" +#define OP_UMOUNT "umount" + #define OP_CREATE "create" #define OP_POST_CREATE "post_create" #define OP_BIND "bind" @@ -127,6 +131,13 @@ struct apparmor_audit_data { int rlim; unsigned long max; } rlim; + struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; struct { int type, protocol; struct sock *sk; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index 30544729878a..7bd21d20a2bd 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 000000000000..a43b1d62e428 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e3017129a404..ee58a2cca74f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -38,6 +38,7 @@ #include "include/policy.h" #include "include/policy_ns.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -479,6 +480,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(const char *dev_name, const struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(const struct path *old_path, + const struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -692,6 +748,10 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(capget, apparmor_capget), LSM_HOOK_INIT(capable, apparmor_capable), + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), + LSM_HOOK_INIT(path_link, apparmor_path_link), LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 000000000000..9e95a41c015c --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,616 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (aad(sa)->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, aad(sa)->mnt.type); + } + if (aad(sa)->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, aad(sa)->mnt.src_name); + } + if (aad(sa)->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, aad(sa)->mnt.trans); + } + if (aad(sa)->mnt.flags) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, aad(sa)->mnt.flags); + audit_log_format(ab, "\""); + } + if (aad(sa)->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, aad(sa)->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, const char *op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + aad(&sa)->name = name; + aad(&sa)->mnt.src_name = src_name; + aad(&sa)->mnt.type = type; + aad(&sa)->mnt.trans = trans; + aad(&sa)->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + aad(&sa)->mnt.data = data; + aad(&sa)->info = info; + aad(&sa)->error = error; + + return aa_audit(audit_type, profile, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, const struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.12/000077500000000000000000000000001522511161100170115ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/v4.12/0001-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000450041522511161100304060ustar00rootroot00000000000000From adbeb027cbafd78a76d5786e082d7c7abb19a591 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 ++++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 59 +++++++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 ++++++++++++++++++++++++++++ security/apparmor/net.c | 148 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 47 +++++++++++- 10 files changed, 415 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70d72b8..d5b291e94264 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index ad369a7aac24..a7dc10be232d 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o + resource.o secid.o file.o policy_ns.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 4f6ac9dbc65d..4b121211e5e7 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1209,6 +1209,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index fdc4774318ba..0df708e8748b 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,6 +127,10 @@ struct apparmor_audit_data { int rlim; unsigned long max; } rlim; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 000000000000..55da1dad8720 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,59 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ + struct lsm_network_audit NAME ## _net = { .sk = (SK), \ + .family = (F)}; \ + DEFINE_AUDIT_DATA(NAME, \ + ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ + LSM_AUDIT_DATA_NONE, \ + OP); \ + NAME.u.net = &(NAME ## _net); \ + aad(&NAME)->net.type = (T); \ + aad(&NAME)->net.protocol = (P) + +#define DEFINE_AUDIT_SK(NAME, OP, SK) \ + DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ + (SK)->sk_protocol) + +extern int aa_net_perm(const char *op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(const char *op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 67bc96afe541..a3d18ea8d730 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -28,6 +28,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "lib.h" #include "resource.h" @@ -132,6 +133,7 @@ struct aa_data { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -174,6 +176,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; struct aa_loaddata *rawdata; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8f3c0f7aca5a..758ddf4a0791 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -33,6 +33,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/policy_ns.h" @@ -587,6 +588,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SHUTDOWN, sk); +} + static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -616,6 +715,19 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 000000000000..b9c8cd0e882e --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,148 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[aad(sa)->net.type]) { + audit_log_string(ab, sock_type_names[aad(sa)->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type); + } + audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, const char *op, u16 family, + int type, int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + DEFINE_AUDIT_NET(sa, op, sk, family, type, protocol); + + aad(&sa)->error = error; + + if (likely(!aad(&sa)->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << aad(&sa)->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error; + } + + return aa_audit(audit_type, profile, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(const char *op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(const char *op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index cf9d670dca94..0eea92aeb02d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -237,6 +237,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index f3422a91353c..89a1bd78f765 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -217,6 +217,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -519,7 +532,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) { struct aa_profile *profile = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; - size_t ns_len; + size_t ns_len, size = 0; struct rhashtable_params params = { 0 }; char *key = NULL; struct aa_data *data; @@ -635,6 +648,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.12/0002-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000030071522511161100324440ustar00rootroot00000000000000From 7ed04b256a6313a83a2d9c94f7295d81acf11848 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 2/3] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index b9c8cd0e882e..5ba19ad1d65c 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -74,7 +74,7 @@ static int audit_net(struct aa_profile *profile, const char *op, u16 family, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask; + u16 denied = (1 << aad(&sa)->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.12/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000651431522511161100316450ustar00rootroot00000000000000From 13765e11a34d38ce04b3c28f21fe94d420746a90 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 13 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 60 ++++ security/apparmor/mount.c | 616 +++++++++++++++++++++++++++++++++++ 9 files changed, 760 insertions(+), 3 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index a7dc10be232d..01368441f230 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o net.o + resource.o secid.o file.o policy_ns.o net.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 4b121211e5e7..8e1c18b23d75 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1205,11 +1205,24 @@ static struct aa_fs_entry aa_fs_entry_policy[] = { { } }; +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 001e133a3c8c..708b7e22b9b5 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -237,7 +237,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_ns *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 1750cc0721c1..3383dc66f30f 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -27,8 +27,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 0df708e8748b..41374ad89547 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -70,6 +70,10 @@ enum audit_type { #define OP_FMMAP "file_mmap" #define OP_FMPROT "file_mprotect" +#define OP_PIVOTROOT "pivotroot" +#define OP_MOUNT "mount" +#define OP_UMOUNT "umount" + #define OP_CREATE "create" #define OP_POST_CREATE "post_create" #define OP_BIND "bind" @@ -127,6 +131,13 @@ struct apparmor_audit_data { int rlim; unsigned long max; } rlim; + struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; struct { int type, protocol; struct sock *sk; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index 30544729878a..7bd21d20a2bd 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 000000000000..a43b1d62e428 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 758ddf4a0791..b57f24045c0d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -38,6 +38,7 @@ #include "include/policy.h" #include "include/policy_ns.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized; @@ -479,6 +480,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(const char *dev_name, const struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(const struct path *old_path, + const struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -692,6 +748,10 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(capget, apparmor_capget), LSM_HOOK_INIT(capable, apparmor_capable), + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), + LSM_HOOK_INIT(path_link, apparmor_path_link), LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 000000000000..9e95a41c015c --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,616 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (aad(sa)->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, aad(sa)->mnt.type); + } + if (aad(sa)->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, aad(sa)->mnt.src_name); + } + if (aad(sa)->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, aad(sa)->mnt.trans); + } + if (aad(sa)->mnt.flags) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, aad(sa)->mnt.flags); + audit_log_format(ab, "\""); + } + if (aad(sa)->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, aad(sa)->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, const char *op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + aad(&sa)->name = name; + aad(&sa)->mnt.src_name = src_name; + aad(&sa)->mnt.type = type; + aad(&sa)->mnt.trans = trans; + aad(&sa)->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + aad(&sa)->mnt.data = data; + aad(&sa)->info = info; + aad(&sa)->error = error; + + return aa_audit(audit_type, profile, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, const struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/000077500000000000000000000000001522511161100170125ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/v4.13/0001-UBUNTU-SAUCE-efi-lockdown-MODSIGN-Fix-module-signatu.patch000066400000000000000000000031001522511161100314170ustar00rootroot00000000000000From 00c72bc198aa85e5da02de2c0c4cc423c82a54f1 Mon Sep 17 00:00:00 2001 From: Fedora Kernel Team Date: Thu, 3 Aug 2017 13:46:51 -0500 Subject: [PATCH 01/17] UBUNTU: SAUCE: (efi-lockdown) MODSIGN: Fix module signature verification BugLink: http://bugs.launchpad.net/bugs/1712168 Currently mod_verify_sig() calls verify_pkcs_7_signature() with trusted_keys=NULL, which causes only the builtin keys to be used to verify the signature. This breaks self-signing of modules with a MOK, as the MOK is loaded into the secondary trusted keyring. Fix this by passing the spacial value trusted_keys=(void *)1UL, which tells verify_pkcs_7_signature() to use the secondary keyring instead. (cherry picked from commit cff4523d65b848f9c41c9e998a735ae2a820da2d git://git.kernel.org/pub/scm/linux/kernel/git/jwboyer/fedora.git) [ saf: Taken from fedora commit without authorship information or much of a commit message; modified so that commit will describe the problem being fixed. ] Signed-off-by: Seth Forshee --- kernel/module_signing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/module_signing.c b/kernel/module_signing.c index 937c844bee4a..d3d6f95a96b4 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -81,6 +81,6 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) } return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, - NULL, VERIFYING_MODULE_SIGNATURE, + (void *)1UL, VERIFYING_MODULE_SIGNATURE, NULL, NULL); } -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch000066400000000000000000000033741522511161100325270ustar00rootroot00000000000000From c6cad5e65a23dcafa1821ca381901297664d9c64 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 6 Jul 2017 10:56:21 +0200 Subject: [PATCH 02/17] apparmor: Fix shadowed local variable in unpack_trans_table() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit with W=2: security/apparmor/policy_unpack.c: In function ‘unpack_trans_table’: security/apparmor/policy_unpack.c:469: warning: declaration of ‘pos’ shadows a previous local security/apparmor/policy_unpack.c:451: warning: shadowed declaration is here Rename the old "pos" to "saved_pos" to fix this. Fixes: 5379a3312024a8be ("apparmor: support v7 transition format compatible with label_parse") Signed-off-by: Geert Uytterhoeven Reviewed-by: Serge Hallyn Signed-off-by: John Johansen (cherry picked from commit 966d631935a578fadb5770f17a957ee1a969d868) --- security/apparmor/policy_unpack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c600f4dd1783..2d5a1a007b06 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -448,7 +448,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) */ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) { - void *pos = e->pos; + void *saved_pos = e->pos; /* exec table is optional */ if (unpack_nameX(e, AA_STRUCT, "xtable")) { @@ -511,7 +511,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) fail: aa_free_domain_entries(&profile->file.trans); - e->pos = pos; + e->pos = saved_pos; return 0; } -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0003-apparmor-Fix-logical-error-in-verify_header.patch000066400000000000000000000025141522511161100307370ustar00rootroot00000000000000From 9934296cba701d429a0fc0cf071a40c8c3a1587e Mon Sep 17 00:00:00 2001 From: Christos Gkekas Date: Sat, 8 Jul 2017 20:50:21 +0100 Subject: [PATCH 03/17] apparmor: Fix logical error in verify_header() verify_header() is currently checking whether interface version is less than 5 *and* greater than 7, which always evaluates to false. Instead it should check whether it is less than 5 *or* greater than 7. Signed-off-by: Christos Gkekas Signed-off-by: John Johansen (cherry picked from commit c54a2175e3a6bf6c697d249bba1aa729e06c7ba8) --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 2d5a1a007b06..bda0dce3b582 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -832,7 +832,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) * if not specified use previous version * Mask off everything that is not kernel abi version */ - if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { + if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) { audit_iface(NULL, NULL, NULL, "unsupported interface version", e, error); return error; -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0004-apparmor-Fix-an-error-code-in-aafs_create.patch000066400000000000000000000024741522511161100302620ustar00rootroot00000000000000From 8b3851c7b83f32f2be9d4b48371ddf033afedf62 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 13 Jul 2017 10:39:20 +0300 Subject: [PATCH 04/17] apparmor: Fix an error code in aafs_create() We accidentally forgot to set the error code on this path. It means we return NULL instead of an error pointer. I looked through a bunch of callers and I don't think it really causes a big issue, but the documentation says we're supposed to return error pointers here. Signed-off-by: Dan Carpenter Acked-by: Serge Hallyn Signed-off-by: John Johansen (cherry picked from commit aee58bf341db52a3a3563c6b972bfd4fc2d41e46) --- security/apparmor/apparmorfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 853c2ec8e0c9..2caeb748070c 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -248,8 +248,10 @@ static struct dentry *aafs_create(const char *name, umode_t mode, inode_lock(dir); dentry = lookup_one_len(name, parent, strlen(name)); - if (IS_ERR(dentry)) + if (IS_ERR(dentry)) { + error = PTR_ERR(dentry); goto fail_lock; + } if (d_really_is_positive(dentry)) { error = -EEXIST; -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch000066400000000000000000000020251522511161100321100ustar00rootroot00000000000000From 4b56e146905bbad2c79ea92e3f49e210ca527572 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 31 Jul 2017 23:44:37 -0700 Subject: [PATCH 05/17] apparmor: Redundant condition: prev_ns. in [label.c:1498] Reported-by: David Binderman Signed-off-by: John Johansen (cherry picked from commit d323d2c17cfcc54b6845bfc1d13bca5cef210fc7) --- security/apparmor/label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index e052eaba1cf6..e324f4df3e34 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -1495,7 +1495,7 @@ static int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view, view = profiles_ns(profile); if (view != profile->ns && - (!prev_ns || (prev_ns && *prev_ns != profile->ns))) { + (!prev_ns || (*prev_ns != profile->ns))) { if (prev_ns) *prev_ns = profile->ns; ns_name = aa_ns_name(view, profile->ns, -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0006-apparmor-add-the-ability-to-mediate-signals.patch000066400000000000000000000300001522511161100306530ustar00rootroot00000000000000From f9e20353a6c5726775867db81b6085e8ab425a36 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 22:56:22 -0700 Subject: [PATCH 06/17] apparmor: add the ability to mediate signals Add signal mediation where the signal can be mediated based on the signal, direction, or the label or the peer/target. The signal perms are verified on a cross check to ensure policy consistency in the case of incremental policy load/replacement. The optimization of skipping the cross check when policy is guaranteed to be consistent (single compile unit) remains to be done. policy rules have the form of SIGNAL_RULE = [ QUALIFIERS ] 'signal' [ SIGNAL ACCESS PERMISSIONS ] [ SIGNAL SET ] [ SIGNAL PEER ] SIGNAL ACCESS PERMISSIONS = SIGNAL ACCESS | SIGNAL ACCESS LIST SIGNAL ACCESS LIST = '(' Comma or space separated list of SIGNAL ACCESS ')' SIGNAL ACCESS = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' | 'receive' ) SIGNAL SET = 'set' '=' '(' SIGNAL LIST ')' SIGNAL LIST = Comma or space separated list of SIGNALS SIGNALS = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32' ) SIGNAL PEER = 'peer' '=' AARE eg. signal, # allow all signals signal send set=(hup, kill) peer=foo, Signed-off-by: John Johansen Acked-by: Seth Arnold (cherry picked from commit c6bf1adaecaa719d7c56338cc43b2982214f2f44) --- security/apparmor/apparmorfs.c | 7 +++ security/apparmor/include/apparmor.h | 1 + security/apparmor/include/audit.h | 2 + security/apparmor/include/ipc.h | 6 +++ security/apparmor/include/sig_names.h | 95 +++++++++++++++++++++++++++++++++ security/apparmor/ipc.c | 99 +++++++++++++++++++++++++++++++++++ security/apparmor/lsm.c | 21 ++++++++ 7 files changed, 231 insertions(+) create mode 100644 security/apparmor/include/sig_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2caeb748070c..a5f9e1aa51f7 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -32,6 +32,7 @@ #include "include/audit.h" #include "include/context.h" #include "include/crypto.h" +#include "include/ipc.h" #include "include/policy_ns.h" #include "include/label.h" #include "include/policy.h" @@ -2129,6 +2130,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_signal[] = { + AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK), + { } +}; + static struct aa_sfs_entry aa_sfs_entry_domain[] = { AA_SFS_FILE_BOOLEAN("change_hat", 1), AA_SFS_FILE_BOOLEAN("change_hatv", 1), @@ -2179,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), AA_SFS_DIR("caps", aa_sfs_entry_caps), AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), + AA_SFS_DIR("signal", aa_sfs_entry_signal), AA_SFS_DIR("query", aa_sfs_entry_query), { } }; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index aaf893f4e4f5..962a20a75e01 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -28,6 +28,7 @@ #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 #define AA_CLASS_PTRACE 9 +#define AA_CLASS_SIGNAL 10 #define AA_CLASS_LABEL 16 #define AA_CLASS_LAST AA_CLASS_LABEL diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index c68839a44351..d9a156ae11b9 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -86,6 +86,7 @@ enum audit_type { #define OP_SHUTDOWN "socket_shutdown" #define OP_PTRACE "ptrace" +#define OP_SIGNAL "signal" #define OP_EXEC "exec" @@ -126,6 +127,7 @@ struct apparmor_audit_data { long pos; const char *ns; } iface; + int signal; struct { int rlim; unsigned long max; diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index 656fdb81c8a0..5ffc218d1e74 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h @@ -27,8 +27,14 @@ struct aa_profile; #define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ AA_MAY_BE_READ | AA_MAY_BE_TRACED) +#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE) + +#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \ + "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \ + "xcpu xfsz vtalrm prof winch io pwr sys emt lost" int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, u32 request); +int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig); #endif /* __AA_IPC_H */ diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h new file mode 100644 index 000000000000..0d4395f231ca --- /dev/null +++ b/security/apparmor/include/sig_names.h @@ -0,0 +1,95 @@ +#include + +#define SIGUNKNOWN 0 +#define MAXMAPPED_SIG 35 +/* provide a mapping of arch signal to internal signal # for mediation + * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO + * map to the same entry those that may/or may not get a separate entry + */ +static const int sig_map[MAXMAPPED_SIG] = { + [0] = MAXMAPPED_SIG, /* existence test */ + [SIGHUP] = 1, + [SIGINT] = 2, + [SIGQUIT] = 3, + [SIGILL] = 4, + [SIGTRAP] = 5, /* -, 5, - */ + [SIGABRT] = 6, /* SIGIOT: -, 6, - */ + [SIGBUS] = 7, /* 10, 7, 10 */ + [SIGFPE] = 8, + [SIGKILL] = 9, + [SIGUSR1] = 10, /* 30, 10, 16 */ + [SIGSEGV] = 11, + [SIGUSR2] = 12, /* 31, 12, 17 */ + [SIGPIPE] = 13, + [SIGALRM] = 14, + [SIGTERM] = 15, + [SIGSTKFLT] = 16, /* -, 16, - */ + [SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */ + [SIGCONT] = 18, /* 19, 18, 25 */ + [SIGSTOP] = 19, /* 17, 19, 23 */ + [SIGTSTP] = 20, /* 18, 20, 24 */ + [SIGTTIN] = 21, /* 21, 21, 26 */ + [SIGTTOU] = 22, /* 22, 22, 27 */ + [SIGURG] = 23, /* 16, 23, 21 */ + [SIGXCPU] = 24, /* 24, 24, 30 */ + [SIGXFSZ] = 25, /* 25, 25, 31 */ + [SIGVTALRM] = 26, /* 26, 26, 28 */ + [SIGPROF] = 27, /* 27, 27, 29 */ + [SIGWINCH] = 28, /* 28, 28, 20 */ + [SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */ + [SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */ +#ifdef SIGSYS + [SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */ +#endif +#ifdef SIGEMT + [SIGEMT] = 32, /* 7, - , 7 */ +#endif +#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */ + [SIGLOST] = 33, /* unused on Linux */ +#endif +#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS + [SIGUNUSED] = 34, /* -, 31, - */ +#endif +}; + +/* this table is ordered post sig_map[sig] mapping */ +static const char *const sig_names[MAXMAPPED_SIG + 1] = { + "unknown", + "hup", + "int", + "quit", + "ill", + "trap", + "abrt", + "bus", + "fpe", + "kill", + "usr1", + "segv", + "usr2", + "pipe", + "alrm", + "term", + "stkflt", + "chld", + "cont", + "stop", + "stp", + "ttin", + "ttou", + "urg", + "xcpu", + "xfsz", + "vtalrm", + "prof", + "winch", + "io", + "pwr", + "sys", + "emt", + "lost", + "unused", + + "exists", /* always last existence test mapped to MAXMAPPED_SIG */ +}; + diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 11e66b5bbc42..66fb9ede9447 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -20,6 +20,7 @@ #include "include/context.h" #include "include/policy.h" #include "include/ipc.h" +#include "include/sig_names.h" /** * audit_ptrace_mask - convert mask to permission string @@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, } +static inline int map_signal_num(int sig) +{ + if (sig > SIGRTMAX) + return SIGUNKNOWN; + else if (sig >= SIGRTMIN) + return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */ + else if (sig <= MAXMAPPED_SIG) + return sig_map[sig]; + return SIGUNKNOWN; +} + +/** + * audit_file_mask - convert mask to permission string + * @buffer: buffer to write string to (NOT NULL) + * @mask: permission mask to convert + */ +static void audit_signal_mask(struct audit_buffer *ab, u32 mask) +{ + if (mask & MAY_READ) + audit_log_string(ab, "receive"); + if (mask & MAY_WRITE) + audit_log_string(ab, "send"); +} + +/** + * audit_cb - call back for signal specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_signal_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (aad(sa)->request & AA_SIGNAL_PERM_MASK) { + audit_log_format(ab, " requested_mask="); + audit_signal_mask(ab, aad(sa)->request); + if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) { + audit_log_format(ab, " denied_mask="); + audit_signal_mask(ab, aad(sa)->denied); + } + } + if (aad(sa)->signal <= MAXMAPPED_SIG) + audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]); + else + audit_log_format(ab, " signal=rtmin+%d", + aad(sa)->signal - 128); + audit_log_format(ab, " peer="); + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, + FLAGS_NONE, GFP_ATOMIC); +} + +/* TODO: update to handle compound name&name2, conditionals */ +static void profile_match_signal(struct aa_profile *profile, const char *label, + int signal, struct aa_perms *perms) +{ + unsigned int state; + + /* TODO: secondary cache check */ + state = aa_dfa_next(profile->policy.dfa, + profile->policy.start[AA_CLASS_SIGNAL], + signal); + state = aa_dfa_match(profile->policy.dfa, state, label); + aa_compute_perms(profile->policy.dfa, state, perms); +} + +static int profile_signal_perm(struct aa_profile *profile, + struct aa_profile *peer, u32 request, + struct common_audit_data *sa) +{ + struct aa_perms perms; + + if (profile_unconfined(profile) || + !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) + return 0; + + aad(sa)->peer = &peer->label; + profile_match_signal(profile, peer->base.hname, aad(sa)->signal, + &perms); + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); +} + +static int aa_signal_cross_perm(struct aa_profile *sender, + struct aa_profile *target, + struct common_audit_data *sa) +{ + return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa), + profile_signal_perm(target, sender, MAY_READ, sa)); +} + +int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) +{ + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); + + aad(&sa)->signal = map_signal_num(sig); + return xcheck_labels_profiles(sender, target, aa_signal_cross_perm, + &sa); +} diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 867bcd154c7e..af22f3dfbcce 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -656,6 +656,26 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +static int apparmor_task_kill(struct task_struct *target, struct siginfo *info, + int sig, u32 secid) +{ + struct aa_label *cl, *tl; + int error; + + if (secid) + /* TODO: after secid to label mapping is done. + * Dealing with USB IO specific behavior + */ + return 0; + cl = __begin_current_label_crit_section(); + tl = aa_get_task_label(target); + error = aa_may_signal(cl, tl, sig); + aa_put_label(tl); + __end_current_label_crit_section(cl); + + return error; +} + static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -697,6 +717,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec), LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), + LSM_HOOK_INIT(task_kill, apparmor_task_kill), }; /* -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0007-apparmor-add-mount-mediation.patch000066400000000000000000000732361522511161100261100ustar00rootroot00000000000000From f37356d0a41499f9222f9f2b9c0147b500ae4285 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:04:47 -0700 Subject: [PATCH 07/17] apparmor: add mount mediation Add basic mount mediation. That allows controlling based on basic mount parameters. It does not include special mount parameters for apparmor, super block labeling, or any triggers for apparmor namespace parameter modifications on pivot root. default userspace policy rules have the form of MOUNT RULE = ( MOUNT | REMOUNT | UMOUNT ) MOUNT = [ QUALIFIERS ] 'mount' [ MOUNT CONDITIONS ] [ SOURCE FILEGLOB ] [ '->' MOUNTPOINT FILEGLOB ] REMOUNT = [ QUALIFIERS ] 'remount' [ MOUNT CONDITIONS ] MOUNTPOINT FILEGLOB UMOUNT = [ QUALIFIERS ] 'umount' [ MOUNT CONDITIONS ] MOUNTPOINT FILEGLOB MOUNT CONDITIONS = [ ( 'fstype' | 'vfstype' ) ( '=' | 'in' ) MOUNT FSTYPE EXPRESSION ] [ 'options' ( '=' | 'in' ) MOUNT FLAGS EXPRESSION ] MOUNT FSTYPE EXPRESSION = ( MOUNT FSTYPE LIST | MOUNT EXPRESSION ) MOUNT FSTYPE LIST = Comma separated list of valid filesystem and virtual filesystem types (eg ext4, debugfs, etc) MOUNT FLAGS EXPRESSION = ( MOUNT FLAGS LIST | MOUNT EXPRESSION ) MOUNT FLAGS LIST = Comma separated list of MOUNT FLAGS. MOUNT FLAGS = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | 'noexec' | 'exec' | 'sync' | 'async' | 'remount' | 'mand' | 'nomand' | 'dirsync' | 'noatime' | 'atime' | 'nodiratime' | 'diratime' | 'bind' | 'rbind' | 'move' | 'verbose' | 'silent' | 'loud' | 'acl' | 'noacl' | 'unbindable' | 'runbindable' | 'private' | 'rprivate' | 'slave' | 'rslave' | 'shared' | 'rshared' | 'relatime' | 'norelatime' | 'iversion' | 'noiversion' | 'strictatime' | 'nouser' | 'user' ) MOUNT EXPRESSION = ( ALPHANUMERIC | AARE ) ... PIVOT ROOT RULE = [ QUALIFIERS ] pivot_root [ oldroot=OLD PUT FILEGLOB ] [ NEW ROOT FILEGLOB ] SOURCE FILEGLOB = FILEGLOB MOUNTPOINT FILEGLOB = FILEGLOB eg. mount, mount /dev/foo, mount options=ro /dev/foo -> /mnt/, mount options in (ro,atime) /dev/foo -> /mnt/, mount options=ro options=atime, Signed-off-by: John Johansen Acked-by: Seth Arnold (cherry picked from commit fa488437d0f95b2e5db1e624341fe0d5a233f729) --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 8 +- security/apparmor/domain.c | 4 +- security/apparmor/include/apparmor.h | 1 + security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 5 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 64 ++++ security/apparmor/mount.c | 696 +++++++++++++++++++++++++++++++++++ 9 files changed, 841 insertions(+), 4 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index a16b195274de..81a34426d024 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o label.o + resource.o secid.o file.o policy_ns.o label.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index a5f9e1aa51f7..8fa6c898c44b 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2159,9 +2159,14 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_mount[] = { + AA_SFS_FILE_STRING("mask", "mount umount pivot_root"), + { } +}; + static struct aa_sfs_entry aa_sfs_entry_ns[] = { AA_SFS_FILE_BOOLEAN("profile", 1), - AA_SFS_FILE_BOOLEAN("pivot_root", 1), + AA_SFS_FILE_BOOLEAN("pivot_root", 0), { } }; @@ -2180,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("policy", aa_sfs_entry_policy), AA_SFS_DIR("domain", aa_sfs_entry_domain), AA_SFS_DIR("file", aa_sfs_entry_file), + AA_SFS_DIR("mount", aa_sfs_entry_mount), AA_SFS_DIR("namespaces", aa_sfs_entry_ns), AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index d0594446ae3f..ffc8c75a6785 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -374,8 +374,8 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted label, or NULL on failure (MAYBE NULL) */ -static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, - const char **name) +struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, + const char **name) { struct aa_label *label = NULL; u32 xtype = xindex & AA_X_TYPE_MASK; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 962a20a75e01..829082c35faa 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -27,6 +27,7 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 #define AA_CLASS_PTRACE 9 #define AA_CLASS_SIGNAL 10 #define AA_CLASS_LABEL 16 diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index d9a156ae11b9..c3fe1c5ef3bc 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -71,6 +71,10 @@ enum audit_type { #define OP_FMPROT "file_mprotect" #define OP_INHERIT "file_inherit" +#define OP_PIVOTROOT "pivotroot" +#define OP_MOUNT "mount" +#define OP_UMOUNT "umount" + #define OP_CREATE "create" #define OP_POST_CREATE "post_create" #define OP_BIND "bind" @@ -132,6 +136,13 @@ struct apparmor_audit_data { int rlim; unsigned long max; } rlim; + struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; }; }; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index bab5810b6e9a..db27403346c5 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -15,6 +15,8 @@ #include #include +#include "label.h" + #ifndef __AA_DOMAIN_H #define __AA_DOMAIN_H @@ -29,6 +31,9 @@ struct aa_domain { #define AA_CHANGE_ONEXEC 4 #define AA_CHANGE_STACK 8 +struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, + const char **name); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 000000000000..25d6067fa6ef --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2017 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_MNT_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_label *label, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_label *label, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_label *label, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_label *label, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_label *label, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_label *label, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index af22f3dfbcce..4ad0b3a45142 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -38,6 +38,7 @@ #include "include/policy.h" #include "include/policy_ns.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized; @@ -511,6 +512,65 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(const char *dev_name, const struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct aa_label *label; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + label = __begin_current_label_crit_section(); + if (!unconfined(label)) { + if (flags & MS_REMOUNT) + error = aa_remount(label, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(label, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(label, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(label, path, dev_name); + else + error = aa_new_mount(label, dev_name, path, type, + flags, data); + } + __end_current_label_crit_section(label); + + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_label *label; + int error = 0; + + label = __begin_current_label_crit_section(); + if (!unconfined(label)) + error = aa_umount(label, mnt, flags); + __end_current_label_crit_section(label); + + return error; +} + +static int apparmor_sb_pivotroot(const struct path *old_path, + const struct path *new_path) +{ + struct aa_label *label; + int error = 0; + + label = aa_get_current_label(); + if (!unconfined(label)) + error = aa_pivotroot(label, old_path, new_path); + aa_put_label(label); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -682,6 +742,10 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(capget, apparmor_capget), LSM_HOOK_INIT(capable, apparmor_capable), + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), + LSM_HOOK_INIT(path_link, apparmor_path_link), LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 000000000000..82a64b58041d --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,696 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (aad(sa)->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, aad(sa)->mnt.type); + } + if (aad(sa)->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, aad(sa)->mnt.src_name); + } + if (aad(sa)->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, aad(sa)->mnt.trans); + } + if (aad(sa)->mnt.flags) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, aad(sa)->mnt.flags); + audit_log_format(ab, "\""); + } + if (aad(sa)->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, aad(sa)->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, const char *op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct aa_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return error; + } + + aad(&sa)->name = name; + aad(&sa)->mnt.src_name = src_name; + aad(&sa)->mnt.type = type; + aad(&sa)->mnt.trans = trans; + aad(&sa)->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + aad(&sa)->mnt.data = data; + aad(&sa)->info = info; + aad(&sa)->error = error; + + return aa_audit(audit_type, profile, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct aa_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char * const mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct aa_perms *perms) +{ + unsigned int state; + + AA_BUG(!dfa); + AA_BUG(!perms); + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + + +static int path_flags(struct aa_profile *profile, const struct path *path) +{ + AA_BUG(!profile); + AA_BUG(!path); + + return profile->path_flags | + (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0); +} + +/** + * match_mnt_path_str - handle path matching for mount + * @profile: the confining profile + * @mntpath: for the mntpnt (NOT NULL) + * @buffer: buffer to be used to lookup mntpath + * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @devinfo: error str if (IS_ERR(@devname)) + * + * Returns: 0 on success else error + */ +static int match_mnt_path_str(struct aa_profile *profile, + const struct path *mntpath, char *buffer, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + const char *devinfo) +{ + struct aa_perms perms = { }; + const char *mntpnt = NULL, *info = NULL; + int pos, error; + + AA_BUG(!profile); + AA_BUG(!mntpath); + AA_BUG(!buffer); + + error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer, + &mntpnt, &info, profile->disconnected); + if (error) + goto audit; + if (IS_ERR(devname)) { + error = PTR_ERR(devname); + devname = NULL; + info = devinfo; + goto audit; + } + + error = -EACCES; + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, &perms); + if (pos) { + info = mnt_info_table[pos]; + goto audit; + } + error = 0; + +audit: + return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL, + flags, data, AA_MAY_MOUNT, &perms, info, error); +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpath: for the mntpnt (NOT NULL) + * @buffer: buffer to be used to lookup mntpath + * @devpath: path devname/src_name (MAYBE NULL) + * @devbuffer: buffer to be used to lookup devname/src_name + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const struct path *path, + char *buffer, struct path *devpath, char *devbuffer, + const char *type, unsigned long flags, void *data, + bool binary) +{ + const char *devname = NULL, *info = NULL; + int error = -EACCES; + + AA_BUG(!profile); + AA_BUG(devpath && !devbuffer); + + if (devpath) { + error = aa_path_name(devpath, path_flags(profile, devpath), + devbuffer, &devname, &info, + profile->disconnected); + if (error) + devname = ERR_PTR(error); + } + + return match_mnt_path_str(profile, path, buffer, devname, type, flags, + data, binary, info); +} + +int aa_remount(struct aa_label *label, const struct path *path, + unsigned long flags, void *data) +{ + struct aa_profile *profile; + char *buffer = NULL; + bool binary; + int error; + + AA_BUG(!label); + AA_BUG(!path); + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + get_buffers(buffer); + error = fn_for_each_confined(label, profile, + match_mnt(profile, path, buffer, NULL, NULL, NULL, + flags, data, binary)); + put_buffers(buffer); + + return error; +} + +int aa_bind_mount(struct aa_label *label, const struct path *path, + const char *dev_name, unsigned long flags) +{ + struct aa_profile *profile; + char *buffer = NULL, *old_buffer = NULL; + struct path old_path; + int error; + + AA_BUG(!label); + AA_BUG(!path); + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + return error; + + get_buffers(buffer, old_buffer); + error = fn_for_each_confined(label, profile, + match_mnt(profile, path, buffer, &old_path, old_buffer, + NULL, flags, NULL, false)); + put_buffers(buffer, old_buffer); + path_put(&old_path); + + return error; +} + +int aa_mount_change_type(struct aa_label *label, const struct path *path, + unsigned long flags) +{ + struct aa_profile *profile; + char *buffer = NULL; + int error; + + AA_BUG(!label); + AA_BUG(!path); + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + get_buffers(buffer); + error = fn_for_each_confined(label, profile, + match_mnt(profile, path, buffer, NULL, NULL, NULL, + flags, NULL, false)); + put_buffers(buffer); + + return error; +} + +int aa_move_mount(struct aa_label *label, const struct path *path, + const char *orig_name) +{ + struct aa_profile *profile; + char *buffer = NULL, *old_buffer = NULL; + struct path old_path; + int error; + + AA_BUG(!label); + AA_BUG(!path); + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + return error; + + get_buffers(buffer, old_buffer); + error = fn_for_each_confined(label, profile, + match_mnt(profile, path, buffer, &old_path, old_buffer, + NULL, MS_MOVE, NULL, false)); + put_buffers(buffer, old_buffer); + path_put(&old_path); + + return error; +} + +int aa_new_mount(struct aa_label *label, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct aa_profile *profile; + char *buffer = NULL, *dev_buffer = NULL; + bool binary = true; + int error; + int requires_dev = 0; + struct path tmp_path, *dev_path = NULL; + + AA_BUG(!label); + AA_BUG(!path); + + if (type) { + struct file_system_type *fstype; + + fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + if (!dev_name || !*dev_name) + return -ENOENT; + + error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path); + if (error) + return error; + dev_path = &tmp_path; + } + } + + get_buffers(buffer, dev_buffer); + if (dev_path) { + error = fn_for_each_confined(label, profile, + match_mnt(profile, path, buffer, dev_path, dev_buffer, + type, flags, data, binary)); + } else { + error = fn_for_each_confined(label, profile, + match_mnt_path_str(profile, path, buffer, dev_name, + type, flags, data, binary, NULL)); + } + put_buffers(buffer, dev_buffer); + if (dev_path) + path_put(dev_path); + + return error; +} + +static int profile_umount(struct aa_profile *profile, struct path *path, + char *buffer) +{ + struct aa_perms perms = { }; + const char *name = NULL, *info = NULL; + unsigned int state; + int error; + + AA_BUG(!profile); + AA_BUG(!path); + + error = aa_path_name(path, path_flags(profile, path), buffer, &name, + &info, profile->disconnected); + if (error) + goto audit; + + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL, + AA_MAY_UMOUNT, &perms, info, error); +} + +int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + char *buffer = NULL; + int error; + struct path path = { .mnt = mnt, .dentry = mnt->mnt_root }; + + AA_BUG(!label); + AA_BUG(!mnt); + + get_buffers(buffer); + error = fn_for_each_confined(label, profile, + profile_umount(profile, &path, buffer)); + put_buffers(buffer); + + return error; +} + +/* helper fn for transition on pivotroot + * + * Returns: label for transition or ERR_PTR. Does not return NULL + */ +static struct aa_label *build_pivotroot(struct aa_profile *profile, + const struct path *new_path, + char *new_buffer, + const struct path *old_path, + char *old_buffer) +{ + const char *old_name, *new_name = NULL, *info = NULL; + const char *trans_name = NULL; + struct aa_perms perms = { }; + unsigned int state; + int error; + + AA_BUG(!profile); + AA_BUG(!new_path); + AA_BUG(!old_path); + + if (profile_unconfined(profile)) + return aa_get_newest_label(&profile->label); + + error = aa_path_name(old_path, path_flags(profile, old_path), + old_buffer, &old_name, &info, + profile->disconnected); + if (error) + goto audit; + error = aa_path_name(new_path, path_flags(profile, new_path), + new_buffer, &new_name, &info, + profile->disconnected); + if (error) + goto audit; + + error = -EACCES; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + + if (AA_MAY_PIVOTROOT & perms.allow) + error = 0; + +audit: + error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name, + NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT, + &perms, info, error); + if (error) + return ERR_PTR(error); + + return aa_get_newest_label(&profile->label); +} + +int aa_pivotroot(struct aa_label *label, const struct path *old_path, + const struct path *new_path) +{ + struct aa_profile *profile; + struct aa_label *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL, *info = NULL; + int error; + + AA_BUG(!label); + AA_BUG(!old_path); + AA_BUG(!new_path); + + get_buffers(old_buffer, new_buffer); + target = fn_label_build(label, profile, GFP_ATOMIC, + build_pivotroot(profile, new_path, new_buffer, + old_path, old_buffer)); + if (!target) { + info = "label build failed"; + error = -ENOMEM; + goto fail; + } else if (!IS_ERR(target)) { + error = aa_replace_current_label(target); + if (error) { + /* TODO: audit target */ + aa_put_label(target); + goto out; + } + } else + /* already audited error */ + error = PTR_ERR(target); +out: + put_buffers(old_buffer, new_buffer); + + return error; + +fail: + /* TODO: add back in auditing of new_name and old_name */ + error = fn_for_each(label, profile, + audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */, + NULL /* old_name */, + NULL, NULL, + 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info, + error)); + goto out; +} -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch000066400000000000000000000043071522511161100324140ustar00rootroot00000000000000From 763d17c9a18b0df7dbec2740f10dc40d378e3cc1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 6 Aug 2017 05:36:40 -0700 Subject: [PATCH 08/17] apparmor: cleanup conditional check for label in label_print Signed-off-by: John Johansen Acked-by: Seth Arnold (cherry picked from commit 7e57939b9d67dcfc2c8348fd0e2c76a2f0349c75) --- security/apparmor/label.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index e324f4df3e34..38be7a89cc31 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -1450,9 +1450,11 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp) * cached label name is present and visible * @label->hname only exists if label is namespace hierachical */ -static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label) +static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label, + int flags) { - if (label->hname && labels_ns(label) == ns) + if (label->hname && (!ns || labels_ns(label) == ns) && + !(flags & ~FLAG_SHOW_MODE)) return true; return false; @@ -1710,10 +1712,8 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns, AA_BUG(!ab); AA_BUG(!label); - if (!ns) - ns = labels_ns(label); - - if (!use_label_hname(ns, label) || display_mode(ns, label, flags)) { + if (!use_label_hname(ns, label, flags) || + display_mode(ns, label, flags)) { len = aa_label_asxprint(&name, ns, label, flags, gfp); if (len == -1) { AA_DEBUG("label print error"); @@ -1738,10 +1738,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, AA_BUG(!f); AA_BUG(!label); - if (!ns) - ns = labels_ns(label); - - if (!use_label_hname(ns, label)) { + if (!use_label_hname(ns, label, flags)) { char *str; int len; @@ -1764,10 +1761,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, { AA_BUG(!label); - if (!ns) - ns = labels_ns(label); - - if (!use_label_hname(ns, label)) { + if (!use_label_hname(ns, label, flags)) { char *str; int len; -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0009-apparmor-add-support-for-absolute-root-view-based-la.patch000066400000000000000000000043721522511161100325070ustar00rootroot00000000000000From 6b092bbbf9e17b10f709d11b3bc2d7e493617934 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 6 Aug 2017 05:39:08 -0700 Subject: [PATCH 09/17] apparmor: add support for absolute root view based labels With apparmor policy virtualization based on policy namespace View's we don't generally want/need absolute root based views, however there are cases like debugging and some secid based conversions where using a root based view is important. Signed-off-by: John Johansen Acked-by: Seth Arnold (cherry picked from commit eadfbf0898eda94cee0d982626aa24a3146db48b) --- security/apparmor/include/label.h | 1 + security/apparmor/label.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 9a283b722755..af22dcbbcb8a 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -310,6 +310,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp); #define FLAG_SHOW_MODE 1 #define FLAG_VIEW_SUBNS 2 #define FLAG_HIDDEN_UNCONFINED 4 +#define FLAG_ABS_ROOT 8 int aa_label_snxprint(char *str, size_t size, struct aa_ns *view, struct aa_label *label, int flags); int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label, diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 38be7a89cc31..52b4ef14840d 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -1607,8 +1607,13 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns, AA_BUG(!str && size != 0); AA_BUG(!label); - if (!ns) + if (flags & FLAG_ABS_ROOT) { + ns = root_ns; + len = snprintf(str, size, "="); + update_for_len(total, len, size, str); + } else if (!ns) { ns = labels_ns(label); + } label_for_each(i, label, profile) { if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) { @@ -1868,6 +1873,9 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, if (*str == '&') str++; } + if (*str == '=') + base = &root_ns->unconfined->label; + error = vec_setup(profile, vec, len, gfp); if (error) return ERR_PTR(error); -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0010-apparmor-make-policy_unpack-able-to-audit-different-.patch000066400000000000000000000165171522511161100324630ustar00rootroot00000000000000From aa4b6bded85552bc5f9f22d2e18ce86c5c17947c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:37:18 -0700 Subject: [PATCH 10/17] apparmor: make policy_unpack able to audit different info messages Switch unpack auditing to using the generic name field in the audit struct and make it so we can start adding new info messages about why an unpack failed. Signed-off-by: John Johansen Acked-by: Seth Arnold (cherry picked from commit 1489d896c5649e9ce1b6000b4857f8baa7a6ab63) --- security/apparmor/include/audit.h | 4 +-- security/apparmor/policy_unpack.c | 52 ++++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index c3fe1c5ef3bc..620e81169659 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -127,9 +127,9 @@ struct apparmor_audit_data { } fs; }; struct { - const char *name; - long pos; + struct aa_profile *profile; const char *ns; + long pos; } iface; int signal; struct { diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index bda0dce3b582..4ede87c30f8b 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -85,9 +85,9 @@ static void audit_cb(struct audit_buffer *ab, void *va) audit_log_format(ab, " ns="); audit_log_untrustedstring(ab, aad(sa)->iface.ns); } - if (aad(sa)->iface.name) { + if (aad(sa)->name) { audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, aad(sa)->iface.name); + audit_log_untrustedstring(ab, aad(sa)->name); } if (aad(sa)->iface.pos) audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos); @@ -114,9 +114,9 @@ static int audit_iface(struct aa_profile *new, const char *ns_name, aad(&sa)->iface.pos = e->pos - e->start; aad(&sa)->iface.ns = ns_name; if (new) - aad(&sa)->iface.name = new->base.hname; + aad(&sa)->name = new->base.hname; else - aad(&sa)->iface.name = name; + aad(&sa)->name = name; aad(&sa)->info = info; aad(&sa)->error = error; @@ -583,6 +583,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) { struct aa_profile *profile = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; + const char *info = "failed to unpack profile"; size_t ns_len; struct rhashtable_params params = { 0 }; char *key = NULL; @@ -604,8 +605,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); if (tmpns) { *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); - if (!*ns_name) + if (!*ns_name) { + info = "out of memory"; goto fail; + } name = tmpname; } @@ -624,12 +627,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) if (IS_ERR(profile->xmatch)) { error = PTR_ERR(profile->xmatch); profile->xmatch = NULL; + info = "bad xmatch"; goto fail; } /* xmatch_len is not optional if xmatch is set */ if (profile->xmatch) { - if (!unpack_u32(e, &tmp, NULL)) + if (!unpack_u32(e, &tmp, NULL)) { + info = "missing xmatch len"; goto fail; + } profile->xmatch_len = tmp; } @@ -637,8 +643,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) (void) unpack_str(e, &profile->disconnected, "disconnected"); /* per profile debug flags (complain, audit) */ - if (!unpack_nameX(e, AA_STRUCT, "flags")) + if (!unpack_nameX(e, AA_STRUCT, "flags")) { + info = "profile missing flags"; goto fail; + } + info = "failed to unpack profile flags"; if (!unpack_u32(e, &tmp, NULL)) goto fail; if (tmp & PACKED_FLAG_HAT) @@ -667,6 +676,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) /* set a default value if path_flags field is not present */ profile->path_flags = PATH_MEDIATE_DELETED; + info = "failed to unpack profile capabilities"; if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) goto fail; if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) @@ -676,6 +686,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) if (!unpack_u32(e, &tmpcap.cap[0], NULL)) goto fail; + info = "failed to unpack upper profile capabilities"; if (unpack_nameX(e, AA_STRUCT, "caps64")) { /* optional upper half of 64 bit caps */ if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) @@ -690,6 +701,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + info = "failed to unpack extended profile capabilities"; if (unpack_nameX(e, AA_STRUCT, "capsx")) { /* optional extended caps mediation mask */ if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) @@ -700,11 +712,14 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } - if (!unpack_rlimits(e, profile)) + if (!unpack_rlimits(e, profile)) { + info = "failed to unpack profile rlimits"; goto fail; + } if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ + info = "failed to unpack policydb"; profile->policy.dfa = unpack_dfa(e); if (IS_ERR(profile->policy.dfa)) { error = PTR_ERR(profile->policy.dfa); @@ -734,6 +749,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) if (IS_ERR(profile->file.dfa)) { error = PTR_ERR(profile->file.dfa); profile->file.dfa = NULL; + info = "failed to unpack profile file rules"; goto fail; } else if (profile->file.dfa) { if (!unpack_u32(e, &profile->file.start, "dfa_start")) @@ -746,10 +762,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) } else profile->file.dfa = aa_get_dfa(nulldfa); - if (!unpack_trans_table(e, profile)) + if (!unpack_trans_table(e, profile)) { + info = "failed to unpack profile transition table"; goto fail; + } if (unpack_nameX(e, AA_STRUCT, "data")) { + info = "out of memory"; profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL); if (!profile->data) goto fail; @@ -761,8 +780,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) params.hashfn = strhash; params.obj_cmpfn = datacmp; - if (rhashtable_init(profile->data, ¶ms)) + if (rhashtable_init(profile->data, ¶ms)) { + info = "failed to init key, value hash table"; goto fail; + } while (unpack_strdup(e, &key, NULL)) { data = kzalloc(sizeof(*data), GFP_KERNEL); @@ -784,12 +805,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) profile->data->p); } - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { + info = "failed to unpack end of key, value data table"; goto fail; + } } - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { + info = "failed to unpack end of profile"; goto fail; + } return profile; @@ -798,8 +823,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) name = NULL; else if (!name) name = "unknown"; - audit_iface(profile, NULL, name, "failed to unpack profile", e, - error); + audit_iface(profile, NULL, name, info, e, error); aa_free_profile(profile); return ERR_PTR(error); -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch000066400000000000000000000050041522511161100312560ustar00rootroot00000000000000From ba3f778a2ef31454032c2ca9c99d9212feb4dcf1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:41:13 -0700 Subject: [PATCH 11/17] apparmor: add more debug asserts to apparmorfs Signed-off-by: John Johansen Acked-by: Seth Arnold (cherry picked from commit 52c9542126fb04df1f12c605b6c22719c9096794) --- security/apparmor/apparmorfs.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 8fa6c898c44b..7acea14c850b 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1446,6 +1446,10 @@ void __aafs_profile_migrate_dents(struct aa_profile *old, { int i; + AA_BUG(!old); + AA_BUG(!new); + AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock)); + for (i = 0; i < AAFS_PROF_SIZEOF; i++) { new->dents[i] = old->dents[i]; if (new->dents[i]) @@ -1509,6 +1513,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) struct dentry *dent = NULL, *dir; int error; + AA_BUG(!profile); + AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock)); + if (!parent) { struct aa_profile *p; p = aa_deref_parent(profile); @@ -1734,6 +1741,7 @@ void __aafs_ns_rmdir(struct aa_ns *ns) if (!ns) return; + AA_BUG(!mutex_is_locked(&ns->lock)); list_for_each_entry(child, &ns->base.profiles, base.list) __aafs_profile_rmdir(child); @@ -1906,6 +1914,10 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) { struct aa_ns *parent, *next; + AA_BUG(!root); + AA_BUG(!ns); + AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock)); + /* is next namespace a child */ if (!list_empty(&ns->sub_ns)) { next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); @@ -1940,6 +1952,9 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) static struct aa_profile *__first_profile(struct aa_ns *root, struct aa_ns *ns) { + AA_BUG(!root); + AA_BUG(ns && !mutex_is_locked(&ns->lock)); + for (; ns; ns = __next_ns(root, ns)) { if (!list_empty(&ns->base.profiles)) return list_first_entry(&ns->base.profiles, @@ -1962,6 +1977,8 @@ static struct aa_profile *__next_profile(struct aa_profile *p) struct aa_profile *parent; struct aa_ns *ns = p->ns; + AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock)); + /* is next profile a child */ if (!list_empty(&p->base.profiles)) return list_first_entry(&p->base.profiles, typeof(*p), -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0012-apparmor-add-base-infastructure-for-socket-mediation.patch000066400000000000000000001006671522511161100326210ustar00rootroot00000000000000From 853cbdfb6924857a2ee2a1cd5b9fa494f8e7efa2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:18:33 -0700 Subject: [PATCH 12/17] apparmor: add base infastructure for socket mediation Provide a basic mediation of sockets. This is not a full net mediation but just whether a spcific family of socket can be used by an application, along with setting up some basic infrastructure for network mediation to follow. the user space rule hav the basic form of NETWORK RULE = [ QUALIFIERS ] 'network' [ DOMAIN ] [ TYPE | PROTOCOL ] DOMAIN = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'mpls' | 'ib' | 'kcm' ) ',' TYPE = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' ) PROTOCOL = ( 'tcp' | 'udp' | 'icmp' ) eg. network, network inet, Signed-off-by: John Johansen Acked-by: Seth Arnold (cherry picked from commit 56387cbe3f287034ee6959cb9e8f419889e38bd9) --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 43 ++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/file.c | 30 +++ security/apparmor/include/audit.h | 26 ++- security/apparmor/include/net.h | 114 +++++++++++ security/apparmor/include/perms.h | 5 +- security/apparmor/include/policy.h | 13 ++ security/apparmor/lib.c | 5 +- security/apparmor/lsm.c | 387 +++++++++++++++++++++++++++++++++++++ security/apparmor/net.c | 184 ++++++++++++++++++ security/apparmor/policy_unpack.c | 47 ++++- 12 files changed, 840 insertions(+), 16 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70d72b8..d5b291e94264 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 81a34426d024..dafdd387d42b 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,11 +4,44 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o label.o mount.o + resource.o secid.o file.o policy_ns.o label.o mount.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h +# Build a lower case string table of address family names +# Transform lines from +# #define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_SFS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + printf '%s' '\#define AA_SFS_AF_MASK "' >> $@ ;\ + sed -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of capability names # Transforms lines from @@ -61,6 +94,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +102,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 7acea14c850b..125dad5c3fde 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2202,6 +2202,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("policy", aa_sfs_entry_policy), AA_SFS_DIR("domain", aa_sfs_entry_domain), AA_SFS_DIR("file", aa_sfs_entry_file), + AA_SFS_DIR("network", aa_sfs_entry_network), AA_SFS_DIR("mount", aa_sfs_entry_mount), AA_SFS_DIR("namespaces", aa_sfs_entry_ns), AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 3382518b87fa..db80221891c6 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -21,6 +21,7 @@ #include "include/context.h" #include "include/file.h" #include "include/match.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/label.h" @@ -566,6 +567,32 @@ static int __file_path_perm(const char *op, struct aa_label *label, return error; } +static int __file_sock_perm(const char *op, struct aa_label *label, + struct aa_label *flabel, struct file *file, + u32 request, u32 denied) +{ + struct socket *sock = (struct socket *) file->private_data; + int error; + + AA_BUG(!sock); + + /* revalidation due to label out of date. No revocation at this time */ + if (!denied && aa_label_is_subset(flabel, label)) + return 0; + + /* TODO: improve to skip profiles cached in flabel */ + error = aa_sock_file_perm(label, op, request, sock); + if (denied) { + /* TODO: improve to skip profiles checked above */ + /* check every profile in file label to is cached */ + last_error(error, aa_sock_file_perm(flabel, op, request, sock)); + } + if (!error) + update_file_ctx(file_ctx(file), label, request); + + return error; +} + /** * aa_file_perm - do permission revalidation check & audit for @file * @op: operation being checked @@ -610,6 +637,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, error = __file_path_perm(op, label, flabel, file, request, denied); + else if (S_ISSOCK(file_inode(file)->i_mode)) + error = __file_sock_perm(op, label, flabel, file, request, + denied); done: rcu_read_unlock(); diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 620e81169659..ff4316e1068d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -121,21 +121,29 @@ struct apparmor_audit_data { /* these entries require a custom callback fn */ struct { struct aa_label *peer; - struct { - const char *target; - kuid_t ouid; - } fs; + union { + struct { + kuid_t ouid; + const char *target; + } fs; + struct { + int type, protocol; + struct sock *peer_sk; + void *addr; + int addrlen; + } net; + int signal; + struct { + int rlim; + unsigned long max; + } rlim; + }; }; struct { struct aa_profile *profile; const char *ns; long pos; } iface; - int signal; - struct { - int rlim; - unsigned long max; - } rlim; struct { const char *src_name; const char *type; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 000000000000..140c8efcf364 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,114 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include +#include + +#include "apparmorfs.h" +#include "label.h" +#include "perms.h" +#include "policy.h" + +#define AA_MAY_SEND AA_MAY_WRITE +#define AA_MAY_RECEIVE AA_MAY_READ + +#define AA_MAY_SHUTDOWN AA_MAY_DELETE + +#define AA_MAY_CONNECT AA_MAY_OPEN +#define AA_MAY_ACCEPT 0x00100000 + +#define AA_MAY_BIND 0x00200000 +#define AA_MAY_LISTEN 0x00400000 + +#define AA_MAY_SETOPT 0x01000000 +#define AA_MAY_GETOPT 0x02000000 + +#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ + AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \ + AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \ + AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT) + +#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ + AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\ + AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \ + AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \ + AA_MAY_MPROT) + +#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \ + AA_MAY_ACCEPT) +struct aa_sk_ctx { + struct aa_label *label; + struct aa_label *peer; + struct path path; +}; + +#define SK_CTX(X) ((X)->sk_security) +#define SOCK_ctx(X) SOCK_INODE(X)->i_security +#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ + struct lsm_network_audit NAME ## _net = { .sk = (SK), \ + .family = (F)}; \ + DEFINE_AUDIT_DATA(NAME, \ + ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ + LSM_AUDIT_DATA_NONE, \ + OP); \ + NAME.u.net = &(NAME ## _net); \ + aad(&NAME)->net.type = (T); \ + aad(&NAME)->net.protocol = (P) + +#define DEFINE_AUDIT_SK(NAME, OP, SK) \ + DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ + (SK)->sk_protocol) + +/* struct aa_net - network confinement data + * @allow: basic network families permissions + * @audit: which network permissions to force audit + * @quiet: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + + +extern struct aa_sfs_entry aa_sfs_entry_network[]; + +void audit_net_cb(struct audit_buffer *ab, void *va); +int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type); +int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, + int type, int protocol); +static inline int aa_profile_af_sk_perm(struct aa_profile *profile, + struct common_audit_data *sa, + u32 request, + struct sock *sk) +{ + return aa_profile_af_perm(profile, sa, request, sk->sk_family, + sk->sk_type); +} +int aa_sk_perm(const char *op, u32 request, struct sock *sk); + +int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index 2b27bb79aec4..af04d5a7d73d 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h @@ -135,9 +135,10 @@ extern struct aa_perms allperms; void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask); -void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask); +void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, + u32 mask); void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, - u32 chrsmask, const char **names, u32 namesmask); + u32 chrsmask, const char * const *names, u32 namesmask); void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms); void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 17fe41a9cac3..4364088a0b9e 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -30,6 +30,7 @@ #include "file.h" #include "lib.h" #include "label.h" +#include "net.h" #include "perms.h" #include "resource.h" @@ -111,6 +112,7 @@ struct aa_data { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -148,6 +150,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; struct aa_loaddata *rawdata; @@ -220,6 +223,16 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, return 0; } +static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, + u16 AF) { + unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); + u16 be_af = cpu_to_be16(AF); + + if (!state) + return 0; + return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); +} + /** * aa_get_profile - increment refcount on profile @p * @p: profile (MAYBE NULL) diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 08ca26bcca77..8818621b5d95 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -211,7 +211,8 @@ void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask) *str = '\0'; } -void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) +void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, + u32 mask) { const char *fmt = "%s"; unsigned int i, perm = 1; @@ -229,7 +230,7 @@ void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) } void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, - u32 chrsmask, const char **names, u32 namesmask) + u32 chrsmask, const char * const *names, u32 namesmask) { char str[33]; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 4ad0b3a45142..cc5ab23a2d84 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -33,6 +33,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/label.h" #include "include/policy.h" @@ -736,6 +737,368 @@ static int apparmor_task_kill(struct task_struct *target, struct siginfo *info, return error; } +/** + * apparmor_sk_alloc_security - allocate and attach the sk_security field + */ +static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags) +{ + struct aa_sk_ctx *ctx; + + ctx = kzalloc(sizeof(*ctx), flags); + if (!ctx) + return -ENOMEM; + + SK_CTX(sk) = ctx; + + return 0; +} + +/** + * apparmor_sk_free_security - free the sk_security field + */ +static void apparmor_sk_free_security(struct sock *sk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + SK_CTX(sk) = NULL; + aa_put_label(ctx->label); + aa_put_label(ctx->peer); + path_put(&ctx->path); + kfree(ctx); +} + +/** + * apparmor_clone_security - clone the sk_security field + */ +static void apparmor_sk_clone_security(const struct sock *sk, + struct sock *newsk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + struct aa_sk_ctx *new = SK_CTX(newsk); + + new->label = aa_get_label(ctx->label); + new->peer = aa_get_label(ctx->peer); + new->path = ctx->path; + path_get(&new->path); +} + +static int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + AA_BUG(!label); + AA_BUG(in_interrupt()); + + return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, + protocol); +} + + +/** + * apparmor_socket_create - check perms before creating a new socket + */ +static int apparmor_socket_create(int family, int type, int protocol, int kern) +{ + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(kern || unconfined(label))) + error = aa_sock_create_perm(label, family, type, protocol); + end_current_label_crit_section(label); + + return error; +} + +/** + * apparmor_socket_post_create - setup the per-socket security struct + * + * Note: + * - kernel sockets currently labeled unconfined but we may want to + * move to a special kernel label + * - socket may not have sk here if created with sock_create_lite or + * sock_alloc. These should be accept cases which will be handled in + * sock_graft. + */ +static int apparmor_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) +{ + struct aa_label *label; + + if (kern) { + struct aa_ns *ns = aa_get_current_ns(); + + label = aa_get_label(ns_unconfined(ns)); + aa_put_ns(ns); + } else + label = aa_get_current_label(); + + if (sock->sk) { + struct aa_sk_ctx *ctx = SK_CTX(sock->sk); + + aa_put_label(ctx->label); + ctx->label = aa_get_label(label); + } + aa_put_label(label); + + return 0; +} + +/** + * apparmor_socket_bind - check perms before bind addr to socket + */ +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); +} + +/** + * apparmor_socket_connect - check perms before connecting @sock to @address + */ +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); +} + +/** + * apparmor_socket_list - check perms before allowing listen + */ +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); +} + +/** + * apparmor_socket_accept - check perms before accepting a new connection. + * + * Note: while @newsock is created and has some information, the accept + * has not been done. + */ +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!newsock); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); +} + +static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!msg); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_socket_sendmsg - check perms before sending msg to another socket + */ +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); +} + +/** + * apparmor_socket_recvmsg - check perms before receiving a message + */ +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); +} + +/* revaliation, get/set attr, shutdown */ +static int aa_sock_perm(const char *op, u32 request, struct socket *sock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_socket_getsockname - check perms before getting the local address + */ +static int apparmor_socket_getsockname(struct socket *sock) +{ + return aa_sock_perm(OP_GETSOCKNAME, AA_MAY_GETATTR, sock); +} + +/** + * apparmor_socket_getpeername - check perms before getting remote address + */ +static int apparmor_socket_getpeername(struct socket *sock) +{ + return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); +} + +/* revaliation, get/set attr, opt */ +static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, + int level, int optname) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_getsockopt - check perms before getting socket options + */ +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + return aa_sock_opt_perm(OP_GETSOCKOPT, AA_MAY_GETOPT, sock, + level, optname); +} + +/** + * apparmor_setsockopt - check perms before setting socket options + */ +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + return aa_sock_opt_perm(OP_SETSOCKOPT, AA_MAY_SETOPT, sock, + level, optname); +} + +/** + * apparmor_socket_shutdown - check perms before shutting down @sock conn + */ +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock); +} + +/** + * apparmor_socket_sock_recv_skb - check perms before associating skb to sk + * + * Note: can not sleep may be called with locks held + * + * dont want protocol specific in __skb_recv_datagram() + * to deny an incoming connection socket_sock_rcv_skb() + */ +static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + return 0; +} + + +static struct aa_label *sk_peer_label(struct sock *sk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + if (ctx->peer) + return ctx->peer; + + return ERR_PTR(-ENOPROTOOPT); +} + +/** + * apparmor_socket_getpeersec_stream - get security context of peer + * + * Note: for tcp only valid if using ipsec or cipso on lan + */ +static int apparmor_socket_getpeersec_stream(struct socket *sock, + char __user *optval, + int __user *optlen, + unsigned int len) +{ + char *name; + int slen, error = 0; + struct aa_label *label; + struct aa_label *peer; + + label = begin_current_label_crit_section(); + peer = sk_peer_label(sock->sk); + if (IS_ERR(peer)) { + error = PTR_ERR(peer); + goto done; + } + slen = aa_label_asxprint(&name, labels_ns(label), peer, + FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | + FLAG_HIDDEN_UNCONFINED, GFP_KERNEL); + /* don't include terminating \0 in slen, it breaks some apps */ + if (slen < 0) { + error = -ENOMEM; + } else { + if (slen > len) { + error = -ERANGE; + } else if (copy_to_user(optval, name, slen)) { + error = -EFAULT; + goto out; + } + if (put_user(slen, optlen)) + error = -EFAULT; +out: + kfree(name); + + } + +done: + end_current_label_crit_section(label); + + return error; +} + +/** + * apparmor_socket_getpeersec_dgram - get security label of packet + * @sock: the peer socket + * @skb: packet data + * @secid: pointer to where to put the secid of the packet + * + * Sets the netlabel socket state on sk from parent + */ +static int apparmor_socket_getpeersec_dgram(struct socket *sock, + struct sk_buff *skb, u32 *secid) + +{ + /* TODO: requires secid support */ + return -ENOPROTOOPT; +} + +/** + * apparmor_sock_graft - Initialize newly created socket + * @sk: child sock + * @parent: parent socket + * + * Note: could set off of SOCK_CTX(parent) but need to track inode and we can + * just set sk security information off of current creating process label + * Labeling of sk for accept case - probably should be sock based + * instead of task, because of the case where an implicitly labeled + * socket is shared by different tasks. + */ +static void apparmor_sock_graft(struct sock *sk, struct socket *parent) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + if (!ctx->label) + ctx->label = aa_get_current_label(); +} + static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -770,6 +1133,30 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security), + LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), + LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb), + LSM_HOOK_INIT(socket_getpeersec_stream, + apparmor_socket_getpeersec_stream), + LSM_HOOK_INIT(socket_getpeersec_dgram, + apparmor_socket_getpeersec_dgram), + LSM_HOOK_INIT(sock_graft, apparmor_sock_graft), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 000000000000..33d54435f8d6 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,184 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/label.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + + +struct aa_sfs_entry aa_sfs_entry_network[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + { } +}; + +static const char * const net_mask_names[] = { + "unknown", + "send", + "receive", + "unknown", + + "create", + "shutdown", + "connect", + "unknown", + + "setattr", + "getattr", + "setcred", + "getcred", + + "chmod", + "chown", + "chgrp", + "lock", + + "mmap", + "mprot", + "unknown", + "unknown", + + "accept", + "bind", + "listen", + "unknown", + + "setopt", + "getopt", + "unknown", + "unknown", + + "unknown", + "unknown", + "unknown", + "unknown", +}; + + +/* audit callback for net specific fields */ +void audit_net_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) + audit_log_string(ab, address_family_names[sa->u.net->family]); + else + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + audit_log_format(ab, " sock_type="); + if (sock_type_names[aad(sa)->net.type]) + audit_log_string(ab, sock_type_names[aad(sa)->net.type]); + else + audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type); + audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol); + + if (aad(sa)->request & NET_PERMS_MASK) { + audit_log_format(ab, " requested_mask="); + aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0, + net_mask_names, NET_PERMS_MASK); + + if (aad(sa)->denied & NET_PERMS_MASK) { + audit_log_format(ab, " denied_mask="); + aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0, + net_mask_names, NET_PERMS_MASK); + } + } + if (aad(sa)->peer) { + audit_log_format(ab, " peer="); + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, + FLAGS_NONE, GFP_ATOMIC); + } +} + + +/* Generic af perm */ +int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type) +{ + struct aa_perms perms = { }; + + AA_BUG(family >= AF_MAX); + AA_BUG(type < 0 || type >= SOCK_MAX); + + if (profile_unconfined(profile)) + return 0; + + perms.allow = (profile->net.allow[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.audit = (profile->net.audit[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.quiet = (profile->net.quiet[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + aa_apply_modes_to_perms(profile, &perms); + + return aa_check_perms(profile, &perms, request, sa, audit_net_cb); +} + +int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, + int type, int protocol) +{ + struct aa_profile *profile; + DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol); + + return fn_for_each_confined(label, profile, + aa_profile_af_perm(profile, &sa, request, family, + type)); +} + +static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk) +{ + struct aa_profile *profile; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!label); + AA_BUG(!sk); + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + aa_profile_af_sk_perm(profile, &sa, request, sk)); +} + +int aa_sk_perm(const char *op, u32 request, struct sock *sk) +{ + struct aa_label *label; + int error; + + AA_BUG(!sk); + AA_BUG(in_interrupt()); + + /* TODO: switch to begin_current_label ???? */ + label = begin_current_label_crit_section(); + error = aa_label_sk_perm(label, op, request, sk); + end_current_label_crit_section(label); + + return error; +} + + +int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + + return aa_label_sk_perm(label, op, request, sock->sk); +} diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 4ede87c30f8b..5a2aec358322 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -275,6 +275,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((__le16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -584,7 +597,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) struct aa_profile *profile = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; const char *info = "failed to unpack profile"; - size_t ns_len; + size_t size = 0, ns_len; struct rhashtable_params params = { 0 }; char *key = NULL; struct aa_data *data; @@ -717,6 +730,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + if (VERSION_LT(e->version, v7)) { + /* pre v7 policy always allowed these */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + } + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ info = "failed to unpack policydb"; -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0013-apparmor-move-new_null_profile-to-after-profile-look.patch000066400000000000000000000131741522511161100326670ustar00rootroot00000000000000From 50d30adbef98a0b6cc531a9413d05f564eb633ee Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Aug 2017 08:59:57 -0700 Subject: [PATCH 13/17] apparmor: move new_null_profile to after profile lookup fns() new_null_profile will need to use some of the profile lookup fns() so move instead of doing forward fn declarations. Signed-off-by: John Johansen (cherry picked from commit cf1e50dfc6f627bc2989b57076b129c330fb3f0a) --- security/apparmor/policy.c | 158 ++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 244ea4a4a8f0..a81a384a63b1 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -289,85 +289,6 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, return NULL; } -/** - * aa_new_null_profile - create or find a null-X learning profile - * @parent: profile that caused this profile to be created (NOT NULL) - * @hat: true if the null- learning profile is a hat - * @base: name to base the null profile off of - * @gfp: type of allocation - * - * Find/Create a null- complain mode profile used in learning mode. The - * name of the profile is unique and follows the format of parent//null-XXX. - * where XXX is based on the @name or if that fails or is not supplied - * a unique number - * - * null profiles are added to the profile list but the list does not - * hold a count on them so that they are automatically released when - * not in use. - * - * Returns: new refcounted profile else NULL on failure - */ -struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, - const char *base, gfp_t gfp) -{ - struct aa_profile *profile; - char *name; - - AA_BUG(!parent); - - if (base) { - name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), - gfp); - if (name) { - sprintf(name, "%s//null-%s", parent->base.hname, base); - goto name; - } - /* fall through to try shorter uniq */ - } - - name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); - if (!name) - return NULL; - sprintf(name, "%s//null-%x", parent->base.hname, - atomic_inc_return(&parent->ns->uniq_null)); - -name: - /* lookup to see if this is a dup creation */ - profile = aa_find_child(parent, basename(name)); - if (profile) - goto out; - - profile = aa_alloc_profile(name, NULL, gfp); - if (!profile) - goto fail; - - profile->mode = APPARMOR_COMPLAIN; - profile->label.flags |= FLAG_NULL; - if (hat) - profile->label.flags |= FLAG_HAT; - profile->path_flags = parent->path_flags; - - /* released on free_profile */ - rcu_assign_pointer(profile->parent, aa_get_profile(parent)); - profile->ns = aa_get_ns(parent->ns); - profile->file.dfa = aa_get_dfa(nulldfa); - profile->policy.dfa = aa_get_dfa(nulldfa); - - mutex_lock(&profile->ns->lock); - __add_profile(&parent->base.profiles, profile); - mutex_unlock(&profile->ns->lock); - - /* refcount released by caller */ -out: - kfree(name); - - return profile; - -fail: - aa_free_profile(profile); - return NULL; -} - /* TODO: profile accounting - setup in remove */ /** @@ -559,6 +480,85 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, } /** + * aa_new_null_profile - create or find a null-X learning profile + * @parent: profile that caused this profile to be created (NOT NULL) + * @hat: true if the null- learning profile is a hat + * @base: name to base the null profile off of + * @gfp: type of allocation + * + * Find/Create a null- complain mode profile used in learning mode. The + * name of the profile is unique and follows the format of parent//null-XXX. + * where XXX is based on the @name or if that fails or is not supplied + * a unique number + * + * null profiles are added to the profile list but the list does not + * hold a count on them so that they are automatically released when + * not in use. + * + * Returns: new refcounted profile else NULL on failure + */ +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + const char *base, gfp_t gfp) +{ + struct aa_profile *profile; + char *name; + + AA_BUG(!parent); + + if (base) { + name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), + gfp); + if (name) { + sprintf(name, "%s//null-%s", parent->base.hname, base); + goto name; + } + /* fall through to try shorter uniq */ + } + + name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); + if (!name) + return NULL; + sprintf(name, "%s//null-%x", parent->base.hname, + atomic_inc_return(&parent->ns->uniq_null)); + +name: + /* lookup to see if this is a dup creation */ + profile = aa_find_child(parent, basename(name)); + if (profile) + goto out; + + profile = aa_alloc_profile(name, NULL, gfp); + if (!profile) + goto fail; + + profile->mode = APPARMOR_COMPLAIN; + profile->label.flags |= FLAG_NULL; + if (hat) + profile->label.flags |= FLAG_HAT; + profile->path_flags = parent->path_flags; + + /* released on free_profile */ + rcu_assign_pointer(profile->parent, aa_get_profile(parent)); + profile->ns = aa_get_ns(parent->ns); + profile->file.dfa = aa_get_dfa(nulldfa); + profile->policy.dfa = aa_get_dfa(nulldfa); + + mutex_lock(&profile->ns->lock); + __add_profile(&parent->base.profiles, profile); + mutex_unlock(&profile->ns->lock); + + /* refcount released by caller */ +out: + kfree(name); + + return profile; + +fail: + aa_free_profile(profile); + return NULL; +} + +/** * replacement_allowed - test to see if replacement is allowed * @profile: profile to test if it can be replaced (MAYBE NULL) * @noreplace: true if replacement shouldn't be allowed but addition is okay -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0014-apparmor-fix-race-condition-in-null-profile-creation.patch000066400000000000000000000040471522511161100325370ustar00rootroot00000000000000From ab3b869791b6122c7be7e68ca4c08e2c2e8815ac Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Aug 2017 05:40:49 -0700 Subject: [PATCH 14/17] apparmor: fix race condition in null profile creation There is a race when null- profile is being created between the initial lookup/creation of the profile and lock/addition of the profile. This could result in multiple version of a profile being added to the list which need to be removed/replaced. Since these are learning profile their is no affect on mediation. Signed-off-by: John Johansen (cherry picked from commit 3aa3de2a4fb8f33ec62b00998bc6b6c6850d41b1) --- security/apparmor/policy.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index a81a384a63b1..4243b0c3f0e4 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -500,7 +500,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, const char *base, gfp_t gfp) { - struct aa_profile *profile; + struct aa_profile *p, *profile; + const char *bname; char *name; AA_BUG(!parent); @@ -523,7 +524,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, name: /* lookup to see if this is a dup creation */ - profile = aa_find_child(parent, basename(name)); + bname = basename(name); + profile = aa_find_child(parent, bname); if (profile) goto out; @@ -544,7 +546,13 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, profile->policy.dfa = aa_get_dfa(nulldfa); mutex_lock(&profile->ns->lock); - __add_profile(&parent->base.profiles, profile); + p = __find_child(&parent->base.profiles, bname); + if (p) { + aa_free_profile(profile); + profile = aa_get_profile(p); + } else { + __add_profile(&parent->base.profiles, profile); + } mutex_unlock(&profile->ns->lock); /* refcount released by caller */ -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch000066400000000000000000000026141522511161100326170ustar00rootroot00000000000000From 7f2cdd6453518ff76c3855255c91306a2b928c9a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Aug 2017 05:48:06 -0700 Subject: [PATCH 15/17] apparmor: ensure unconfined profiles have dfas initialized Generally unconfined has early bailout tests and does not need the dfas initialized, however if an early bailout test is ever missed it will result in an oops. Be defensive and initialize the unconfined profile to have null dfas (no permission) so if an early bailout test is missed we fail closed (no perms granted) instead of oopsing. Signed-off-by: John Johansen (cherry picked from commit 034ad2d248927722bdcd1aedb62634cdc2049113) --- security/apparmor/policy_ns.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 351d3bab3a3d..62a3589c62ab 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -112,6 +112,8 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR | FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; ns->unconfined->mode = APPARMOR_UNCONFINED; + ns->unconfined->file.dfa = aa_get_dfa(nulldfa); + ns->unconfined->policy.dfa = aa_get_dfa(nulldfa); /* ns and ns->unconfined share ns->unconfined refcount */ ns->unconfined->ns = ns; -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch000066400000000000000000000027051522511161100325740ustar00rootroot00000000000000From 8daf877473653c06a28c86bf72d63ce7e5c1d542 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Aug 2017 09:33:48 -0700 Subject: [PATCH 16/17] apparmor: fix incorrect type assignment when freeing proxies sparse reports poisoning the proxy->label before freeing the struct is resulting in a sparse build warning. ../security/apparmor/label.c:52:30: warning: incorrect type in assignment (different address spaces) ../security/apparmor/label.c:52:30: expected struct aa_label [noderef] *label ../security/apparmor/label.c:52:30: got struct aa_label * fix with RCU_INIT_POINTER as this is one of those cases where rcu_assign_pointer() is not needed. Signed-off-by: John Johansen (cherry picked from commit 76e22e212a850bbd16cf49f9c586d4635507e0b5) --- security/apparmor/label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 52b4ef14840d..c5b99b954580 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -49,7 +49,7 @@ static void free_proxy(struct aa_proxy *proxy) /* p->label will not updated any more as p is dead */ aa_put_label(rcu_dereference_protected(proxy->label, true)); memset(proxy, 0, sizeof(*proxy)); - proxy->label = (struct aa_label *) PROXY_POISON; + RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON); kfree(proxy); } } -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch000066400000000000000000001204361522511161100275230ustar00rootroot00000000000000From a3b0cb6676a04cdad5cc357bc422d0398083b435 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:27:23 -0700 Subject: [PATCH 17/17] UBUNTU: SAUCE: apparmor: af_unix mediation af_socket mediation did not make it into 4.14 so add remaining out of tree patch Signed-off-by: John Johansen --- security/apparmor/Makefile | 3 +- security/apparmor/af_unix.c | 651 ++++++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 6 + security/apparmor/file.c | 4 +- security/apparmor/include/af_unix.h | 114 +++++++ security/apparmor/include/net.h | 16 +- security/apparmor/include/path.h | 1 + security/apparmor/include/policy.h | 2 +- security/apparmor/lsm.c | 169 ++++++---- security/apparmor/net.c | 174 +++++++++- 10 files changed, 1072 insertions(+), 68 deletions(-) create mode 100644 security/apparmor/af_unix.c create mode 100644 security/apparmor/include/af_unix.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index dafdd387d42b..ef39226ff4aa 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o label.o mount.o net.o + resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ + af_unix.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c new file mode 100644 index 000000000000..c6876db2dbde --- /dev/null +++ b/security/apparmor/af_unix.c @@ -0,0 +1,651 @@ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2014 Canonical Ltd. + * + * 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. + */ + +#include + +#include "include/af_unix.h" +#include "include/apparmor.h" +#include "include/context.h" +#include "include/file.h" +#include "include/label.h" +#include "include/path.h" +#include "include/policy.h" + +static inline struct sock *aa_sock(struct unix_sock *u) +{ + return &u->sk; +} + +static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label, + struct unix_sock *u, int flags) +{ + AA_BUG(!label); + AA_BUG(!u); + AA_BUG(!UNIX_FS(aa_sock(u))); + + if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE)) + return 0; + + mask &= NET_FS_PERMS; + if (!u->path.dentry) { + struct path_cond cond = { }; + struct aa_perms perms = { }; + struct aa_profile *profile; + + /* socket path has been cleared because it is being shutdown + * can only fall back to original sun_path request + */ + struct aa_sk_ctx *ctx = SK_CTX(&u->sk); + if (ctx->path.dentry) + return aa_path_perm(op, label, &ctx->path, flags, mask, + &cond); + return fn_for_each_confined(label, profile, + ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ? + __aa_path_perm(op, profile, + u->addr->name->sun_path, mask, + &cond, flags, &perms) : + aa_audit_file(profile, &nullperms, op, mask, + u->addr->name->sun_path, NULL, + NULL, cond.uid, + "Failed name lookup - " + "deleted entry", -EACCES)); + } else { + /* the sunpath may not be valid for this ns so use the path */ + struct path_cond cond = { u->path.dentry->d_inode->i_uid, + u->path.dentry->d_inode->i_mode + }; + + return aa_path_perm(op, label, &u->path, flags, mask, &cond); + } + + return 0; +} + +/* passing in state returned by PROFILE_MEDIATES_AF */ +static unsigned int match_to_prot(struct aa_profile *profile, + unsigned int state, int type, int protocol, + const char **info) +{ + __be16 buffer[2]; + buffer[0] = cpu_to_be16(type); + buffer[1] = cpu_to_be16(protocol); + state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, + 4); + if (!state) + *info = "failed type and protocol match"; + return state; +} + +static unsigned int match_addr(struct aa_profile *profile, unsigned int state, + struct sockaddr_un *addr, int addrlen) +{ + if (addr) + /* include leading \0 */ + state = aa_dfa_match_len(profile->policy.dfa, state, + addr->sun_path, + unix_addr_len(addrlen)); + else + /* anonymous end point */ + state = aa_dfa_match_len(profile->policy.dfa, state, "\x01", + 1); + /* todo change to out of band */ + state = aa_dfa_null_transition(profile->policy.dfa, state); + return state; +} + +static unsigned int match_to_local(struct aa_profile *profile, + unsigned int state, int type, int protocol, + struct sockaddr_un *addr, int addrlen, + const char **info) +{ + state = match_to_prot(profile, state, type, protocol, info); + if (state) { + state = match_addr(profile, state, addr, addrlen); + if (state) { + /* todo: local label matching */ + state = aa_dfa_null_transition(profile->policy.dfa, + state); + if (!state) + *info = "failed local label match"; + } else + *info = "failed local address match"; + } + + return state; +} + +static unsigned int match_to_sk(struct aa_profile *profile, + unsigned int state, struct unix_sock *u, + const char **info) +{ + struct sockaddr_un *addr = NULL; + int addrlen = 0; + + if (u->addr) { + addr = u->addr->name; + addrlen = u->addr->len; + } + + return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol, + addr, addrlen, info); +} + +#define CMD_ADDR 1 +#define CMD_LISTEN 2 +#define CMD_OPT 4 + +static inline unsigned int match_to_cmd(struct aa_profile *profile, + unsigned int state, struct unix_sock *u, + char cmd, const char **info) +{ + state = match_to_sk(profile, state, u, info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1); + if (!state) + *info = "failed cmd selection match"; + } + + return state; +} + +static inline unsigned int match_to_peer(struct aa_profile *profile, + unsigned int state, + struct unix_sock *u, + struct sockaddr_un *peer_addr, + int peer_addrlen, + const char **info) +{ + state = match_to_cmd(profile, state, u, CMD_ADDR, info); + if (state) { + state = match_addr(profile, state, peer_addr, peer_addrlen); + if (!state) + *info = "failed peer address match"; + } + return state; +} + +static int do_perms(struct aa_profile *profile, unsigned int state, u32 request, + struct common_audit_data *sa) +{ + struct aa_perms perms; + + AA_BUG(!profile); + + aa_compute_perms(profile->policy.dfa, state, &perms); + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, sa, + audit_net_cb); +} + +static int match_label(struct aa_profile *profile, struct aa_profile *peer, + unsigned int state, u32 request, + struct common_audit_data *sa) +{ + AA_BUG(!profile); + AA_BUG(!peer); + + aad(sa)->peer = &peer->label; + + if (state) { + state = aa_dfa_match(profile->policy.dfa, state, + peer->base.hname); + if (!state) + aad(sa)->info = "failed peer label match"; + } + return do_perms(profile, state, request, sa); +} + + +/* unix sock creation comes before we know if the socket will be an fs + * socket + * v6 - semantics are handled by mapping in profile load + * v7 - semantics require sock create for tasks creating an fs socket. + */ +static int profile_create_perm(struct aa_profile *profile, int family, + int type, int protocol) +{ + unsigned int state; + DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol); + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + + if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) { + state = match_to_prot(profile, state, type, protocol, + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_CREATE, &sa); + } + + return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type); +} + +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + struct aa_profile *profile; + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + profile_create_perm(profile, family, type, protocol)); +} + + +static inline int profile_sk_perm(struct aa_profile *profile, const char *op, + u32 request, struct sock *sk) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + state = match_to_sk(profile, state, unix_sk(sk), + &aad(&sa)->info); + return do_perms(profile, state, request, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, request, sk); +} + +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk) +{ + struct aa_profile *profile; + + return fn_for_each_confined(label, profile, + profile_sk_perm(profile, op, request, sk)); +} + +static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + if (unconfined(label)) + return 0; + if (UNIX_FS(sock->sk)) + return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0); + + return aa_unix_label_sk_perm(label, op, request, sock->sk); +} + +/* revaliation, get/set attr */ +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) +{ + struct aa_label *label; + int error; + + label = begin_current_label_crit_section(); + error = unix_label_sock_perm(label, op, request, sock); + end_current_label_crit_section(label); + + return error; +} + +static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, + struct sockaddr *addr, int addrlen) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_BIND, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(addr->sa_family != AF_UNIX); + AA_BUG(profile_unconfined(profile)); + AA_BUG(unix_addr_fs(addr, addrlen)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + /* bind for abstract socket */ + aad(&sa)->net.addr = unix_addr(addr); + aad(&sa)->net.addrlen = addrlen; + + state = match_to_local(profile, state, + sk->sk_type, sk->sk_protocol, + unix_addr(addr), addrlen, + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_BIND, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk); +} + +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + /* fs bind is handled by mknod */ + if (!(unconfined(label) || unix_addr_fs(address, addrlen))) + error = fn_for_each_confined(label, profile, + profile_bind_perm(profile, sock->sk, address, + addrlen)); + end_current_label_crit_section(label); + + return error; +} + +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + /* unix connections are covered by the + * - unix_stream_connect (stream) and unix_may_send hooks (dgram) + * - fs connect is handled by open + */ + return 0; +} + +static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, + int backlog) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_LISTEN, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + __be16 b = cpu_to_be16(backlog); + + state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN, + &aad(&sa)->info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &b, 2); + if (!state) + aad(&sa)->info = "failed listen backlog match"; + } + return do_perms(profile, state, AA_MAY_LISTEN, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk); +} + +int aa_unix_listen_perm(struct socket *sock, int backlog) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_listen_perm(profile, sock->sk, + backlog)); + end_current_label_crit_section(label); + + return error; +} + + +static inline int profile_accept_perm(struct aa_profile *profile, + struct sock *sk, + struct sock *newsk) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + state = match_to_sk(profile, state, unix_sk(sk), + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_ACCEPT, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk); +} + +/* ability of sock to connect, not peer address binding */ +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_accept_perm(profile, sock->sk, + newsock->sk)); + end_current_label_crit_section(label); + + return error; +} + + +/* dgram handled by unix_may_sendmsg, right to send on stream done at connect + * could do per msg unix_stream here + */ +/* sendmsg, recvmsg */ +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + return 0; +} + + +static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request, + struct sock *sk, int level, int optname) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + __be16 b = cpu_to_be16(optname); + + state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT, + &aad(&sa)->info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &b, 2); + if (!state) + aad(&sa)->info = "failed sockopt match"; + } + return do_perms(profile, state, request, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, request, sk); +} + +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_opt_perm(profile, op, request, + sock->sk, level, optname)); + end_current_label_crit_section(label); + + return error; +} + +/* null peer_label is allowed, in which case the peer_sk label is used */ +static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label, + struct common_audit_data *sa) +{ + unsigned int state; + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + AA_BUG(!sk); + AA_BUG(!peer_sk); + AA_BUG(UNIX_FS(peer_sk)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); + struct aa_profile *peerp; + struct sockaddr_un *addr = NULL; + int len = 0; + if (unix_sk(peer_sk)->addr) { + addr = unix_sk(peer_sk)->addr->name; + len = unix_sk(peer_sk)->addr->len; + } + state = match_to_peer(profile, state, unix_sk(sk), + addr, len, &aad(sa)->info); + if (!peer_label) + peer_label = peer_ctx->label; + return fn_for_each_in_ns(peer_label, peerp, + match_label(profile, peerp, state, request, + sa)); + } + + return aa_profile_af_sk_perm(profile, sa, request, sk); +} + +/** + * + * Requires: lock held on both @sk and @peer_sk + */ +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label) +{ + struct unix_sock *peeru = unix_sk(peer_sk); + struct unix_sock *u = unix_sk(sk); + + AA_BUG(!label); + AA_BUG(!sk); + AA_BUG(!peer_sk); + + if (UNIX_FS(aa_sock(peeru))) + return unix_fs_perm(op, request, label, peeru, 0); + else if (UNIX_FS(aa_sock(u))) + return unix_fs_perm(op, request, label, u, 0); + else { + struct aa_profile *profile; + DEFINE_AUDIT_SK(sa, op, sk); + aad(&sa)->net.peer_sk = peer_sk; + + /* TODO: ns!!! */ + if (!net_eq(sock_net(sk), sock_net(peer_sk))) { + ; + } + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + profile_peer_perm(profile, op, request, sk, + peer_sk, peer_label, &sa)); + } +} + + +/* from net/unix/af_unix.c */ +static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) +{ + if (unlikely(sk1 == sk2) || !sk2) { + unix_state_lock(sk1); + return; + } + if (sk1 < sk2) { + unix_state_lock(sk1); + unix_state_lock_nested(sk2); + } else { + unix_state_lock(sk2); + unix_state_lock_nested(sk1); + } +} + +static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) +{ + if (unlikely(sk1 == sk2) || !sk2) { + unix_state_unlock(sk1); + return; + } + unix_state_unlock(sk1); + unix_state_unlock(sk2); +} + +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + struct sock *peer_sk = NULL; + u32 sk_req = request & ~NET_PEER_MASK; + int error = 0; + + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(sock->sk->sk_family != AF_UNIX); + + /* TODO: update sock label with new task label */ + unix_state_lock(sock->sk); + peer_sk = unix_peer(sock->sk); + if (peer_sk) + sock_hold(peer_sk); + if (!unix_connected(sock) && sk_req) { + error = unix_label_sock_perm(label, op, sk_req, sock); + if (!error) { + // update label + } + } + unix_state_unlock(sock->sk); + if (!peer_sk) + return error; + + unix_state_double_lock(sock->sk, peer_sk); + if (UNIX_FS(sock->sk)) { + error = unix_fs_perm(op, request, label, unix_sk(sock->sk), + PATH_SOCK_COND); + } else if (UNIX_FS(peer_sk)) { + error = unix_fs_perm(op, request, label, unix_sk(peer_sk), + PATH_SOCK_COND); + } else { + struct aa_sk_ctx *pctx = SK_CTX(peer_sk); + if (sk_req) + error = aa_unix_label_sk_perm(label, op, sk_req, + sock->sk); + last_error(error, + xcheck(aa_unix_peer_perm(label, op, + MAY_READ | MAY_WRITE, + sock->sk, peer_sk, NULL), + aa_unix_peer_perm(pctx->label, op, + MAY_READ | MAY_WRITE, + peer_sk, sock->sk, label))); + } + + unix_state_double_unlock(sock->sk, peer_sk); + sock_put(peer_sk); + + return error; +} diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 125dad5c3fde..20cdb1c4b266 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2187,6 +2187,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_dbus[] = { + AA_SFS_FILE_STRING("mask", "acquire send receive"), + { } +}; + static struct aa_sfs_entry aa_sfs_entry_query_label[] = { AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), AA_SFS_FILE_BOOLEAN("data", 1), @@ -2210,6 +2215,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("caps", aa_sfs_entry_caps), AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), AA_SFS_DIR("signal", aa_sfs_entry_signal), + AA_SFS_DIR("dbus", aa_sfs_entry_dbus), AA_SFS_DIR("query", aa_sfs_entry_query), { } }; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index db80221891c6..e62791106900 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -16,6 +16,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/context.h" @@ -289,7 +290,8 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, { int e = 0; - if (profile_unconfined(profile)) + if (profile_unconfined(profile) || + ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX))) return 0; aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); if (request & ~perms->allow) diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h new file mode 100644 index 000000000000..d1b7f2316be4 --- /dev/null +++ b/security/apparmor/include/af_unix.h @@ -0,0 +1,114 @@ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2014 Canonical Ltd. + * + * 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 __AA_AF_UNIX_H + +#include + +#include "label.h" +//#include "include/net.h" + +#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) +#define unix_abstract_name_len(L) (unix_addr_len(L) - 1) +#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len)) +#define addr_unix_abstract_name(B) ((B)[0] == 0) +#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0) +#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr)) +//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr)) + +#define unix_addr(A) ((struct sockaddr_un *)(A)) +#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) +#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path)) + +#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr) +/* from net/unix/af_unix.c */ +#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) && \ + unix_sk(U)->addr->hash < UNIX_HASH_SIZE) +#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0]) +#define unix_peer(sk) (unix_sk(sk)->peer) +#define unix_connected(S) ((S)->state == SS_CONNECTED) + +static inline void print_unix_addr(struct sockaddr_un *A, int L) +{ + char *buf = (A) ? (char *) &(A)->sun_path : NULL; + int len = unix_addr_len(L); + if (!buf || len <= 0) + printk(" "); + else if (buf[0]) + printk(" %s", buf); + else + /* abstract name len includes leading \0 */ + printk(" %d @%.*s", len - 1, len - 1, buf+1); +}; + +/* + printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \ + #SK , \ +*/ +#define print_unix_sk(SK) \ +do { \ + struct unix_sock *u = unix_sk(SK); \ + printk("%s: f %d, t %d, p %d", #SK , \ + (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \ + if (u->addr) \ + print_unix_addr(u->addr->name, u->addr->len); \ + else \ + print_unix_addr(NULL, sizeof(sa_family_t)); \ + /* printk("\n");*/ \ +} while (0) + +#define print_sk(SK) \ +do { \ + if (!(SK)) { \ + printk("%s: %s is null\n", __FUNCTION__, #SK); \ + } else if ((SK)->sk_family == PF_UNIX) { \ + print_unix_sk(SK); \ + printk("\n"); \ + } else { \ + printk("%s: %s: family %d\n", __FUNCTION__, #SK , \ + (SK)->sk_family); \ + } \ +} while (0) + +#define print_sock_addr(U) \ +do { \ + printk("%s:\n", __FUNCTION__); \ + printk(" sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : ""); print_sk(sock); \ + printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : ""); print_sk(other); \ + printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : ""); print_sk(newsk); \ +} while (0) + + + + +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label); +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk); +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_listen_perm(struct socket *sock, int backlog); +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + +#endif /* __AA_AF_UNIX_H */ diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index 140c8efcf364..0ae45240c352 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -90,8 +90,6 @@ extern struct aa_sfs_entry aa_sfs_entry_network[]; void audit_net_cb(struct audit_buffer *ab, void *va); int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, u32 request, u16 family, int type); -int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, - int type, int protocol); static inline int aa_profile_af_sk_perm(struct aa_profile *profile, struct common_audit_data *sa, u32 request, @@ -100,8 +98,20 @@ static inline int aa_profile_af_sk_perm(struct aa_profile *profile, return aa_profile_af_perm(profile, sa, request, sk->sk_family, sk->sk_type); } -int aa_sk_perm(const char *op, u32 request, struct sock *sk); +int aa_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_sock_listen_perm(struct socket *sock, int backlog); +int aa_sock_accept_perm(struct socket *sock, struct socket *newsock); +int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, struct socket *sock); diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 05fb3305671e..26762db2207d 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -18,6 +18,7 @@ enum path_flags { PATH_IS_DIR = 0x1, /* path is a directory */ + PATH_SOCK_COND = 0x2, PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 4364088a0b9e..26660a1a50b0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -226,7 +226,7 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, u16 AF) { unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); - u16 be_af = cpu_to_be16(AF); + __be16 be_af = cpu_to_be16(AF); if (!state) return 0; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index cc5ab23a2d84..0ede66d80a53 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -26,6 +26,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/apparmorfs.h" #include "include/audit.h" @@ -782,16 +783,96 @@ static void apparmor_sk_clone_security(const struct sock *sk, path_get(&new->path); } -static int aa_sock_create_perm(struct aa_label *label, int family, int type, - int protocol) +static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk) { - AA_BUG(!label); - AA_BUG(in_interrupt()); + if (sk->sk_family == PF_UNIX && UNIX_FS(sk)) + return &unix_sk(sk)->path; + else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk)) + return &unix_sk(newsk)->path; + return NULL; +} + +/** + * apparmor_unix_stream_connect - check perms before making unix domain conn + * + * peer is locked when this hook is called + */ +static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk, + struct sock *newsk) +{ + struct aa_sk_ctx *sk_ctx = SK_CTX(sk); + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); + struct aa_sk_ctx *new_ctx = SK_CTX(newsk); + struct aa_label *label; + struct path *path; + int error; - return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, - protocol); + label = __begin_current_label_crit_section(); + error = aa_unix_peer_perm(label, OP_CONNECT, + (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), + sk, peer_sk, NULL); + if (!UNIX_FS(peer_sk)) { + last_error(error, + aa_unix_peer_perm(peer_ctx->label, OP_CONNECT, + (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE), + peer_sk, sk, label)); + } + __end_current_label_crit_section(label); + + if (error) + return error; + + /* label newsk if it wasn't labeled in post_create. Normally this + * would be done in sock_graft, but because we are directly looking + * at the peer_sk to obtain peer_labeling for unix socks this + * does not work + */ + if (!new_ctx->label) + new_ctx->label = aa_get_label(peer_ctx->label); + + /* Cross reference the peer labels for SO_PEERSEC */ + if (new_ctx->peer) + aa_put_label(new_ctx->peer); + + if (sk_ctx->peer) + aa_put_label(sk_ctx->peer); + + new_ctx->peer = aa_get_label(sk_ctx->label); + sk_ctx->peer = aa_get_label(peer_ctx->label); + + path = UNIX_FS_CONN_PATH(sk, peer_sk); + if (path) { + new_ctx->path = *path; + sk_ctx->path = *path; + path_get(path); + path_get(path); + } + return 0; } +/** + * apparmor_unix_may_send - check perms before conn or sending unix dgrams + * + * other is locked when this hook is called + * + * dgram connect calls may_send, peer setup but path not copied????? + */ +static int apparmor_unix_may_send(struct socket *sock, struct socket *peer) +{ + struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk); + struct aa_label *label; + int error; + + label = __begin_current_label_crit_section(); + error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND, + sock->sk, peer->sk, NULL), + aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG, + AA_MAY_RECEIVE, + peer->sk, sock->sk, label)); + __end_current_label_crit_section(label); + + return error; +} /** * apparmor_socket_create - check perms before creating a new socket @@ -849,12 +930,7 @@ static int apparmor_socket_post_create(struct socket *sock, int family, static int apparmor_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!address); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); + return aa_sock_bind_perm(sock, address, addrlen); } /** @@ -863,12 +939,7 @@ static int apparmor_socket_bind(struct socket *sock, static int apparmor_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!address); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); + return aa_sock_connect_perm(sock, address, addrlen); } /** @@ -876,11 +947,7 @@ static int apparmor_socket_connect(struct socket *sock, */ static int apparmor_socket_listen(struct socket *sock, int backlog) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); + return aa_sock_listen_perm(sock, backlog); } /** @@ -891,23 +958,7 @@ static int apparmor_socket_listen(struct socket *sock, int backlog) */ static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!newsock); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); -} - -static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, - struct msghdr *msg, int size) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!msg); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); + return aa_sock_accept_perm(sock, newsock); } /** @@ -928,16 +979,6 @@ static int apparmor_socket_recvmsg(struct socket *sock, return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); } -/* revaliation, get/set attr, shutdown */ -static int aa_sock_perm(const char *op, u32 request, struct socket *sock) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); -} - /** * apparmor_socket_getsockname - check perms before getting the local address */ @@ -954,17 +995,6 @@ static int apparmor_socket_getpeername(struct socket *sock) return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); } -/* revaliation, get/set attr, opt */ -static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, - int level, int optname) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); -} - /** * apparmor_getsockopt - check perms before getting socket options */ @@ -1009,11 +1039,25 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) static struct aa_label *sk_peer_label(struct sock *sk) { + struct sock *peer_sk; struct aa_sk_ctx *ctx = SK_CTX(sk); if (ctx->peer) return ctx->peer; + if (sk->sk_family != PF_UNIX) + return ERR_PTR(-ENOPROTOOPT); + + /* check for sockpair peering which does not go through + * security_unix_stream_connect + */ + peer_sk = unix_peer(sk); + if (peer_sk) { + ctx = SK_CTX(peer_sk); + if (ctx->label) + return ctx->label; + } + return ERR_PTR(-ENOPROTOOPT); } @@ -1137,6 +1181,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect), + LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 33d54435f8d6..dd1953b08e58 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -12,6 +12,7 @@ * License. */ +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/context.h" @@ -24,6 +25,7 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + AA_SFS_FILE_BOOLEAN("af_unix", 1), { } }; @@ -69,6 +71,36 @@ static const char * const net_mask_names[] = { "unknown", }; +static void audit_unix_addr(struct audit_buffer *ab, const char *str, + struct sockaddr_un *addr, int addrlen) +{ + int len = unix_addr_len(addrlen); + + if (!addr || len <= 0) { + audit_log_format(ab, " %s=none", str); + } else if (addr->sun_path[0]) { + audit_log_format(ab, " %s=", str); + audit_log_untrustedstring(ab, addr->sun_path); + } else { + audit_log_format(ab, " %s=\"@", str); + if (audit_string_contains_control(&addr->sun_path[1], len - 1)) + audit_log_n_hex(ab, &addr->sun_path[1], len - 1); + else + audit_log_format(ab, "%.*s", len - 1, + &addr->sun_path[1]); + audit_log_format(ab, "\""); + } +} + +static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str, + struct sock *sk) +{ + struct unix_sock *u = unix_sk(sk); + if (u && u->addr) + audit_unix_addr(ab, str, u->addr->name, u->addr->len); + else + audit_unix_addr(ab, str, NULL, 0); +} /* audit callback for net specific fields */ void audit_net_cb(struct audit_buffer *ab, void *va) @@ -98,6 +130,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va) net_mask_names, NET_PERMS_MASK); } } + if (sa->u.net->family == AF_UNIX) { + if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr) + audit_unix_addr(ab, "addr", + unix_addr(aad(sa)->net.addr), + aad(sa)->net.addrlen); + else + audit_unix_sk_addr(ab, "addr", sa->u.net->sk); + if (aad(sa)->request & NET_PEER_MASK) { + if (aad(sa)->net.addr) + audit_unix_addr(ab, "peer_addr", + unix_addr(aad(sa)->net.addr), + aad(sa)->net.addrlen); + else + audit_unix_sk_addr(ab, "peer_addr", + aad(sa)->net.peer_sk); + } + } if (aad(sa)->peer) { audit_log_format(ab, " peer="); aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, @@ -172,6 +221,127 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk) return error; } +#define af_select(FAMILY, FN, DEF_FN) \ +({ \ + int __e; \ + switch ((FAMILY)) { \ + case AF_UNIX: \ + __e = aa_unix_ ## FN; \ + break; \ + default: \ + __e = DEF_FN; \ + } \ + __e; \ +}) + +/* TODO: push into lsm.c ???? */ + +/* revaliation, get/set attr, shutdown */ +int aa_sock_perm(const char *op, u32 request, struct socket *sock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + sock_perm(op, request, sock), + aa_sk_perm(op, request, sock->sk)); +} + +int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + AA_BUG(!label); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(family, + create_perm(label, family, type, protocol), + aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, + type, protocol)); +} + +int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + bind_perm(sock, address, addrlen), + aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); +} + +int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + connect_perm(sock, address, addrlen), + aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); +} + +int aa_sock_listen_perm(struct socket *sock, int backlog) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + listen_perm(sock, backlog), + aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); +} + +/* ability of sock to connect, not peer address binding */ +int aa_sock_accept_perm(struct socket *sock, struct socket *newsock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!newsock); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + accept_perm(sock, newsock), + aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); +} + +/* sendmsg, recvmsg */ +int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!msg); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + msg_perm(op, request, sock, msg, size), + aa_sk_perm(op, request, sock->sk)); +} + +/* revaliation, get/set attr, opt */ +int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + opt_perm(op, request, sock, level, optname), + aa_sk_perm(op, request, sock->sk)); +} int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, struct socket *sock) @@ -180,5 +350,7 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, AA_BUG(!sock); AA_BUG(!sock->sk); - return aa_label_sk_perm(label, op, request, sock->sk); + return af_select(sock->sk->sk_family, + file_perm(label, op, request, sock), + aa_label_sk_perm(label, op, request, sock->sk)); } -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.13/README000066400000000000000000000003041522511161100176670ustar00rootroot00000000000000The old out of tree patches have been dropped. This series is a backport of the patches currently in security-next scheduled for 4.14, with the exception of the last patch for af_unix mediation. apparmor-5.0.2/kernel-patches/v4.14/000077500000000000000000000000001522511161100170135ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/v4.14/0001-apparmor-add-base-infastructure-for-socket-mediation.patch000066400000000000000000001007571522511161100326200ustar00rootroot00000000000000From f34488a615da4b0dd68f697587f1cf13e4535e5d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:18:33 -0700 Subject: [PATCH 1/2] apparmor: add base infastructure for socket mediation Provide a basic mediation of sockets. This is not a full net mediation but just whether a spcific family of socket can be used by an application, along with setting up some basic infrastructure for network mediation to follow. the user space rule hav the basic form of NETWORK RULE = [ QUALIFIERS ] 'network' [ DOMAIN ] [ TYPE | PROTOCOL ] DOMAIN = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'mpls' | 'ib' | 'kcm' ) ',' TYPE = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' ) PROTOCOL = ( 'tcp' | 'udp' | 'icmp' ) eg. network, network inet, Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 43 ++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/file.c | 30 +++ security/apparmor/include/audit.h | 26 ++- security/apparmor/include/net.h | 114 +++++++++++ security/apparmor/include/perms.h | 5 +- security/apparmor/include/policy.h | 13 ++ security/apparmor/lib.c | 5 +- security/apparmor/lsm.c | 387 +++++++++++++++++++++++++++++++++++++ security/apparmor/net.c | 184 ++++++++++++++++++ security/apparmor/policy_unpack.c | 51 ++++- 12 files changed, 844 insertions(+), 16 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70d72b8..d5b291e94264 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 9a6b4033d52b..e7ff2183532a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,11 +5,44 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o label.o mount.o + resource.o secid.o file.o policy_ns.o label.o mount.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h +# Build a lower case string table of address family names +# Transform lines from +# #define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_SFS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + printf '%s' '\#define AA_SFS_AF_MASK "' >> $@ ;\ + sed -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of capability names # Transforms lines from @@ -62,6 +95,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -69,3 +103,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index caaf51dda648..518d5928661b 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2202,6 +2202,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("policy", aa_sfs_entry_policy), AA_SFS_DIR("domain", aa_sfs_entry_domain), AA_SFS_DIR("file", aa_sfs_entry_file), + AA_SFS_DIR("network", aa_sfs_entry_network), AA_SFS_DIR("mount", aa_sfs_entry_mount), AA_SFS_DIR("namespaces", aa_sfs_entry_ns), AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 3382518b87fa..db80221891c6 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -21,6 +21,7 @@ #include "include/context.h" #include "include/file.h" #include "include/match.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/label.h" @@ -566,6 +567,32 @@ static int __file_path_perm(const char *op, struct aa_label *label, return error; } +static int __file_sock_perm(const char *op, struct aa_label *label, + struct aa_label *flabel, struct file *file, + u32 request, u32 denied) +{ + struct socket *sock = (struct socket *) file->private_data; + int error; + + AA_BUG(!sock); + + /* revalidation due to label out of date. No revocation at this time */ + if (!denied && aa_label_is_subset(flabel, label)) + return 0; + + /* TODO: improve to skip profiles cached in flabel */ + error = aa_sock_file_perm(label, op, request, sock); + if (denied) { + /* TODO: improve to skip profiles checked above */ + /* check every profile in file label to is cached */ + last_error(error, aa_sock_file_perm(flabel, op, request, sock)); + } + if (!error) + update_file_ctx(file_ctx(file), label, request); + + return error; +} + /** * aa_file_perm - do permission revalidation check & audit for @file * @op: operation being checked @@ -610,6 +637,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, error = __file_path_perm(op, label, flabel, file, request, denied); + else if (S_ISSOCK(file_inode(file)->i_mode)) + error = __file_sock_perm(op, label, flabel, file, request, + denied); done: rcu_read_unlock(); diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 620e81169659..ff4316e1068d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -121,21 +121,29 @@ struct apparmor_audit_data { /* these entries require a custom callback fn */ struct { struct aa_label *peer; - struct { - const char *target; - kuid_t ouid; - } fs; + union { + struct { + kuid_t ouid; + const char *target; + } fs; + struct { + int type, protocol; + struct sock *peer_sk; + void *addr; + int addrlen; + } net; + int signal; + struct { + int rlim; + unsigned long max; + } rlim; + }; }; struct { struct aa_profile *profile; const char *ns; long pos; } iface; - int signal; - struct { - int rlim; - unsigned long max; - } rlim; struct { const char *src_name; const char *type; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 000000000000..140c8efcf364 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,114 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include +#include + +#include "apparmorfs.h" +#include "label.h" +#include "perms.h" +#include "policy.h" + +#define AA_MAY_SEND AA_MAY_WRITE +#define AA_MAY_RECEIVE AA_MAY_READ + +#define AA_MAY_SHUTDOWN AA_MAY_DELETE + +#define AA_MAY_CONNECT AA_MAY_OPEN +#define AA_MAY_ACCEPT 0x00100000 + +#define AA_MAY_BIND 0x00200000 +#define AA_MAY_LISTEN 0x00400000 + +#define AA_MAY_SETOPT 0x01000000 +#define AA_MAY_GETOPT 0x02000000 + +#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ + AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \ + AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \ + AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT) + +#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ + AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\ + AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \ + AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \ + AA_MAY_MPROT) + +#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \ + AA_MAY_ACCEPT) +struct aa_sk_ctx { + struct aa_label *label; + struct aa_label *peer; + struct path path; +}; + +#define SK_CTX(X) ((X)->sk_security) +#define SOCK_ctx(X) SOCK_INODE(X)->i_security +#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ + struct lsm_network_audit NAME ## _net = { .sk = (SK), \ + .family = (F)}; \ + DEFINE_AUDIT_DATA(NAME, \ + ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ + LSM_AUDIT_DATA_NONE, \ + OP); \ + NAME.u.net = &(NAME ## _net); \ + aad(&NAME)->net.type = (T); \ + aad(&NAME)->net.protocol = (P) + +#define DEFINE_AUDIT_SK(NAME, OP, SK) \ + DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ + (SK)->sk_protocol) + +/* struct aa_net - network confinement data + * @allow: basic network families permissions + * @audit: which network permissions to force audit + * @quiet: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + + +extern struct aa_sfs_entry aa_sfs_entry_network[]; + +void audit_net_cb(struct audit_buffer *ab, void *va); +int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type); +int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, + int type, int protocol); +static inline int aa_profile_af_sk_perm(struct aa_profile *profile, + struct common_audit_data *sa, + u32 request, + struct sock *sk) +{ + return aa_profile_af_perm(profile, sa, request, sk->sk_family, + sk->sk_type); +} +int aa_sk_perm(const char *op, u32 request, struct sock *sk); + +int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index 2b27bb79aec4..af04d5a7d73d 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h @@ -135,9 +135,10 @@ extern struct aa_perms allperms; void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask); -void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask); +void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, + u32 mask); void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, - u32 chrsmask, const char **names, u32 namesmask); + u32 chrsmask, const char * const *names, u32 namesmask); void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms); void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 17fe41a9cac3..4364088a0b9e 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -30,6 +30,7 @@ #include "file.h" #include "lib.h" #include "label.h" +#include "net.h" #include "perms.h" #include "resource.h" @@ -111,6 +112,7 @@ struct aa_data { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -148,6 +150,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; struct aa_loaddata *rawdata; @@ -220,6 +223,16 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, return 0; } +static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, + u16 AF) { + unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); + u16 be_af = cpu_to_be16(AF); + + if (!state) + return 0; + return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); +} + /** * aa_get_profile - increment refcount on profile @p * @p: profile (MAYBE NULL) diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 08ca26bcca77..8818621b5d95 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -211,7 +211,8 @@ void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask) *str = '\0'; } -void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) +void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, + u32 mask) { const char *fmt = "%s"; unsigned int i, perm = 1; @@ -229,7 +230,7 @@ void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) } void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, - u32 chrsmask, const char **names, u32 namesmask) + u32 chrsmask, const char * const *names, u32 namesmask) { char str[33]; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 1346ee5be04f..72b915dfcaf7 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -33,6 +33,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/label.h" #include "include/policy.h" @@ -736,6 +737,368 @@ static int apparmor_task_kill(struct task_struct *target, struct siginfo *info, return error; } +/** + * apparmor_sk_alloc_security - allocate and attach the sk_security field + */ +static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags) +{ + struct aa_sk_ctx *ctx; + + ctx = kzalloc(sizeof(*ctx), flags); + if (!ctx) + return -ENOMEM; + + SK_CTX(sk) = ctx; + + return 0; +} + +/** + * apparmor_sk_free_security - free the sk_security field + */ +static void apparmor_sk_free_security(struct sock *sk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + SK_CTX(sk) = NULL; + aa_put_label(ctx->label); + aa_put_label(ctx->peer); + path_put(&ctx->path); + kfree(ctx); +} + +/** + * apparmor_clone_security - clone the sk_security field + */ +static void apparmor_sk_clone_security(const struct sock *sk, + struct sock *newsk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + struct aa_sk_ctx *new = SK_CTX(newsk); + + new->label = aa_get_label(ctx->label); + new->peer = aa_get_label(ctx->peer); + new->path = ctx->path; + path_get(&new->path); +} + +static int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + AA_BUG(!label); + AA_BUG(in_interrupt()); + + return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, + protocol); +} + + +/** + * apparmor_socket_create - check perms before creating a new socket + */ +static int apparmor_socket_create(int family, int type, int protocol, int kern) +{ + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(kern || unconfined(label))) + error = aa_sock_create_perm(label, family, type, protocol); + end_current_label_crit_section(label); + + return error; +} + +/** + * apparmor_socket_post_create - setup the per-socket security struct + * + * Note: + * - kernel sockets currently labeled unconfined but we may want to + * move to a special kernel label + * - socket may not have sk here if created with sock_create_lite or + * sock_alloc. These should be accept cases which will be handled in + * sock_graft. + */ +static int apparmor_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) +{ + struct aa_label *label; + + if (kern) { + struct aa_ns *ns = aa_get_current_ns(); + + label = aa_get_label(ns_unconfined(ns)); + aa_put_ns(ns); + } else + label = aa_get_current_label(); + + if (sock->sk) { + struct aa_sk_ctx *ctx = SK_CTX(sock->sk); + + aa_put_label(ctx->label); + ctx->label = aa_get_label(label); + } + aa_put_label(label); + + return 0; +} + +/** + * apparmor_socket_bind - check perms before bind addr to socket + */ +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); +} + +/** + * apparmor_socket_connect - check perms before connecting @sock to @address + */ +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); +} + +/** + * apparmor_socket_list - check perms before allowing listen + */ +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); +} + +/** + * apparmor_socket_accept - check perms before accepting a new connection. + * + * Note: while @newsock is created and has some information, the accept + * has not been done. + */ +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!newsock); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); +} + +static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!msg); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_socket_sendmsg - check perms before sending msg to another socket + */ +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); +} + +/** + * apparmor_socket_recvmsg - check perms before receiving a message + */ +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); +} + +/* revaliation, get/set attr, shutdown */ +static int aa_sock_perm(const char *op, u32 request, struct socket *sock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_socket_getsockname - check perms before getting the local address + */ +static int apparmor_socket_getsockname(struct socket *sock) +{ + return aa_sock_perm(OP_GETSOCKNAME, AA_MAY_GETATTR, sock); +} + +/** + * apparmor_socket_getpeername - check perms before getting remote address + */ +static int apparmor_socket_getpeername(struct socket *sock) +{ + return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); +} + +/* revaliation, get/set attr, opt */ +static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, + int level, int optname) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_getsockopt - check perms before getting socket options + */ +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + return aa_sock_opt_perm(OP_GETSOCKOPT, AA_MAY_GETOPT, sock, + level, optname); +} + +/** + * apparmor_setsockopt - check perms before setting socket options + */ +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + return aa_sock_opt_perm(OP_SETSOCKOPT, AA_MAY_SETOPT, sock, + level, optname); +} + +/** + * apparmor_socket_shutdown - check perms before shutting down @sock conn + */ +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock); +} + +/** + * apparmor_socket_sock_recv_skb - check perms before associating skb to sk + * + * Note: can not sleep may be called with locks held + * + * dont want protocol specific in __skb_recv_datagram() + * to deny an incoming connection socket_sock_rcv_skb() + */ +static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + return 0; +} + + +static struct aa_label *sk_peer_label(struct sock *sk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + if (ctx->peer) + return ctx->peer; + + return ERR_PTR(-ENOPROTOOPT); +} + +/** + * apparmor_socket_getpeersec_stream - get security context of peer + * + * Note: for tcp only valid if using ipsec or cipso on lan + */ +static int apparmor_socket_getpeersec_stream(struct socket *sock, + char __user *optval, + int __user *optlen, + unsigned int len) +{ + char *name; + int slen, error = 0; + struct aa_label *label; + struct aa_label *peer; + + label = begin_current_label_crit_section(); + peer = sk_peer_label(sock->sk); + if (IS_ERR(peer)) { + error = PTR_ERR(peer); + goto done; + } + slen = aa_label_asxprint(&name, labels_ns(label), peer, + FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | + FLAG_HIDDEN_UNCONFINED, GFP_KERNEL); + /* don't include terminating \0 in slen, it breaks some apps */ + if (slen < 0) { + error = -ENOMEM; + } else { + if (slen > len) { + error = -ERANGE; + } else if (copy_to_user(optval, name, slen)) { + error = -EFAULT; + goto out; + } + if (put_user(slen, optlen)) + error = -EFAULT; +out: + kfree(name); + + } + +done: + end_current_label_crit_section(label); + + return error; +} + +/** + * apparmor_socket_getpeersec_dgram - get security label of packet + * @sock: the peer socket + * @skb: packet data + * @secid: pointer to where to put the secid of the packet + * + * Sets the netlabel socket state on sk from parent + */ +static int apparmor_socket_getpeersec_dgram(struct socket *sock, + struct sk_buff *skb, u32 *secid) + +{ + /* TODO: requires secid support */ + return -ENOPROTOOPT; +} + +/** + * apparmor_sock_graft - Initialize newly created socket + * @sk: child sock + * @parent: parent socket + * + * Note: could set off of SOCK_CTX(parent) but need to track inode and we can + * just set sk security information off of current creating process label + * Labeling of sk for accept case - probably should be sock based + * instead of task, because of the case where an implicitly labeled + * socket is shared by different tasks. + */ +static void apparmor_sock_graft(struct sock *sk, struct socket *parent) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + if (!ctx->label) + ctx->label = aa_get_current_label(); +} + static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -770,6 +1133,30 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security), + LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), + LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb), + LSM_HOOK_INIT(socket_getpeersec_stream, + apparmor_socket_getpeersec_stream), + LSM_HOOK_INIT(socket_getpeersec_dgram, + apparmor_socket_getpeersec_dgram), + LSM_HOOK_INIT(sock_graft, apparmor_sock_graft), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 000000000000..33d54435f8d6 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,184 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/label.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + + +struct aa_sfs_entry aa_sfs_entry_network[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + { } +}; + +static const char * const net_mask_names[] = { + "unknown", + "send", + "receive", + "unknown", + + "create", + "shutdown", + "connect", + "unknown", + + "setattr", + "getattr", + "setcred", + "getcred", + + "chmod", + "chown", + "chgrp", + "lock", + + "mmap", + "mprot", + "unknown", + "unknown", + + "accept", + "bind", + "listen", + "unknown", + + "setopt", + "getopt", + "unknown", + "unknown", + + "unknown", + "unknown", + "unknown", + "unknown", +}; + + +/* audit callback for net specific fields */ +void audit_net_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) + audit_log_string(ab, address_family_names[sa->u.net->family]); + else + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + audit_log_format(ab, " sock_type="); + if (sock_type_names[aad(sa)->net.type]) + audit_log_string(ab, sock_type_names[aad(sa)->net.type]); + else + audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type); + audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol); + + if (aad(sa)->request & NET_PERMS_MASK) { + audit_log_format(ab, " requested_mask="); + aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0, + net_mask_names, NET_PERMS_MASK); + + if (aad(sa)->denied & NET_PERMS_MASK) { + audit_log_format(ab, " denied_mask="); + aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0, + net_mask_names, NET_PERMS_MASK); + } + } + if (aad(sa)->peer) { + audit_log_format(ab, " peer="); + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, + FLAGS_NONE, GFP_ATOMIC); + } +} + + +/* Generic af perm */ +int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type) +{ + struct aa_perms perms = { }; + + AA_BUG(family >= AF_MAX); + AA_BUG(type < 0 || type >= SOCK_MAX); + + if (profile_unconfined(profile)) + return 0; + + perms.allow = (profile->net.allow[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.audit = (profile->net.audit[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.quiet = (profile->net.quiet[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + aa_apply_modes_to_perms(profile, &perms); + + return aa_check_perms(profile, &perms, request, sa, audit_net_cb); +} + +int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, + int type, int protocol) +{ + struct aa_profile *profile; + DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol); + + return fn_for_each_confined(label, profile, + aa_profile_af_perm(profile, &sa, request, family, + type)); +} + +static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk) +{ + struct aa_profile *profile; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!label); + AA_BUG(!sk); + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + aa_profile_af_sk_perm(profile, &sa, request, sk)); +} + +int aa_sk_perm(const char *op, u32 request, struct sock *sk) +{ + struct aa_label *label; + int error; + + AA_BUG(!sk); + AA_BUG(in_interrupt()); + + /* TODO: switch to begin_current_label ???? */ + label = begin_current_label_crit_section(); + error = aa_label_sk_perm(label, op, request, sk); + end_current_label_crit_section(label); + + return error; +} + + +int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + + return aa_label_sk_perm(label, op, request, sock->sk); +} diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 4ede87c30f8b..e348f8dec45d 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -275,6 +275,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((__le16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -584,7 +597,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) struct aa_profile *profile = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; const char *info = "failed to unpack profile"; - size_t ns_len; + size_t size = 0, ns_len; struct rhashtable_params params = { 0 }; char *key = NULL; struct aa_data *data; @@ -717,6 +730,42 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } else { + /* support policy pre AF socket mediation */ + for (i = 0; i < AF_MAX; i++) + profile->net.allow[i] = 0xffff; + } + if (VERSION_LT(e->version, v7)) { + /* pre v7 policy always allowed these */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + } + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ info = "failed to unpack policydb"; -- 2.14.1 apparmor-5.0.2/kernel-patches/v4.14/0002-apparmor-af_unix-mediation.patch000066400000000000000000001205061522511161100256360ustar00rootroot00000000000000From 2e7f6d0dc0f1d3642950f529b451af73fa1baf9c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:27:23 -0700 Subject: [PATCH 2/2] apparmor: af_unix mediation af_socket mediation did not make it into 4.14 so add remaining out of tree patch Signed-off-by: John Johansen Signed-off-by: Seth Forshee --- security/apparmor/Makefile | 3 +- security/apparmor/af_unix.c | 651 ++++++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 6 + security/apparmor/file.c | 4 +- security/apparmor/include/af_unix.h | 114 +++++++ security/apparmor/include/net.h | 16 +- security/apparmor/include/path.h | 1 + security/apparmor/include/policy.h | 2 +- security/apparmor/lsm.c | 169 ++++++---- security/apparmor/net.c | 174 +++++++++- 10 files changed, 1072 insertions(+), 68 deletions(-) create mode 100644 security/apparmor/af_unix.c create mode 100644 security/apparmor/include/af_unix.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index e7ff2183532a..90c118f39e13 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o label.o mount.o net.o + resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ + af_unix.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c new file mode 100644 index 000000000000..c6876db2dbde --- /dev/null +++ b/security/apparmor/af_unix.c @@ -0,0 +1,651 @@ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2014 Canonical Ltd. + * + * 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. + */ + +#include + +#include "include/af_unix.h" +#include "include/apparmor.h" +#include "include/context.h" +#include "include/file.h" +#include "include/label.h" +#include "include/path.h" +#include "include/policy.h" + +static inline struct sock *aa_sock(struct unix_sock *u) +{ + return &u->sk; +} + +static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label, + struct unix_sock *u, int flags) +{ + AA_BUG(!label); + AA_BUG(!u); + AA_BUG(!UNIX_FS(aa_sock(u))); + + if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE)) + return 0; + + mask &= NET_FS_PERMS; + if (!u->path.dentry) { + struct path_cond cond = { }; + struct aa_perms perms = { }; + struct aa_profile *profile; + + /* socket path has been cleared because it is being shutdown + * can only fall back to original sun_path request + */ + struct aa_sk_ctx *ctx = SK_CTX(&u->sk); + if (ctx->path.dentry) + return aa_path_perm(op, label, &ctx->path, flags, mask, + &cond); + return fn_for_each_confined(label, profile, + ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ? + __aa_path_perm(op, profile, + u->addr->name->sun_path, mask, + &cond, flags, &perms) : + aa_audit_file(profile, &nullperms, op, mask, + u->addr->name->sun_path, NULL, + NULL, cond.uid, + "Failed name lookup - " + "deleted entry", -EACCES)); + } else { + /* the sunpath may not be valid for this ns so use the path */ + struct path_cond cond = { u->path.dentry->d_inode->i_uid, + u->path.dentry->d_inode->i_mode + }; + + return aa_path_perm(op, label, &u->path, flags, mask, &cond); + } + + return 0; +} + +/* passing in state returned by PROFILE_MEDIATES_AF */ +static unsigned int match_to_prot(struct aa_profile *profile, + unsigned int state, int type, int protocol, + const char **info) +{ + __be16 buffer[2]; + buffer[0] = cpu_to_be16(type); + buffer[1] = cpu_to_be16(protocol); + state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, + 4); + if (!state) + *info = "failed type and protocol match"; + return state; +} + +static unsigned int match_addr(struct aa_profile *profile, unsigned int state, + struct sockaddr_un *addr, int addrlen) +{ + if (addr) + /* include leading \0 */ + state = aa_dfa_match_len(profile->policy.dfa, state, + addr->sun_path, + unix_addr_len(addrlen)); + else + /* anonymous end point */ + state = aa_dfa_match_len(profile->policy.dfa, state, "\x01", + 1); + /* todo change to out of band */ + state = aa_dfa_null_transition(profile->policy.dfa, state); + return state; +} + +static unsigned int match_to_local(struct aa_profile *profile, + unsigned int state, int type, int protocol, + struct sockaddr_un *addr, int addrlen, + const char **info) +{ + state = match_to_prot(profile, state, type, protocol, info); + if (state) { + state = match_addr(profile, state, addr, addrlen); + if (state) { + /* todo: local label matching */ + state = aa_dfa_null_transition(profile->policy.dfa, + state); + if (!state) + *info = "failed local label match"; + } else + *info = "failed local address match"; + } + + return state; +} + +static unsigned int match_to_sk(struct aa_profile *profile, + unsigned int state, struct unix_sock *u, + const char **info) +{ + struct sockaddr_un *addr = NULL; + int addrlen = 0; + + if (u->addr) { + addr = u->addr->name; + addrlen = u->addr->len; + } + + return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol, + addr, addrlen, info); +} + +#define CMD_ADDR 1 +#define CMD_LISTEN 2 +#define CMD_OPT 4 + +static inline unsigned int match_to_cmd(struct aa_profile *profile, + unsigned int state, struct unix_sock *u, + char cmd, const char **info) +{ + state = match_to_sk(profile, state, u, info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1); + if (!state) + *info = "failed cmd selection match"; + } + + return state; +} + +static inline unsigned int match_to_peer(struct aa_profile *profile, + unsigned int state, + struct unix_sock *u, + struct sockaddr_un *peer_addr, + int peer_addrlen, + const char **info) +{ + state = match_to_cmd(profile, state, u, CMD_ADDR, info); + if (state) { + state = match_addr(profile, state, peer_addr, peer_addrlen); + if (!state) + *info = "failed peer address match"; + } + return state; +} + +static int do_perms(struct aa_profile *profile, unsigned int state, u32 request, + struct common_audit_data *sa) +{ + struct aa_perms perms; + + AA_BUG(!profile); + + aa_compute_perms(profile->policy.dfa, state, &perms); + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, sa, + audit_net_cb); +} + +static int match_label(struct aa_profile *profile, struct aa_profile *peer, + unsigned int state, u32 request, + struct common_audit_data *sa) +{ + AA_BUG(!profile); + AA_BUG(!peer); + + aad(sa)->peer = &peer->label; + + if (state) { + state = aa_dfa_match(profile->policy.dfa, state, + peer->base.hname); + if (!state) + aad(sa)->info = "failed peer label match"; + } + return do_perms(profile, state, request, sa); +} + + +/* unix sock creation comes before we know if the socket will be an fs + * socket + * v6 - semantics are handled by mapping in profile load + * v7 - semantics require sock create for tasks creating an fs socket. + */ +static int profile_create_perm(struct aa_profile *profile, int family, + int type, int protocol) +{ + unsigned int state; + DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol); + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + + if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) { + state = match_to_prot(profile, state, type, protocol, + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_CREATE, &sa); + } + + return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type); +} + +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + struct aa_profile *profile; + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + profile_create_perm(profile, family, type, protocol)); +} + + +static inline int profile_sk_perm(struct aa_profile *profile, const char *op, + u32 request, struct sock *sk) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + state = match_to_sk(profile, state, unix_sk(sk), + &aad(&sa)->info); + return do_perms(profile, state, request, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, request, sk); +} + +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk) +{ + struct aa_profile *profile; + + return fn_for_each_confined(label, profile, + profile_sk_perm(profile, op, request, sk)); +} + +static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + if (unconfined(label)) + return 0; + if (UNIX_FS(sock->sk)) + return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0); + + return aa_unix_label_sk_perm(label, op, request, sock->sk); +} + +/* revaliation, get/set attr */ +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) +{ + struct aa_label *label; + int error; + + label = begin_current_label_crit_section(); + error = unix_label_sock_perm(label, op, request, sock); + end_current_label_crit_section(label); + + return error; +} + +static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, + struct sockaddr *addr, int addrlen) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_BIND, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(addr->sa_family != AF_UNIX); + AA_BUG(profile_unconfined(profile)); + AA_BUG(unix_addr_fs(addr, addrlen)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + /* bind for abstract socket */ + aad(&sa)->net.addr = unix_addr(addr); + aad(&sa)->net.addrlen = addrlen; + + state = match_to_local(profile, state, + sk->sk_type, sk->sk_protocol, + unix_addr(addr), addrlen, + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_BIND, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk); +} + +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + /* fs bind is handled by mknod */ + if (!(unconfined(label) || unix_addr_fs(address, addrlen))) + error = fn_for_each_confined(label, profile, + profile_bind_perm(profile, sock->sk, address, + addrlen)); + end_current_label_crit_section(label); + + return error; +} + +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + /* unix connections are covered by the + * - unix_stream_connect (stream) and unix_may_send hooks (dgram) + * - fs connect is handled by open + */ + return 0; +} + +static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, + int backlog) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_LISTEN, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + __be16 b = cpu_to_be16(backlog); + + state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN, + &aad(&sa)->info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &b, 2); + if (!state) + aad(&sa)->info = "failed listen backlog match"; + } + return do_perms(profile, state, AA_MAY_LISTEN, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk); +} + +int aa_unix_listen_perm(struct socket *sock, int backlog) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_listen_perm(profile, sock->sk, + backlog)); + end_current_label_crit_section(label); + + return error; +} + + +static inline int profile_accept_perm(struct aa_profile *profile, + struct sock *sk, + struct sock *newsk) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + state = match_to_sk(profile, state, unix_sk(sk), + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_ACCEPT, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk); +} + +/* ability of sock to connect, not peer address binding */ +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_accept_perm(profile, sock->sk, + newsock->sk)); + end_current_label_crit_section(label); + + return error; +} + + +/* dgram handled by unix_may_sendmsg, right to send on stream done at connect + * could do per msg unix_stream here + */ +/* sendmsg, recvmsg */ +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + return 0; +} + + +static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request, + struct sock *sk, int level, int optname) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + __be16 b = cpu_to_be16(optname); + + state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT, + &aad(&sa)->info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &b, 2); + if (!state) + aad(&sa)->info = "failed sockopt match"; + } + return do_perms(profile, state, request, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, request, sk); +} + +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_opt_perm(profile, op, request, + sock->sk, level, optname)); + end_current_label_crit_section(label); + + return error; +} + +/* null peer_label is allowed, in which case the peer_sk label is used */ +static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label, + struct common_audit_data *sa) +{ + unsigned int state; + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + AA_BUG(!sk); + AA_BUG(!peer_sk); + AA_BUG(UNIX_FS(peer_sk)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); + struct aa_profile *peerp; + struct sockaddr_un *addr = NULL; + int len = 0; + if (unix_sk(peer_sk)->addr) { + addr = unix_sk(peer_sk)->addr->name; + len = unix_sk(peer_sk)->addr->len; + } + state = match_to_peer(profile, state, unix_sk(sk), + addr, len, &aad(sa)->info); + if (!peer_label) + peer_label = peer_ctx->label; + return fn_for_each_in_ns(peer_label, peerp, + match_label(profile, peerp, state, request, + sa)); + } + + return aa_profile_af_sk_perm(profile, sa, request, sk); +} + +/** + * + * Requires: lock held on both @sk and @peer_sk + */ +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label) +{ + struct unix_sock *peeru = unix_sk(peer_sk); + struct unix_sock *u = unix_sk(sk); + + AA_BUG(!label); + AA_BUG(!sk); + AA_BUG(!peer_sk); + + if (UNIX_FS(aa_sock(peeru))) + return unix_fs_perm(op, request, label, peeru, 0); + else if (UNIX_FS(aa_sock(u))) + return unix_fs_perm(op, request, label, u, 0); + else { + struct aa_profile *profile; + DEFINE_AUDIT_SK(sa, op, sk); + aad(&sa)->net.peer_sk = peer_sk; + + /* TODO: ns!!! */ + if (!net_eq(sock_net(sk), sock_net(peer_sk))) { + ; + } + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + profile_peer_perm(profile, op, request, sk, + peer_sk, peer_label, &sa)); + } +} + + +/* from net/unix/af_unix.c */ +static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) +{ + if (unlikely(sk1 == sk2) || !sk2) { + unix_state_lock(sk1); + return; + } + if (sk1 < sk2) { + unix_state_lock(sk1); + unix_state_lock_nested(sk2); + } else { + unix_state_lock(sk2); + unix_state_lock_nested(sk1); + } +} + +static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) +{ + if (unlikely(sk1 == sk2) || !sk2) { + unix_state_unlock(sk1); + return; + } + unix_state_unlock(sk1); + unix_state_unlock(sk2); +} + +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + struct sock *peer_sk = NULL; + u32 sk_req = request & ~NET_PEER_MASK; + int error = 0; + + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(sock->sk->sk_family != AF_UNIX); + + /* TODO: update sock label with new task label */ + unix_state_lock(sock->sk); + peer_sk = unix_peer(sock->sk); + if (peer_sk) + sock_hold(peer_sk); + if (!unix_connected(sock) && sk_req) { + error = unix_label_sock_perm(label, op, sk_req, sock); + if (!error) { + // update label + } + } + unix_state_unlock(sock->sk); + if (!peer_sk) + return error; + + unix_state_double_lock(sock->sk, peer_sk); + if (UNIX_FS(sock->sk)) { + error = unix_fs_perm(op, request, label, unix_sk(sock->sk), + PATH_SOCK_COND); + } else if (UNIX_FS(peer_sk)) { + error = unix_fs_perm(op, request, label, unix_sk(peer_sk), + PATH_SOCK_COND); + } else { + struct aa_sk_ctx *pctx = SK_CTX(peer_sk); + if (sk_req) + error = aa_unix_label_sk_perm(label, op, sk_req, + sock->sk); + last_error(error, + xcheck(aa_unix_peer_perm(label, op, + MAY_READ | MAY_WRITE, + sock->sk, peer_sk, NULL), + aa_unix_peer_perm(pctx->label, op, + MAY_READ | MAY_WRITE, + peer_sk, sock->sk, label))); + } + + unix_state_double_unlock(sock->sk, peer_sk); + sock_put(peer_sk); + + return error; +} diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 518d5928661b..63a8a462fc96 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2187,6 +2187,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_dbus[] = { + AA_SFS_FILE_STRING("mask", "acquire send receive"), + { } +}; + static struct aa_sfs_entry aa_sfs_entry_query_label[] = { AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), AA_SFS_FILE_BOOLEAN("data", 1), @@ -2210,6 +2215,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("caps", aa_sfs_entry_caps), AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), AA_SFS_DIR("signal", aa_sfs_entry_signal), + AA_SFS_DIR("dbus", aa_sfs_entry_dbus), AA_SFS_DIR("query", aa_sfs_entry_query), { } }; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index db80221891c6..e62791106900 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -16,6 +16,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/context.h" @@ -289,7 +290,8 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, { int e = 0; - if (profile_unconfined(profile)) + if (profile_unconfined(profile) || + ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX))) return 0; aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); if (request & ~perms->allow) diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h new file mode 100644 index 000000000000..d1b7f2316be4 --- /dev/null +++ b/security/apparmor/include/af_unix.h @@ -0,0 +1,114 @@ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2014 Canonical Ltd. + * + * 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 __AA_AF_UNIX_H + +#include + +#include "label.h" +//#include "include/net.h" + +#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) +#define unix_abstract_name_len(L) (unix_addr_len(L) - 1) +#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len)) +#define addr_unix_abstract_name(B) ((B)[0] == 0) +#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0) +#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr)) +//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr)) + +#define unix_addr(A) ((struct sockaddr_un *)(A)) +#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) +#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path)) + +#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr) +/* from net/unix/af_unix.c */ +#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) && \ + unix_sk(U)->addr->hash < UNIX_HASH_SIZE) +#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0]) +#define unix_peer(sk) (unix_sk(sk)->peer) +#define unix_connected(S) ((S)->state == SS_CONNECTED) + +static inline void print_unix_addr(struct sockaddr_un *A, int L) +{ + char *buf = (A) ? (char *) &(A)->sun_path : NULL; + int len = unix_addr_len(L); + if (!buf || len <= 0) + printk(" "); + else if (buf[0]) + printk(" %s", buf); + else + /* abstract name len includes leading \0 */ + printk(" %d @%.*s", len - 1, len - 1, buf+1); +}; + +/* + printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \ + #SK , \ +*/ +#define print_unix_sk(SK) \ +do { \ + struct unix_sock *u = unix_sk(SK); \ + printk("%s: f %d, t %d, p %d", #SK , \ + (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \ + if (u->addr) \ + print_unix_addr(u->addr->name, u->addr->len); \ + else \ + print_unix_addr(NULL, sizeof(sa_family_t)); \ + /* printk("\n");*/ \ +} while (0) + +#define print_sk(SK) \ +do { \ + if (!(SK)) { \ + printk("%s: %s is null\n", __FUNCTION__, #SK); \ + } else if ((SK)->sk_family == PF_UNIX) { \ + print_unix_sk(SK); \ + printk("\n"); \ + } else { \ + printk("%s: %s: family %d\n", __FUNCTION__, #SK , \ + (SK)->sk_family); \ + } \ +} while (0) + +#define print_sock_addr(U) \ +do { \ + printk("%s:\n", __FUNCTION__); \ + printk(" sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : ""); print_sk(sock); \ + printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : ""); print_sk(other); \ + printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : ""); print_sk(newsk); \ +} while (0) + + + + +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label); +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk); +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_listen_perm(struct socket *sock, int backlog); +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + +#endif /* __AA_AF_UNIX_H */ diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index 140c8efcf364..0ae45240c352 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -90,8 +90,6 @@ extern struct aa_sfs_entry aa_sfs_entry_network[]; void audit_net_cb(struct audit_buffer *ab, void *va); int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, u32 request, u16 family, int type); -int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, - int type, int protocol); static inline int aa_profile_af_sk_perm(struct aa_profile *profile, struct common_audit_data *sa, u32 request, @@ -100,8 +98,20 @@ static inline int aa_profile_af_sk_perm(struct aa_profile *profile, return aa_profile_af_perm(profile, sa, request, sk->sk_family, sk->sk_type); } -int aa_sk_perm(const char *op, u32 request, struct sock *sk); +int aa_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_sock_listen_perm(struct socket *sock, int backlog); +int aa_sock_accept_perm(struct socket *sock, struct socket *newsock); +int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, struct socket *sock); diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 05fb3305671e..26762db2207d 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -18,6 +18,7 @@ enum path_flags { PATH_IS_DIR = 0x1, /* path is a directory */ + PATH_SOCK_COND = 0x2, PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 4364088a0b9e..26660a1a50b0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -226,7 +226,7 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, u16 AF) { unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); - u16 be_af = cpu_to_be16(AF); + __be16 be_af = cpu_to_be16(AF); if (!state) return 0; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 72b915dfcaf7..5533d2f1d9de 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -26,6 +26,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/apparmorfs.h" #include "include/audit.h" @@ -782,16 +783,96 @@ static void apparmor_sk_clone_security(const struct sock *sk, path_get(&new->path); } -static int aa_sock_create_perm(struct aa_label *label, int family, int type, - int protocol) +static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk) { - AA_BUG(!label); - AA_BUG(in_interrupt()); + if (sk->sk_family == PF_UNIX && UNIX_FS(sk)) + return &unix_sk(sk)->path; + else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk)) + return &unix_sk(newsk)->path; + return NULL; +} + +/** + * apparmor_unix_stream_connect - check perms before making unix domain conn + * + * peer is locked when this hook is called + */ +static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk, + struct sock *newsk) +{ + struct aa_sk_ctx *sk_ctx = SK_CTX(sk); + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); + struct aa_sk_ctx *new_ctx = SK_CTX(newsk); + struct aa_label *label; + struct path *path; + int error; - return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, - protocol); + label = __begin_current_label_crit_section(); + error = aa_unix_peer_perm(label, OP_CONNECT, + (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), + sk, peer_sk, NULL); + if (!UNIX_FS(peer_sk)) { + last_error(error, + aa_unix_peer_perm(peer_ctx->label, OP_CONNECT, + (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE), + peer_sk, sk, label)); + } + __end_current_label_crit_section(label); + + if (error) + return error; + + /* label newsk if it wasn't labeled in post_create. Normally this + * would be done in sock_graft, but because we are directly looking + * at the peer_sk to obtain peer_labeling for unix socks this + * does not work + */ + if (!new_ctx->label) + new_ctx->label = aa_get_label(peer_ctx->label); + + /* Cross reference the peer labels for SO_PEERSEC */ + if (new_ctx->peer) + aa_put_label(new_ctx->peer); + + if (sk_ctx->peer) + aa_put_label(sk_ctx->peer); + + new_ctx->peer = aa_get_label(sk_ctx->label); + sk_ctx->peer = aa_get_label(peer_ctx->label); + + path = UNIX_FS_CONN_PATH(sk, peer_sk); + if (path) { + new_ctx->path = *path; + sk_ctx->path = *path; + path_get(path); + path_get(path); + } + return 0; } +/** + * apparmor_unix_may_send - check perms before conn or sending unix dgrams + * + * other is locked when this hook is called + * + * dgram connect calls may_send, peer setup but path not copied????? + */ +static int apparmor_unix_may_send(struct socket *sock, struct socket *peer) +{ + struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk); + struct aa_label *label; + int error; + + label = __begin_current_label_crit_section(); + error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND, + sock->sk, peer->sk, NULL), + aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG, + AA_MAY_RECEIVE, + peer->sk, sock->sk, label)); + __end_current_label_crit_section(label); + + return error; +} /** * apparmor_socket_create - check perms before creating a new socket @@ -849,12 +930,7 @@ static int apparmor_socket_post_create(struct socket *sock, int family, static int apparmor_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!address); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); + return aa_sock_bind_perm(sock, address, addrlen); } /** @@ -863,12 +939,7 @@ static int apparmor_socket_bind(struct socket *sock, static int apparmor_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!address); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); + return aa_sock_connect_perm(sock, address, addrlen); } /** @@ -876,11 +947,7 @@ static int apparmor_socket_connect(struct socket *sock, */ static int apparmor_socket_listen(struct socket *sock, int backlog) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); + return aa_sock_listen_perm(sock, backlog); } /** @@ -891,23 +958,7 @@ static int apparmor_socket_listen(struct socket *sock, int backlog) */ static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!newsock); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); -} - -static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, - struct msghdr *msg, int size) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!msg); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); + return aa_sock_accept_perm(sock, newsock); } /** @@ -928,16 +979,6 @@ static int apparmor_socket_recvmsg(struct socket *sock, return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); } -/* revaliation, get/set attr, shutdown */ -static int aa_sock_perm(const char *op, u32 request, struct socket *sock) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); -} - /** * apparmor_socket_getsockname - check perms before getting the local address */ @@ -954,17 +995,6 @@ static int apparmor_socket_getpeername(struct socket *sock) return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); } -/* revaliation, get/set attr, opt */ -static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, - int level, int optname) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); -} - /** * apparmor_getsockopt - check perms before getting socket options */ @@ -1009,11 +1039,25 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) static struct aa_label *sk_peer_label(struct sock *sk) { + struct sock *peer_sk; struct aa_sk_ctx *ctx = SK_CTX(sk); if (ctx->peer) return ctx->peer; + if (sk->sk_family != PF_UNIX) + return ERR_PTR(-ENOPROTOOPT); + + /* check for sockpair peering which does not go through + * security_unix_stream_connect + */ + peer_sk = unix_peer(sk); + if (peer_sk) { + ctx = SK_CTX(peer_sk); + if (ctx->label) + return ctx->label; + } + return ERR_PTR(-ENOPROTOOPT); } @@ -1137,6 +1181,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect), + LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 33d54435f8d6..dd1953b08e58 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -12,6 +12,7 @@ * License. */ +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/context.h" @@ -24,6 +25,7 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + AA_SFS_FILE_BOOLEAN("af_unix", 1), { } }; @@ -69,6 +71,36 @@ static const char * const net_mask_names[] = { "unknown", }; +static void audit_unix_addr(struct audit_buffer *ab, const char *str, + struct sockaddr_un *addr, int addrlen) +{ + int len = unix_addr_len(addrlen); + + if (!addr || len <= 0) { + audit_log_format(ab, " %s=none", str); + } else if (addr->sun_path[0]) { + audit_log_format(ab, " %s=", str); + audit_log_untrustedstring(ab, addr->sun_path); + } else { + audit_log_format(ab, " %s=\"@", str); + if (audit_string_contains_control(&addr->sun_path[1], len - 1)) + audit_log_n_hex(ab, &addr->sun_path[1], len - 1); + else + audit_log_format(ab, "%.*s", len - 1, + &addr->sun_path[1]); + audit_log_format(ab, "\""); + } +} + +static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str, + struct sock *sk) +{ + struct unix_sock *u = unix_sk(sk); + if (u && u->addr) + audit_unix_addr(ab, str, u->addr->name, u->addr->len); + else + audit_unix_addr(ab, str, NULL, 0); +} /* audit callback for net specific fields */ void audit_net_cb(struct audit_buffer *ab, void *va) @@ -98,6 +130,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va) net_mask_names, NET_PERMS_MASK); } } + if (sa->u.net->family == AF_UNIX) { + if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr) + audit_unix_addr(ab, "addr", + unix_addr(aad(sa)->net.addr), + aad(sa)->net.addrlen); + else + audit_unix_sk_addr(ab, "addr", sa->u.net->sk); + if (aad(sa)->request & NET_PEER_MASK) { + if (aad(sa)->net.addr) + audit_unix_addr(ab, "peer_addr", + unix_addr(aad(sa)->net.addr), + aad(sa)->net.addrlen); + else + audit_unix_sk_addr(ab, "peer_addr", + aad(sa)->net.peer_sk); + } + } if (aad(sa)->peer) { audit_log_format(ab, " peer="); aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, @@ -172,6 +221,127 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk) return error; } +#define af_select(FAMILY, FN, DEF_FN) \ +({ \ + int __e; \ + switch ((FAMILY)) { \ + case AF_UNIX: \ + __e = aa_unix_ ## FN; \ + break; \ + default: \ + __e = DEF_FN; \ + } \ + __e; \ +}) + +/* TODO: push into lsm.c ???? */ + +/* revaliation, get/set attr, shutdown */ +int aa_sock_perm(const char *op, u32 request, struct socket *sock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + sock_perm(op, request, sock), + aa_sk_perm(op, request, sock->sk)); +} + +int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + AA_BUG(!label); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(family, + create_perm(label, family, type, protocol), + aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, + type, protocol)); +} + +int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + bind_perm(sock, address, addrlen), + aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); +} + +int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + connect_perm(sock, address, addrlen), + aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); +} + +int aa_sock_listen_perm(struct socket *sock, int backlog) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + listen_perm(sock, backlog), + aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); +} + +/* ability of sock to connect, not peer address binding */ +int aa_sock_accept_perm(struct socket *sock, struct socket *newsock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!newsock); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + accept_perm(sock, newsock), + aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); +} + +/* sendmsg, recvmsg */ +int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!msg); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + msg_perm(op, request, sock, msg, size), + aa_sk_perm(op, request, sock->sk)); +} + +/* revaliation, get/set attr, opt */ +int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + opt_perm(op, request, sock, level, optname), + aa_sk_perm(op, request, sock->sk)); +} int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, struct socket *sock) @@ -180,5 +350,7 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, AA_BUG(!sock); AA_BUG(!sock->sk); - return aa_label_sk_perm(label, op, request, sock->sk); + return af_select(sock->sk->sk_family, + file_perm(label, op, request, sock), + aa_label_sk_perm(label, op, request, sock->sk)); } -- 2.14.1 apparmor-5.0.2/kernel-patches/v4.14/README000066400000000000000000000002021522511161100176650ustar00rootroot00000000000000This is based on v4.14 final base socket mediation and af_unix-mediation are the last two remaining patches that are out of tree apparmor-5.0.2/kernel-patches/v4.15/000077500000000000000000000000001522511161100170145ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/v4.15/0001-apparmor-add-base-infastructure-for-socket-mediation.patch000066400000000000000000001006111522511161100326060ustar00rootroot00000000000000From ae291c63ebb649f8af0bd491ea44e48b5c55526c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:18:33 -0700 Subject: [PATCH 1/2] apparmor: add base infastructure for socket mediation Provide a basic mediation of sockets. This is not a full net mediation but just whether a spcific family of socket can be used by an application, along with setting up some basic infrastructure for network mediation to follow. the user space rule hav the basic form of NETWORK RULE = [ QUALIFIERS ] 'network' [ DOMAIN ] [ TYPE | PROTOCOL ] DOMAIN = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'mpls' | 'ib' | 'kcm' ) ',' TYPE = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' ) PROTOCOL = ( 'tcp' | 'udp' | 'icmp' ) eg. network, network inet, Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 43 ++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/file.c | 30 +++ security/apparmor/include/audit.h | 16 +- security/apparmor/include/net.h | 114 +++++++++++ security/apparmor/include/perms.h | 5 +- security/apparmor/include/policy.h | 13 ++ security/apparmor/lib.c | 5 +- security/apparmor/lsm.c | 387 +++++++++++++++++++++++++++++++++++++ security/apparmor/net.c | 184 ++++++++++++++++++ security/apparmor/policy_unpack.c | 51 ++++- 12 files changed, 838 insertions(+), 12 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70d72b8..d5b291e94264 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 9a6b4033d52b..e7ff2183532a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,11 +5,44 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o label.o mount.o + resource.o secid.o file.o policy_ns.o label.o mount.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h +# Build a lower case string table of address family names +# Transform lines from +# #define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_SFS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + printf '%s' '\#define AA_SFS_AF_MASK "' >> $@ ;\ + sed -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of capability names # Transforms lines from @@ -62,6 +95,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -69,3 +103,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d4fa04d91439..694c4f48a975 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2202,6 +2202,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("policy", aa_sfs_entry_policy), AA_SFS_DIR("domain", aa_sfs_entry_domain), AA_SFS_DIR("file", aa_sfs_entry_file), + AA_SFS_DIR("network", aa_sfs_entry_network), AA_SFS_DIR("mount", aa_sfs_entry_mount), AA_SFS_DIR("namespaces", aa_sfs_entry_ns), AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), diff --git a/security/apparmor/file.c b/security/apparmor/file.c index e79bf44396a3..86d57e56fabe 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -21,6 +21,7 @@ #include "include/context.h" #include "include/file.h" #include "include/match.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/label.h" @@ -560,6 +561,32 @@ static int __file_path_perm(const char *op, struct aa_label *label, return error; } +static int __file_sock_perm(const char *op, struct aa_label *label, + struct aa_label *flabel, struct file *file, + u32 request, u32 denied) +{ + struct socket *sock = (struct socket *) file->private_data; + int error; + + AA_BUG(!sock); + + /* revalidation due to label out of date. No revocation at this time */ + if (!denied && aa_label_is_subset(flabel, label)) + return 0; + + /* TODO: improve to skip profiles cached in flabel */ + error = aa_sock_file_perm(label, op, request, sock); + if (denied) { + /* TODO: improve to skip profiles checked above */ + /* check every profile in file label to is cached */ + last_error(error, aa_sock_file_perm(flabel, op, request, sock)); + } + if (!error) + update_file_ctx(file_ctx(file), label, request); + + return error; +} + /** * aa_file_perm - do permission revalidation check & audit for @file * @op: operation being checked @@ -604,6 +631,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, error = __file_path_perm(op, label, flabel, file, request, denied); + else if (S_ISSOCK(file_inode(file)->i_mode)) + error = __file_sock_perm(op, label, flabel, file, request, + denied); done: rcu_read_unlock(); diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4ac095118717..ff4316e1068d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -123,10 +123,20 @@ struct apparmor_audit_data { struct aa_label *peer; union { struct { - const char *target; kuid_t ouid; + const char *target; } fs; + struct { + int type, protocol; + struct sock *peer_sk; + void *addr; + int addrlen; + } net; int signal; + struct { + int rlim; + unsigned long max; + } rlim; }; }; struct { @@ -134,10 +144,6 @@ struct apparmor_audit_data { const char *ns; long pos; } iface; - struct { - int rlim; - unsigned long max; - } rlim; struct { const char *src_name; const char *type; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 000000000000..140c8efcf364 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,114 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include +#include + +#include "apparmorfs.h" +#include "label.h" +#include "perms.h" +#include "policy.h" + +#define AA_MAY_SEND AA_MAY_WRITE +#define AA_MAY_RECEIVE AA_MAY_READ + +#define AA_MAY_SHUTDOWN AA_MAY_DELETE + +#define AA_MAY_CONNECT AA_MAY_OPEN +#define AA_MAY_ACCEPT 0x00100000 + +#define AA_MAY_BIND 0x00200000 +#define AA_MAY_LISTEN 0x00400000 + +#define AA_MAY_SETOPT 0x01000000 +#define AA_MAY_GETOPT 0x02000000 + +#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ + AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \ + AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \ + AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT) + +#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ + AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\ + AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \ + AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \ + AA_MAY_MPROT) + +#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \ + AA_MAY_ACCEPT) +struct aa_sk_ctx { + struct aa_label *label; + struct aa_label *peer; + struct path path; +}; + +#define SK_CTX(X) ((X)->sk_security) +#define SOCK_ctx(X) SOCK_INODE(X)->i_security +#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ + struct lsm_network_audit NAME ## _net = { .sk = (SK), \ + .family = (F)}; \ + DEFINE_AUDIT_DATA(NAME, \ + ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ + LSM_AUDIT_DATA_NONE, \ + OP); \ + NAME.u.net = &(NAME ## _net); \ + aad(&NAME)->net.type = (T); \ + aad(&NAME)->net.protocol = (P) + +#define DEFINE_AUDIT_SK(NAME, OP, SK) \ + DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ + (SK)->sk_protocol) + +/* struct aa_net - network confinement data + * @allow: basic network families permissions + * @audit: which network permissions to force audit + * @quiet: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + + +extern struct aa_sfs_entry aa_sfs_entry_network[]; + +void audit_net_cb(struct audit_buffer *ab, void *va); +int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type); +int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, + int type, int protocol); +static inline int aa_profile_af_sk_perm(struct aa_profile *profile, + struct common_audit_data *sa, + u32 request, + struct sock *sk) +{ + return aa_profile_af_perm(profile, sa, request, sk->sk_family, + sk->sk_type); +} +int aa_sk_perm(const char *op, u32 request, struct sock *sk); + +int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index d7b7e7115160..38aa6247d00f 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h @@ -138,9 +138,10 @@ extern struct aa_perms allperms; void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask); -void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask); +void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, + u32 mask); void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, - u32 chrsmask, const char **names, u32 namesmask); + u32 chrsmask, const char * const *names, u32 namesmask); void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms); void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 17fe41a9cac3..4364088a0b9e 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -30,6 +30,7 @@ #include "file.h" #include "lib.h" #include "label.h" +#include "net.h" #include "perms.h" #include "resource.h" @@ -111,6 +112,7 @@ struct aa_data { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -148,6 +150,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; struct aa_loaddata *rawdata; @@ -220,6 +223,16 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, return 0; } +static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, + u16 AF) { + unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); + u16 be_af = cpu_to_be16(AF); + + if (!state) + return 0; + return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); +} + /** * aa_get_profile - increment refcount on profile @p * @p: profile (MAYBE NULL) diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 4d5e98e49d5e..068a9f471f77 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -211,7 +211,8 @@ void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask) *str = '\0'; } -void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) +void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, + u32 mask) { const char *fmt = "%s"; unsigned int i, perm = 1; @@ -229,7 +230,7 @@ void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) } void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, - u32 chrsmask, const char **names, u32 namesmask) + u32 chrsmask, const char * const *names, u32 namesmask) { char str[33]; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 9a65eeaf7dfa..0cd717614fd0 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -33,6 +33,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/label.h" #include "include/policy.h" @@ -736,6 +737,368 @@ static int apparmor_task_kill(struct task_struct *target, struct siginfo *info, return error; } +/** + * apparmor_sk_alloc_security - allocate and attach the sk_security field + */ +static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags) +{ + struct aa_sk_ctx *ctx; + + ctx = kzalloc(sizeof(*ctx), flags); + if (!ctx) + return -ENOMEM; + + SK_CTX(sk) = ctx; + + return 0; +} + +/** + * apparmor_sk_free_security - free the sk_security field + */ +static void apparmor_sk_free_security(struct sock *sk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + SK_CTX(sk) = NULL; + aa_put_label(ctx->label); + aa_put_label(ctx->peer); + path_put(&ctx->path); + kfree(ctx); +} + +/** + * apparmor_clone_security - clone the sk_security field + */ +static void apparmor_sk_clone_security(const struct sock *sk, + struct sock *newsk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + struct aa_sk_ctx *new = SK_CTX(newsk); + + new->label = aa_get_label(ctx->label); + new->peer = aa_get_label(ctx->peer); + new->path = ctx->path; + path_get(&new->path); +} + +static int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + AA_BUG(!label); + AA_BUG(in_interrupt()); + + return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, + protocol); +} + + +/** + * apparmor_socket_create - check perms before creating a new socket + */ +static int apparmor_socket_create(int family, int type, int protocol, int kern) +{ + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(kern || unconfined(label))) + error = aa_sock_create_perm(label, family, type, protocol); + end_current_label_crit_section(label); + + return error; +} + +/** + * apparmor_socket_post_create - setup the per-socket security struct + * + * Note: + * - kernel sockets currently labeled unconfined but we may want to + * move to a special kernel label + * - socket may not have sk here if created with sock_create_lite or + * sock_alloc. These should be accept cases which will be handled in + * sock_graft. + */ +static int apparmor_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) +{ + struct aa_label *label; + + if (kern) { + struct aa_ns *ns = aa_get_current_ns(); + + label = aa_get_label(ns_unconfined(ns)); + aa_put_ns(ns); + } else + label = aa_get_current_label(); + + if (sock->sk) { + struct aa_sk_ctx *ctx = SK_CTX(sock->sk); + + aa_put_label(ctx->label); + ctx->label = aa_get_label(label); + } + aa_put_label(label); + + return 0; +} + +/** + * apparmor_socket_bind - check perms before bind addr to socket + */ +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); +} + +/** + * apparmor_socket_connect - check perms before connecting @sock to @address + */ +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); +} + +/** + * apparmor_socket_list - check perms before allowing listen + */ +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); +} + +/** + * apparmor_socket_accept - check perms before accepting a new connection. + * + * Note: while @newsock is created and has some information, the accept + * has not been done. + */ +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!newsock); + AA_BUG(in_interrupt()); + + return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); +} + +static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!msg); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_socket_sendmsg - check perms before sending msg to another socket + */ +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); +} + +/** + * apparmor_socket_recvmsg - check perms before receiving a message + */ +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); +} + +/* revaliation, get/set attr, shutdown */ +static int aa_sock_perm(const char *op, u32 request, struct socket *sock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_socket_getsockname - check perms before getting the local address + */ +static int apparmor_socket_getsockname(struct socket *sock) +{ + return aa_sock_perm(OP_GETSOCKNAME, AA_MAY_GETATTR, sock); +} + +/** + * apparmor_socket_getpeername - check perms before getting remote address + */ +static int apparmor_socket_getpeername(struct socket *sock) +{ + return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); +} + +/* revaliation, get/set attr, opt */ +static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, + int level, int optname) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return aa_sk_perm(op, request, sock->sk); +} + +/** + * apparmor_getsockopt - check perms before getting socket options + */ +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + return aa_sock_opt_perm(OP_GETSOCKOPT, AA_MAY_GETOPT, sock, + level, optname); +} + +/** + * apparmor_setsockopt - check perms before setting socket options + */ +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + return aa_sock_opt_perm(OP_SETSOCKOPT, AA_MAY_SETOPT, sock, + level, optname); +} + +/** + * apparmor_socket_shutdown - check perms before shutting down @sock conn + */ +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock); +} + +/** + * apparmor_socket_sock_recv_skb - check perms before associating skb to sk + * + * Note: can not sleep may be called with locks held + * + * dont want protocol specific in __skb_recv_datagram() + * to deny an incoming connection socket_sock_rcv_skb() + */ +static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + return 0; +} + + +static struct aa_label *sk_peer_label(struct sock *sk) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + if (ctx->peer) + return ctx->peer; + + return ERR_PTR(-ENOPROTOOPT); +} + +/** + * apparmor_socket_getpeersec_stream - get security context of peer + * + * Note: for tcp only valid if using ipsec or cipso on lan + */ +static int apparmor_socket_getpeersec_stream(struct socket *sock, + char __user *optval, + int __user *optlen, + unsigned int len) +{ + char *name; + int slen, error = 0; + struct aa_label *label; + struct aa_label *peer; + + label = begin_current_label_crit_section(); + peer = sk_peer_label(sock->sk); + if (IS_ERR(peer)) { + error = PTR_ERR(peer); + goto done; + } + slen = aa_label_asxprint(&name, labels_ns(label), peer, + FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | + FLAG_HIDDEN_UNCONFINED, GFP_KERNEL); + /* don't include terminating \0 in slen, it breaks some apps */ + if (slen < 0) { + error = -ENOMEM; + } else { + if (slen > len) { + error = -ERANGE; + } else if (copy_to_user(optval, name, slen)) { + error = -EFAULT; + goto out; + } + if (put_user(slen, optlen)) + error = -EFAULT; +out: + kfree(name); + + } + +done: + end_current_label_crit_section(label); + + return error; +} + +/** + * apparmor_socket_getpeersec_dgram - get security label of packet + * @sock: the peer socket + * @skb: packet data + * @secid: pointer to where to put the secid of the packet + * + * Sets the netlabel socket state on sk from parent + */ +static int apparmor_socket_getpeersec_dgram(struct socket *sock, + struct sk_buff *skb, u32 *secid) + +{ + /* TODO: requires secid support */ + return -ENOPROTOOPT; +} + +/** + * apparmor_sock_graft - Initialize newly created socket + * @sk: child sock + * @parent: parent socket + * + * Note: could set off of SOCK_CTX(parent) but need to track inode and we can + * just set sk security information off of current creating process label + * Labeling of sk for accept case - probably should be sock based + * instead of task, because of the case where an implicitly labeled + * socket is shared by different tasks. + */ +static void apparmor_sock_graft(struct sock *sk, struct socket *parent) +{ + struct aa_sk_ctx *ctx = SK_CTX(sk); + + if (!ctx->label) + ctx->label = aa_get_current_label(); +} + static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -770,6 +1133,30 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security), + LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), + LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb), + LSM_HOOK_INIT(socket_getpeersec_stream, + apparmor_socket_getpeersec_stream), + LSM_HOOK_INIT(socket_getpeersec_dgram, + apparmor_socket_getpeersec_dgram), + LSM_HOOK_INIT(sock_graft, apparmor_sock_graft), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 000000000000..33d54435f8d6 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,184 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/label.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + + +struct aa_sfs_entry aa_sfs_entry_network[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + { } +}; + +static const char * const net_mask_names[] = { + "unknown", + "send", + "receive", + "unknown", + + "create", + "shutdown", + "connect", + "unknown", + + "setattr", + "getattr", + "setcred", + "getcred", + + "chmod", + "chown", + "chgrp", + "lock", + + "mmap", + "mprot", + "unknown", + "unknown", + + "accept", + "bind", + "listen", + "unknown", + + "setopt", + "getopt", + "unknown", + "unknown", + + "unknown", + "unknown", + "unknown", + "unknown", +}; + + +/* audit callback for net specific fields */ +void audit_net_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) + audit_log_string(ab, address_family_names[sa->u.net->family]); + else + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + audit_log_format(ab, " sock_type="); + if (sock_type_names[aad(sa)->net.type]) + audit_log_string(ab, sock_type_names[aad(sa)->net.type]); + else + audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type); + audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol); + + if (aad(sa)->request & NET_PERMS_MASK) { + audit_log_format(ab, " requested_mask="); + aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0, + net_mask_names, NET_PERMS_MASK); + + if (aad(sa)->denied & NET_PERMS_MASK) { + audit_log_format(ab, " denied_mask="); + aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0, + net_mask_names, NET_PERMS_MASK); + } + } + if (aad(sa)->peer) { + audit_log_format(ab, " peer="); + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, + FLAGS_NONE, GFP_ATOMIC); + } +} + + +/* Generic af perm */ +int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type) +{ + struct aa_perms perms = { }; + + AA_BUG(family >= AF_MAX); + AA_BUG(type < 0 || type >= SOCK_MAX); + + if (profile_unconfined(profile)) + return 0; + + perms.allow = (profile->net.allow[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.audit = (profile->net.audit[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.quiet = (profile->net.quiet[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + aa_apply_modes_to_perms(profile, &perms); + + return aa_check_perms(profile, &perms, request, sa, audit_net_cb); +} + +int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, + int type, int protocol) +{ + struct aa_profile *profile; + DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol); + + return fn_for_each_confined(label, profile, + aa_profile_af_perm(profile, &sa, request, family, + type)); +} + +static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk) +{ + struct aa_profile *profile; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!label); + AA_BUG(!sk); + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + aa_profile_af_sk_perm(profile, &sa, request, sk)); +} + +int aa_sk_perm(const char *op, u32 request, struct sock *sk) +{ + struct aa_label *label; + int error; + + AA_BUG(!sk); + AA_BUG(in_interrupt()); + + /* TODO: switch to begin_current_label ???? */ + label = begin_current_label_crit_section(); + error = aa_label_sk_perm(label, op, request, sk); + end_current_label_crit_section(label); + + return error; +} + + +int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + + return aa_label_sk_perm(label, op, request, sock->sk); +} diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 59a1a25b7d43..769d2c55bdae 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -275,6 +275,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((__le16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -584,7 +597,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) struct aa_profile *profile = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; const char *info = "failed to unpack profile"; - size_t ns_len; + size_t size = 0, ns_len; struct rhashtable_params params = { 0 }; char *key = NULL; struct aa_data *data; @@ -717,6 +730,42 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } else { + /* support policy pre AF socket mediation */ + for (i = 0; i < AF_MAX; i++) + profile->net.allow[i] = 0xffff; + } + if (VERSION_LT(e->version, v7)) { + /* pre v7 policy always allowed these */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + } + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ info = "failed to unpack policydb"; -- 2.14.1 apparmor-5.0.2/kernel-patches/v4.15/0002-apparmor-af_unix-mediation.patch000066400000000000000000001205061522511161100256370ustar00rootroot00000000000000From a3a1dea7d72da33f004f4c5c2e9de91f3311d336 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:27:23 -0700 Subject: [PATCH 2/2] apparmor: af_unix mediation af_socket mediation did not make it into 4.14 so add remaining out of tree patch Signed-off-by: John Johansen Signed-off-by: Seth Forshee --- security/apparmor/Makefile | 3 +- security/apparmor/af_unix.c | 651 ++++++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 6 + security/apparmor/file.c | 4 +- security/apparmor/include/af_unix.h | 114 +++++++ security/apparmor/include/net.h | 16 +- security/apparmor/include/path.h | 1 + security/apparmor/include/policy.h | 2 +- security/apparmor/lsm.c | 169 ++++++---- security/apparmor/net.c | 174 +++++++++- 10 files changed, 1072 insertions(+), 68 deletions(-) create mode 100644 security/apparmor/af_unix.c create mode 100644 security/apparmor/include/af_unix.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index e7ff2183532a..90c118f39e13 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o label.o mount.o net.o + resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ + af_unix.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c new file mode 100644 index 000000000000..c6876db2dbde --- /dev/null +++ b/security/apparmor/af_unix.c @@ -0,0 +1,651 @@ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2014 Canonical Ltd. + * + * 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. + */ + +#include + +#include "include/af_unix.h" +#include "include/apparmor.h" +#include "include/context.h" +#include "include/file.h" +#include "include/label.h" +#include "include/path.h" +#include "include/policy.h" + +static inline struct sock *aa_sock(struct unix_sock *u) +{ + return &u->sk; +} + +static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label, + struct unix_sock *u, int flags) +{ + AA_BUG(!label); + AA_BUG(!u); + AA_BUG(!UNIX_FS(aa_sock(u))); + + if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE)) + return 0; + + mask &= NET_FS_PERMS; + if (!u->path.dentry) { + struct path_cond cond = { }; + struct aa_perms perms = { }; + struct aa_profile *profile; + + /* socket path has been cleared because it is being shutdown + * can only fall back to original sun_path request + */ + struct aa_sk_ctx *ctx = SK_CTX(&u->sk); + if (ctx->path.dentry) + return aa_path_perm(op, label, &ctx->path, flags, mask, + &cond); + return fn_for_each_confined(label, profile, + ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ? + __aa_path_perm(op, profile, + u->addr->name->sun_path, mask, + &cond, flags, &perms) : + aa_audit_file(profile, &nullperms, op, mask, + u->addr->name->sun_path, NULL, + NULL, cond.uid, + "Failed name lookup - " + "deleted entry", -EACCES)); + } else { + /* the sunpath may not be valid for this ns so use the path */ + struct path_cond cond = { u->path.dentry->d_inode->i_uid, + u->path.dentry->d_inode->i_mode + }; + + return aa_path_perm(op, label, &u->path, flags, mask, &cond); + } + + return 0; +} + +/* passing in state returned by PROFILE_MEDIATES_AF */ +static unsigned int match_to_prot(struct aa_profile *profile, + unsigned int state, int type, int protocol, + const char **info) +{ + __be16 buffer[2]; + buffer[0] = cpu_to_be16(type); + buffer[1] = cpu_to_be16(protocol); + state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, + 4); + if (!state) + *info = "failed type and protocol match"; + return state; +} + +static unsigned int match_addr(struct aa_profile *profile, unsigned int state, + struct sockaddr_un *addr, int addrlen) +{ + if (addr) + /* include leading \0 */ + state = aa_dfa_match_len(profile->policy.dfa, state, + addr->sun_path, + unix_addr_len(addrlen)); + else + /* anonymous end point */ + state = aa_dfa_match_len(profile->policy.dfa, state, "\x01", + 1); + /* todo change to out of band */ + state = aa_dfa_null_transition(profile->policy.dfa, state); + return state; +} + +static unsigned int match_to_local(struct aa_profile *profile, + unsigned int state, int type, int protocol, + struct sockaddr_un *addr, int addrlen, + const char **info) +{ + state = match_to_prot(profile, state, type, protocol, info); + if (state) { + state = match_addr(profile, state, addr, addrlen); + if (state) { + /* todo: local label matching */ + state = aa_dfa_null_transition(profile->policy.dfa, + state); + if (!state) + *info = "failed local label match"; + } else + *info = "failed local address match"; + } + + return state; +} + +static unsigned int match_to_sk(struct aa_profile *profile, + unsigned int state, struct unix_sock *u, + const char **info) +{ + struct sockaddr_un *addr = NULL; + int addrlen = 0; + + if (u->addr) { + addr = u->addr->name; + addrlen = u->addr->len; + } + + return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol, + addr, addrlen, info); +} + +#define CMD_ADDR 1 +#define CMD_LISTEN 2 +#define CMD_OPT 4 + +static inline unsigned int match_to_cmd(struct aa_profile *profile, + unsigned int state, struct unix_sock *u, + char cmd, const char **info) +{ + state = match_to_sk(profile, state, u, info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1); + if (!state) + *info = "failed cmd selection match"; + } + + return state; +} + +static inline unsigned int match_to_peer(struct aa_profile *profile, + unsigned int state, + struct unix_sock *u, + struct sockaddr_un *peer_addr, + int peer_addrlen, + const char **info) +{ + state = match_to_cmd(profile, state, u, CMD_ADDR, info); + if (state) { + state = match_addr(profile, state, peer_addr, peer_addrlen); + if (!state) + *info = "failed peer address match"; + } + return state; +} + +static int do_perms(struct aa_profile *profile, unsigned int state, u32 request, + struct common_audit_data *sa) +{ + struct aa_perms perms; + + AA_BUG(!profile); + + aa_compute_perms(profile->policy.dfa, state, &perms); + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, sa, + audit_net_cb); +} + +static int match_label(struct aa_profile *profile, struct aa_profile *peer, + unsigned int state, u32 request, + struct common_audit_data *sa) +{ + AA_BUG(!profile); + AA_BUG(!peer); + + aad(sa)->peer = &peer->label; + + if (state) { + state = aa_dfa_match(profile->policy.dfa, state, + peer->base.hname); + if (!state) + aad(sa)->info = "failed peer label match"; + } + return do_perms(profile, state, request, sa); +} + + +/* unix sock creation comes before we know if the socket will be an fs + * socket + * v6 - semantics are handled by mapping in profile load + * v7 - semantics require sock create for tasks creating an fs socket. + */ +static int profile_create_perm(struct aa_profile *profile, int family, + int type, int protocol) +{ + unsigned int state; + DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol); + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + + if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) { + state = match_to_prot(profile, state, type, protocol, + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_CREATE, &sa); + } + + return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type); +} + +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + struct aa_profile *profile; + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + profile_create_perm(profile, family, type, protocol)); +} + + +static inline int profile_sk_perm(struct aa_profile *profile, const char *op, + u32 request, struct sock *sk) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + state = match_to_sk(profile, state, unix_sk(sk), + &aad(&sa)->info); + return do_perms(profile, state, request, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, request, sk); +} + +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk) +{ + struct aa_profile *profile; + + return fn_for_each_confined(label, profile, + profile_sk_perm(profile, op, request, sk)); +} + +static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + if (unconfined(label)) + return 0; + if (UNIX_FS(sock->sk)) + return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0); + + return aa_unix_label_sk_perm(label, op, request, sock->sk); +} + +/* revaliation, get/set attr */ +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) +{ + struct aa_label *label; + int error; + + label = begin_current_label_crit_section(); + error = unix_label_sock_perm(label, op, request, sock); + end_current_label_crit_section(label); + + return error; +} + +static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, + struct sockaddr *addr, int addrlen) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_BIND, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(addr->sa_family != AF_UNIX); + AA_BUG(profile_unconfined(profile)); + AA_BUG(unix_addr_fs(addr, addrlen)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + /* bind for abstract socket */ + aad(&sa)->net.addr = unix_addr(addr); + aad(&sa)->net.addrlen = addrlen; + + state = match_to_local(profile, state, + sk->sk_type, sk->sk_protocol, + unix_addr(addr), addrlen, + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_BIND, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk); +} + +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + /* fs bind is handled by mknod */ + if (!(unconfined(label) || unix_addr_fs(address, addrlen))) + error = fn_for_each_confined(label, profile, + profile_bind_perm(profile, sock->sk, address, + addrlen)); + end_current_label_crit_section(label); + + return error; +} + +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + /* unix connections are covered by the + * - unix_stream_connect (stream) and unix_may_send hooks (dgram) + * - fs connect is handled by open + */ + return 0; +} + +static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, + int backlog) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_LISTEN, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + __be16 b = cpu_to_be16(backlog); + + state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN, + &aad(&sa)->info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &b, 2); + if (!state) + aad(&sa)->info = "failed listen backlog match"; + } + return do_perms(profile, state, AA_MAY_LISTEN, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk); +} + +int aa_unix_listen_perm(struct socket *sock, int backlog) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_listen_perm(profile, sock->sk, + backlog)); + end_current_label_crit_section(label); + + return error; +} + + +static inline int profile_accept_perm(struct aa_profile *profile, + struct sock *sk, + struct sock *newsk) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + state = match_to_sk(profile, state, unix_sk(sk), + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_ACCEPT, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk); +} + +/* ability of sock to connect, not peer address binding */ +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_accept_perm(profile, sock->sk, + newsock->sk)); + end_current_label_crit_section(label); + + return error; +} + + +/* dgram handled by unix_may_sendmsg, right to send on stream done at connect + * could do per msg unix_stream here + */ +/* sendmsg, recvmsg */ +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + return 0; +} + + +static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request, + struct sock *sk, int level, int optname) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + __be16 b = cpu_to_be16(optname); + + state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT, + &aad(&sa)->info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &b, 2); + if (!state) + aad(&sa)->info = "failed sockopt match"; + } + return do_perms(profile, state, request, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, request, sk); +} + +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_opt_perm(profile, op, request, + sock->sk, level, optname)); + end_current_label_crit_section(label); + + return error; +} + +/* null peer_label is allowed, in which case the peer_sk label is used */ +static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label, + struct common_audit_data *sa) +{ + unsigned int state; + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + AA_BUG(!sk); + AA_BUG(!peer_sk); + AA_BUG(UNIX_FS(peer_sk)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); + struct aa_profile *peerp; + struct sockaddr_un *addr = NULL; + int len = 0; + if (unix_sk(peer_sk)->addr) { + addr = unix_sk(peer_sk)->addr->name; + len = unix_sk(peer_sk)->addr->len; + } + state = match_to_peer(profile, state, unix_sk(sk), + addr, len, &aad(sa)->info); + if (!peer_label) + peer_label = peer_ctx->label; + return fn_for_each_in_ns(peer_label, peerp, + match_label(profile, peerp, state, request, + sa)); + } + + return aa_profile_af_sk_perm(profile, sa, request, sk); +} + +/** + * + * Requires: lock held on both @sk and @peer_sk + */ +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label) +{ + struct unix_sock *peeru = unix_sk(peer_sk); + struct unix_sock *u = unix_sk(sk); + + AA_BUG(!label); + AA_BUG(!sk); + AA_BUG(!peer_sk); + + if (UNIX_FS(aa_sock(peeru))) + return unix_fs_perm(op, request, label, peeru, 0); + else if (UNIX_FS(aa_sock(u))) + return unix_fs_perm(op, request, label, u, 0); + else { + struct aa_profile *profile; + DEFINE_AUDIT_SK(sa, op, sk); + aad(&sa)->net.peer_sk = peer_sk; + + /* TODO: ns!!! */ + if (!net_eq(sock_net(sk), sock_net(peer_sk))) { + ; + } + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + profile_peer_perm(profile, op, request, sk, + peer_sk, peer_label, &sa)); + } +} + + +/* from net/unix/af_unix.c */ +static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) +{ + if (unlikely(sk1 == sk2) || !sk2) { + unix_state_lock(sk1); + return; + } + if (sk1 < sk2) { + unix_state_lock(sk1); + unix_state_lock_nested(sk2); + } else { + unix_state_lock(sk2); + unix_state_lock_nested(sk1); + } +} + +static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) +{ + if (unlikely(sk1 == sk2) || !sk2) { + unix_state_unlock(sk1); + return; + } + unix_state_unlock(sk1); + unix_state_unlock(sk2); +} + +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + struct sock *peer_sk = NULL; + u32 sk_req = request & ~NET_PEER_MASK; + int error = 0; + + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(sock->sk->sk_family != AF_UNIX); + + /* TODO: update sock label with new task label */ + unix_state_lock(sock->sk); + peer_sk = unix_peer(sock->sk); + if (peer_sk) + sock_hold(peer_sk); + if (!unix_connected(sock) && sk_req) { + error = unix_label_sock_perm(label, op, sk_req, sock); + if (!error) { + // update label + } + } + unix_state_unlock(sock->sk); + if (!peer_sk) + return error; + + unix_state_double_lock(sock->sk, peer_sk); + if (UNIX_FS(sock->sk)) { + error = unix_fs_perm(op, request, label, unix_sk(sock->sk), + PATH_SOCK_COND); + } else if (UNIX_FS(peer_sk)) { + error = unix_fs_perm(op, request, label, unix_sk(peer_sk), + PATH_SOCK_COND); + } else { + struct aa_sk_ctx *pctx = SK_CTX(peer_sk); + if (sk_req) + error = aa_unix_label_sk_perm(label, op, sk_req, + sock->sk); + last_error(error, + xcheck(aa_unix_peer_perm(label, op, + MAY_READ | MAY_WRITE, + sock->sk, peer_sk, NULL), + aa_unix_peer_perm(pctx->label, op, + MAY_READ | MAY_WRITE, + peer_sk, sock->sk, label))); + } + + unix_state_double_unlock(sock->sk, peer_sk); + sock_put(peer_sk); + + return error; +} diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 694c4f48a975..850c401502f1 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2187,6 +2187,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_dbus[] = { + AA_SFS_FILE_STRING("mask", "acquire send receive"), + { } +}; + static struct aa_sfs_entry aa_sfs_entry_query_label[] = { AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), AA_SFS_FILE_BOOLEAN("data", 1), @@ -2210,6 +2215,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("caps", aa_sfs_entry_caps), AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), AA_SFS_DIR("signal", aa_sfs_entry_signal), + AA_SFS_DIR("dbus", aa_sfs_entry_dbus), AA_SFS_DIR("query", aa_sfs_entry_query), { } }; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 86d57e56fabe..348c9ff3da4e 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -16,6 +16,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/context.h" @@ -283,7 +284,8 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, { int e = 0; - if (profile_unconfined(profile)) + if (profile_unconfined(profile) || + ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX))) return 0; aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); if (request & ~perms->allow) diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h new file mode 100644 index 000000000000..d1b7f2316be4 --- /dev/null +++ b/security/apparmor/include/af_unix.h @@ -0,0 +1,114 @@ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2014 Canonical Ltd. + * + * 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 __AA_AF_UNIX_H + +#include + +#include "label.h" +//#include "include/net.h" + +#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) +#define unix_abstract_name_len(L) (unix_addr_len(L) - 1) +#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len)) +#define addr_unix_abstract_name(B) ((B)[0] == 0) +#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0) +#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr)) +//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr)) + +#define unix_addr(A) ((struct sockaddr_un *)(A)) +#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) +#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path)) + +#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr) +/* from net/unix/af_unix.c */ +#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) && \ + unix_sk(U)->addr->hash < UNIX_HASH_SIZE) +#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0]) +#define unix_peer(sk) (unix_sk(sk)->peer) +#define unix_connected(S) ((S)->state == SS_CONNECTED) + +static inline void print_unix_addr(struct sockaddr_un *A, int L) +{ + char *buf = (A) ? (char *) &(A)->sun_path : NULL; + int len = unix_addr_len(L); + if (!buf || len <= 0) + printk(" "); + else if (buf[0]) + printk(" %s", buf); + else + /* abstract name len includes leading \0 */ + printk(" %d @%.*s", len - 1, len - 1, buf+1); +}; + +/* + printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \ + #SK , \ +*/ +#define print_unix_sk(SK) \ +do { \ + struct unix_sock *u = unix_sk(SK); \ + printk("%s: f %d, t %d, p %d", #SK , \ + (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \ + if (u->addr) \ + print_unix_addr(u->addr->name, u->addr->len); \ + else \ + print_unix_addr(NULL, sizeof(sa_family_t)); \ + /* printk("\n");*/ \ +} while (0) + +#define print_sk(SK) \ +do { \ + if (!(SK)) { \ + printk("%s: %s is null\n", __FUNCTION__, #SK); \ + } else if ((SK)->sk_family == PF_UNIX) { \ + print_unix_sk(SK); \ + printk("\n"); \ + } else { \ + printk("%s: %s: family %d\n", __FUNCTION__, #SK , \ + (SK)->sk_family); \ + } \ +} while (0) + +#define print_sock_addr(U) \ +do { \ + printk("%s:\n", __FUNCTION__); \ + printk(" sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : ""); print_sk(sock); \ + printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : ""); print_sk(other); \ + printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : ""); print_sk(newsk); \ +} while (0) + + + + +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label); +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk); +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_listen_perm(struct socket *sock, int backlog); +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + +#endif /* __AA_AF_UNIX_H */ diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index 140c8efcf364..0ae45240c352 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -90,8 +90,6 @@ extern struct aa_sfs_entry aa_sfs_entry_network[]; void audit_net_cb(struct audit_buffer *ab, void *va); int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, u32 request, u16 family, int type); -int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, - int type, int protocol); static inline int aa_profile_af_sk_perm(struct aa_profile *profile, struct common_audit_data *sa, u32 request, @@ -100,8 +98,20 @@ static inline int aa_profile_af_sk_perm(struct aa_profile *profile, return aa_profile_af_perm(profile, sa, request, sk->sk_family, sk->sk_type); } -int aa_sk_perm(const char *op, u32 request, struct sock *sk); +int aa_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_sock_listen_perm(struct socket *sock, int backlog); +int aa_sock_accept_perm(struct socket *sock, struct socket *newsock); +int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, struct socket *sock); diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 05fb3305671e..26762db2207d 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -18,6 +18,7 @@ enum path_flags { PATH_IS_DIR = 0x1, /* path is a directory */ + PATH_SOCK_COND = 0x2, PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 4364088a0b9e..26660a1a50b0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -226,7 +226,7 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, u16 AF) { unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); - u16 be_af = cpu_to_be16(AF); + __be16 be_af = cpu_to_be16(AF); if (!state) return 0; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 0cd717614fd0..245c98ef311e 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -26,6 +26,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/apparmorfs.h" #include "include/audit.h" @@ -782,16 +783,96 @@ static void apparmor_sk_clone_security(const struct sock *sk, path_get(&new->path); } -static int aa_sock_create_perm(struct aa_label *label, int family, int type, - int protocol) +static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk) { - AA_BUG(!label); - AA_BUG(in_interrupt()); + if (sk->sk_family == PF_UNIX && UNIX_FS(sk)) + return &unix_sk(sk)->path; + else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk)) + return &unix_sk(newsk)->path; + return NULL; +} + +/** + * apparmor_unix_stream_connect - check perms before making unix domain conn + * + * peer is locked when this hook is called + */ +static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk, + struct sock *newsk) +{ + struct aa_sk_ctx *sk_ctx = SK_CTX(sk); + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); + struct aa_sk_ctx *new_ctx = SK_CTX(newsk); + struct aa_label *label; + struct path *path; + int error; - return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, - protocol); + label = __begin_current_label_crit_section(); + error = aa_unix_peer_perm(label, OP_CONNECT, + (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), + sk, peer_sk, NULL); + if (!UNIX_FS(peer_sk)) { + last_error(error, + aa_unix_peer_perm(peer_ctx->label, OP_CONNECT, + (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE), + peer_sk, sk, label)); + } + __end_current_label_crit_section(label); + + if (error) + return error; + + /* label newsk if it wasn't labeled in post_create. Normally this + * would be done in sock_graft, but because we are directly looking + * at the peer_sk to obtain peer_labeling for unix socks this + * does not work + */ + if (!new_ctx->label) + new_ctx->label = aa_get_label(peer_ctx->label); + + /* Cross reference the peer labels for SO_PEERSEC */ + if (new_ctx->peer) + aa_put_label(new_ctx->peer); + + if (sk_ctx->peer) + aa_put_label(sk_ctx->peer); + + new_ctx->peer = aa_get_label(sk_ctx->label); + sk_ctx->peer = aa_get_label(peer_ctx->label); + + path = UNIX_FS_CONN_PATH(sk, peer_sk); + if (path) { + new_ctx->path = *path; + sk_ctx->path = *path; + path_get(path); + path_get(path); + } + return 0; } +/** + * apparmor_unix_may_send - check perms before conn or sending unix dgrams + * + * other is locked when this hook is called + * + * dgram connect calls may_send, peer setup but path not copied????? + */ +static int apparmor_unix_may_send(struct socket *sock, struct socket *peer) +{ + struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk); + struct aa_label *label; + int error; + + label = __begin_current_label_crit_section(); + error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND, + sock->sk, peer->sk, NULL), + aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG, + AA_MAY_RECEIVE, + peer->sk, sock->sk, label)); + __end_current_label_crit_section(label); + + return error; +} /** * apparmor_socket_create - check perms before creating a new socket @@ -849,12 +930,7 @@ static int apparmor_socket_post_create(struct socket *sock, int family, static int apparmor_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!address); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); + return aa_sock_bind_perm(sock, address, addrlen); } /** @@ -863,12 +939,7 @@ static int apparmor_socket_bind(struct socket *sock, static int apparmor_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!address); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); + return aa_sock_connect_perm(sock, address, addrlen); } /** @@ -876,11 +947,7 @@ static int apparmor_socket_connect(struct socket *sock, */ static int apparmor_socket_listen(struct socket *sock, int backlog) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); + return aa_sock_listen_perm(sock, backlog); } /** @@ -891,23 +958,7 @@ static int apparmor_socket_listen(struct socket *sock, int backlog) */ static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) { - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!newsock); - AA_BUG(in_interrupt()); - - return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); -} - -static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, - struct msghdr *msg, int size) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(!msg); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); + return aa_sock_accept_perm(sock, newsock); } /** @@ -928,16 +979,6 @@ static int apparmor_socket_recvmsg(struct socket *sock, return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); } -/* revaliation, get/set attr, shutdown */ -static int aa_sock_perm(const char *op, u32 request, struct socket *sock) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); -} - /** * apparmor_socket_getsockname - check perms before getting the local address */ @@ -954,17 +995,6 @@ static int apparmor_socket_getpeername(struct socket *sock) return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); } -/* revaliation, get/set attr, opt */ -static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, - int level, int optname) -{ - AA_BUG(!sock); - AA_BUG(!sock->sk); - AA_BUG(in_interrupt()); - - return aa_sk_perm(op, request, sock->sk); -} - /** * apparmor_getsockopt - check perms before getting socket options */ @@ -1009,11 +1039,25 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) static struct aa_label *sk_peer_label(struct sock *sk) { + struct sock *peer_sk; struct aa_sk_ctx *ctx = SK_CTX(sk); if (ctx->peer) return ctx->peer; + if (sk->sk_family != PF_UNIX) + return ERR_PTR(-ENOPROTOOPT); + + /* check for sockpair peering which does not go through + * security_unix_stream_connect + */ + peer_sk = unix_peer(sk); + if (peer_sk) { + ctx = SK_CTX(peer_sk); + if (ctx->label) + return ctx->label; + } + return ERR_PTR(-ENOPROTOOPT); } @@ -1137,6 +1181,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect), + LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 33d54435f8d6..dd1953b08e58 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -12,6 +12,7 @@ * License. */ +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/context.h" @@ -24,6 +25,7 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + AA_SFS_FILE_BOOLEAN("af_unix", 1), { } }; @@ -69,6 +71,36 @@ static const char * const net_mask_names[] = { "unknown", }; +static void audit_unix_addr(struct audit_buffer *ab, const char *str, + struct sockaddr_un *addr, int addrlen) +{ + int len = unix_addr_len(addrlen); + + if (!addr || len <= 0) { + audit_log_format(ab, " %s=none", str); + } else if (addr->sun_path[0]) { + audit_log_format(ab, " %s=", str); + audit_log_untrustedstring(ab, addr->sun_path); + } else { + audit_log_format(ab, " %s=\"@", str); + if (audit_string_contains_control(&addr->sun_path[1], len - 1)) + audit_log_n_hex(ab, &addr->sun_path[1], len - 1); + else + audit_log_format(ab, "%.*s", len - 1, + &addr->sun_path[1]); + audit_log_format(ab, "\""); + } +} + +static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str, + struct sock *sk) +{ + struct unix_sock *u = unix_sk(sk); + if (u && u->addr) + audit_unix_addr(ab, str, u->addr->name, u->addr->len); + else + audit_unix_addr(ab, str, NULL, 0); +} /* audit callback for net specific fields */ void audit_net_cb(struct audit_buffer *ab, void *va) @@ -98,6 +130,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va) net_mask_names, NET_PERMS_MASK); } } + if (sa->u.net->family == AF_UNIX) { + if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr) + audit_unix_addr(ab, "addr", + unix_addr(aad(sa)->net.addr), + aad(sa)->net.addrlen); + else + audit_unix_sk_addr(ab, "addr", sa->u.net->sk); + if (aad(sa)->request & NET_PEER_MASK) { + if (aad(sa)->net.addr) + audit_unix_addr(ab, "peer_addr", + unix_addr(aad(sa)->net.addr), + aad(sa)->net.addrlen); + else + audit_unix_sk_addr(ab, "peer_addr", + aad(sa)->net.peer_sk); + } + } if (aad(sa)->peer) { audit_log_format(ab, " peer="); aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, @@ -172,6 +221,127 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk) return error; } +#define af_select(FAMILY, FN, DEF_FN) \ +({ \ + int __e; \ + switch ((FAMILY)) { \ + case AF_UNIX: \ + __e = aa_unix_ ## FN; \ + break; \ + default: \ + __e = DEF_FN; \ + } \ + __e; \ +}) + +/* TODO: push into lsm.c ???? */ + +/* revaliation, get/set attr, shutdown */ +int aa_sock_perm(const char *op, u32 request, struct socket *sock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + sock_perm(op, request, sock), + aa_sk_perm(op, request, sock->sk)); +} + +int aa_sock_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + AA_BUG(!label); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(family, + create_perm(label, family, type, protocol), + aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, + type, protocol)); +} + +int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + bind_perm(sock, address, addrlen), + aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); +} + +int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!address); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + connect_perm(sock, address, addrlen), + aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); +} + +int aa_sock_listen_perm(struct socket *sock, int backlog) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + listen_perm(sock, backlog), + aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); +} + +/* ability of sock to connect, not peer address binding */ +int aa_sock_accept_perm(struct socket *sock, struct socket *newsock) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!newsock); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + accept_perm(sock, newsock), + aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); +} + +/* sendmsg, recvmsg */ +int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(!msg); + /* TODO: .... */ + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + msg_perm(op, request, sock, msg, size), + aa_sk_perm(op, request, sock->sk)); +} + +/* revaliation, get/set attr, opt */ +int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname) +{ + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(in_interrupt()); + + return af_select(sock->sk->sk_family, + opt_perm(op, request, sock, level, optname), + aa_sk_perm(op, request, sock->sk)); +} int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, struct socket *sock) @@ -180,5 +350,7 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, AA_BUG(!sock); AA_BUG(!sock->sk); - return aa_label_sk_perm(label, op, request, sock->sk); + return af_select(sock->sk->sk_family, + file_perm(label, op, request, sock), + aa_label_sk_perm(label, op, request, sock->sk)); } -- 2.14.1 apparmor-5.0.2/kernel-patches/v4.17/000077500000000000000000000000001522511161100170165ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/v4.17/0001-apparmor-patch-to-provide-compatibility-with-v2.x-ne.patch000066400000000000000000000205221522511161100324350ustar00rootroot00000000000000From 02e2bc1b330f7e15dba671547a256a6f900f6e5d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 17 Jun 2018 03:56:25 -0700 Subject: [PATCH 1/3] apparmor: patch to provide compatibility with v2.x net rules The networking rules upstreamed in 4.17 have a deliberate abi break with the older 2.x network rules. This patch provides compatibility with the older rules for those still using an apparmor 2.x userspace and still want network rules to work on a newer kernel. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/apparmor.h | 2 +- security/apparmor/include/net.h | 11 ++++++++ security/apparmor/include/policy.h | 2 ++ security/apparmor/net.c | 31 ++++++++++++++++----- security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 54 ++++++++++++++++++++++++++++++++++-- 7 files changed, 92 insertions(+), 10 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 1fdcc7d5a977..32f0e660ffd0 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2272,6 +2272,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("domain", aa_sfs_entry_domain), AA_SFS_DIR("file", aa_sfs_entry_file), AA_SFS_DIR("network_v8", aa_sfs_entry_network), + AA_SFS_DIR("network", aa_sfs_entry_network_compat), AA_SFS_DIR("mount", aa_sfs_entry_mount), AA_SFS_DIR("namespaces", aa_sfs_entry_ns), AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 73d63b58d875..17d89f3badc6 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -24,7 +24,7 @@ #define AA_CLASS_UNKNOWN 1 #define AA_CLASS_FILE 2 #define AA_CLASS_CAP 3 -#define AA_CLASS_DEPRECATED 4 +#define AA_CLASS_NET_COMPAT 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 #define AA_CLASS_MOUNT 7 diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index ec7228e857a9..579b59a40ea4 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -72,6 +72,16 @@ struct aa_sk_ctx { DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ (SK)->sk_protocol) +/* struct aa_net - network confinement data + * @allow: basic network families permissions + * @audit: which network permissions to force audit + * @quiet: which network permissions to quiet rejects + */ +struct aa_net_compat { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; #define af_select(FAMILY, FN, DEF_FN) \ ({ \ @@ -84,6 +94,7 @@ struct aa_sk_ctx { }) extern struct aa_sfs_entry aa_sfs_entry_network[]; +extern struct aa_sfs_entry aa_sfs_entry_network_compat[]; void audit_net_cb(struct audit_buffer *ab, void *va); int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 6c93e62383e6..4006fa9fc9f1 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -112,6 +112,7 @@ struct aa_data { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net_compat: v2 compat network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -149,6 +150,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net_compat *net_compat; int xattr_count; char **xattrs; diff --git a/security/apparmor/net.c b/security/apparmor/net.c index bb24cfa0a164..bf6aaefc3a5f 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -27,6 +27,11 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { { } }; +struct aa_sfs_entry aa_sfs_entry_network_compat[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + { } +}; + static const char * const net_mask_names[] = { "unknown", "send", @@ -119,14 +124,26 @@ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, if (profile_unconfined(profile)) return 0; state = PROFILE_MEDIATES(profile, AA_CLASS_NET); - if (!state) + if (state) { + if (!state) + return 0; + buffer[0] = cpu_to_be16(family); + buffer[1] = cpu_to_be16((u16) type); + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &buffer, 4); + aa_compute_perms(profile->policy.dfa, state, &perms); + } else if (profile->net_compat) { + /* 2.x socket mediation compat */ + perms.allow = (profile->net_compat->allow[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.audit = (profile->net_compat->audit[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.quiet = (profile->net_compat->quiet[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + + } else { return 0; - - buffer[0] = cpu_to_be16(family); - buffer[1] = cpu_to_be16((u16) type); - state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, - 4); - aa_compute_perms(profile->policy.dfa, state, &perms); + } aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, sa, audit_net_cb); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c07493ce2376..d1a869699040 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -227,6 +227,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); aa_free_rlimit_rules(&profile->rlimits); + kzfree(profile->net_compat); for (i = 0; i < profile->xattr_count; i++) kzfree(profile->xattrs[i]); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index b9e6b2cafa69..a1b07e6c163d 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -37,7 +37,7 @@ #define v5 5 /* base version */ #define v6 6 /* per entry policydb mediation check */ -#define v7 7 +#define v7 7 /* v2 compat networking */ #define v8 8 /* full network masking */ /* @@ -292,6 +292,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((__le16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -621,7 +634,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) struct aa_profile *profile = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; const char *info = "failed to unpack profile"; - size_t ns_len; + size_t size = 0, ns_len; struct rhashtable_params params = { 0 }; char *key = NULL; struct aa_data *data; @@ -759,6 +772,43 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + size = unpack_array(e, "net_allowed_af"); + if (size || VERSION_LT(e->version, v8)) { + profile->net_compat = kzalloc(sizeof(struct aa_net_compat), GFP_KERNEL); + if (!profile->net_compat) { + info = "out of memory"; + goto fail; + } + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net_compat->allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net_compat->audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net_compat->quiet[i], NULL)) + goto fail; + } + if (size && !unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + if (VERSION_LT(e->version, v7)) { + /* pre v7 policy always allowed these */ + profile->net_compat->allow[AF_UNIX] = 0xffff; + profile->net_compat->allow[AF_NETLINK] = 0xffff; + } + } + + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ info = "failed to unpack policydb"; -- 2.14.1 apparmor-5.0.2/kernel-patches/v4.17/0002-apparmor-af_unix-mediation.patch000066400000000000000000001045571522511161100256510ustar00rootroot00000000000000From 1aae75e96831bb26d1ced782c633c39c877c252f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 18 Jul 2017 23:27:23 -0700 Subject: [PATCH 2/3] apparmor: af_unix mediation af_socket mediation did not make it into 4.17 so add remaining out of tree patch Signed-off-by: John Johansen --- security/apparmor/Makefile | 3 +- security/apparmor/af_unix.c | 652 ++++++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 6 + security/apparmor/file.c | 4 +- security/apparmor/include/af_unix.h | 114 +++++++ security/apparmor/include/net.h | 4 + security/apparmor/include/path.h | 1 + security/apparmor/include/policy.h | 10 +- security/apparmor/lsm.c | 113 +++++++ security/apparmor/net.c | 53 ++- security/apparmor/policy_unpack.c | 6 +- 11 files changed, 957 insertions(+), 9 deletions(-) create mode 100644 security/apparmor/af_unix.c create mode 100644 security/apparmor/include/af_unix.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index ff23fcfefe19..fad407f6f62c 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o secid.o file.o policy_ns.o label.o mount.o net.o + resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ + af_unix.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c new file mode 100644 index 000000000000..54b3796f63d0 --- /dev/null +++ b/security/apparmor/af_unix.c @@ -0,0 +1,652 @@ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2018 Canonical Ltd. + * + * 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. + */ + +#include + +#include "include/audit.h" +#include "include/af_unix.h" +#include "include/apparmor.h" +#include "include/file.h" +#include "include/label.h" +#include "include/path.h" +#include "include/policy.h" +#include "include/cred.h" + +static inline struct sock *aa_sock(struct unix_sock *u) +{ + return &u->sk; +} + +static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label, + struct unix_sock *u, int flags) +{ + AA_BUG(!label); + AA_BUG(!u); + AA_BUG(!UNIX_FS(aa_sock(u))); + + if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE)) + return 0; + + mask &= NET_FS_PERMS; + if (!u->path.dentry) { + struct path_cond cond = { }; + struct aa_perms perms = { }; + struct aa_profile *profile; + + /* socket path has been cleared because it is being shutdown + * can only fall back to original sun_path request + */ + struct aa_sk_ctx *ctx = SK_CTX(&u->sk); + if (ctx->path.dentry) + return aa_path_perm(op, label, &ctx->path, flags, mask, + &cond); + return fn_for_each_confined(label, profile, + ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ? + __aa_path_perm(op, profile, + u->addr->name->sun_path, mask, + &cond, flags, &perms) : + aa_audit_file(profile, &nullperms, op, mask, + u->addr->name->sun_path, NULL, + NULL, cond.uid, + "Failed name lookup - " + "deleted entry", -EACCES)); + } else { + /* the sunpath may not be valid for this ns so use the path */ + struct path_cond cond = { u->path.dentry->d_inode->i_uid, + u->path.dentry->d_inode->i_mode + }; + + return aa_path_perm(op, label, &u->path, flags, mask, &cond); + } + + return 0; +} + +/* passing in state returned by PROFILE_MEDIATES_AF */ +static unsigned int match_to_prot(struct aa_profile *profile, + unsigned int state, int type, int protocol, + const char **info) +{ + __be16 buffer[2]; + buffer[0] = cpu_to_be16(type); + buffer[1] = cpu_to_be16(protocol); + state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, + 4); + if (!state) + *info = "failed type and protocol match"; + return state; +} + +static unsigned int match_addr(struct aa_profile *profile, unsigned int state, + struct sockaddr_un *addr, int addrlen) +{ + if (addr) + /* include leading \0 */ + state = aa_dfa_match_len(profile->policy.dfa, state, + addr->sun_path, + unix_addr_len(addrlen)); + else + /* anonymous end point */ + state = aa_dfa_match_len(profile->policy.dfa, state, "\x01", + 1); + /* todo change to out of band */ + state = aa_dfa_null_transition(profile->policy.dfa, state); + return state; +} + +static unsigned int match_to_local(struct aa_profile *profile, + unsigned int state, int type, int protocol, + struct sockaddr_un *addr, int addrlen, + const char **info) +{ + state = match_to_prot(profile, state, type, protocol, info); + if (state) { + state = match_addr(profile, state, addr, addrlen); + if (state) { + /* todo: local label matching */ + state = aa_dfa_null_transition(profile->policy.dfa, + state); + if (!state) + *info = "failed local label match"; + } else + *info = "failed local address match"; + } + + return state; +} + +static unsigned int match_to_sk(struct aa_profile *profile, + unsigned int state, struct unix_sock *u, + const char **info) +{ + struct sockaddr_un *addr = NULL; + int addrlen = 0; + + if (u->addr) { + addr = u->addr->name; + addrlen = u->addr->len; + } + + return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol, + addr, addrlen, info); +} + +#define CMD_ADDR 1 +#define CMD_LISTEN 2 +#define CMD_OPT 4 + +static inline unsigned int match_to_cmd(struct aa_profile *profile, + unsigned int state, struct unix_sock *u, + char cmd, const char **info) +{ + state = match_to_sk(profile, state, u, info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1); + if (!state) + *info = "failed cmd selection match"; + } + + return state; +} + +static inline unsigned int match_to_peer(struct aa_profile *profile, + unsigned int state, + struct unix_sock *u, + struct sockaddr_un *peer_addr, + int peer_addrlen, + const char **info) +{ + state = match_to_cmd(profile, state, u, CMD_ADDR, info); + if (state) { + state = match_addr(profile, state, peer_addr, peer_addrlen); + if (!state) + *info = "failed peer address match"; + } + return state; +} + +static int do_perms(struct aa_profile *profile, unsigned int state, u32 request, + struct common_audit_data *sa) +{ + struct aa_perms perms; + + AA_BUG(!profile); + + aa_compute_perms(profile->policy.dfa, state, &perms); + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, sa, + audit_net_cb); +} + +static int match_label(struct aa_profile *profile, struct aa_profile *peer, + unsigned int state, u32 request, + struct common_audit_data *sa) +{ + AA_BUG(!profile); + AA_BUG(!peer); + + aad(sa)->peer = &peer->label; + + if (state) { + state = aa_dfa_match(profile->policy.dfa, state, + peer->base.hname); + if (!state) + aad(sa)->info = "failed peer label match"; + } + return do_perms(profile, state, request, sa); +} + + +/* unix sock creation comes before we know if the socket will be an fs + * socket + * v6 - semantics are handled by mapping in profile load + * v7 - semantics require sock create for tasks creating an fs socket. + */ +static int profile_create_perm(struct aa_profile *profile, int family, + int type, int protocol) +{ + unsigned int state; + DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol); + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + + if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) { + state = match_to_prot(profile, state, type, protocol, + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_CREATE, &sa); + } + + return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type); +} + +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + struct aa_profile *profile; + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + profile_create_perm(profile, family, type, protocol)); +} + + +static inline int profile_sk_perm(struct aa_profile *profile, const char *op, + u32 request, struct sock *sk) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + state = match_to_sk(profile, state, unix_sk(sk), + &aad(&sa)->info); + return do_perms(profile, state, request, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, request, sk); +} + +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk) +{ + struct aa_profile *profile; + + return fn_for_each_confined(label, profile, + profile_sk_perm(profile, op, request, sk)); +} + +static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + if (unconfined(label)) + return 0; + if (UNIX_FS(sock->sk)) + return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0); + + return aa_unix_label_sk_perm(label, op, request, sock->sk); +} + +/* revaliation, get/set attr */ +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) +{ + struct aa_label *label; + int error; + + label = begin_current_label_crit_section(); + error = unix_label_sock_perm(label, op, request, sock); + end_current_label_crit_section(label); + + return error; +} + +static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, + struct sockaddr *addr, int addrlen) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_BIND, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(addr->sa_family != AF_UNIX); + AA_BUG(profile_unconfined(profile)); + AA_BUG(unix_addr_fs(addr, addrlen)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + /* bind for abstract socket */ + aad(&sa)->net.addr = unix_addr(addr); + aad(&sa)->net.addrlen = addrlen; + + state = match_to_local(profile, state, + sk->sk_type, sk->sk_protocol, + unix_addr(addr), addrlen, + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_BIND, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk); +} + +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + /* fs bind is handled by mknod */ + if (!(unconfined(label) || unix_addr_fs(address, addrlen))) + error = fn_for_each_confined(label, profile, + profile_bind_perm(profile, sock->sk, address, + addrlen)); + end_current_label_crit_section(label); + + return error; +} + +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + /* unix connections are covered by the + * - unix_stream_connect (stream) and unix_may_send hooks (dgram) + * - fs connect is handled by open + */ + return 0; +} + +static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, + int backlog) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_LISTEN, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + __be16 b = cpu_to_be16(backlog); + + state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN, + &aad(&sa)->info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &b, 2); + if (!state) + aad(&sa)->info = "failed listen backlog match"; + } + return do_perms(profile, state, AA_MAY_LISTEN, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk); +} + +int aa_unix_listen_perm(struct socket *sock, int backlog) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_listen_perm(profile, sock->sk, + backlog)); + end_current_label_crit_section(label); + + return error; +} + + +static inline int profile_accept_perm(struct aa_profile *profile, + struct sock *sk, + struct sock *newsk) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + state = match_to_sk(profile, state, unix_sk(sk), + &aad(&sa)->info); + return do_perms(profile, state, AA_MAY_ACCEPT, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk); +} + +/* ability of sock to connect, not peer address binding */ +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_accept_perm(profile, sock->sk, + newsock->sk)); + end_current_label_crit_section(label); + + return error; +} + + +/* dgram handled by unix_may_sendmsg, right to send on stream done at connect + * could do per msg unix_stream here + */ +/* sendmsg, recvmsg */ +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + return 0; +} + + +static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request, + struct sock *sk, int level, int optname) +{ + unsigned int state; + DEFINE_AUDIT_SK(sa, op, sk); + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(UNIX_FS(sk)); + AA_BUG(profile_unconfined(profile)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + __be16 b = cpu_to_be16(optname); + + state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT, + &aad(&sa)->info); + if (state) { + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &b, 2); + if (!state) + aad(&sa)->info = "failed sockopt match"; + } + return do_perms(profile, state, request, &sa); + } + + return aa_profile_af_sk_perm(profile, &sa, request, sk); +} + +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname) +{ + struct aa_profile *profile; + struct aa_label *label; + int error = 0; + + label = begin_current_label_crit_section(); + if (!(unconfined(label) || UNIX_FS(sock->sk))) + error = fn_for_each_confined(label, profile, + profile_opt_perm(profile, op, request, + sock->sk, level, optname)); + end_current_label_crit_section(label); + + return error; +} + +/* null peer_label is allowed, in which case the peer_sk label is used */ +static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label, + struct common_audit_data *sa) +{ + unsigned int state; + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + AA_BUG(!sk); + AA_BUG(!peer_sk); + AA_BUG(UNIX_FS(peer_sk)); + + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); + if (state) { + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); + struct aa_profile *peerp; + struct sockaddr_un *addr = NULL; + int len = 0; + if (unix_sk(peer_sk)->addr) { + addr = unix_sk(peer_sk)->addr->name; + len = unix_sk(peer_sk)->addr->len; + } + state = match_to_peer(profile, state, unix_sk(sk), + addr, len, &aad(sa)->info); + if (!peer_label) + peer_label = peer_ctx->label; + return fn_for_each_in_ns(peer_label, peerp, + match_label(profile, peerp, state, request, + sa)); + } + + return aa_profile_af_sk_perm(profile, sa, request, sk); +} + +/** + * + * Requires: lock held on both @sk and @peer_sk + */ +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label) +{ + struct unix_sock *peeru = unix_sk(peer_sk); + struct unix_sock *u = unix_sk(sk); + + AA_BUG(!label); + AA_BUG(!sk); + AA_BUG(!peer_sk); + + if (UNIX_FS(aa_sock(peeru))) + return unix_fs_perm(op, request, label, peeru, 0); + else if (UNIX_FS(aa_sock(u))) + return unix_fs_perm(op, request, label, u, 0); + else { + struct aa_profile *profile; + DEFINE_AUDIT_SK(sa, op, sk); + aad(&sa)->net.peer_sk = peer_sk; + + /* TODO: ns!!! */ + if (!net_eq(sock_net(sk), sock_net(peer_sk))) { + ; + } + + if (unconfined(label)) + return 0; + + return fn_for_each_confined(label, profile, + profile_peer_perm(profile, op, request, sk, + peer_sk, peer_label, &sa)); + } +} + + +/* from net/unix/af_unix.c */ +static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) +{ + if (unlikely(sk1 == sk2) || !sk2) { + unix_state_lock(sk1); + return; + } + if (sk1 < sk2) { + unix_state_lock(sk1); + unix_state_lock_nested(sk2); + } else { + unix_state_lock(sk2); + unix_state_lock_nested(sk1); + } +} + +static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) +{ + if (unlikely(sk1 == sk2) || !sk2) { + unix_state_unlock(sk1); + return; + } + unix_state_unlock(sk1); + unix_state_unlock(sk2); +} + +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +{ + struct sock *peer_sk = NULL; + u32 sk_req = request & ~NET_PEER_MASK; + int error = 0; + + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(sock->sk->sk_family != AF_UNIX); + + /* TODO: update sock label with new task label */ + unix_state_lock(sock->sk); + peer_sk = unix_peer(sock->sk); + if (peer_sk) + sock_hold(peer_sk); + if (!unix_connected(sock) && sk_req) { + error = unix_label_sock_perm(label, op, sk_req, sock); + if (!error) { + // update label + } + } + unix_state_unlock(sock->sk); + if (!peer_sk) + return error; + + unix_state_double_lock(sock->sk, peer_sk); + if (UNIX_FS(sock->sk)) { + error = unix_fs_perm(op, request, label, unix_sk(sock->sk), + PATH_SOCK_COND); + } else if (UNIX_FS(peer_sk)) { + error = unix_fs_perm(op, request, label, unix_sk(peer_sk), + PATH_SOCK_COND); + } else { + struct aa_sk_ctx *pctx = SK_CTX(peer_sk); + if (sk_req) + error = aa_unix_label_sk_perm(label, op, sk_req, + sock->sk); + last_error(error, + xcheck(aa_unix_peer_perm(label, op, + MAY_READ | MAY_WRITE, + sock->sk, peer_sk, NULL), + aa_unix_peer_perm(pctx->label, op, + MAY_READ | MAY_WRITE, + peer_sk, sock->sk, label))); + } + + unix_state_double_unlock(sock->sk, peer_sk); + sock_put(peer_sk); + + return error; +} diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 32f0e660ffd0..b931bae4f1a2 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2256,6 +2256,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_dbus[] = { + AA_SFS_FILE_STRING("mask", "acquire send receive"), + { } +}; + static struct aa_sfs_entry aa_sfs_entry_query_label[] = { AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), AA_SFS_FILE_BOOLEAN("data", 1), @@ -2280,6 +2285,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("caps", aa_sfs_entry_caps), AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), AA_SFS_DIR("signal", aa_sfs_entry_signal), + AA_SFS_DIR("dbus", aa_sfs_entry_dbus), AA_SFS_DIR("query", aa_sfs_entry_query), { } }; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 224b2fef93ca..67e70e094858 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -16,6 +16,7 @@ #include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/cred.h" @@ -283,7 +284,8 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, { int e = 0; - if (profile_unconfined(profile)) + if (profile_unconfined(profile) || + ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX))) return 0; aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); if (request & ~perms->allow) diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h new file mode 100644 index 000000000000..d1b7f2316be4 --- /dev/null +++ b/security/apparmor/include/af_unix.h @@ -0,0 +1,114 @@ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2014 Canonical Ltd. + * + * 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 __AA_AF_UNIX_H + +#include + +#include "label.h" +//#include "include/net.h" + +#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) +#define unix_abstract_name_len(L) (unix_addr_len(L) - 1) +#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len)) +#define addr_unix_abstract_name(B) ((B)[0] == 0) +#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0) +#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr)) +//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr)) + +#define unix_addr(A) ((struct sockaddr_un *)(A)) +#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) +#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path)) + +#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr) +/* from net/unix/af_unix.c */ +#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) && \ + unix_sk(U)->addr->hash < UNIX_HASH_SIZE) +#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0]) +#define unix_peer(sk) (unix_sk(sk)->peer) +#define unix_connected(S) ((S)->state == SS_CONNECTED) + +static inline void print_unix_addr(struct sockaddr_un *A, int L) +{ + char *buf = (A) ? (char *) &(A)->sun_path : NULL; + int len = unix_addr_len(L); + if (!buf || len <= 0) + printk(" "); + else if (buf[0]) + printk(" %s", buf); + else + /* abstract name len includes leading \0 */ + printk(" %d @%.*s", len - 1, len - 1, buf+1); +}; + +/* + printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \ + #SK , \ +*/ +#define print_unix_sk(SK) \ +do { \ + struct unix_sock *u = unix_sk(SK); \ + printk("%s: f %d, t %d, p %d", #SK , \ + (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \ + if (u->addr) \ + print_unix_addr(u->addr->name, u->addr->len); \ + else \ + print_unix_addr(NULL, sizeof(sa_family_t)); \ + /* printk("\n");*/ \ +} while (0) + +#define print_sk(SK) \ +do { \ + if (!(SK)) { \ + printk("%s: %s is null\n", __FUNCTION__, #SK); \ + } else if ((SK)->sk_family == PF_UNIX) { \ + print_unix_sk(SK); \ + printk("\n"); \ + } else { \ + printk("%s: %s: family %d\n", __FUNCTION__, #SK , \ + (SK)->sk_family); \ + } \ +} while (0) + +#define print_sock_addr(U) \ +do { \ + printk("%s:\n", __FUNCTION__); \ + printk(" sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : ""); print_sk(sock); \ + printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : ""); print_sk(other); \ + printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : ""); print_sk(newsk); \ +} while (0) + + + + +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label); +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, + struct sock *sk); +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_listen_perm(struct socket *sock, int backlog); +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + +#endif /* __AA_AF_UNIX_H */ diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index 579b59a40ea4..48e07dcbb44d 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -53,6 +53,7 @@ struct aa_sk_ctx { struct aa_label *label; struct aa_label *peer; + struct path path; }; #define SK_CTX(X) ((X)->sk_security) @@ -87,6 +88,9 @@ struct aa_net_compat { ({ \ int __e; \ switch ((FAMILY)) { \ + case AF_UNIX: \ + __e = aa_unix_ ## FN; \ + break; \ default: \ __e = DEF_FN; \ } \ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index e042b994f2b8..29ab20eba812 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -18,6 +18,7 @@ enum path_flags { PATH_IS_DIR = 0x1, /* path is a directory */ + PATH_SOCK_COND = 0x2, PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 4006fa9fc9f1..35da41f14056 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -232,9 +232,13 @@ static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); __be16 be_af = cpu_to_be16(AF); - if (!state) - return 0; - return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); + if (!state) { + state = PROFILE_MEDIATES(profile, AA_CLASS_NET_COMPAT); + if (!state) + return 0; + } + state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); + return state; } /** diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ce2b89e9ad94..7a6b1bd8e046 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -23,8 +23,10 @@ #include #include #include +#include #include +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/apparmorfs.h" #include "include/audit.h" @@ -776,6 +778,7 @@ static void apparmor_sk_free_security(struct sock *sk) SK_CTX(sk) = NULL; aa_put_label(ctx->label); aa_put_label(ctx->peer); + path_put(&ctx->path); kfree(ctx); } @@ -790,6 +793,99 @@ static void apparmor_sk_clone_security(const struct sock *sk, new->label = aa_get_label(ctx->label); new->peer = aa_get_label(ctx->peer); + new->path = ctx->path; + path_get(&new->path); +} + +static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk) +{ + if (sk->sk_family == PF_UNIX && UNIX_FS(sk)) + return &unix_sk(sk)->path; + else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk)) + return &unix_sk(newsk)->path; + return NULL; +} + +/** + * apparmor_unix_stream_connect - check perms before making unix domain conn + * + * peer is locked when this hook is called + */ +static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk, + struct sock *newsk) +{ + struct aa_sk_ctx *sk_ctx = SK_CTX(sk); + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); + struct aa_sk_ctx *new_ctx = SK_CTX(newsk); + struct aa_label *label; + struct path *path; + int error; + + label = __begin_current_label_crit_section(); + error = aa_unix_peer_perm(label, OP_CONNECT, + (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), + sk, peer_sk, NULL); + if (!UNIX_FS(peer_sk)) { + last_error(error, + aa_unix_peer_perm(peer_ctx->label, OP_CONNECT, + (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE), + peer_sk, sk, label)); + } + __end_current_label_crit_section(label); + + if (error) + return error; + + /* label newsk if it wasn't labeled in post_create. Normally this + * would be done in sock_graft, but because we are directly looking + * at the peer_sk to obtain peer_labeling for unix socks this + * does not work + */ + if (!new_ctx->label) + new_ctx->label = aa_get_label(peer_ctx->label); + + /* Cross reference the peer labels for SO_PEERSEC */ + if (new_ctx->peer) + aa_put_label(new_ctx->peer); + + if (sk_ctx->peer) + aa_put_label(sk_ctx->peer); + + new_ctx->peer = aa_get_label(sk_ctx->label); + sk_ctx->peer = aa_get_label(peer_ctx->label); + + path = UNIX_FS_CONN_PATH(sk, peer_sk); + if (path) { + new_ctx->path = *path; + sk_ctx->path = *path; + path_get(path); + path_get(path); + } + return 0; +} + +/** + * apparmor_unix_may_send - check perms before conn or sending unix dgrams + * + * other is locked when this hook is called + * + * dgram connect calls may_send, peer setup but path not copied????? + */ +static int apparmor_unix_may_send(struct socket *sock, struct socket *peer) +{ + struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk); + struct aa_label *label; + int error; + + label = __begin_current_label_crit_section(); + error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND, + sock->sk, peer->sk, NULL), + aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG, + AA_MAY_RECEIVE, + peer->sk, sock->sk, label)); + __end_current_label_crit_section(label); + + return error; } /** @@ -1027,11 +1123,25 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) static struct aa_label *sk_peer_label(struct sock *sk) { + struct sock *peer_sk; struct aa_sk_ctx *ctx = SK_CTX(sk); if (ctx->peer) return ctx->peer; + if (sk->sk_family != PF_UNIX) + return ERR_PTR(-ENOPROTOOPT); + + /* check for sockpair peering which does not go through + * security_unix_stream_connect + */ + peer_sk = unix_peer(sk); + if (peer_sk) { + ctx = SK_CTX(peer_sk); + if (ctx->label) + return ctx->label; + } + return ERR_PTR(-ENOPROTOOPT); } @@ -1155,6 +1265,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect), + LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), diff --git a/security/apparmor/net.c b/security/apparmor/net.c index bf6aaefc3a5f..042aee4408c1 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -12,6 +12,7 @@ * License. */ +#include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" #include "include/cred.h" @@ -29,6 +30,7 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { struct aa_sfs_entry aa_sfs_entry_network_compat[] = { AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + AA_SFS_FILE_BOOLEAN("af_unix", 1), { } }; @@ -74,6 +76,36 @@ static const char * const net_mask_names[] = { "unknown", }; +static void audit_unix_addr(struct audit_buffer *ab, const char *str, + struct sockaddr_un *addr, int addrlen) +{ + int len = unix_addr_len(addrlen); + + if (!addr || len <= 0) { + audit_log_format(ab, " %s=none", str); + } else if (addr->sun_path[0]) { + audit_log_format(ab, " %s=", str); + audit_log_untrustedstring(ab, addr->sun_path); + } else { + audit_log_format(ab, " %s=\"@", str); + if (audit_string_contains_control(&addr->sun_path[1], len - 1)) + audit_log_n_hex(ab, &addr->sun_path[1], len - 1); + else + audit_log_format(ab, "%.*s", len - 1, + &addr->sun_path[1]); + audit_log_format(ab, "\""); + } +} + +static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str, + struct sock *sk) +{ + struct unix_sock *u = unix_sk(sk); + if (u && u->addr) + audit_unix_addr(ab, str, u->addr->name, u->addr->len); + else + audit_unix_addr(ab, str, NULL, 0); +} /* audit callback for net specific fields */ void audit_net_cb(struct audit_buffer *ab, void *va) @@ -103,6 +135,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va) net_mask_names, NET_PERMS_MASK); } } + if (sa->u.net->family == AF_UNIX) { + if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr) + audit_unix_addr(ab, "addr", + unix_addr(aad(sa)->net.addr), + aad(sa)->net.addrlen); + else + audit_unix_sk_addr(ab, "addr", sa->u.net->sk); + if (aad(sa)->request & NET_PEER_MASK) { + if (aad(sa)->net.addr) + audit_unix_addr(ab, "peer_addr", + unix_addr(aad(sa)->net.addr), + aad(sa)->net.addrlen); + else + audit_unix_sk_addr(ab, "peer_addr", + aad(sa)->net.peer_sk); + } + } if (aad(sa)->peer) { audit_log_format(ab, " peer="); aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, @@ -200,5 +249,7 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, AA_BUG(!sock); AA_BUG(!sock->sk); - return aa_label_sk_perm(label, op, request, sock->sk); + return af_select(sock->sk->sk_family, + file_perm(label, op, request, sock), + aa_label_sk_perm(label, op, request, sock->sk)); } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a1b07e6c163d..9c9a329fd2d7 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -296,13 +296,13 @@ static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) { if (unpack_nameX(e, AA_U16, name)) { if (!inbounds(e, sizeof(u16))) - return 0; + return false; if (data) *data = le16_to_cpu(get_unaligned((__le16 *) e->pos)); e->pos += sizeof(u16); - return 1; + return true; } - return 0; + return false; } static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) -- 2.14.1 apparmor-5.0.2/kernel-patches/v4.17/0003-apparmor-fix-use-after-free-in-sk_peer_label.patch000066400000000000000000000030201522511161100310200ustar00rootroot00000000000000From 45ff74bd5a009ab8f9648531fa11fce55b9a67fd Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 26 Jun 2018 20:19:19 -0700 Subject: [PATCH 3/3] apparmor: fix use after free in sk_peer_label BugLink: http://bugs.launchpad.net/bugs/1778646 Signed-off-by: John Johansen --- security/apparmor/lsm.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 7a6b1bd8e046..0d2925389947 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1125,9 +1125,10 @@ static struct aa_label *sk_peer_label(struct sock *sk) { struct sock *peer_sk; struct aa_sk_ctx *ctx = SK_CTX(sk); + struct aa_label *label = ERR_PTR(-ENOPROTOOPT); if (ctx->peer) - return ctx->peer; + return aa_get_label(ctx->peer); if (sk->sk_family != PF_UNIX) return ERR_PTR(-ENOPROTOOPT); @@ -1135,14 +1136,15 @@ static struct aa_label *sk_peer_label(struct sock *sk) /* check for sockpair peering which does not go through * security_unix_stream_connect */ - peer_sk = unix_peer(sk); + peer_sk = unix_peer_get(sk); if (peer_sk) { ctx = SK_CTX(peer_sk); if (ctx->label) - return ctx->label; + label = aa_get_label(ctx->label); + sock_put(peer_sk); } - return ERR_PTR(-ENOPROTOOPT); + return label; } /** @@ -1186,6 +1188,7 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock, } + aa_put_label(peer); done: end_current_label_crit_section(label); -- 2.14.1 apparmor-5.0.2/kernel-patches/v4.8/000077500000000000000000000000001522511161100167365ustar00rootroot00000000000000apparmor-5.0.2/kernel-patches/v4.8/0001-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch000066400000000000000000000440631522511161100303370ustar00rootroot00000000000000From 269384ead6a3c82ac31fd3778e899ccc6a54358e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 4 Oct 2010 15:03:36 -0700 Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: basic networking rules Base support for network mediation. Signed-off-by: John Johansen --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++++++++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/net.h | 44 ++++++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++++++++ 10 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/net.c diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70d72b8..d5b291e94264 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore @@ -1,5 +1,6 @@ # # Generated include files # +net_names.h capability_names.h rlim_names.h diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df874818..5dbb72f46452 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o net.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o -clean-files := capability_names.h rlim_names.h +clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of capability names @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ +# Build a lower case string table of address family names +# Transform lines from +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ # Build a lower case string table of rlimit names. # Transforms lines from @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(src)/Makefile @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ $(src)/Makefile $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 729e595119ed..181d961e6d58 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -807,6 +807,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index ba3dfd17f23f..5d3c419b17d9 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -125,6 +125,10 @@ struct apparmor_audit_data { u32 denied; kuid_t ouid; } fs; + struct { + int type, protocol; + struct sock *sk; + } net; }; }; diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 000000000000..cb8a12109b7a --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,44 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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 __AA_NET_H +#define __AA_NET_H + +#include + +#include "apparmorfs.h" + +/* struct aa_net - network confinement data + * @allowed: basic network families permissions + * @audit_network: which network permissions to force audit + * @quiet_network: which network permissions to quiet rejects + */ +struct aa_net { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; + +extern struct aa_fs_entry aa_fs_entry_network[]; + +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, + int type, int protocol, struct sock *sk); +extern int aa_revalidate_sk(int op, struct sock *sk); + +static inline void aa_free_net_rules(struct aa_net *new) +{ + /* NOP */ +} + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 52275f040a5f..4fc4dacc1101 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "net.h" #include "resource.h" extern const char *const aa_profile_mode_names[]; @@ -176,6 +177,7 @@ struct aa_replacedby { * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile + * @net: network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -217,6 +219,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net net; struct aa_rlimit rlimits; unsigned char *hash; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 41b8cb115801..d96b5f7c1912 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -32,6 +32,7 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" +#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -584,6 +585,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +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_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, + NULL); + return error; +} + +static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_BIND, sk); +} + +static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_CONNECT, sk); +} + +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SENDMSG, sk); +} + +static int apparmor_socket_recvmsg(struct socket *sock, + struct msghdr *msg, int size, int flags) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_RECVMSG, sk); +} + +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -613,6 +712,19 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), LSM_HOOK_INIT(cred_free, apparmor_cred_free), LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 000000000000..003dd18c61a5 --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,162 @@ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/net.h" +#include "include/policy.h" + +#include "net_names.h" + +struct aa_fs_entry aa_fs_entry_network[] = { + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), + { } +}; + +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " family="); + if (address_family_names[sa->u.net->family]) { + audit_log_string(ab, address_family_names[sa->u.net->family]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } + audit_log_format(ab, " sock_type="); + if (sock_type_names[sa->aad->net.type]) { + audit_log_string(ab, sock_type_names[sa->aad->net.type]); + } else { + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); +} + +/** + * audit_net - audit network access + * @profile: profile being enforced (NOT NULL) + * @op: operation being checked + * @family: network family + * @type: network type + * @protocol: network protocol + * @sk: socket auditing is being applied to + * @error: error code for failure else 0 + * + * Returns: %0 or sa->error else other errorcode on failure + */ +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + int protocol, struct sock *sk, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = { }; + struct lsm_network_audit net = { }; + if (sk) { + sa.type = LSM_AUDIT_DATA_NET; + } else { + sa.type = LSM_AUDIT_DATA_NONE; + } + /* todo fill in socket addr info */ + sa.aad = &aad; + sa.u.net = &net; + sa.aad->op = op, + sa.u.net->family = family; + sa.u.net->sk = sk; + sa.aad->net.type = type; + sa.aad->net.protocol = protocol; + sa.aad->error = error; + + if (likely(!sa.aad->error)) { + u16 audit_mask = profile->net.audit[sa.u.net->family]; + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !(1 << sa.aad->net.type & audit_mask))) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; + + if ((denied & quiet_mask) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** + * aa_net_perm - very course network access check + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @family: network family + * @type: network type + * @protocol: network protocol + * + * Returns: %0 else error if permission denied + */ +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, + int protocol, struct sock *sk) +{ + u16 family_mask; + int error; + + 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->net.allow[family]; + + error = (family_mask & (1 << type)) ? 0 : -EACCES; + + return audit_net(profile, op, family, type, protocol, sk, error); +} + +/** + * aa_revalidate_sk - Revalidate access to a sock + * @op: operation being checked + * @sk: sock being revalidated (NOT NULL) + * + * Returns: %0 else error if permission denied + */ +int aa_revalidate_sk(int op, struct sock *sk) +{ + struct aa_profile *profile; + int error = 0; + + /* aa_revalidate_sk should not be called from interrupt context + * don't mediate these calls as they are not task related + */ + if (in_interrupt()) + return 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, + sk->sk_protocol, sk); + + return error; +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 179e68d7dc5f..f1a8541760e8 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 138120698f83..7dc15ff91299 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -193,6 +193,19 @@ fail: return 0; } +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) + return 0; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return 1; + } + return 0; +} + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + size = unpack_array(e, "net_allowed_af"); + if (size) { + + for (i = 0; i < size; i++) { + /* discard extraneous rules that this kernel will + * never request + */ + if (i >= AF_MAX) { + u16 tmp; + if (!unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL) || + !unpack_u16(e, &tmp, NULL)) + goto fail; + continue; + } + if (!unpack_u16(e, &profile->net.allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + } + /* + * allow unix domain and netlink sockets they are handled + * by IPC + */ + profile->net.allow[AF_UNIX] = 0xffff; + profile->net.allow[AF_NETLINK] = 0xffff; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ profile->policy.dfa = unpack_dfa(e); -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.8/0002-apparmor-Fix-quieting-of-audit-messages-for-network-.patch000066400000000000000000000030051522511161100323670ustar00rootroot00000000000000From 88ba6f37ed824ca24901fca7e399168db5e46f12 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jun 2012 17:34:00 -0700 Subject: [PATCH 2/3] apparmor: Fix quieting of audit messages for network mediation If a profile specified a quieting of network denials for a given rule by either the quiet or deny rule qualifiers, the resultant quiet mask for denied requests was applied incorrectly, resulting in two potential bugs. 1. The misapplied quiet mask would prevent denials from being correctly tested against the kill mask/mode. Thus network access requests that should have resulted in the application being killed did not. 2. The actual quieting of the denied network request was not being applied. This would result in network rejections always being logged even when they had been specifically marked as quieted. Signed-off-by: John Johansen --- security/apparmor/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 003dd18c61a5..6e6e5c981006 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, } else { u16 quiet_mask = profile->net.quiet[sa.u.net->family]; u16 kill_mask = 0; - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + u16 denied = (1 << sa.aad->net.type); if (denied & kill_mask) audit_type = AUDIT_APPARMOR_KILL; -- 2.11.0 apparmor-5.0.2/kernel-patches/v4.8/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch000066400000000000000000000660441522511161100315730ustar00rootroot00000000000000From 6556d6523f74e90a801503d28e7b8dcc5caa6a1b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 10:58:05 -0700 Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount Add the ability for apparmor to do mediation of mount operations. Mount rules require an updated apparmor_parser (2.8 series) for policy compilation. The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=] remount is just a short cut for mount options=remount where [conds] can be fstype= options= Example mount commands mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ umount, umount /m*, See the apparmor userspace for full documentation Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 15 +- security/apparmor/audit.c | 4 + security/apparmor/domain.c | 2 +- security/apparmor/include/apparmor.h | 3 +- security/apparmor/include/audit.h | 11 + security/apparmor/include/domain.h | 2 + security/apparmor/include/mount.h | 54 +++ security/apparmor/lsm.c | 60 ++++ security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ 10 files changed, 769 insertions(+), 4 deletions(-) create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/mount.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5dbb72f46452..89b344541868 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o + resource.o sid.o file.o net.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h net_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 181d961e6d58..5fb67f60bace 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -800,7 +800,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_policy[] = { AA_FS_FILE_BOOLEAN("set_load", 1), - {} + { } +}; + +static struct aa_fs_entry aa_fs_entry_mount[] = { + AA_FS_FILE_STRING("mask", "mount umount"), + { } +}; + +static struct aa_fs_entry aa_fs_entry_namespaces[] = { + AA_FS_FILE_BOOLEAN("profile", 1), + AA_FS_FILE_BOOLEAN("pivot_root", 1), + { } }; static struct aa_fs_entry aa_fs_entry_features[] = { @@ -808,6 +819,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_DIR("mount", aa_fs_entry_mount), + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), AA_FS_DIR("caps", aa_fs_entry_caps), diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3a7f1da1425e..c2a8b8ac38a7 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -44,6 +44,10 @@ const char *const op_table[] = { "file_mmap", "file_mprotect", + "pivotroot", + "mount", + "umount", + "create", "post_create", "bind", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index fc3036b34e51..f2a83b4430db 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -236,7 +236,7 @@ static const char *next_name(int xtype, const char *name) * * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 5d721e990876..b57da7b9f8bd 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -30,8 +30,9 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_MOUNT /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 5d3c419b17d9..b9f1d57984ca 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -72,6 +72,10 @@ enum aa_ops { OP_FMMAP, OP_FMPROT, + OP_PIVOTROOT, + OP_MOUNT, + OP_UMOUNT, + OP_CREATE, OP_POST_CREATE, OP_BIND, @@ -120,6 +124,13 @@ struct apparmor_audit_data { unsigned long max; } rlim; struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + struct { const char *target; u32 request; u32 denied; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464f0a3f..a3f70c58ef3d 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,6 +23,8 @@ struct aa_domain { char **table; }; +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); void apparmor_bprm_committing_creds(struct linux_binprm *bprm); diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 000000000000..a43b1d62e428 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,54 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2012 Canonical Ltd. + * + * 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 __AA_MOUNT_H +#define __AA_MOUNT_H + +#include +#include + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_profile *profile, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index d96b5f7c1912..5ff9984cba5a 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -36,6 +36,7 @@ #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" +#include "include/mount.h" /* Flag indicating whether initialization completed */ int apparmor_initialized __initdata; @@ -469,6 +470,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } +static int apparmor_sb_mount(const char *dev_name, const struct path *path, + const char *type, unsigned long flags, void *data) +{ + struct aa_profile *profile; + int error = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + + flags &= ~AA_MS_IGNORE_MASK; + + profile = __aa_current_profile(); + if (!unconfined(profile)) { + if (flags & MS_REMOUNT) + error = aa_remount(profile, path, flags, data); + else if (flags & MS_BIND) + error = aa_bind_mount(profile, path, dev_name, flags); + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE)) + error = aa_mount_change_type(profile, path, flags); + else if (flags & MS_MOVE) + error = aa_move_mount(profile, path, dev_name); + else + error = aa_new_mount(profile, dev_name, path, type, + flags, data); + } + return error; +} + +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_umount(profile, mnt, flags); + + return error; +} + +static int apparmor_sb_pivotroot(const struct path *old_path, + const struct path *new_path) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_pivotroot(profile, old_path, new_path); + + return error; +} + static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { @@ -689,6 +745,10 @@ static struct security_hook_list apparmor_hooks[] = { LSM_HOOK_INIT(capget, apparmor_capget), LSM_HOOK_INIT(capable, apparmor_capable), + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), + LSM_HOOK_INIT(path_link, apparmor_path_link), LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 000000000000..9cf9170b4976 --- /dev/null +++ b/security/apparmor/mount.c @@ -0,0 +1,620 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + * + * 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. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/match.h" +#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + + +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) +{ + if (flags & MS_RDONLY) + audit_log_format(ab, "ro"); + else + audit_log_format(ab, "rw"); + if (flags & MS_NOSUID) + audit_log_format(ab, ", nosuid"); + if (flags & MS_NODEV) + audit_log_format(ab, ", nodev"); + if (flags & MS_NOEXEC) + audit_log_format(ab, ", noexec"); + if (flags & MS_SYNCHRONOUS) + audit_log_format(ab, ", sync"); + if (flags & MS_REMOUNT) + audit_log_format(ab, ", remount"); + if (flags & MS_MANDLOCK) + audit_log_format(ab, ", mand"); + if (flags & MS_DIRSYNC) + audit_log_format(ab, ", dirsync"); + if (flags & MS_NOATIME) + audit_log_format(ab, ", noatime"); + if (flags & MS_NODIRATIME) + audit_log_format(ab, ", nodiratime"); + if (flags & MS_BIND) + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); + if (flags & MS_MOVE) + audit_log_format(ab, ", move"); + if (flags & MS_SILENT) + audit_log_format(ab, ", silent"); + if (flags & MS_POSIXACL) + audit_log_format(ab, ", acl"); + if (flags & MS_UNBINDABLE) + audit_log_format(ab, flags & MS_REC ? ", runbindable" : + ", unbindable"); + if (flags & MS_PRIVATE) + audit_log_format(ab, flags & MS_REC ? ", rprivate" : + ", private"); + if (flags & MS_SLAVE) + audit_log_format(ab, flags & MS_REC ? ", rslave" : + ", slave"); + if (flags & MS_SHARED) + audit_log_format(ab, flags & MS_REC ? ", rshared" : + ", shared"); + if (flags & MS_RELATIME) + audit_log_format(ab, ", relatime"); + if (flags & MS_I_VERSION) + audit_log_format(ab, ", iversion"); + if (flags & MS_STRICTATIME) + audit_log_format(ab, ", strictatime"); + if (flags & MS_NOUSER) + audit_log_format(ab, ", nouser"); +} + +/** + * audit_cb - call back for mount specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->mnt.type) { + audit_log_format(ab, " fstype="); + audit_log_untrustedstring(ab, sa->aad->mnt.type); + } + if (sa->aad->mnt.src_name) { + audit_log_format(ab, " srcname="); + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); + } + if (sa->aad->mnt.trans) { + audit_log_format(ab, " trans="); + audit_log_untrustedstring(ab, sa->aad->mnt.trans); + } + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { + audit_log_format(ab, " flags=\""); + audit_mnt_flags(ab, sa->aad->mnt.flags); + audit_log_format(ab, "\""); + } + if (sa->aad->mnt.data) { + audit_log_format(ab, " options="); + audit_log_untrustedstring(ab, sa->aad->mnt.data); + } +} + +/** + * audit_mount - handle the auditing of mount operations + * @profile: the profile being enforced (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated (NOT NULL) + * @name: name of object being mediated (MAYBE NULL) + * @src_name: src_name of object being mediated (MAYBE_NULL) + * @type: type of filesystem (MAYBE_NULL) + * @trans: name of trans (MAYBE NULL) + * @flags: filesystem idependent mount flags + * @data: filesystem mount flags + * @request: permissions requested + * @perms: the permissions computed for the request (NOT NULL) + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, + const char *name, const char *src_name, + const char *type, const char *trans, + unsigned long flags, const void *data, u32 request, + struct file_perms *perms, const char *info, int error) +{ + int audit_type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa = { }; + struct apparmor_audit_data aad = { }; + + if (likely(!error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + request &= mask; + + if (likely(!request)) + return 0; + audit_type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + request = request & ~perms->allow; + + if (request & perms->kill) + audit_type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + request &= ~perms->quiet; + + if (!request) + return COMPLAIN_MODE(profile) ? + complain_error(error) : error; + } + + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + sa.aad->op = op; + sa.aad->name = name; + sa.aad->mnt.src_name = src_name; + sa.aad->mnt.type = type; + sa.aad->mnt.trans = trans; + sa.aad->mnt.flags = flags; + if (data && (perms->audit & AA_AUDIT_DATA)) + sa.aad->mnt.data = data; + sa.aad->info = info; + sa.aad->error = error; + + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** + * match_mnt_flags - Do an ordered match on mount flags + * @dfa: dfa to match against + * @state: state to start in + * @flags: mount flags to match against + * + * Mount flags are encoded as an ordered match. This is done instead of + * checking against a simple bitmask, to allow for logical operations + * on the flags. + * + * Returns: next state after flags match + */ +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, + unsigned long flags) +{ + unsigned int i; + + for (i = 0; i <= 31 ; ++i) { + if ((1 << i) & flags) + state = aa_dfa_next(dfa, state, i + 1); + } + + return state; +} + +/** + * compute_mnt_perms - compute mount permission associated with @state + * @dfa: dfa to match against (NOT NULL) + * @state: state match finished in + * + * Returns: mount permissions + */ +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, + unsigned int state) +{ + struct file_perms perms; + + perms.kill = 0; + perms.allow = dfa_user_allow(dfa, state); + perms.audit = dfa_user_audit(dfa, state); + perms.quiet = dfa_user_quiet(dfa, state); + perms.xindex = dfa_user_xindex(dfa, state); + + return perms; +} + +static const char const *mnt_info_table[] = { + "match succeeded", + "failed mntpnt match", + "failed srcname match", + "failed type match", + "failed flags match", + "failed data match" +}; + +/* + * Returns 0 on success else element that match failed in, this is the + * index into the mnt_info_table above + */ +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, + const char *mntpnt, const char *devname, + const char *type, unsigned long flags, + void *data, bool binary, struct file_perms *perms) +{ + unsigned int state; + + state = aa_dfa_match(dfa, start, mntpnt); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 1; + + if (devname) + state = aa_dfa_match(dfa, state, devname); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 2; + + if (type) + state = aa_dfa_match(dfa, state, type); + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 3; + + state = match_mnt_flags(dfa, state, flags); + if (!state) + return 4; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + + /* only match data if not binary and the DFA flags data is expected */ + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { + state = aa_dfa_null_transition(dfa, state); + if (!state) + return 4; + + state = aa_dfa_match(dfa, state, data); + if (!state) + return 5; + *perms = compute_mnt_perms(dfa, state); + if (perms->allow & AA_MAY_MOUNT) + return 0; + } + + /* failed at end of flags match */ + return 4; +} + +/** + * match_mnt - handle path matching for mount + * @profile: the confining profile + * @mntpnt: string for the mntpnt (NOT NULL) + * @devname: string for the devname/src_name (MAYBE NULL) + * @type: string for the dev type (MAYBE NULL) + * @flags: mount flags to match + * @data: fs mount data (MAYBE NULL) + * @binary: whether @data is binary + * @perms: Returns: permission found by the match + * @info: Returns: infomation string about the match for logging + * + * Returns: 0 on success else error + */ +static int match_mnt(struct aa_profile *profile, const char *mntpnt, + const char *devname, const char *type, + unsigned long flags, void *data, bool binary, + struct file_perms *perms, const char **info) +{ + int pos; + + if (!profile->policy.dfa) + return -EACCES; + + pos = do_match_mnt(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + mntpnt, devname, type, flags, data, binary, perms); + if (pos) { + *info = mnt_info_table[pos]; + return -EACCES; + } + + return 0; +} + +static int path_flags(struct aa_profile *profile, const struct path *path) +{ + return profile->path_flags | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + +int aa_remount(struct aa_profile *profile, const struct path *path, + unsigned long flags, void *data) +{ + struct file_perms perms = { }; + const char *name, *info = NULL; + char *buffer = NULL; + int binary, error; + + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_bind_mount(struct aa_profile *profile, const struct path *path, + const char *dev_name, unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!dev_name || !*dev_name) + return -EINVAL; + + flags &= MS_REC | MS_BIND; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, + unsigned long flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + /* These are the flags allowed by do_change_type() */ + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | + MS_UNBINDABLE); + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, + &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + + return error; +} + +int aa_move_mount(struct aa_profile *profile, const struct path *path, + const char *orig_name) +{ + struct file_perms perms = { }; + char *buffer = NULL, *old_buffer = NULL; + const char *name, *old_name = NULL, *info = NULL; + struct path old_path; + int error; + + if (!orig_name || !*orig_name) + return -EINVAL; + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); + if (error) + goto audit; + + error = aa_path_name(&old_path, path_flags(profile, &old_path), + &old_buffer, &old_name, &info); + path_put(&old_path); + if (error) + goto audit; + + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, + info, error); + kfree(buffer); + kfree(old_buffer); + + return error; +} + +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data) +{ + struct file_perms perms = { }; + char *buffer = NULL, *dev_buffer = NULL; + const char *name = NULL, *dev_name = NULL, *info = NULL; + int binary = 1; + int error; + + dev_name = orig_dev_name; + if (type) { + int requires_dev; + struct file_system_type *fstype = get_fs_type(type); + if (!fstype) + return -ENODEV; + + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; + put_filesystem(fstype); + + if (requires_dev) { + struct path dev_path; + + if (!dev_name || !*dev_name) { + error = -ENOENT; + goto out; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); + if (error) + goto audit; + + error = aa_path_name(&dev_path, + path_flags(profile, &dev_path), + &dev_buffer, &dev_name, &info); + path_put(&dev_path); + if (error) + goto audit; + } + } + + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, + &info); + if (error) + goto audit; + + error = match_mnt(profile, name, dev_name, type, flags, data, binary, + &perms, &info); + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, + error); + kfree(buffer); + kfree(dev_buffer); + +out: + return error; + +} + +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ + struct file_perms perms = { }; + char *buffer = NULL; + const char *name, *info = NULL; + int error; + + struct path path = { mnt, mnt->mnt_root }; + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, + &info); + if (error) + goto audit; + + if (!error && profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_UMOUNT & ~perms.allow) + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + + return error; +} + +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, + const struct path *new_path) +{ + struct file_perms perms = { }; + struct aa_profile *target = NULL; + char *old_buffer = NULL, *new_buffer = NULL; + const char *old_name, *new_name = NULL, *info = NULL; + int error; + + error = aa_path_name(old_path, path_flags(profile, old_path), + &old_buffer, &old_name, &info); + if (error) + goto audit; + + error = aa_path_name(new_path, path_flags(profile, new_path), + &new_buffer, &new_name, &info); + if (error) + goto audit; + + if (profile->policy.dfa) { + unsigned int state; + state = aa_dfa_match(profile->policy.dfa, + profile->policy.start[AA_CLASS_MOUNT], + new_name); + state = aa_dfa_null_transition(profile->policy.dfa, state); + state = aa_dfa_match(profile->policy.dfa, state, old_name); + perms = compute_mnt_perms(profile->policy.dfa, state); + } + + if (AA_MAY_PIVOTROOT & perms.allow) { + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + target = x_table_lookup(profile, perms.xindex); + if (!target) + error = -ENOENT; + else + error = aa_replace_current_profile(target); + } + } else + error = -EACCES; + +audit: + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, + old_name, NULL, target ? target->base.name : NULL, + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); + aa_put_profile(target); + kfree(old_buffer); + kfree(new_buffer); + + return error; +} -- 2.11.0 apparmor-5.0.2/libraries/000077500000000000000000000000001522511161100153065ustar00rootroot00000000000000apparmor-5.0.2/libraries/libapparmor/000077500000000000000000000000001522511161100176165ustar00rootroot00000000000000apparmor-5.0.2/libraries/libapparmor/AUTHORS000066400000000000000000000001101522511161100206560ustar00rootroot00000000000000Steve Beattie Matt Barringer apparmor-5.0.2/libraries/libapparmor/COPYING.LGPL000066400000000000000000000635001522511161100214120ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! apparmor-5.0.2/libraries/libapparmor/ChangeLog000066400000000000000000000000011522511161100213570ustar00rootroot00000000000000 apparmor-5.0.2/libraries/libapparmor/INSTALL000066400000000000000000000224321522511161100206520ustar00rootroot00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Here is a another example: /bin/bash ./configure CONFIG_SHELL=/bin/bash Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent configuration-related scripts to be executed by `/bin/bash'. `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. apparmor-5.0.2/libraries/libapparmor/Makefile.am000066400000000000000000000002521522511161100216510ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign 1.4 NAME = libapparmor SRCDIR = src SUBDIRS = doc src include swig testsuite EXTRA_DIST = AUTHORS ChangeLog COPYING.LGPL INSTALL NEWS README apparmor-5.0.2/libraries/libapparmor/NEWS000066400000000000000000000000541522511161100203140ustar00rootroot00000000000000- 2007-06-24 - Initial release, version 0.6 apparmor-5.0.2/libraries/libapparmor/README000066400000000000000000000002031522511161100204710ustar00rootroot00000000000000What little documentation exists is in include/aalogparse.h. Please file bugs using https://gitlab.com/apparmor/apparmor/-/issues apparmor-5.0.2/libraries/libapparmor/autogen.sh000077500000000000000000000022201522511161100216130ustar00rootroot00000000000000#!/bin/sh DIE=0 package=libapparmor (autoconf --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have autoconf installed to compile $package." echo "Download the appropriate package for your distribution," echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" DIE=1 } (automake --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have automake installed to compile $package." echo "Download the appropriate package for your system," echo "or get the source from one of the GNU ftp sites" echo "listed in http://www.gnu.org/order/ftp.html" DIE=1 } (libtoolize --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have libtool installed to compile $package." echo "Download the appropriate package for your system," echo "or get the source from one of the GNU ftp sites" echo "listed in http://www.gnu.org/order/ftp.html" DIE=1 } if test "$DIE" -eq 1; then exit 1 fi echo "Running aclocal" aclocal echo "Running autoconf" autoconf --force echo "Running libtoolize" libtoolize --automake -c --force echo "Running automake" automake -ac apparmor-5.0.2/libraries/libapparmor/configure.ac000066400000000000000000000103011522511161100220770ustar00rootroot00000000000000m4_define([__apparmor_version], m4_sinclude(common/Version)) m4_ifdef(__apparmor_version, , m4_define([__apparmor_version], m4_sinclude(../../common/Version))) m4_define([__aalen], decr(len(__apparmor_version))) m4_define([apparmor_version], m4_substr(__apparmor_version, 0, __aalen)) AC_INIT([libapparmor1],[apparmor_version],[bugs@apparmor.net (public) or security@apparmor.net (nonpublic)],,[apparmor.net]) AM_INIT_AUTOMAKE([-Wall]) # Specifying noyywrap here doesn't fix the warning AM_PROG_LEX AC_PROG_YACC AC_PROG_SED PKG_PROG_PKG_CONFIG AC_PATH_PROG([SWIG], [swig]) AC_MSG_CHECKING([whether the libapparmor debug output should be enabled]) AC_ARG_ENABLE([debug_output], [AS_HELP_STRING([--enable-debug-output], [generate the libapparmor debug output [[default=no]]])], [AC_MSG_RESULT([$enableval])], [enable_debug_output=no] [AC_MSG_RESULT([$enable_debug_output])]) AS_IF([test "$enable_debug_output" = "yes"], [AC_DEFINE([ENABLE_DEBUG_OUTPUT], [1], [debug output])]) AC_MSG_CHECKING([whether the libapparmor man pages should be generated]) AC_ARG_ENABLE(man_pages, [AS_HELP_STRING([--enable-man-pages], [generate the libapparmor man pages [[default=yes]]])], [AC_MSG_RESULT($enableval)], [enable_man_pages=yes] [AC_MSG_RESULT($enable_man_pages)]) if test "$enable_man_pages" = "yes"; then sinclude(m4/ac_podchecker.m4) PROG_PODCHECKER sinclude(m4/ac_pod2man.m4) PROG_POD2MAN fi AC_MSG_CHECKING([whether python bindings are enabled]) AC_ARG_WITH(python, [ --with-python enable the python wrapper [[default=no]]], [AC_MSG_RESULT($withval)], [AC_MSG_RESULT(no)]) if test "$with_python" = "yes"; then test -z "$SWIG" && AC_MSG_ERROR([swig is required when enabling python bindings]) AC_PATH_PROG(PYTHON, python3) test -z "$PYTHON" && AC_MSG_ERROR([python is required when enabling python bindings]) sinclude(m4/ac_python_devel.m4) AC_PYTHON_DEVEL AM_PATH_PYTHON([3.0]) fi AC_MSG_CHECKING([whether perl bindings are enabled]) AC_ARG_WITH(perl, [ --with-perl enable the perl wrapper [[default=no]]], [AC_MSG_RESULT($withval)], [AC_MSG_RESULT(no)]) if test "$with_perl" = "yes"; then test -z "$SWIG" && AC_MSG_ERROR([swig is required when enabling perl bindings]) AC_PATH_PROG(PERL, perl) test -z "$PERL" && AC_MSG_ERROR([perl is required when enabling perl bindings]) perl_includedir="`$PERL -e 'use Config; print $Config{archlib}'`/CORE" AS_IF([test -e "$perl_includedir/perl.h"], enable_perl=yes, enable_perl=no) fi AC_MSG_CHECKING([whether ruby bindings are enabled]) AC_ARG_WITH(ruby, [ --with-ruby enable the ruby wrapper [[default=no]]], [AC_MSG_RESULT($withval)], [AC_MSG_RESULT(no)]) if test "$with_ruby" = "yes"; then test -z "$SWIG" && AC_MSG_ERROR([swig is required when enabling ruby bindings]) AC_PATH_PROG([RUBY], [ruby]) test -z "$RUBY" && AC_MSG_ERROR([ruby is required when enabling ruby bindings]) fi AM_CONDITIONAL(ENABLE_MAN_PAGES, test x$enable_man_pages = xyes) AM_CONDITIONAL(HAVE_PYTHON, test x$with_python = xyes) AM_CONDITIONAL(HAVE_PERL, test x$with_perl = xyes) AM_CONDITIONAL(HAVE_RUBY, test x$with_ruby = xyes) # Keep because we use grep in a test AC_PROG_EGREP AC_CHECK_HEADERS(unistd.h stdint.h syslog.h linux/lsm.h) AC_CHECK_FUNCS([asprintf __secure_getenv secure_getenv reallocarray]) AC_C_CONST AM_PROG_AR LT_INIT # Specifying C99 is required for older systems AC_PROG_CC_C99 if test "$ac_cv_prog_cc_c99" = "no"; then AC_MSG_ERROR([C99 mode is required to build libapparmor]) fi AC_PROG_CXX m4_ifndef([AX_CHECK_COMPILE_FLAG], [AC_MSG_ERROR(['autoconf-archive' missing])]) EXTRA_CFLAGS="-Wall $EXTRA_WARNINGS -fPIC" AX_CHECK_COMPILE_FLAG([-flto-partition=none], , , [-Werror]) AS_VAR_IF([ax_cv_check_cflags__Werror__flto_partition_none], [yes], [EXTRA_CFLAGS="$EXTRA_CFLAGS -flto-partition=none"] ,) AC_SUBST([AM_CFLAGS], ["$EXTRA_CFLAGS"]) AC_SUBST([AM_CXXFLAGS], ["$EXTRA_CFLAGS"]) AC_CONFIG_FILES([Makefile doc/Makefile src/Makefile swig/Makefile swig/perl/Makefile swig/perl/Makefile.PL swig/python/Makefile swig/python/setup.py swig/python/test/Makefile swig/ruby/Makefile testsuite/Makefile testsuite/config/Makefile testsuite/libaalogparse.test/Makefile testsuite/lib/Makefile include/Makefile include/sys/Makefile ]) AC_OUTPUT apparmor-5.0.2/libraries/libapparmor/doc/000077500000000000000000000000001522511161100203635ustar00rootroot00000000000000apparmor-5.0.2/libraries/libapparmor/doc/Makefile.am000066400000000000000000000014401522511161100224160ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in POD2MAN = pod2man PODCHECKER = podchecker if ENABLE_MAN_PAGES man_MANS = aa_change_hat.2 aa_change_profile.2 aa_stack_profile.2 aa_getcon.2 aa_find_mountpoint.2 aa_splitcon.3 aa_query_label.2 aa_features.3 aa_kernel_interface.3 aa_policy_cache.3 PODS = $(subst .2,.pod,$(man_MANS)) $(subst .3,.pod,$(man_MANS)) EXTRA_DIST = $(man_MANS) $(PODS) ## delete man pages at make clean CLEANFILES = $(man_MANS) %.2: %.pod $(PODCHECKER) -warnings -warnings $< $(POD2MAN) \ --section=2 \ --release="AppArmor $(VERSION)" \ --center="AppArmor" \ --stderr \ $< > $@ %.3: %.pod $(PODCHECKER) -warnings -warnings $< $(POD2MAN) \ --section=3 \ --release="AppArmor $(VERSION)" \ --center="AppArmor" \ --stderr \ $< > $@ endif apparmor-5.0.2/libraries/libapparmor/doc/aa_change_hat.pod000066400000000000000000000202501522511161100236100ustar00rootroot00000000000000# This publication is intellectual property of Novell Inc. and Canonical # Ltd. Its contents can be duplicated, either in part or in whole, provided # that a copyright label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither SUSE LINUX GmbH, Canonical Ltd, the authors, nor the translators # shall be held liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. SUSE LINUX GmbH # and Canonical Ltd. essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa_change_hat - change to or from a "hat" within a AppArmor profile =head1 SYNOPSIS B<#include Esys/apparmor.hE> B B B Link with B<-lapparmor> when compiling. =head1 DESCRIPTION An AppArmor profile applies to an executable program; if a portion of the program needs different access permissions than other portions, the program can "change hats" to a different role, also known as a subprofile. To change into a new hat, it calls one of the family of change_hat functions to do so. It passes in a pointer to the I which it wants to change into, and a 64bit I. The I is used to return out of the subprofile at a later time. The aa_change_hat() function allows specifying the name of a single I that the application wants to change into. A pointer to the name of the I is passed along with the I. If the profile is not present the call will fail with the appropriate error. The aa_change_hatv() function allows passing a I terminated vector of pointers to I names which will be tried in order. The first I in the vector that exists will be transitioned to and if none of the I exist the call will fail with the appropriate error. The aa_change_hat_vargs() function is a convenience wrapper for the aa_change_hatv() function. After the I it takes an arbitrary number of pointers to I names. Similar to execl(3), aa_change_hat_vargs() assembles the list of I names into a vector and calls aa_change_hatv(). If a program wants to return out of the current subprofile to the original profile, it calls aa_change_hat() with a pointer to NULL as the I, and the original I value. If the I does not match the original I passed into the kernel when the program entered the subprofile, the change back to the original profile will not happen, and the current task will be killed. If the I matches the original token, then the process will change back to the original profile. As both read(2) and write(2) are mediated, a file must be listed in a subprofile definition if the file is to be accessed while the process is in a "hat". =head1 RETURN VALUE On success zero is returned. On error, -1 is returned, and errno(3) is set appropriately. =head1 ERRORS =over 4 =item B The apparmor kernel module is not loaded or the communication via the F file did not conform to protocol. =item B Insufficient kernel memory was available. =item B The calling application is not confined by apparmor, the specified I is not a I, the task is being ptraced and the tracing task does not have permission to trace the specified I or the no_new_privs execution bit is enabled. =item B The application's profile has no hats defined for it. =item B The specified I does not exist in this profile but other hats are defined. =item B The specified magic token did not match, and permissions to change to the specified I has been denied. This will in most situations also result in the task being killed, to prevent brute force attacks. =back =head1 EXAMPLE The following code examples shows simple, if contrived, uses of aa_change_hat(); a typical use of aa_change_hat() will separate privileged portions of a process from unprivileged portions of a process, such as keeping unauthenticated network traffic handling separate from authenticated network traffic handling in OpenSSH or executing user-supplied CGI scripts in apache. The use of random(3) is simply illustrative. Use of F is recommended. First, a simple high-level overview of aa_change_hat() use: void foo (void) { unsigned long magic_token; /* get a random magic token value from our huge entropy pool */ magic_token = random_function(); /* change into the subprofile while * we do stuff we don't trust */ aa_change_hat("stuff_we_dont_trust", magic_token); /* Go do stuff we don't trust -- this is all * done in *this* process space, no separate * fork()/exec()'s are done. */ interpret_perl_stuff(stuff_from_user); /* now change back to our original profile */ aa_change_hat(NULL, magic_token); } Second, an example to show that files not listed in a subprofile ("hat") aren't accessible after an aa_change_hat() call: #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int fd; unsigned long tok; char buf[10]; /* random() is a poor choice */ tok = random(); /* open /etc/passwd outside of any hat */ if ((fd=open("/etc/passwd", O_RDONLY)) < 0) perror("Failure opening /etc/passwd"); /* confirm for ourselves that we can really read /etc/passwd */ memset(&buf, 0, 10); if (read(fd, &buf, 10) == -1) { perror("Failure reading /etc/passwd pre-hat"); _exit(1); } buf[9] = '\0'; printf("/etc/passwd: %s\n", buf); /* change hat to the "hat" subprofile, which should not have * read access to /etc/passwd -- even though we have a valid * file descriptor at the time of the aa_change_hat() call. */ if (aa_change_hat("hat", tok)) { perror("Failure changing hat -- aborting"); _exit(1); } /* confirm that we cannot read /etc/passwd */ lseek(fd,0,SEEK_SET); memset(&buf, 0, 10); if (read(fd, &buf, 10) == -1) perror("Failure reading /etc/passwd post-hat"); buf[9] = '\0'; printf("/etc/passwd: %s\n", buf); return 0; } This code example requires the following profile to be loaded with apparmor_parser(8): /tmp/ch { /etc/ld.so.cache mr, /etc/locale/** r, /etc/localtime r, /usr/share/locale/** r, /usr/share/zoneinfo/** r, /usr/lib/locale/** mr, /usr/lib/gconv/*.so mr, /usr/lib/gconv/gconv-modules* mr, /lib/ld-*.so* mrix, /lib/libc*.so* mr, /lib/libapparmor*.so* mr, /dev/pts/* rw, /tmp/ch mr, /etc/passwd r, ^hat { /dev/pts/* rw, } } The output when run: $ /tmp/ch /etc/passwd: root:x:0: Failure reading /etc/passwd post-hat: Permission denied /etc/passwd: $ =head1 BUGS None known. If you find any, please report them at L. Note that aa_change_hat(2) provides no memory barriers between different areas of a program; if address space separation is required, then separate processes should be used. =head1 SEE ALSO apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2), aa_getcon(2) and L. =cut apparmor-5.0.2/libraries/libapparmor/doc/aa_change_profile.pod000066400000000000000000000147331522511161100245050ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd. # essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa_change_profile, aa_change_onexec - change a task's profile =head1 SYNOPSIS B<#include Esys/apparmor.hE> B B Link with B<-lapparmor> when compiling. =head1 DESCRIPTION An AppArmor profile applies to an executable program; if a portion of the program needs different access permissions than other portions, the program can "change profile" to a different profile. To change into a new profile, it can use the aa_change_profile() function to do so. It passes in a pointer to the I to transition to. Confined programs wanting to use aa_change_profile() need to have rules permitting changing to the named profile. See apparmor.d(8) for details. If a program wants to return out of the current profile to the original profile, it may use aa_change_hat(2). Otherwise, the two profiles must have rules permitting changing between the two profiles. Open file descriptors may not be remediated after a call to aa_change_profile() so the calling program must close(2) open file descriptors to ensure they are not available after calling aa_change_profile(). As aa_change_profile() is typically used just before execve(2), you may want to use open(2) or fcntl(2) with close-on-exec. The aa_change_onexec() function is like the aa_change_profile() function except it specifies that the profile transition should take place on the next exec instead of immediately. The delayed profile change takes precedence over any exec transition rules within the confining profile. Delaying the profile boundary has a couple of advantages: it removes the need for stub transition profiles, and the exec boundary is a natural security layer where potentially sensitive memory is unmapped. =head1 RETURN VALUE On success zero is returned. On error, -1 is returned, and errno(3) is set appropriately. =head1 ERRORS =over 4 =item B The apparmor kernel module is not loaded, neither a profile nor a namespace was specified, or the communication via the F file did not conform to protocol. =item B Insufficient kernel memory was available. =item B The calling application is confined by apparmor and the no_new_privs bit is set. =item B The task does not have sufficient permissions to change its domain. =item B The specified profile does not exist, or is not visible from the current Namespace. =back =head1 EXAMPLE The following example shows a simple, if contrived, use of aa_change_profile(); a typical use of aa_change_profile() will aa_change_profile() just before an execve(2) so that the new child process is permanently confined. #include #include #include #include #include #include #include #include int main(int argc, char * argv[]) { int fd; char buf[10]; char *execve_args[4]; printf("Before aa_change_profile():\n"); if ((fd=open("/etc/passwd", O_RDONLY)) < 0) { perror("Failure opening /etc/passwd"); return 1; } /* Confirm for ourselves that we can really read /etc/passwd */ memset(&buf, 0, 10); if (read(fd, &buf, 10) == -1) { perror("Failure reading /etc/passwd"); return 1; } buf[9] = '\0'; printf("/etc/passwd: %s\n", buf); close(fd); printf("After aa_change_profile():\n"); /* change profile to the "i_cant_be_trusted_anymore" profile, which * should not have read access to /etc/passwd. */ if (aa_change_profile("i_cant_be_trusted_anymore") < 0) { perror("Failure changing profile -- aborting"); _exit(1); } /* confirm that we cannot read /etc/passwd */ execve_args[0] = "/usr/bin/head"; execve_args[1] = "-1"; execve_args[2] = "/etc/passwd"; execve_args[3] = NULL; execve("/usr/bin/head", execve_args, NULL); perror("execve"); _exit(1); } This code example requires a profile similar to the following to be loaded with apparmor_parser(8): profile i_cant_be_trusted_anymore { /etc/ld.so.cache mr, /lib/ld-*.so* mrix, /lib/libc*.so* mr, /usr/bin/head ix, } The output when run: $ /tmp/change_p Before aa_change_profile(): /etc/passwd: root:x:0: After aa_change_profile(): /usr/bin/head: cannot open `/etc/passwd' for reading: Permission denied $ If /tmp/change_p is to be confined as well, then the following profile can be used (in addition to the one for 'i_cant_be_trusted_anymore', above): # Confine change_p to be able to read /etc/passwd and aa_change_profile() # to the 'i_cant_be_trusted_anymore' profile. /tmp/change_p { /etc/ld.so.cache mr, /lib/ld-*.so* mrix, /lib/libc*.so* mr, /etc/passwd r, # Needed for aa_change_profile() /usr/lib/libapparmor*.so* mr, /proc/[0-9]*/attr/current w, change_profile -> i_cant_be_trusted_anymore, } =head1 BUGS None known. If you find any, please report them at L. Note that using aa_change_profile(2) without execve(2) provides no memory barriers between different areas of a program; if address space separation is required, then separate processes should be used. =head1 SEE ALSO apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_hat(2) and L. =cut apparmor-5.0.2/libraries/libapparmor/doc/aa_features.pod000066400000000000000000000165351522511161100233600ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd. # essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa_features - an opaque object representing a set of AppArmor kernel features aa_features_new - create a new aa_features object based on a path aa_features_new_from_string - create a new aa_features object based on a string aa_features_new_from_kernel - create a new aa_features object based on the current kernel aa_features_ref - increments the ref count of an aa_features object aa_features_unref - decrements the ref count and frees the aa_features object when 0 aa_features_write_to_fd - write a string representation of an aa_features object to a file descriptor aa_features_write_to_file - write a string representation of an aa_features object to a file aa_features_is_equal - equality test for two aa_features objects aa_features_supports - provides aa_features object support status aa_features_id - provides unique identifier for an aa_features object aa_features_value - the value associated with a given feature. =head1 SYNOPSIS B<#include Esys/apparmor.hE> B B B B B B B B B B B B B Link with B<-lapparmor> when compiling. =head1 DESCRIPTION The I object contains information about the AppArmor features supported by a kernel. The feature support information is based upon the files AppArmor represents in securityfs, which is typically found at /sys/kernel/security/apparmor/features/. That information may be parsed and turned into a string or flat file in order to represent a set of features of a kernel that is not currently running. The aa_features_new() function creates an I object based upon a directory file descriptor and path. The I can point to a file or directory. See the openat(2) man page for examples of I and I. The allocated I object must be freed using aa_features_unref(). The aa_features_new_from_file() function is similar except that it accepts an open file as the argument. The allocated I object must be freed using aa_features_unref(). The aa_features_new_from_string() function is similar except that it accepts a NUL-terminated string representation of the AppArmor features as the I argument. The length of the features string, not counting the NUL-terminator, must be specified as the I argument. The allocated I object must be freed using aa_features_unref(). The aa_features_new_from_kernel() function creates an I object from the current running kernel. The allocated I object must be freed using aa_features_unref(). aa_features_ref() increments the reference count on the I object. aa_features_unref() decrements the reference count on the I object and releases all corresponding resources when the reference count reaches zero. The aa_features_write_to_fd() function writes a string representation of the I object to the file descriptor specified by the I. The aa_features_write_to_file() function writes a string representation of the I object to the file specified by the I and I combination. aa_features_is_equal() can be used to detect if the I and I objects are equal. The definition of equality is private to libapparmor and may be changed in ways that do not break backward compatibility. The aa_features_supports() function can be used to query the I object to determine if a feature is supported. The I argument should be equal to the path, relative to the "apparmor/features/" directory of securityfs, of the feature to query. For example, to test if policy version 6 is supported, I would be "policy/versions/v6". The aa_features_id() function returns a string representation of an identifier that can be used to uniquely identify an I object. The mechanism for generating the string representation is internal to libapparmor and subject to change but an example implementation is applying a hash function to the features string. =head1 RETURN VALUE The aa_features_new() family of functions return 0 on success and I<*features> will point to an I object that must be freed by aa_features_unref(). -1 is returned on error, with errno set appropriately, and I<*features> will be set to NULL. aa_features_ref() returns the value of I. aa_features_write_to_file() returns 0 on success. -1 is returned on error, with errno set appropriately. aa_features_is_equal() returns true if I and I are equal and false if they are not equal. aa_features_supports() returns true if the feature represented by I is supported and false if it is not supported. aa_features_id() returns a string identifying I which must be freed by the caller. NULL is returned on error, with errno set appropriately. aa_features_value() returns a null terminated string with is associated length which must be freed by the caller. NULL is returned on error, with errno set to ENOENT if the feature was not found, ENODIR if the specified feature does not resolve to a leaf feature. =head1 ERRORS The errno value will be set according to the underlying error in the I family of functions that return -1 or NULL on error. =head1 NOTES The aa_features_id() function can be found in libapparmor version 2.13. All the other aa_feature functions described above are present in libapparmor version 2.10. aa_features_unref() saves the value of errno when called and restores errno before exiting in libapparmor version 2.12 and newer. =head1 BUGS None known. If you find any, please report them at L. =head1 SEE ALSO openat(2), aa-features-abi(1) and L. =cut apparmor-5.0.2/libraries/libapparmor/doc/aa_find_mountpoint.pod000066400000000000000000000061751522511161100247550ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd. # essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa_is_enabled - determine if apparmor is available aa_find_mountpoint - find where the apparmor interface filesystem is mounted =head1 SYNOPSIS B<#include Esys/apparmor.hE> B B Link with B<-lapparmor> when compiling. =head1 DESCRIPTION The aa_is_enabled function returns true (1) if apparmor is enabled. If it isn't it sets the errno(3) to reflect the reason it is not enabled and returns 0. The aa_find_mountpoint function finds where the apparmor filesystem is mounted on the system, and returns a string containing the mount path. It is the caller's responsibility to free(3) the returned path. =head1 RETURN VALUE B On success 1 is returned. On error, 0 is returned, and errno(3) is set appropriately. B On success zero is returned. On error, -1 is returned, and errno(3) is set appropriately. =head1 ERRORS # podchecker warns about duplicate link targets for EACCES, EBUSY, ENOENT, # and ENOMEM, but this is a warning that is safe to ignore. B =over 4 =item B AppArmor extensions to the system are not available. =item B AppArmor is available on the system but has been disabled at boot. =item B AppArmor is available but only via private interfaces. =item B AppArmor is available (and maybe even enforcing policy) but the interface is not available. =item B Insufficient memory was available. =item B Did not have sufficient permissions to determine if AppArmor is enabled. =item B Did not have sufficient permissions to determine if AppArmor is enabled. =item B AppArmor is enabled but does not have access to shared interfaces, and only private interfaces are available. =back B =over 4 =item B Insufficient memory was available. =item B Access to the required paths was denied. =item B The apparmor filesystem mount could not be found =back =head1 BUGS None known. If you find any, please report them at L. =head1 SEE ALSO apparmor(7), apparmor.d(5), apparmor_parser(8), and L. =cut apparmor-5.0.2/libraries/libapparmor/doc/aa_getcon.pod000066400000000000000000000116601522511161100230130ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd. # essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa_getprocattr_raw, aa_getprocattr - read and parse procattr data aa_getcon, aa_gettaskcon - get task confinement information aa_getpeercon - get the confinement of a socket's other end (peer) =head1 SYNOPSIS B<#include Esys/apparmor.hE> B B B B B B Link with B<-lapparmor> when compiling. =head1 DESCRIPTION The aa_getcon function gets the current AppArmor confinement context for the current task. The confinement context consists of a label and a mode. The label is usually just the name of the AppArmor profile restricting the task, but it may include the profile namespace or in some cases a set of profile names (known as a stack of profiles). The mode is a string that describes how the kernel is enforcing the policy defined in the profile. Profiles loaded in "enforce" mode will result in enforcement of the policy defined in the profile as well as reporting policy violation attempts. Profiles in "complain" mode will not enforce policy but instead report policy violation attempts. Some examples of possible returned *label strings are "unconfined", "/sbin/dhclient", and "Firefox". The string can consist of any non-NUL characters but it will be NUL-terminated. The *label string must be freed using free(). The possible *mode strings are "enforce" and "complain". Additionally, *mode may be NULL when *label is "unconfined". B. The *label and *mode strings come from a single buffer allocation and are separated by a NUL character. The aa_gettaskcon function is like the aa_getcon function except it will work for any arbitrary task in the system. The aa_getpeercon function is similar to that of aa_gettaskcon except that it returns the confinement information for task on the other end of a socket connection. The aa_getpeercon_raw function is the backend for the aa_getpeercon function and does not handle buffer allocation. The aa_getprocattr function is the backend for the aa_getcon and aa_gettaskcon functions and handles the reading and parsing of the confinement data from different arbitrary attr files and returns the processed results in an allocated buffer. The aa_getprocattr_raw() is the backend for the aa_getprocattr function and does not handle buffer allocation. =head1 RETURN VALUE On success size of data placed in the buffer is returned, this includes the mode if present and any terminating characters. On error, -1 is returned, and errno(3) is set appropriately. =head1 ERRORS =over 4 =item B The apparmor kernel module is not loaded or the communication via the F did not conform to protocol. =item B Insufficient kernel memory was available. =item B Access to the specified I was denied. =item B The specified I does not exist or is not visible. =item B The confinement data is too large to fit in the supplied buffer. =item B The kernel doesn't support the SO_PEERLABEL option in sockets. This happens mainly when the kernel lacks 'fine grained unix mediation' support. It also can happen on LSM stacking kernels where another LSM has claimed this interface and decides to return this error, although this is really a corner case. =back =head1 NOTES If aa_getpeercon_raw returns -1 and errno is ERANGE, the value of *len can be used to reallocate buf so that it is sufficiently large enough to store the confinement data. =head1 BUGS None known. If you find any, please report them at L. =head1 SEE ALSO apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2), aa_splitcon(3) and L. =cut apparmor-5.0.2/libraries/libapparmor/doc/aa_kernel_interface.pod000066400000000000000000000156511522511161100250400ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd. # essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa_kernel_interface - an opaque object representing the AppArmor kernel interface for policy loading, replacing, and removing aa_kernel_interface_new - create a new aa_kernel_interface object from an optional path aa_kernel_interface_ref - increments the ref count of an aa_kernel_interface object aa_kernel_interface_unref - decrements the ref count and frees the aa_kernel_interface object when 0 aa_kernel_interface_load_policy - load a policy from a buffer into the kernel aa_kernel_interface_load_policy_from_file - load a policy from a file into the kernel aa_kernel_interface_load_policy_from_fd - load a policy from a file descriptor into the kernel aa_kernel_interface_replace_policy - replace a policy in the kernel with a policy from a buffer aa_kernel_interface_replace_policy_from_file - replace a policy in the kernel with a policy from a file aa_kernel_interface_replace_policy_from_fd - replace a policy in the kernel with a policy from a file descriptor aa_kernel_interface_remove_policy - remove a policy from the kernel aa_kernel_interface_write_policy - write a policy to a file descriptor =head1 SYNOPSIS B<#include Esys/apparmor.hE> B B B B B B B B B B B B Link with B<-lapparmor> when compiling. =head1 DESCRIPTION The I object contains information about the AppArmor kernel interface for policy loading, replacing, and removing. The aa_kernel_interface_new() function creates an I object based on an optional I object and an optional path to the apparmor directory of securityfs, which is typically found at "/sys/kernel/security/apparmor/". If I is NULL, then the features of the current kernel are used. When specifying a valid I object, it must be compatible with the features of the currently running kernel. If I is NULL, then the default location is used. The allocated I object must be freed using aa_kernel_interface_unref(). aa_kernel_interface_ref() increments the reference count on the I object. aa_kernel_interface_unref() decrements the reference count on the I object and releases all corresponding resources when the reference count reaches zero. The aa_kernel_interface_load() family of functions load a policy into the kernel. The operation will fail if a policy of the same name is already loaded. Use the aa_kernel_interface_replace() family of functions if you wish to replace a previously loaded policy with a new policy of the same name. The aa_kernel_interface_replace() functions can also be used to load a policy that does not correspond to a previously loaded policy. When loading or replacing from a buffer, the I will contain binary data. The I argument must specify the size of the I argument. When loading or replacing from a file, the I and I combination are used to specify the location of the file. See the openat(2) man page for examples of I and I. It is also possible to load or replace from a file descriptor specified by the I argument. The file must be open for reading and the file offset must be set appropriately. The aa_kernel_interface_remove_policy() function can be used to unload a previously loaded policy. The fully qualified policy name must be specified with the I argument. The operation will fail if a policy matching I is not found. The aa_kernel_interface_write_policy() function allows for a policy, which is stored in I and consists of I bytes, to be written to a file descriptor. The I must be open for writing and the file offset must be set appropriately. =head1 RETURN VALUE The aa_kernel_interface_new() function returns 0 on success and I<*kernel_interface> will point to an I object that must be freed by aa_kernel_interface_unref(). -1 is returned on error, with errno set appropriately, and I<*kernel_interface> will be set to NULL. aa_kernel_interface_ref() returns the value of I. The aa_kernel_interface_load() family of functions, the aa_kernel_interface_replace() family of functions, aa_kernel_interface_remove(), and aa_kernel_interface_write_policy() return 0 on success. -1 is returned on error, with errno set appropriately. =head1 ERRORS The errno value will be set according to the underlying error in the I family of functions that return -1 on error. =head1 NOTES All aa_kernel_interface functions described above are present in libapparmor version 2.10 and newer. aa_kernel_interface_unref() saves the value of errno when called and restores errno before exiting in libapparmor version 2.12 and newer. =head1 BUGS None known. If you find any, please report them at L. =head1 SEE ALSO aa_features(3), openat(2) and L. =cut apparmor-5.0.2/libraries/libapparmor/doc/aa_policy_cache.pod000066400000000000000000000172771522511161100241700ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd. # essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa_policy_cache - an opaque object representing an AppArmor policy cache aa_policy_cache_new - create a new aa_policy_cache object from a path aa_policy_cache_ref - increments the ref count of an aa_policy_cache object aa_policy_cache_unref - decrements the ref count and frees the aa_policy_cache object when 0 aa_policy_cache_remove - removes all policy cache files under a path aa_policy_cache_replace_all - performs a kernel policy replacement of all cached policies aa_policy_cache_dir_path - returns the path to the aa_policy_cache directory aa_policy_cache_dir_path_preview - returns a preview of the path to the aa_policy_cache directory without an existing aa_policy_cache object =head1 SYNOPSIS B<#include Esys/apparmor.hE> B B B B B B B B B Link with B<-lapparmor> when compiling. =head1 DESCRIPTION The I object contains information about a set of AppArmor policy cache files. The policy cache files are the binary representation of a human-readable AppArmor profile. The binary representation is the form that is loaded into the kernel. The aa_policy_cache_new() function creates an I object based upon a directory file descriptor and path. See the openat(2) man page for examples of I and I. The I must point to a directory and it will be used as the basis for the location of policy cache files. See I to find out which directory will be used to store the binary policy cache files. If additional overlay cache directories are used (see I) the directory specified in I is the first directory searched and is the writable overlay. If I is NULL, then the features of the current kernel are used. When specifying a valid I object, it must be compatible with the features of the kernel of interest. The value of I should be equal to the number of caches that should be allowed before old caches are automatically reaped. The definition of what is considered to be an old cache is private to libapparmor. Specifying 0 means that no new caches should be created and only existing, valid caches may be used. Specifying UINT16_MAX means that a new cache may be created and that the reaping of old caches is disabled. The allocated I object must be freed using aa_policy_cache_unref(). The aa_policy_cache_add_ro_dir() function adds an existing cache directory to the policy cache, as a readonly layer under the primary directory the cache was created with. When the cache is searched for an existing cache file the primary directory will be searched and then the readonly directories in the order that they were added to the policy cache. This allows the policy cache to be seeded with precompiled policy that can be updated by overlaying the read only cache file with one written to the primary cache dir. aa_policy_cache_ref() increments the reference count on the I object. aa_policy_cache_unref() decrements the reference count on the I object and releases all corresponding resources when the reference count reaches zero. The aa_policy_cache_remove() function deletes all of the policy cache files based upon a directory file descriptor and path. The I must point to a directory. See the openat(2) man page for examples of I and I. The aa_policy_cache_replace_all() function can be used to perform a policy replacement of all of the cache policies in the cache directory represented by the I object. If I is NULL, then the current kernel interface is used. When specifying a valid I object, it must be the interface of the currently running kernel. The aa_policy_cache_dir_path() function provides the path to the cache directory for a I object at I in the policy cache overlay of cache directories. A I of 0 will always be present and is the first directory to search in an overlay of cache directories, and will also be the writable cache directory layer. Binary policy cache files will be located in the directory returned by this function. The aa_policy_cache_dir_levels() function provides access to the number of directories that are being overlaid to create the policy cache. =head1 RETURN VALUE The aa_policy_cache_new() function returns 0 on success and I<*policy_cache> will point to an I object that must be freed by aa_policy_cache_unref(). -1 is returned on error, with errno set appropriately, and I<*policy_cache> will be set to NULL. aa_policy_cache_ref() returns the value of I. aa_policy_cache_remove() and aa_policy_cache_replace_all() return 0 on success. -1 is returned on error, with errno set appropriately. aa_policy_cache_dir_path() returns a path string which must be freed by the caller. NULL is returned on error, with errno set appropriately. aa_policy_cache_dir_levels() returns a number indicating the number of directory levels there are associated with the I. aa_policy_cache_dir_path_preview() is the same as aa_policy_cache_dir_path() except that it doesn't require an existing I object. This is useful if the calling program cannot create an I object due to lack of privileges needed to create the cache directory. =head1 ERRORS The errno value will be set according to the underlying error in the I family of functions that return -1 or NULL on error. =head1 NOTES All aa_policy_cache functions described above, except for the aa_policy_cache_dir_path() function was added in libapparmor version 2.13. All the other aa_policy_cache functions described above are present in libapparmor version 2.10. aa_policy_cache_unref() saves the value of errno when called and restores errno before exiting in libapparmor version 2.12 and newer. =head1 BUGS None known. If you find any, please report them at L. =head1 SEE ALSO aa_features(3), aa_kernel_interface(3), openat(2) and L. =cut apparmor-5.0.2/libraries/libapparmor/doc/aa_query_label.pod000066400000000000000000000112411522511161100240330ustar00rootroot00000000000000# This publication is intellectual property of Canonical Ltd. Its contents # can be duplicated, either in part or in whole, provided that a copyright # label is visibly located on each copy. # # All information found in this book has been compiled with utmost # attention to detail. However, this does not guarantee complete accuracy. # Neither Canonical Ltd, the authors, nor the translators shall be held # liable for possible errors or the consequences thereof. # # Many of the software and hardware descriptions cited in this book # are registered trademarks. All trade names are subject to copyright # restrictions and may be registered trade marks. Canonical Ltd. # essentially adhere to the manufacturer's spelling. # # Names of products and trademarks appearing in this book (with or without # specific notation) are likewise subject to trademark and trade protection # laws and may thus fall under copyright restrictions. # =pod =head1 NAME aa_query_label - query access permission associated with a label aa_query_file_path, aa_query_file_path_len - query access permissions of a file path aa_query_link_path, aa_query_link_path_len - query access permissions of a link path =head1 SYNOPSIS B<#include Esys/apparmor.hE> B B B B B Link with B<-lapparmor> when compiling. =head1 DESCRIPTION The B function fetches the current permissions granted by the specified I - append -- conflicts with write =item B - unconfined execute =item B - unconfined execute -- use ld.so(8) secure-execution mode =item B - discrete profile execute =item B - discrete profile execute -- use ld.so(8) secure-execution mode =item B - transition to subprofile on execute =item B - transition to subprofile on execute -- use ld.so(8) secure-execution mode =item B - inherit execute =item B - discrete profile execute with inherit fallback =item B - discrete profile execute with inherit fallback -- use ld.so(8) secure-execution mode =item B - transition to subprofile on execute with inherit fallback =item B - transition to subprofile on execute with inherit fallback -- use ld.so(8) secure-execution mode =item B - discrete profile execute with fallback to unconfined =item B - discrete profile execute with fallback to unconfined -- use ld.so(8) secure-execution mode =item B - transition to subprofile on execute with fallback to unconfined =item B - transition to subprofile on execute with fallback to unconfined -- use ld.so(8) secure-execution mode =item B - disallow execute (in rules with the deny qualifier) =item B - allow PROT_EXEC with mmap(2) calls =item B - link =item B - lock =back =head2 Access Modes Details =over 4 =item B Allows the program to have read access to the file or directory listing. Read access is required for shell scripts and other interpreted content. =item B Allows the program to have write access to the file. Files and directories must have this permission if they are to be unlinked (removed.) Write mode is not required on a directory to rename or create files within the directory. This mode conflicts with append mode. =item B Allows the program to have a limited appending only write access to the file. Append mode will prevent an application from opening the file for write unless it passes the O_APPEND parameter flag on open. The mode conflicts with Write mode. =item B Allows the program to execute the program without any AppArmor profile being applied to the program. This mode is useful when a confined program needs to be able to perform a privileged operation, such as rebooting the machine. By placing the privileged section in another executable and granting unconfined execution rights, it is possible to bypass the mandatory constraints imposed on all confined processes. For more information on what is constrained, see the apparmor(7) man page. B 'ux' should only be used in very special cases. It enables the designated child processes to be run without any AppArmor protection. 'ux' does not use ld.so(8) secure-execution mode to clear variables such as LD_PRELOAD; as a result, the calling domain may have an undue amount of influence over the callee. Use this mode only if the child absolutely must be run unconfined and LD_PRELOAD must be used. Any profile using this mode provides negligible security. Use at your own risk. Incompatible with other exec transition modes and the deny qualifier. =item B 'Ux' allows the named program to run in 'ux' mode, but AppArmor will invoke the Linux Kernel's B routines to set ld.so(8) secure-execution mode and clear environment variables such as LD_PRELOAD, similar to setuid programs. (See ld.so(8) for more information.) B 'Ux' should only be used in very special cases. It enables the designated child processes to be run without any AppArmor protection. Use this mode only if the child absolutely must be run unconfined. Use at your own risk. Incompatible with other exec transition modes and the deny qualifier. =item B This mode requires that a discrete security profile is defined for a program executed and forces an AppArmor domain transition. If there is no profile defined then the access will be denied. B 'px' does not use ld.so(8) secure-execution mode to clear variables such as LD_PRELOAD; as a result, the calling domain may have an undue amount of influence over the callee. Incompatible with other exec transition modes and the deny qualifier. =item B 'Px' allows the named program to run in 'px' mode, but AppArmor will invoke the Linux Kernel's B routines to set ld.so(8) secure-execution mode and clear environment variables such as LD_PRELOAD, similar to setuid programs. (See ld.so(8) for more information.) Incompatible with other exec transition modes and the deny qualifier. =item B This mode requires that a local security profile is defined and forces an AppArmor domain transition to the named profile. If there is no profile defined then the access will be denied. B 'cx' does not use ld.so(8) secure-execution mode to clear variables such as LD_PRELOAD; as a result, the calling domain may have an undue amount of influence over the callee. Incompatible with other exec transition modes and the deny qualifier. =item B 'Cx' allows the named program to run in 'cx' mode, but AppArmor will invoke the Linux Kernel's B routines to set ld.so(8) secure-execution mode and clear environment variables such as LD_PRELOAD, similar to setuid programs. (See ld.so(8) for more information.) Incompatible with other exec transition modes and the deny qualifier. =item B Prevent the normal AppArmor domain transition on execve(2) when the profiled program executes the named program. Instead, the executed resource will inherit the current profile. This mode is useful when a confined program needs to call another confined program without gaining the permissions of the target's profile, or losing the permissions of the current profile. There is no version to set secure-execution mode because 'ix' executions don't change privileges. Incompatible with other exec transition modes and the deny qualifier. =item B These modes attempt to perform a domain transition as specified by the matching permission (shown below) and if that transition fails to find the matching profile the domain transition proceeds using the 'ix' transition mode. 'Pix' == 'Px' with fallback to 'ix' 'pix' == 'px' with fallback to 'ix' 'Cix' == 'Cx' with fallback to 'ix' 'cix' == 'cx' with fallback to 'ix' Incompatible with other exec transition modes and the deny qualifier. =item B These modes attempt to perform a domain transition as specified by the matching permission (shown below) and if that transition fails to find the matching profile the domain transition proceeds using the 'ux' transition mode if 'pux', 'cux' or the 'Ux' transition mode if 'PUx', 'CUx' is used. 'PUx' == 'Px' with fallback to 'Ux' 'pux' == 'px' with fallback to 'ux' 'CUx' == 'Cx' with fallback to 'Ux' 'cux' == 'cx' with fallback to 'ux' Incompatible with other exec transition modes and the deny qualifier. =item B For rules including the deny modifier, only 'x' is allowed to deny execute. The 'ix', 'Px', 'px', 'Cx', 'cx' and the fallback modes conflict with the deny modifier. =item B The directed ('px', 'Px', 'pix', 'Pix', 'pux', 'PUx') profile and subprofile ('cx', 'Cx', 'cix', 'Cix', 'cux', 'CUx') transitions normally determine the profile to transition to from the executable name. It is however possible to specify the name of the profile that the transition should use. The name of the profile to transition to is specified using the '-E' followed by the name of the profile to transition to. Eg. /bin/** px -> profile, Incompatible with other exec transition modes. =item B This mode allows a file to be mapped into memory using mmap(2)'s PROT_EXEC flag. This flag marks the pages executable; it is used on some architectures to provide non-executable data pages, which can complicate exploit attempts. AppArmor uses this mode to limit which files a well-behaved program (or all programs on architectures that enforce non-executable memory access controls) may use as libraries, to limit the effect of invalid B<-L> flags given to ld(1) and B, B, given to ld.so(8). =item B Allows the program to be able to create a link with this name. When a link is created, the new link B have a subset of permissions as the original file (with the exception that the destination does not have to have link access.) If there is an 'x' rule on the new link, it must match the original file exactly. =item B Allows the program to be able lock a file with this name. This permission covers both advisory and mandatory locking. =item B File rules can be specified with the access permission either leading or trailing the file glob. Eg. rw /**, # leading permissions /** rw, # trailing permissions When leading permissions are used further rule options and context may be allowed, Eg. l /foo -> /bar, # lead 'l' link permission is equivalent to link rules =back =head2 Link rules Link rules allow specifying permission to form a hard link as a link target pair. If the subset condition is specified then the permissions to access the link file must be a subset of the profiles permissions to access the target file. If there is an 'x' rule on the new link, it must match the original file exactly. Eg. /file1 r, /file2 rwk, /link* rw, link subset /link* -> /**, The link rule allows linking of /link to both /file1 or /file2 by name however because the /link file has 'rw' permissions it is not allowed to link to /file1 because that would grant an access path to /file1 with more permissions than the 'r' permissions the profile specifies. A link of /link to /file2 would be allowed because the 'rw' permissions of /link are a subset of the 'rwk' permissions for /file1. The link rule is equivalent to specifying the 'l' link permission as a leading permission with no other file access permissions. When this is done the link rule options can be specified. The following link rule is equivalent to the 'l' permission file rule link /foo -> bar, l /foo -> /bar, File rules that specify the 'l' permission and don't specify the extend link permissions map to link rules as follows. /foo l, l /foo, link subset /foo -> /**, =head2 Comments Comments start with # and may begin at any place within a line. The comment ends when the line ends. This is the same comment style as shell scripts. =head2 Capabilities The only capabilities a confined process may use may be enumerated; for the complete list, please refer to capabilities(7). Note that granting some capabilities renders AppArmor confinement for that domain advisory; while open(2), read(2), write(2), etc., will still return error when access is not granted, some capabilities allow loading kernel modules, arbitrary access to IPC, ability to bypass discretionary access controls, and other operations that are typically reserved for the root user. =head2 Network Rules AppArmor supports simple coarse grained network mediation. The network rule restrict all socket(2) based operations. The mediation done is a coarse-grained check on whether a socket of a given type and family can be created, read, or written. Network netlink(7) rules may only specify type 'dgram' and 'raw'. AppArmor network rules are accumulated so that the granted network permissions are the union of all the listed network rule permissions. AppArmor network rules are broad and general and become more restrictive as further information is specified. eg. network, #allow access to all networking network tcp, #allow access to tcp network inet tcp, #allow access to tcp only for inet4 addresses network inet6 tcp, #allow access to tcp only for inet6 addresses network netlink raw, #allow access to AF_NETLINK SOCK_RAW =head3 Network permissions Network rule permissions are implied when a rule does not explicitly state an access list. By default if a rule does not have an access list all permissions that are compatible with the specified set of local and peer conditionals are implied. The create, bind, listen, shutdown, getattr, setattr, getopt, and setopt permissions are local socket permissions. They are only applied to the local socket and can't be specified in rules that have a peer conditional. The accept permission applies to the combination of a local and peer socket. The connect, send, and receive permissions are peer socket permissions. =head3 Mediation of inet/inet6 family AppArmor supports fine grained mediation of the inet and inet6 families by using the ip and port conditionals. The ip conditional accepts both IPv4 and IPv6 using the regular representation of four octets separated by '.' for IPv4 and eight groups of four hexadecimal numbers separated by ':' for IPv6. Contiguous leading zeros can be replaced by '::' once. On a connected socket, the sender and receiver don't need to be specified in the recvfrom and sendto system calls. In that case, and with unbounded sockets, the IP address is none, or unknown. Unknown or Unbound IP addresses are represented in policy by the 'none' keyword. When the ip conditional is omitted, then all IP addresses will be allowed: IPv4, IPv6 and none. If INADDR_ANY or in6addr_any is used, then the ip conditional can be omitted or they can be represented by: network ip=::, #allow in6addr_any network ip=0.0.0.0; #allow INADDR_ANY The network rules support the specification of local and remote IP addresses, ports, and port ranges. network ip=127.0.0.1 port=8080, network peer=(ip=10.139.15.23 port=8081), network ip=fd74:1820:b03a:b361::cf32 peer=(ip=fd74:1820:b03a:b361::a0f9), network port=8080 peer=(port=8081), network ip=127.0.0.1 port=8080 peer=(ip=10.139.15.23 port=8081), network ip=127.0.0.1 port=8080-8084, =head4 Network Interface Restrictions If kernel support is enabled, it is possible to restrict network access based on the interface that communication can be sent or received on by specifying the I conditional. eg. network inet interface=eth0 port=8080, The interface conditional is limited to mediating the acceptance of connections, and the sending, and receiving of data. If a rule does not specify the interface conditional, it does not restrict the interface that can be used. The sending side of the interface conditional makes use of packet labeling. Currently this is limited to the use of the SecMark. The use of the SecMark has several complications and limitations, which are discussed in the Limitations, and SecMark sections. The receiving task can also mediate the interface that a packet arrives on. Packet labeling is not needed for inbound interface mediation. This means that interface mediation of in bound connections, and messages has fewer limitations than outbound mediation. =head5 Limitations of the Interface conditional There are several restrictions around interface mediation. For the interface conditional to function AppArmor must be compiled with support for SecMarks, NetFilter, and the kernel AppArmor module must also support the mediation. If any of these conditions are not met then the network interface rule will be downgraded, or ignored. The rule downgrade can be turned into a compile time error using Werror. It is possible for the above limitations to be met and for AppArmor to still not enforce outbound interface mediation. The SecMark interface supports only a single LSM, if another LSM claims the interface before AppArmor during boot, AppArmor will not be able to enforce the outbound interface conditional. =head6 Socket Address Family Restrictions The interface conditional is limited to the inet, and inet6 address families, and currently only uses the SecMark label, this means the packet label is limited to use on host and is not carried across the network. =head6 Profile Flags and Rule Prefix Moodifier Limitations =over 8 =item B: The audit profile flag may not trigger unless the profile contains rules assigning the profile label, or matching an interface. The audit prefix on a rule with the interface conditional does not have this limitation. =item B: The complain profile flag may not trigger unless the profile contains rules assigning the profile label, or matching the interface. The complain prefix on a rule with the interface conditional does not have this limitation. =item B: The interface conditional is incompatible with the prompt rule prefix, and the prompt flag will not trigger for interface failures. =item B: The interface conditional is incompatible with the kill rule prefix, and profile flag for inbound packets. =item B: The interface conditional is incompatible with the halt rule prefix, and profile flag for inbound packets. =back =head6 Interaction with Firewalls The outbound interface conditional interacts with and potentially conflicts with explicit firewall packet labeling that uses the SecMark. This is because the interface conditional uses the label on the outbound packet to mediate which interface the packet is allowed to be sent to. Explicitly setting the label via a firewall rule can change the label on the packet and potentially cause the interface conditional to not be applied, or even cause the wrong interface mediation to be applied. Where possible, when multiple labels are specified, AppArmor will assign a label stack to the packet ensuring the interface conditional is applied, and if it can't do that it will raise a policy compilation error. See the SecMark section for information on how the interface restrictions interact with NetFilter and firewall rules. =head6 Additional Information for Firewalls The interface mediation may be done via SecMark packet labeling in the NetFilter B hook. If it is done via SecMark, any other firewall rule run before the AppArmor hook in the B hook may result in interface mediation not working correctly. Interface mediation is done in the B netfilter hook, and is subject to any changes a previous hook may have done to packet routing information. That is to say it is possible that some firewall rule type applications could interfere with AppArmor interface mediation. =head4 Label Match Conditional The label match conditional allows specifying the label that a received packet is allowed to have, or in the case of socket based operations the label that the socket has. The label match is performed by matching the specified label pattern against the SecMark (or any other future supported type of label) on the packet, or in the case of socket operations the label on the socket. The local label match conditional is the conditional used for socket label matching. network label=foo, The peer label match conditional is the conditional used when matching in coming network packet labels. network receive peer=(label=foo), network inet peer=(label=httpd), network ip=127.0.0.1 peer=(label=httpd), The label on a socket is set by the confinement of the task that opened the socket, and is potentially extended if the socket is passed to other tasks with different confinement. The label on a packet (currently just the SecMark) can be set by an AppArmor rule (interface or explicit assignment), or by system firewalls like iptables. For the purposes of network packet label matching if a packet does not have a label (SecMark) it will be associated with the special label of I. =head5 Notes on label matching AppArmor will only do packet label matching when profiles have explicit rules supporting it. Otherwise AppArmor will short circuit label match mediation, reducing the overhead. For packet label matching, the socket label is used as a proxy for the receiving task. Generally this does not affect mediation however, if a socket is shared between tasks with different confinement, what is allowed will be the intersection of what is allowed by all profiles in the socket label. This is because AppArmor does not control which task will receive the packet, and so it can only allow what is allowed by all tasks that could potentially receive the packet. Similarly, unless the interface match conditional is used or an explicit label assignment rule is used, AppArmor does not set the label on packets to avoid any overhead when it is not necessary. =head4 Specifying Socket and Packet Labeling AppArmor by default will label sockets with the creating tasks confinement. This labeling may be extended to a label stack when the socket is shared between tasks of different confinement. AppArmor by default does not label network packets, as that can have performance implications, and it is not always needed. Instead AppArmor tries to be smart about packet labeling on applying and matching labels when needed to support the specified rules. =head5 Implicit packet labeling Some rules will result in a packet being given the label of the sending task. Eg. the interface conditional will implicitly label outgoing packets with the tasks confinement (note: in some cases the sockets label may be used as a proxy). Implicitly labeling packets works if you are only considering AppArmor network rules. However if the admin would like to also be able to control packets based on AppAmor labeling at the firewall then implicit labeling controls are not sufficient, and explicit labeling must be used. =head5 Explicit labeling AppArmor allows specifying an explicit label for both the socket and the packet, using the assignment conditional. =head6 explicit socket labeling For the socket it the local assignment conditional is used. network label:=@{profile_name}, In this case the use of @{profile_name} is equivalent to the implicit labeling that AppArmor already does. =head6 explicit packet labeling For packet labels the peer assignment condition is used. network send inet peer=(label:=@{profile_name}), In this case the label set is the same label that implied labeling set except that it will be set on every network packet, not just the ones implied by the interface conditional. Explicit labeling can interact with and conflict with implied labeling so current AppArmor only supports setting the explicit label to the same label that implied labeling would provide, thus avoiding the conflict. There are plans to extend explicit labeling to allow specifying any label but that is not available yet. =head5 kernel socket labeling Packets sent by the kernel may either have the type of I or I dependent on whether AppArmor is configured to label kernel packets or not. If labeling of kernel packets is enabled, AppArmor will label all kernel packets. This can have a small impact on network performance so, for those systems that need it, kernel socket labeling can be disabled. =head4 SecMark, NetFilter, and Firewalls When AppArmor profiles are attached to processes the network rules can be thought of as an application level firewall, handling network traffic in the context of the application. System level firewalling is generally left to existing firewalls that admins are already familiar with. However the AppArmor application firewall can and will interact with the system firewall, and it is important to understand how and where this interaction can occur. In Linux, firewalls are built on top of the kernel's NetFilter subsystem which provides five different hook points to manage the flow of packets in the system: PRE_ROUTING, LOCAL_IN, FORWARD, LOCAL_OUT, and POST_ROUTING. In addition it provides some fields for the firewall, network, and security subsystems to store data on the packet as it traverses through the system. +-------------------------Loop Back<------------------------+ | | v ^ Dev -->PRE_ROUTING--->[ROUTE]--->FWD-------->POST_ROTUING--> Dev | | | ^ | Conntrack | Mangle | Mangle Mangle | Filter | NAT (Src) NAT (Dst) | | Conntrack (QDisc) | [ROUTE] AppArmor Interface | | AppArmor Label Match V | Conntrack----LOCAL_IN LOCAL OUT---AppArmor SecMark Mangle | ^ Conntrack AppArmor SecMark | | Mangle | | NAT (Dst) v | Filter apparmor_sock_rcv_skb | apparmor_inet_conn_request | | | | | v ^ Process Process AppArmor uses SecMark, and ConnSecMark, to pass state information with the packet as it is passed through the system. In addition it uses several of the hook points to make mediation decisions. The interface conditional does not allow explicitly setting the SecMark. Instead it is set based on the confining label of the sending task. More specifically, in a stacking situation it's set based on the subset of profiles in the label with matching interface conditionals. It may be possible to set the SecMark explicitly in the future but it is not supported at this time. AppArmor does not currently support explicitly specifying a SecMark match conditional (label=) match at this time either. Though that ability will be made available in a future patch. However firewalls are free to match the SecMark if AppArmor sets it. AppArmor's SecMark support has several restrictions, and limitations. AppArmor must be built with SecMark support enabled, and it shares the SecMark with all other LSMs on the system, as such AppArmor must be able to claim the SecMark interface on boot. This means AppArmor must be in the B security module list before any other LSM that may try to claim the interface. AppArmor netfilter hooks are executed with a SELINUX_FISRT priority, so they will be executed before most firewall rules. However it is possible to create hooks filters that will compete with and may fire before the AppArmor code. AppArmor will set the secmark to the subject label in the B hook. It will filter interfaces based on the secmark in the B and security_sock_rcv_skb hook (roughly that same as LOCAL_INPUT for firewalls. =head3 Medidation of the unix family See B for both socket and fs based unix socket mediation. =head2 Unix socket rules AppArmor supports fine grained mediation of unix domain abstract and anonymous sockets. Unix domain sockets with file system paths are mediated via file access rules. Abstract unix domain sockets is a nonportable Linux extension of unix domain sockets, see unix(7) for more information. =head3 Unix socket address paths The sun_path component (aka the socket address) of a unix domain socket is specified by the addr= conditional. If an address conditional is not specified as part of a rule then the rule matches both abstract and anonymous sockets. In AppArmor the address of an abstract unix domain socket begins with the I<@> character, similar to how they are reported (as paths) by netstat -x. The address then follows and may contain pattern matching and any characters including the null character. In AppArmor null characters must be specified by using an escape sequence I<\000> or I<\x00>. The pattern matching is the same as is used by file path matching so * will not match I even though it has no special meaning within an abstract socket name. Eg. unix addr=@*, Autobound unix domain sockets have a unix sun_path assigned to them by the kernel, as such specifying a policy based address is not possible. The autobinding of sockets can be controlled by specifying the special I keyword. Eg. unix addr=auto, To indicate that the rule only applies to auto binding of unix domain sockets. It is important to note this only applies to the I permission as once the socket is bound to an address it is indistinguishable from a socket that have an addr bound with a specified name. When the I keyword is used with other permissions or as part of a peer addr it will be replaced with a pattern that can match an autobound socket. Eg. For some kernels unix rw addr=auto, is transformed to unix rw addr=@[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9], It is important to note, this pattern may match abstract sockets that were not autobound but have an addr that fits what is generated by the kernel when autobinding a socket. Anonymous unix domain sockets have no sun_path associated with the socket address, however it can be specified with the special I keyword to indicate the rule only applies to anonymous unix domain sockets. Eg. unix addr=none, If the address component of a rule is not specified then the rule applies to autobind, abstract and anonymous sockets. =head3 Unix socket permissions Unix domain socket rules are accumulated so that the granted unix socket permissions are the union of all the listed unix rule permissions. Unix domain socket rules are broad and general and become more restrictive as further information is specified. Policy may be specified down to the socket address (aka sun_path) and label level. The content of the communication is not examined. Unix socket rule permissions are implied when a rule does not explicitly state an access list. By default if a rule does not have an access list all permissions that are compatible with the specified set of local and peer conditionals are implied. The create, bind, listen, shutdown, getattr, setattr, getopt, and setopt permissions are local socket permissions. They are only applied to the local socket and can't be specified in rules that have a peer component. The accept permission applies to the combination of a local and peer socket. The connect, send, and receive permissions are peer socket permissions. Only the peer socket permissions will be applied to rules that don't specify permissions and contain a peer component. =head3 Example Unix domain socket rules: # Allow all permissions to unix sockets unix, # Explicitly allow all unix permissions unix (create, listen, accept, connect, send, receive, getattr, setattr, setopt, getopt), # Explicitly deny unix socket access deny unix, # Allow create and use of abstract and anonymous sockets for profile_name unix peer=(label=@{profile_name}), # Allow receiving via unix sockets from unconfined unix (receive) peer=(label=unconfined), # Allow getattr and shutdown on anonymous sockets unix (getattr, shutdown) addr=none, # Allow SOCK_STREAM connect, receive and send on an abstract socket @bar # with peer running under profile '/foo' unix (connect, receive, send) type=stream peer=(label=/foo,addr="@bar"), # Allow accepting connections from and receiving from peer running under # profile '/bar' on abstract socket '@foo' unix (accept, receive) addr=@foo peer=(label=/bar), =head3 Abstract unix domain sockets autobind Abstract unix domain sockets can autobind to an address. The autobind address is a unique 5 digit string of decimal numbers, eg. @00001. There is nothing that prevents a task from manually binding to addresses with a similar pattern so it is impossible to reliably identify autobind addresses from a regular address. =head3 Interaction of network rules and fine grained unix domain socket rules The coarse grained networking rules can be used to control unix domain sockets as well. When fine grained unix domain socket mediation is available the coarse grained network rule is mapped into the equivalent unix socket rule. E.G. network unix, => unix, network unix stream, => unix stream, Fine grained mediation rules however can not be losslessly converted back to the coarse grained network rule; e.g. unix bind addr=@example, Has no exact match under coarse grained network rules, the closest match is the much wider permission rule of network unix, =head2 Mount Rules AppArmor supports mount mediation and allows specifying filesystem types and mount flags. The syntax of mount rules in AppArmor is based on the mount(8) command syntax. Mount rules must contain one of the mount, remount or umount keywords, but all mount conditions are optional. Unspecified optional conditionals are assumed to match all entries (eg, not specifying fstype means all fstypes are matched). Due to the complexity of the mount command and how options may be specified, AppArmor allows specifying conditionals three different ways: =over 4 =item 1. If a conditional is specified using '=', then the rule only grants permission for mounts matching the exactly specified options. For example, an AppArmor policy with the following rule: mount options=ro /dev/foo -> /mnt/, Would match: $ mount -o ro /dev/foo /mnt but not either of these: $ mount -o ro,atime /dev/foo /mnt $ mount -o rw /dev/foo /mnt =item 2. If a conditional is specified using 'in', then the rule grants permission for mounts matching any combination of the specified options. For example, if an AppArmor policy has the following rule: mount options in (ro,atime) /dev/foo -> /mnt/, all of these mount commands will match: $ mount -o ro /dev/foo /mnt $ mount -o ro,atime /dev/foo /mnt $ mount -o atime /dev/foo /mnt but none of these will: $ mount -o ro,sync /dev/foo /mnt $ mount -o ro,atime,sync /dev/foo /mnt $ mount -o rw /dev/foo /mnt $ mount -o rw,noatime /dev/foo /mnt $ mount /dev/foo /mnt =item 3. If multiple conditionals are specified in a single mount rule, then the rule grants permission for each set of options. This provides a shorthand when writing mount rules which might help to logically break up a conditional. For example, if an AppArmor policy has the following rule: mount options=ro options=atime, both of these mount commands will match: $ mount -o ro /dev/foo /mnt $ mount -o atime /dev/foo /mnt but this one will not: $ mount -o ro,atime /dev/foo /mnt =back Note that separate mount rules are distinct and the options do not accumulate. For example, these AppArmor mount rules: mount options=ro, mount options=atime, are not equivalent to either of these mount rules: mount options=(ro,atime), mount options in (ro,atime), To help clarify the flexibility and complexity of mount rules, here are some example rules with accompanying matching commands: =over 4 =item B the 'mount' rule without any conditionals is the most generic and allows any mount. Equivalent to 'mount fstype=** options=** ** -E /**'. =item B allow mounting of /dev/foo anywhere with any options. Some matching mount commands: $ mount /dev/foo /mnt $ mount -t ext3 /dev/foo /mnt $ mount -t vfat /dev/foo /mnt $ mount -o ro,atime,noexec,nodiratime /dev/foo /srv/some/mountpoint =item B allow mounting of /dev/foo anywhere, as read only. Some matching mount commands: $ mount -o ro /dev/foo /mnt $ mount -o ro /dev/foo /some/where/else =item B allow mount of /dev/foo anywhere, as read only and using inode access times. Some matching mount commands: $ mount -o ro,atime /dev/foo /mnt $ mount -o ro,atime /dev/foo /some/where/else =item B allow mount of /dev/foo anywhere using some combination of 'ro' and 'atime' (see above). Some matching mount commands: $ mount -o ro /dev/foo /mnt $ mount -o atime /dev/foo /some/where/else $ mount -o ro,atime /dev/foo /some/other/place =item B allow mount of /dev/foo anywhere as read only, and allow mount of /dev/foo anywhere using inode access times. Note this is expressed as two different rules. Matches: $ mount -o ro /dev/foo /mnt/1 $ mount -o atime /dev/foo /mnt/2 =item B<< mount -E /mnt/**, >> allow mounting anything under a directory in /mnt/**. Some matching mount commands: $ mount /dev/foo1 /mnt/1 $ mount -o ro,atime,noexec,nodiratime /dev/foo2 /mnt/deep/path/foo2 =item B<< mount options=ro -E /mnt/**, >> allow mounting anything under /mnt/**, as read only. Some matching mount commands: $ mount -o ro /dev/foo1 /mnt/1 $ mount -o ro /dev/foo2 /mnt/deep/path/foo2 =item B<< mount fstype=ext3 options=(rw,atime) /dev/sdb1 -E /mnt/stick/, >> allow mounting an ext3 filesystem in /dev/sdb1 on /mnt/stick as read/write and using inode access times. Matches only: $ mount -o rw,atime /dev/sdb1 /mnt/stick =item B<< mount options=(ro, atime) options in (nodev, user) /dev/foo -E /mnt/, >> allow mounting /dev/foo on /mnt/ read only and using inode access times, in addition to allowing some combination of 'nodev' and 'user' to be added on top. Matches only: $ mount -o ro,atime /dev/foo /mnt $ mount -o ro,atime,nodev /dev/foo /mnt $ mount -o ro,atime,user /dev/foo /mnt $ mount -o ro,atime,nodev,user /dev/foo /mnt =back =head2 Message Queue rules AppArmor supports mediation of POSIX and SYSV message queues. AppArmor Message Queue permissions are implied when a rule does not explicitly state an access list. By default, all Message Queue permissions are implied. AppArmor Message Queue permissions become more restricted as further information is specified. Policy can be specified by determining its access mode, type, label, and message queue name. Regarding access modes, 'r' and 'read' are used to read messages from the queue. 'w' and 'write' are used to write to the message queue. 'create' is used to create the message queue, and 'open' is used to get the message queue identifier when the queue is already created. 'delete' is used to remove the message queue. The access modes to get and set attributes of the message queue are 'setattr' and 'getattr'. The type of the policy can be either 'posix' or 'sysv'. This information is relevant when the message queue name is not specified, and when specified can be inferred by the queue name, since message queues' name for posix must start with '/', and message queues' key for SYSV must be a positive integer. The policy label is the label assigned to the message queue when it is created. The message queue name can be either a string starting with '/' if the type is POSIX, or a positive integer if the type is SYSV. If the type is not specified, then it will be inferred by the queue name. Example AppArmor Message Queue rules: # Allow all Message Queue access mqueue, # Explicitly allow all Message Queue access, mqueue (create, open, delete, read, write, getattr, setattr), # Explicitly deny use of Message Queue deny mqueue, # Allow all access for POSIX queue of name /bar mqueue type=posix /bar, # Allow create permission for a SYSV queue of label foo mqueue create label=foo 123, =head2 User Namespace Rules User namespaces are part of many sandboxing and containerization solutions. They provide a way for a non-system root process to be root within the container. Unfortunately this opens up attack surface in the kernel and has been part of several exploit chains. As such AppArmor can be used to restrict the creation of user namespaces to select processes. User namespace permission are implied when a rule does not explicitly state an access list. The rule becomes more restrictive as further information is specified. Note: user namespace creation may be restricted so that it is not available to unprivieged unconfined processes. If this is the case any process trying to create user namespaces will require a profile that allows the necessary permissions. =over 4 =item B Allow creation of user namespaces. =back Example userns rules: =over 4 # Allow all userns perms userns, # Allow creation of a userns userns create, =back =head2 IO_URing Rules AppArmor supports mediation of the new Linux high speed IO interface. There is limited mediation at this time to just a few permissions at the moment. IO Uring permission are implied when a rule does not explicitly state an access list. The rule becomes more restrictive as further information is specified. Note: io_uring access may be restricted so that it is not available to unprivileged unconfined processes. If this is the case any process trying to use io_uring will require a profile that allows the necessary io_uring permissions. =over 4 =item B All the task confined by the profile to spawn a io_uring polling thread. =item B Grants the task confined by the profile to override (change) its credentials to the specified label, when executing an io_uring operation. =back Example IO_URING rules: =over 4 # Allow io_uring operations io_uring, # Allow creation of a polling thread io_uring sqpoll, # Allow task to override credentials during io_uring operation io_uring override_creds label=new_creds, =back =head2 Pivot Root Rules AppArmor mediates changing of the root filesystem through the pivot_root(2) system call. The syntax of 'pivot_root' rules in AppArmor is based on the pivot_root(2) system call parameters with the notable exception that the ordering is reversed. The path corresponding to the put_old parameter of pivot_root(2) is optionally specified in the 'pivot_root' rule using the 'oldroot=' prefix. AppArmor 'pivot_root' rules can specify a profile transition to occur during the pivot_root(2) system call. Note that currently, this feature is not supported by any kernel. When this feature will be supported, AppArmor will only transition the process calling pivot_root(2) to the new profile. The paths specified in 'pivot_root' rules must end with '/' since they are directories. Here are some example 'pivot_root' rules: # Allow any pivot pivot_root, # Allow pivoting to any new root directory and putting the old root # directory at /mnt/root/old/ pivot_root oldroot=/mnt/root/old/, # Allow pivoting the root directory to /mnt/root/ pivot_root /mnt/root/, # Allow pivoting to /mnt/root/ and putting the old root directory at # /mnt/root/old/ pivot_root oldroot=/mnt/root/old/ /mnt/root/, # Allow pivoting to /mnt/root/, putting the old root directory at # /mnt/root/old/ and transition to the /mnt/root/sbin/init profile pivot_root oldroot=/mnt/root/old/ /mnt/root/ -> /mnt/root/sbin/init, =head2 PTrace rules AppArmor supports mediation of ptrace(2). AppArmor PTrace rules are accumulated so that the granted PTrace permissions are the union of all the listed PTrace rule permissions. AppArmor PTrace permissions are implied when a rule does not explicitly state an access list. By default, all PTrace permissions are implied. The trace and tracedby permissions govern ptrace(2) while read and readby govern certain proc(5) filesystem accesses, kcmp(2), futexes (get_robust_list(2)) and perf trace events. For a ptrace operation to be allowed the profile of the tracing process and the profile of the target task must both have the correct permissions. For example, the profile of the process attaching to another task must have the trace permission for the target task's profile, and the task being traced must have the tracedby permission for the tracing process' profile. Example AppArmor PTrace rules: # Allow all PTrace access ptrace, # Explicitly allow all PTrace access, ptrace (read, readby, trace, tracedby), # Explicitly deny use of ptrace(2) deny ptrace (trace), # Allow unconfined processes (eg, a debugger) to ptrace us ptrace (readby, tracedby) peer=unconfined, # Allow ptrace of a process running under the /usr/bin/foo profile ptrace (trace) peer=/usr/bin/foo, =head2 Signal rules AppArmor supports mediation of signal(7). AppArmor signal rules are accumulated so that the granted signal permissions are the union of all the listed signal rule permissions. AppArmor signal permissions are implied when a rule does not explicitly state an access list. By default, all signal permissions are implied. For the sending of a signal to be allowed, the profile of the sending process and the profile of the target task must both have the correct permissions. For example, the profile of a process sending a signal to another task must have the send permission for the target task's profile, and the task receiving the signal must have a receive permission for the sending process' profile. Example AppArmor signal rules: # Allow all signal access signal, # Explicitly deny sending the HUP and INT signals deny signal (send) set=(hup, int), # Allow unconfined processes to send us signals signal (receive) peer=unconfined, # Allow sending of signals to a process running under the /usr/bin/foo # profile signal (send) peer=/usr/bin/foo, # Allow checking for PID existence signal (receive, send) set=("exists"), # Allow us to signal ourselves using the built-in @{profile_name} variable signal peer=@{profile_name}, # Allow two real-time signals signal set=(rtmin+0 rtmin+32), =head2 DBus rules AppArmor supports DBus mediation. The mediation is performed in conjunction with the DBus daemon. The DBus daemon verifies that communications over the bus are permitted by AppArmor policy. AppArmor DBus rules are accumulated so that the granted DBus permissions are the union of all the listed DBus rule permissions. AppArmor DBus rules are broad and general and become more restrictive as further information is specified. Policy may be specified down to the interface member level (method or signal name), however the contents of messages are not examined. Some AppArmor DBus permissions are not compatible with all AppArmor DBus rules. The 'bind' permission cannot be used in message rules. The 'send' and 'receive' permissions cannot be used in service rules. The 'eavesdrop' permission cannot be used in rules containing any conditionals outside of the 'bus' conditional. 'r' and 'read' are synonyms for 'receive'. 'w' and 'write' are synonyms for 'send'. 'rw' is a synonym for both 'send' and 'receive'. AppArmor DBus permissions are implied when a rule does not explicitly state an access list. By default, all DBus permissions are implied. Only message permissions are implied for message rules and only service permissions are implied for service rules. Example AppArmor DBus rules: # Allow all DBus access dbus, # Explicitly allow all DBus access, dbus (send, receive, bind), # Deny send/receive/bind access to the session bus deny dbus bus=session, # Allow bind access for a particular name on any bus dbus bind name=com.example.ExampleName, # Allow receive access for a particular path and interface dbus receive path=/com/example/path interface=com.example.Interface, # Deny send/receive access to the system bus for a particular interface deny dbus bus=system interface=com.example.ExampleInterface, # Allow send access for a particular path, interface, member, and pair of # peer names: dbus send bus=session path=/com/example/path interface=com.example.Interface member=ExampleMethod peer=(name=(com.example.ExampleName1|com.example.ExampleName2)), # Allow receive access for all unconfined peers dbus receive peer=(label=unconfined), # Allow eavesdropping on the system bus dbus eavesdrop bus=system, # Allow and audit all eavesdropping audit dbus eavesdrop, =head2 change_profile rules AppArmor supports self directed profile transitions via the change_profile api. Change_profile rules control which permissions for which profiles a confined task can transition to. The profile name can contain AppArmor pattern matching to specify different profiles. change_profile -> **, The change_profile api allows the transition to be delayed until when a task executes another application. If an exec rule transition is specified for the application and the change_profile api is used to make a transition at exec time, the transition specified by the change_profile api takes precedence. The Change_profile permission can restrict which profiles can be transitioned to based off of the executable name by specifying the exec condition. change_profile /bin/bash -> new_profile, The restricting of the transition profile to a given executable at exec time is only useful when then current task is allowed to make dynamic decisions about what confinement should be, but the decision set needs to be controlled. A list of profiles or multiple rules can be used to specify the profiles in the set. Eg. change_profile /bin/bash -> {new_profile1,new_profile2,new_profile3}, An exec rule can be used to specify a transition for the executable, if the transition should be allowed even if the change_profile api has not been used to select a transition for those available in the change_profile rule set. Eg. /bin/bash Px -> new_profile1, change_profile /bin/bash -> {new_profile1,new_profile2,new_profile3}, The exec mode dictates whether or not the Linux Kernel's B routines should be used to set ld.so(8) secure-execution mode and clear environment variables such as LD_PRELOAD, similar to setuid programs. (See ld.so(8) for more information.) The B mode sets up secure-execution mode for the new application, and B mode disables AppArmor's requirement for it (the kernel and/or libc may still turn it on). An exec mode can only be specified when an exec condition is present. change_profile safe /bin/bash -> new_profile, Not all kernels support B mode and the parser will downgrade rules to B mode in that situation. If no exec mode is specified, the default is B mode in kernels that support it. =head2 all rule The all rule is used to add a generic rule for all supported rule types. This is useful when policy wants to define a black list instead of white list, but can also be useful to add an access qualifier to all rules. Eg. Black list allow all, # begin blacklist deny file, deny unix, Eg. Adding audit qualifier audit access all, =head2 rlimit rules AppArmor can set and control the resource limits associated with a profile as described in the setrlimit(2) man page. The AppArmor rlimit controls allow setting of limits and restricting changes of them and these actions can be audited. Enforcement of the set limits is handled by the standard kernel enforcement mechanism for rlimits and will not result in an audited AppArmor message if the limit is enforced. If a profile does not have an rlimit rule associated with a given rlimit then the rlimit is left alone and regular access, including changing the limit, is allowed. However if the profile sets an rlimit then the current limit is checked and if greater than the limit specified in the rule it will be changed to the specified limit. AppArmor rlimit rules control the hard limit of an application and ensure that if the hard limit is lowered that the soft limit does not exceed the hard limit value. Eg. set rlimit data <= 100M, set rlimit nproc <= 10, set rlimit nice <= 5, =head2 Variables AppArmor's policy language allows embedding variables into file rules to enable easier configuration for some common (and pervasive) setups. Variables may have multiple values assigned, but any variable assignments must be made before the start of the profile. The parser will automatically expand variables to include all values that they have been assigned; it is an error to reference a variable without setting at least one value. You can use empty quotes ("") to explicitly add an empty value. At the time of this writing, the following variables are defined in the provided AppArmor policy: @{HOME} @{HOMEDIRS} @{multiarch} @{pid} @{pids} @{PROC} @{securityfs} @{apparmorfs} @{sys} @{tid} @{run} @{XDG_DESKTOP_DIR} @{XDG_DOWNLOAD_DIR} @{XDG_TEMPLATES_DIR} @{XDG_PUBLICSHARE_DIR} @{XDG_DOCUMENTS_DIR} @{XDG_MUSIC_DIR} @{XDG_PICTURES_DIR} @{XDG_VIDEOS_DIR} These are defined in files in F and are used in many of the abstractions described later. You may also add files in F for site-specific customization of B<@{HOMEDIRS}>, F for B<@{multiarch}> and F for B<@{XDG_*}>. =head3 Special builtin variables AppArmor has some builtin variables that are not declared in policy but are available to be used in policy. @{profile_name} - the profile name @{attach_path} - the profile exec attachment path - if one has been defined @{exec_path} - the executables path The B<@{profile_name}> variable is set to the profile name and may be used in all policy. It is only defined when used inside of a profile. The B<@{attach_path}> variable is only defined if the profile will attach to an executable. It will be the path attachment specification or if that is not defined it may be the profile's name if the profile name is a path. The B<@{exec_path}> variable like B<@{attach_path}> is only defined if the profile attaches to an executable. If the kernel supports it as a kernel variable, it will be set to the specific path that matches the executable at run time. If the kernel does not support kernel variables it will have the same value as B<@{attach_path}>. =head3 Assignment of variables AppArmor provides 3 different assignment operators for variables. = does a straight assignment of the right hand side to the variable. The variable must not be declared before or an error will occur. If there is a chance that the variable could be declared before the +=, :=, or ?= assignment operators can be used instead. += will add new set values to a previously declared variable ?= will assign the right hand side to the variable only if the variable was not previously declared. := does a straight assignment of the right hand side to the variable. If the variable has already been declared before the assignment the rhs will override the preexisting variable value(s). =head3 Notes on variable expansion and the / character It is important to note that how AppArmor performs variable expansion depends on the context where a variable is used. When a variable is expanded it can result in a string with multiple path characters next to each other, in a way that is not evident when looking at policy. Eg. =over 4 Given the following variable definition and rule @{HOME}=/home/*/ file rw @{HOME}/*, The variable expansion results in a rule of file rw /home/*//*. =back When this occurs in a context where a path is expected, AppArmor will canonicalize the path by collapsing consecutive / characters into a single character. For the above example, this would be file rw /home/*/*, There is one exception to this rule, when the consecutive / characters are at the beginning of a path, this indicates a posix namespace and the characters will not be collapsed. Eg. =over 4 @{HOME}=/home/*/ file rw /@{HOME}/*, will result in an expansion of file rw //home/*//*, which is collapsed to file rw //home/*/*, Note: that the leading // in the above example is not collapsed to a single /. However the second // (that was also seen in the first example) is collapsed. =back =head3 Boolean Variables In addition to the set variables AppArmor supports boolean variables. These begin with a B<$> and can only be used in conditional expressions. Boolean variables provide a convenient way to enable/disable policy rules that have been wrapped in the proper if condition. =head2 Alias rules AppArmor also provides alias rules for remapping paths for site-specific layouts. They are an alternative form of path rewriting to using variables, and are done after variable resolution. Alias rules must occur within the preamble of the profile. System-wide aliases are found in F, which is included by F. F is typically included at the beginning of an AppArmor profile. =head2 Globbing (AARE) File resources and other parameters accepting an AARE may be specified with a globbing syntax similar to that used by popular shells, such as csh(1), bash(1), zsh(1). =over 4 =item B<*> can substitute for any number of characters, excepting '/' =item B<**> can substitute for any number of characters, including '/' =item B can substitute for any single character excepting '/' =item B<[abc]> will substitute for the single character a, b, or c =item B<[a-c]> will substitute for the single character a, b, or c =item B<[^a-c]> will substitute for any single character not matching a, b or c =item B<{ab,cd}> will expand to one rule to match ab, one rule to match cd Can also include variables. =item B<@{variable}> will expand to all values assigned to the given variable. =back When AppArmor looks up a directory the pathname being looked up will end with a slash (e.g., F); otherwise it will not end with a slash. Only rules that match a trailing slash will match directories. Some examples, none matching the F directory itself, are: =over 4 =item B Files directly in F. =item B Directories directly in F. =item B Files and directories anywhere underneath F. =item B Directories anywhere underneath F. =back =head2 Rule Qualifiers There are several rule qualifiers that can be applied to permission rules. Rule qualifiers can modify the rule and/or permissions within the rule. =over 4 =item B Specifies the priority of the rule. Currently the allowed range is -1000 to 1000 with the default priority of rule is 0. Rules with higher priority are given preferences and will completely override permissions of lower priority rules where they overlap. When rules partially overlap the permissions of the higher priority rule will completely override lower priority rules within in overlap. Within a given priority level rules that overlap will accumulate permissions in the standard AppArmor fashion. =item B Specifies that permissions requests that match the rule are allowed. This is the default value for rules and does not need to be specified. Conflicts with the I qualifier. =item B Specifies that permissions requests that match the rule should be recorded to the audit log. =item B Specifies that permissions requests that match the rule should be denied without logging. Can be combined with 'audit' to enable logging. Conflicts with the I qualifier. =item B Specifies that the task must have the same euid/fsuid as the object being referenced by the permission check. =back =head3 Qualifier Blocks Rule Qualifiers can be applied to multiple rules at a time by grouping the rules into a rule block. audit { /foo r, network, } =head2 #include mechanism AppArmor provides an easy abstraction mechanism to group common access requirements; this abstraction is an extremely flexible way to grant site-specific rights and makes writing new AppArmor profiles very simple by assembling the needed building blocks for any given program. The use of '#include' is modelled directly after cpp(1); its use will replace the '#include' statement with the specified file's contents. The leading '#' is optional, and the '#include' keyword can be followed by an option conditional 'if exists' that specifies profile compilation should continue if the specified file or directory is not found. B<#include "/absolute/path"> specifies that F should be used. B<#include "relative/path"> specifies that F should be used, where the path is relative to the current working directory. B<#include Emagic/pathE> is the most common usage; it will load F relative to a directory specified to apparmor_parser(8). F is the AppArmor default. The supplied AppArmor profiles follow several conventions; the abstractions stored in F are some large clusters that are used in most profiles. What follows are short descriptions of how some of the abstractions are used. =over 4 =item F Includes accesses to device files used for audio applications. =item F Includes access to files and services typically necessary for services that perform user authentication. =item F Includes files that should be readable and writable in all profiles. =item F Includes many files used by bash; useful for interactive shells and programs that call system(3). =item F Includes read and write access to the device files controlling the virtual console, sshd(8), xterm(1), etc. This abstraction is needed for many programs that interact with users. =item F Includes access to fonts and the font libraries. =item F Includes read and write access to GNOME configuration files, as well as read access to GNOME libraries. =item F Includes read and write access to KDE configuration files, as well as read access to KDE libraries. =item F Includes file access rules needed for common kerberos clients. =item F Includes file rules to allow DNS, LDAP, NIS, SMB, user and group password databases, services, and protocols lookups. =item F Includes read access to perl modules. =item F =item F =item F =item F =item F Some profiles for typical "user" programs will use these include files to describe rights that users have in the system. =item F Includes write access to files used to maintain wtmp(5) and utmp(5) databases, used with the w(1) and associated commands. =item F Includes read access to libraries, configuration files, X authentication files, and the X socket. =back Some of the abstractions rely on variables that are set in files in the F directory. These variables are currently B<@{HOME}> and B<@{HOMEDIRS}>. Variables cannot be set in profile scope; they can only be set before the profile. Therefore, any profiles that use abstractions should either B<#include Etunables/globalE> or otherwise ensure that B<@{HOME}> and B<@{HOMEDIRS}> are set before starting the profile definition. The aa-autodep(8) and aa-genprof(8) utilities will automatically emit B<#include Etunables/globalE> in generated profiles. =head2 Feature ABI The feature abi tells AppArmor which feature set the policy was developed under. This is important to ensure that kernels with a different feature set don't enforce features that the policy doesn't support, which can result in unexpected application failures. When policy is compiled both the kernel feature abi and policy feature abi are consulted to build a policy that will work for the system's kernel. If the kernel supports a feature not supported by the policy then policy will be built so that the kernel does NOT enforce that feature. If the policy supports a feature not supported by the kernel the compile may downgrade the rule with the feature to something the kernel supports, drop the rule completely, or fail the compile. If the policy abi is specified as B then the running kernel's abi will be used. This should never be used in shipped policy as it can cause system breakage when a new kernel is installed. The special abi B is equivalent to not specifying an ABI. =head3 ABI compatibility with AppArmor 2.x AppArmor 3 remains compatible with AppArmor 2.x by detecting when a profile does not have a feature ABI specified. In this case the policy compile will either apply the pinned feature ABI as specified by the config file or the command line, or if neither of those are applied by using a default feature ABI. It is important to note that the default feature ABI does not support new features added in AppArmor 3 or later. =head1 EXAMPLE An example AppArmor profile: # which feature abi the policy was developed with abi , # a variable definition in the preamble @{HOME} = /home/*/ /root/ # a comment about foo. /usr/bin/foo { /bin/mount ux, /dev/{,u}random r, /etc/ld.so.cache r, /etc/foo.conf r, /etc/foo/* r, /lib/ld-*.so* rmix, /lib/lib*.so* r, /proc/[0-9]** r, /usr/lib/** r, /tmp/foo.* lrw, @{HOME}/.foo_file rw, /usr/bin/baz Cx -> baz, # a comment about foo's hat (subprofile), bar. ^bar { /lib/ld-*.so* rmix, /usr/bin/bar rmix, /var/spool/* rwl, } # a comment about foo's subprofile, baz. profile baz { #include owner /proc/[0-9]*/stat r, /bin/bash ixr, /var/lib/baz/ r, owner /var/lib/baz/* rw, } } =head2 Conditional rules AppArmor provides a mechanism to conditionally enable and disable rules in a profile. Rules that are to be conditionally used can be wrapped in an I condition block with the condition expression being controlled by setting variable values. The condition expression can be composed of variables, boolean variables, a B check, the comparison operators B, B<==>, B, B<>>, B<>=>, B<<>, and B<<=>, open '(' and close ')' parentheses, and the boolean operators B, B, and B. Boolean operators are evaluated left to write with priority in order of the following list. =over 4 =item B - tests whether the left text or variable is a subset of the text or variable on the right. The subset check is done by a full match, therefore partial string matches would not evaluate to true. =item B<==> - tests whether both text or variable evaluate to the same value. =item B - tests whether both text or variable evaluate to different values. =item B<>> - tests whether the left text or variable is greater than the right text or variable. Comparisons between integers and set variables will fail to compile unless the variable contents are exactly one value, an integer. Other comparison will be strictly lexicographical. =item B<>=> - tests whether the left text or variable is greater than or equal to the right text or variable. Comparisons between integers and set variables will fail to compile unless the variable contents are exactly one value, an integer. Other comparison will be strictly lexicographical. =item B<<> - tests whether the left text or variable is lesser than the right text or variable. Comparisons between integers and set variables will fail to compile unless the variable contents are exactly one value, an integer. Other comparison will be strictly lexicographical. =item B<<=> - tests whether the left text or variable is lesser than or equal to the right text or variable. Comparisons between integers and set variables will fail to compile unless the variable contents are exactly one value, an integer. Other comparison will be strictly lexicographical. =item B - tests whether the following variable or boolean variable has been defined/created. =item B - open '(' and close ')' paretheses are used to group operations by priority. =item B - boolean B operator, negates the value of the following expression. =item B - boolean B operator, both expressions being combined with B must be true for the result to be true. =item B - boolean B operator, either one or both expressions being combined with B can be true for the result to be true. =back An example of a profile conditional if ${distro_mods} and defined @{HOME} { /@{HOME}/.foo_file rw, } else { /home/*/.foo_file rw, } Since lexicographical comparisons using the B<>>, B<>=>, B<<>, B<<=> operators could lead to mistakes when comparing integers, comparisons between variables and integers will fail to compile unless the variable contains an integer. Eg. @{BAR} = /home/user/ /home/user2/ @{BAR} += /home/user3/ @{TEST_VERSION} = 2 @{BAZ} = 10 /usr/bin/foo { if /home/user/ in @{BAR} { /** r, } if @{TEST_VERSION} >= @{BAZ} { /** w, } else if 10 > @{TEST_VERSION} { /** rw, } if @{BAZ} <= 10 { /** rw, } } =head1 FILES =over 4 =item F =back =head1 KNOWN BUGS =over 4 =item * Mount options support the use of pattern matching but mount flags are not correctly intersected against specified patterns. Eg, 'mount options=**,' should be equivalent to 'mount,', but it is not. (LP: #965690) =item * The fstype may not be matched against when certain mount command flags are used. Specifically fstype matching currently only works when creating a new mount and not remount, bind, etc. =item * Mount rules with multiple 'options' conditionals are not applied as documented but instead merged such that 'options in (ro,nodev) options in (atime)' is equivalent to 'options in (ro,nodev,atime)'. =item * When specifying mount options with the 'in' conditional, both the positive and negative values match when specifying one or the other. Eg, 'rw' matches when 'ro' is specified and 'dev' matches when 'nodev' is specified such that 'options in (ro,nodev)' is equivalent to 'options in (rw,dev)'. =back =head1 SEE ALSO apparmor(7), apparmor_parser(8), apparmor_xattrs(7), aa-complain(1), aa-enforce(1), aa_change_hat(2), aa_change_profile(2), mod_apparmor(5), and L. =cut apparmor-5.0.2/parser/apparmor.pod000066400000000000000000000233651522511161100171640ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, # 2008, 2009 # NOVELL (All rights reserved) # # Copyright (c) 2010 # Canonical Ltd. (All rights reserved) # # Copyright (c) 2013 # Christian Boltz (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- =pod =head1 NAME AppArmor - kernel enhancement to confine programs to a limited set of resources. =head1 DESCRIPTION AppArmor is a kernel enhancement to confine programs to a limited set of resources. AppArmor's unique security model is to bind access control attributes to programs rather than to users. AppArmor confinement is provided via I loaded into the kernel via apparmor_parser(8), typically through the F systemd unit, which is used like this: # systemctl start apparmor # systemctl reload apparmor AppArmor can operate in two modes: I, and I: =over 4 =item * I - Profiles loaded in enforcement mode will result in enforcement of the policy defined in the profile as well as reporting policy violation attempts to syslogd. =item * I - Profiles loaded in C mode will not enforce policy. Instead, it will report policy violation attempts. This mode is convenient for developing profiles. To manage complain mode for individual profiles the utilities aa-complain(8) and aa-enforce(8) can be used. These utilities take a program name as an argument. =back Profiles are traditionally stored in files in F under filenames with the convention of replacing the B in pathnames with B<.> (except for the root B) so profiles are easier to manage (e.g. the F profile would be named F). Profiles are applied to a process at exec(3) time (as seen through the execve(2) system call): once a profile is loaded for a program, that program will be confined on the next exec(3). If a process is already running under a profile, when one replaces that profile in the kernel, the updated profile is applied immediately to that process. On the other hand, a process that is already running unconfined cannot be confined. AppArmor supports the Linux kernel's securityfs filesystem, and makes available the list of the profiles currently loaded; to mount the filesystem: # mount -tsecurityfs securityfs /sys/kernel/security $ cat /sys/kernel/security/apparmor/profiles /usr/bin/mutt /usr/bin/gpg ... Normally, the initscript will mount securityfs if it has not already been done. AppArmor also restricts what privileged operations a confined process may execute, even if the process is running as root. A confined process cannot call the following system calls: create_module(2) delete_module(2) init_module(2) ioperm(2) iopl(2) ptrace(2) reboot(2) setdomainname(2) sethostname(2) swapoff(2) swapon(2) sysctl(2) =head2 Complain mode Instead of denying access to resources the profile does not have a rule for AppArmor can "allow" the access and log a message for the operation that triggers it. This is called I. It is important to note that rules that are present in the profile are still applied, so allow rules will still quiet or force audit messages, and deny rules will still result in denials and quieting of denial messages (see I if this is a problem). Complain mode can be used to develop profiles incrementally as an application is exercised. The logged accesses can be added to the profile and then can the application further exercised to discover further additions that are needed. Because AppArmor allows the accesses the application will behave as it would if AppArmor was not confining it. B complain mode does not provide any security, only auditing, while it is enabled. It should not be used in a hostile environment or bad behaviors may be logged and added to the profile as if they are resource accesses that should be used by the application. B complain mode can be very noisy with new or empty profiles, but with developed profiles might not log anything if the profile covers the application behavior well. See I if complain mode is generating too many log messages. To set a profile and any children or hat profiles the profile may contain into complain mode use aa-complain /etc/apparmor.d/ To manually set a specific profile in complain mode, add the C flag, and then manually reload the profile: profile foo flags=(complain) { ... } Note that the C flag must also be added manually to any hats or children profiles of the profile or they will continue to use the previous mode. To enable complain mode globally, run: echo -n complain > /sys/module/apparmor/parameters/mode or to set it on boot add: apparmor.mode=complain as a kernel boot parameter. B Setting complain mode globally disables all apparmor security protections. It can be useful during debugging or profile development, but setting it selectively on a per profile basis is safer. =head1 ERRORS When a confined process tries to access a file it does not have permission to access, the kernel will report a message through audit, similar to: audit(1386511672.612:238): apparmor="DENIED" operation="exec" parent=7589 profile="/tmp/sh" name="/bin/uname" pid=7605 comm="sh" requested_mask="x" denied_mask="x" fsuid=0 ouid=0 audit(1386511672.613:239): apparmor="DENIED" operation="open" parent=7589 profile="/tmp/sh" name="/bin/uname" pid=7605 comm="sh" requested_mask="r" denied_mask="r" fsuid=0 ouid=0 audit(1386511772.804:246): apparmor="DENIED" operation="capable" parent=7246 profile="/tmp/sh" pid=7589 comm="sh" pid=7589 comm="sh" capability=2 capname="dac_override" The permissions requested by the process are described in the operation= and denied_mask= (for files - capabilities etc. use a slightly different log format). The "name" and process id of the running program are reported, as well as the profile name including any "hat" that may be active, separated by "//". ("Name" is in quotes, because the process name is limited to 15 bytes; it is the same as reported through the Berkeley process accounting.) For confined processes running under a profile that has been loaded in complain mode, enforcement will not take place and the log messages reported to audit will be of the form: audit(1386512577.017:275): apparmor="ALLOWED" operation="open" parent=8012 profile="/usr/bin/du" name="/etc/apparmor.d/tunables/" pid=8049 comm="du" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0 audit(1386512577.017:276): apparmor="ALLOWED" operation="open" parent=8012 profile="/usr/bin/du" name="/etc/apparmor.d/tunables/" pid=8049 comm="du" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0 If the userland auditd is not running, the kernel will send audit events to klogd; klogd will send the messages to syslog, which will log the messages with the KERN facility. Thus, DENIED and ALLOWED messages may go to either F or F, depending upon local configuration. =head1 DEBUGGING AppArmor provides a few facilities to log more information, which can help debugging profiles. =head2 Enable debug mode When debug mode is enabled, AppArmor will log a few extra messages to dmesg (not via the audit subsystem). For example, the logs will state when ld.so(8) secure-execution mode has been applied in a profile transition. To enable debug mode, run: echo 1 > /sys/module/apparmor/parameters/debug or to set it on boot add: apparmor.debug=1 as a kernel boot parameter. =head2 Turn off deny audit quieting By default, operations that trigger C rules are not logged. This is called I. To turn off deny audit quieting, run: echo -n noquiet >/sys/module/apparmor/parameters/audit or to set it on boot add: apparmor.audit=noquiet as a kernel boot parameter. =head2 Force audit mode AppArmor can log a message for every operation that triggers a rule configured in the policy. This is called I. B Force audit mode can be extremely noisy even for a single profile, let alone when enabled globally. To set a specific profile in force audit mode, add the C flag: profile foo flags=(audit) { ... } To enable force audit mode globally, run: echo -n all > /sys/module/apparmor/parameters/audit or to set it on boot add: apparmor.audit=all as a kernel boot parameter. B If auditd is not running, to avoid losing too many of the extra log messages, you will likely have to turn off rate limiting by doing: echo 0 > /proc/sys/kernel/printk_ratelimit But even then the kernel ring buffer may overflow and you might lose messages. Else, if auditd is running, see auditd(8) and auditd.conf(5). =head1 FILES =over 4 =item F =item F =item F =item F =back =head1 SEE ALSO apparmor_parser(8), aa_change_hat(2), apparmor.d(5), aa-autodep(1), clean(1), auditd(8), aa-unconfined(8), aa-enforce(1), aa-complain(1), and L. =cut apparmor-5.0.2/parser/apparmor_parser.pod000066400000000000000000000370131522511161100205330ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, # 2008, 2009 # NOVELL (All rights reserved) # # Copyright (c) 2010 - 2012 # Canonical Ltd. (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- =pod =head1 NAME apparmor_parser - loads AppArmor profiles into the kernel =head1 SYNOPSIS BcommandE [profiles]...> BcommandE> B =head1 DESCRIPTION B is used as a general tool to compile, and manage AppArmor policy, including loading new apparmor.d(5) profiles into the Linux kernel. AppArmor profiles restrict the operations available to processes. The B are loaded into the Linux kernel by the B program. The B may be specified by file name or a directory name containing a set of profiles. If a directory is specified then the B will try to do a profile load for each file in the directory that is not a dot file, or explicitly black listed (*.dpkg-new, *.dpkg-old, *.dpkg-dist, *.dpkg-bak, *.dpkg-remove, *.pacsave, *.pacnew, *.rpmnew, *.rpmsave, *.orig, *.rej, *~). The B will fall back to taking input from standard input if a profile or directory is not supplied. The input supplied to B should be in the format described in apparmor.d(5). =head1 COMMANDS The command set is broken into four subcategories. =over 4 =item unprivileged commands Commands that don't require any privilege and don't operate on profiles. =item unprivileged profile commands Commands that operate on a profile either specified on the command line or read from stdin if no profile was specified. =item privileged commands Commands that require the MAC_ADMIN capability within the affected AppArmor namespace to load policy into the kernel or filesystem write permissions to update the affected privileged files (cache etc). =item privileged profile commands Commands that require privilege and operate on profiles. =back =head1 Unprivileged commands =over 4 =item -V, --version Print the version number and exit. =item -h, --help Give a quick reference guide. =back =head1 Unprivileged profile commands =over 4 =item -N, --names Produce a list of policies from a given set of profiles (implies -K). =item -p, --preprocess Apply preprocessing to the input profile(s) by flattening includes into the output profile and dump to stdout. =item -S, --stdout Writes a binary (cached) profile to stdout (implies -K and -T). =item -o file, --ofile file Writes a binary (cached) profile to the specified file (implies -K and -T) =back =head1 Privileged commands =over 4 =item --purge-cache Unconditionally clear out cached profiles. =back =head1 Privileged profile commands =over 4 =item -a, --add Insert the AppArmor definitions given into the kernel. This is the default action. This gives an error message if a AppArmor definition by the same name already exists in the kernel, or if the parser doesn't understand its input. It reports when an addition succeeded. =item -r, --replace This flag is required if an AppArmor definition by the same name already exists in the kernel; used to replace the definition already in the kernel with the definition given on standard input. =item -R, --remove This flag is used to remove an AppArmor definition already in the kernel. Note that it still requires a complete AppArmor definition as described in apparmor.d(5) even though the contents of the definition aren't used. =back =head1 OPTIONS =over 4 =item -B, --binary Treat the profile files specified on the command line (or stdin if none specified) as binary cache files, produced with the -S or -o options, and load to the kernel as specified by -a, -r, and -R (implies -K and -T). =item -C, --Complain Force the profile to load in complain mode. =item -b n, --base n Set the base directory for resolving #include directives defined as relative paths. =item -I n, --Include n Add element n to the search path when resolving #include directives defined as an absolute paths. =item -f n, --apparmorfs n Set the location of the apparmor security filesystem (default is "/sys/kernel/security/apparmor"). =item --policy-features n Specify the feature set that the policy was developed under. This does not override feature ABI rules. =item --override-policy-abi n Specify the feature set that the policy was developed under and override any feature ABI rules that the policy may be using. =item --kernel-features n Specify the feature set of the kernel that the policy is being compiled for. If not specified this will be determined by the system's kernel. =item -M n, --features-file n Use the features file located at path "n" (default is /etc/apparmor.d/cache/.features). If the --cache-loc option is present, the ".features" file in the specified cache directory is used. Note: this sets both the --kernel-features and --policy-features to be the same. =item -m n, --match-string n Only use match features "n". Note: this sets both the --kernel-features and --policy-features to be the same. =item -n n, --namespace-string n Force a profile to load in the namespace "n". =item -X, --readimpliesX In the case of profiles that are loading on systems were READ_IMPLIES_EXEC is set in the kernel for a given process, load the profile so that any "r" flags are processed as "mr". =item -k, --show-cache Report the cache processing (hit/miss details) when loading or saving cached profiles. =item -K, --skip-cache Perform no caching at all: disables -W, implies -T. =item -T, --skip-read-cache By default, if a profile's cache is found in the location specified by --cache-loc and the timestamp is newer than the profile, it will be loaded from the cache. This option disables this cache loading behavior. =item -W, --write-cache Write out cached profiles to the location specified in --cache-loc. Off by default. In cases where abstractions have been changed, and the parser is running with "--replace", it may make sense to also use "--skip-read-cache" with the "--write-cache" option. =item --skip-bad-cache Skip updating the cache if it contains cached profiles in a bad or inconsistent state =item -L, --cache-loc Set the location(s) of the cache directory. This option can accept a comma separated list of directories, which will be searched in order to find a matching cache. The first matching cache file found is used even if a directory later in the search order may contain a newer cache file. If multiple directories are specified and --write-cache has been specified then cache writes will be made to the first directory in the list, all other directories will be treated as read only. If a cache directory name needs to have a comma as part of the name, it can be specified by using a backslash to escape the comma character in the directory name. If not specified the cache location defaults to /var/cache/apparmor =item --print-cache-dir Print the cache directory location. This path will be a subdirectory of the directory specified by --cache-loc. The subdirectory used will be influenced by the features available in the currently running kernel or by the features specified with the --match-string or --features-file options. =item -Q, --skip-kernel-load Perform all actions except the actual loading of a profile into the kernel. This is useful for testing profile generation, caching, etc, without making changes to the running kernel profiles. This also removes the need for privilege to execute the commands that manage policy in the kernel =item -q, --quiet Do not report on the profiles as they are loaded, and not show warnings. =item -v, --verbose Report on the profiles as they are loaded, and show warnings. =item --warn=n Enable various warnings during policy compilation. A single warn flag can be specified per --warn option, but the --warn flag can be passed multiple times. apparmor_parser --warn=rule-not-enforced ... A specific warning can be disabled by prepending I- to the flag apparmor_parser --warn=no-rule-not-enforced ... Use --help=warn to see a full list of which warn flags are supported. =item --Werror[=n] Convert warnings into errors during policy compilation. If the optional flag is not specified all warnings become errors. If the optional flag is specified only the class of warnings specified will become errors. A single flag can be specified per --Werror option, but the --Werror flag can be passed multiple times. apparmor_parser --Werror=deprecated ... Use --help=warn or --help=Werror to see a full list of which warn flags are supported. =item -d, --debug Given once, only checks the profiles to ensure syntactic correctness. Given twice, dumps its interpretation of the profile for checking. =item -D n, --dump=n Debug flag for dumping various structures and passes of policy compilation. A single dump flag can be specified per --dump option, but the dump flag can be passed multiple times. Note progress flags tend to also imply the matching stats flag. apparmor_parser --dump=dfa-stats --dump=trans-stats Use --help=dump to see a full list of which dump flags are supported =item -j n, --jobs=n Set the number of jobs used to compile the specified policy. Where n can be 0 - disable jobs and use the main process for all compilation # - a specific number of jobs auto - the # of cpus in the in the system x# - # * number of cpus Eg. -j8 OR --jobs=8 allows for 8 parallel jobs -jauto OR --jobs=auto sets the jobs to the # of cpus -jx4 OR --jobs=x4 sets the jobs to # of cpus * 4 -jx1 is equivalent to -jauto The default value is the number of cpus in the system. Note that if jobs is a positive integer number the --jobs-max parameter is automatically set to the same value. =item -c[n], --zstd-compress-level=n Compress the profile using the zstd algorithm at level n. Where n can be on of: none - disable compression, equivalent to 0 default - default apparmor preset compression level fast - use a pre-chosen value that is fast small - use a pre-chosen value that will result in better compression (slower) 0-22 - a number in the range 0-22, where 0 disables A compression level of 0 disables compression entirely. Levels otherwise range up to 22, though levels of 20 and above (the 'ultra' levels) require significantly more memory and are much slower. Policies can be loaded and stored in the kernel in compressed format. Using a higher level of compression, policy can save both CPU time and memory, especially when compressed policies are retrieved from an existing cache. If the compression level is unspecified, a default value of 10 is used. --zstd-compress-level values only affect container compression unlike the --Optimize flag which adjusts multiple optimization parameters. See also the optimization flag zstd-recompress. =item --max-jobs n When --jobs is set to a scaling value (ie. auto or xN) the specify a hard cap on the value that can be specified by the --jobs flag. It takes the same set of options available to the --jobs option, and defaults to 8*cpus =item -O n, --optimize=n Set the optimization flags used by policy compilation. A single optimization flag can be toggled per -O option, but the optimize flag can be passed multiple times. Turning off some phases of the optimization can make it so that policy can't complete compilation due to size constraints (it is entirely possible to create a dfa with millions of states that will take days or longer to compile). Note: The parser is set to use a balanced default set of flags, that will result in reasonable compression but not take excessive amounts of time to complete. Use --help=optimize to see a full list of which optimization flags are supported. =over 4 =item B Force recompression of the policy file if the compression level is higher than the currently cached file. =back =item --abort-on-error Abort processing of profiles on the first error encountered, otherwise the parser will continue to try to compile other profiles if specified. Note: If an error is encountered while processing profiles the last error encountered will be used to set the exit code. =item --skip-bad-cache-rebuild The default behavior of the parser is to check if a cached version of a profile exists and if it does it attempt to load it into the kernel. If that load is rejected, then the parser will attempt to rebuild the cache file, and load again. This option tells the parser to not attempt to rebuild the cache on failure, instead the parser continues on with processing the remaining profiles. =item --estimated-compile-size Adjust the internal parameter used to estimate how aggressive the parser can be when compiling policy. This may include changes to how or when caches are dropped or how many compile units (jobs) are launched. The value should slightly larger than the largest Resident Set Size (RSS) encountered for the type of policy being compiled. A value that is too small may result in the parser exhausting system resources when compiling large policy. A value too large may slow policy compiles down. The value specified may include a suffix of I, I, I, to make it easier to adjust the size. Note: config-file and command line options will override values chosen by tuning affected by the option. =item --config-file Specify the config file to use instead of /etc/apparmor/parser.conf. This option will be processed early before regular options regardless of the order it is specified in. =item --print-config-file Print the config file location that will be used. =back =head1 CONFIG FILE An optional config file /etc/apparmor/parser.conf can be used to specify the default options for the parser, which then can be overridden using the command line options. The config file ignores leading whitespace and treats lines that begin with # as comments. Config options are specified one per line using the same format as the longform command line options (without the preceding --). Eg. #comment optimize=no-expr-tree optimize=compress-fast As with the command line some options accumulate and others override, ie. when there are conflicting versions of switch the last option is the one chosen. Eg. Optimize=no-minimize Optimize=minimize would result in Optimize=minimize being set. The Include, Dump, and Optimize options accumulate except for the inversion option (no-X vs. X), and a couple options that work by setting/clearing multiple options (compress-small). In that case the option will override the flags it sets but will may accumulate with others. All other options override previously set values. =head1 BUGS If you find any bugs, please report them at L. =head1 SEE ALSO apparmor(7), apparmor.d(5), aa_change_hat(2), and L. =cut apparmor-5.0.2/parser/apparmor_xattrs.pod000066400000000000000000000060571522511161100205700ustar00rootroot00000000000000# ---------------------------------------------------------------------- # Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, # 2008, 2009 # NOVELL (All rights reserved) # # Copyright (c) 2010 # Canonical Ltd. (All rights reserved) # # Copyright (c) 2013 # Christian Boltz (All rights reserved) # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # ---------------------------------------------------------------------- =pod =head1 NAME apparmor_xattrs - AppArmor profile xattr(7) matching =head1 DESCRIPTION AppArmor profiles can conditionally match files based on the presence and value of extended attributes in addition to file path. The following profile applies to any file under "/usr/bin" where the "security.apparmor" extended attribute has the value "trusted": profile trusted /usr/bin/* xattrs=(security.apparmor="trusted") { # ... } Note that "security.apparmor" and "trusted" are arbitrary, and profiles can match based on the value of any attribute. The xattrs value may also contain a path regex: profile trusted /usr/bin/* xattrs=(user.trust="tier/*") { # ... } The getfattr(1) and setfattr(1) tools can be used to view and manage xattr values: $ setfattr -n 'security.apparmor' -v 'trusted' /usr/bin/example-tool $ getfattr --absolute-names -d -m - /usr/bin/example-tool # file: usr/bin/example-tool security.apparmor="trusted" The priority of each profile is determined by the length of the path, then the number of xattrs specified. A more specific path is preferred over xattr matches: # Highest priority, longest path. profile example1 /usr/bin/example-tool { # ... } # Lower priority than the longer path, but higher priority than a rule # with fewer xattr matches. profile example2 /usr/** xattrs=( security.apparmor="trusted" user.domain="**" ) { # ... } # Lowest priority. Same path length as the second profile, but has # fewer xattr matches. profile example2 /usr/** { # ... } xattr matching requires the following kernel feature: /sys/kernel/security/apparmor/features/domain/attach_conditions/xattr =head1 KNOWN ISSUES AppArmor profiles currently can't reliably match extended attributes with binary values such as security.evm and security.ima. In the future AppArmor may gain the ability to match based on the presence of certain attributes while ignoring their values. =head1 SEE ALSO apparmor(8), apparmor_parser(8), apparmor.d(5), xattr(7), aa-autodep(1), clean(1), auditd(8), getfattr(1), setfattr(1), and L. =cut apparmor-5.0.2/parser/base_af_names.h000066400000000000000000000011021522511161100175340ustar00rootroot00000000000000AF_UNSPEC 0, AF_UNIX 1, AF_INET 2, AF_AX25 3, AF_IPX 4, AF_APPLETALK 5, AF_NETROM 6, AF_BRIDGE 7, AF_ATMPVC 8, AF_X25 9, AF_INET6 10, AF_ROSE 11, AF_NETBEUI 13, AF_SECURITY 14, AF_KEY 15, AF_NETLINK 16, AF_PACKET 17, AF_ASH 18, AF_ECONET 19, AF_ATMSVC 20, AF_RDS 21, AF_SNA 22, AF_IRDA 23, AF_PPPOX 24, AF_WANPIPE 25, AF_LLC 26, AF_IB 27, AF_MPLS 28, AF_CAN 29, AF_TIPC 30, AF_BLUETOOTH 31, AF_IUCV 32, AF_RXRPC 33, AF_ISDN 34, AF_PHONET 35, AF_IEEE802154 36, AF_CAIF 37, AF_ALG 38, AF_NFC 39, AF_VSOCK 40, AF_KCM 41, AF_QIPCRTR 42, AF_SMC 43, AF_XDP 44, AF_MCTP 45, AF_MAX 46, apparmor-5.0.2/parser/base_cap_names.h000066400000000000000000000054401522511161100177220ustar00rootroot00000000000000{"audit_control", CAP_AUDIT_CONTROL, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"audit_read", CAP_AUDIT_READ, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"audit_write", CAP_AUDIT_WRITE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"block_suspend", CAP_BLOCK_SUSPEND, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"bpf", CAP_BPF, CAP_SYS_ADMIN, CAPFLAG_BASE_FEATURE}, {"checkpoint_restore", CAP_CHECKPOINT_RESTORE, CAP_SYS_ADMIN, CAPFLAG_BASE_FEATURE}, {"chown", CAP_CHOWN, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"dac_override", CAP_DAC_OVERRIDE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"dac_read_search", CAP_DAC_READ_SEARCH, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"fowner", CAP_FOWNER, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"fsetid", CAP_FSETID, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"ipc_lock", CAP_IPC_LOCK, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"ipc_owner", CAP_IPC_OWNER, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"kill", CAP_KILL, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"lease", CAP_LEASE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"linux_immutable", CAP_LINUX_IMMUTABLE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"mac_admin", CAP_MAC_ADMIN, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"mac_override", CAP_MAC_OVERRIDE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"mknod", CAP_MKNOD, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"net_admin", CAP_NET_ADMIN, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"net_bind_service", CAP_NET_BIND_SERVICE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"net_broadcast", CAP_NET_BROADCAST, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"net_raw", CAP_NET_RAW, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"perfmon", CAP_PERFMON, CAP_SYS_ADMIN, CAPFLAG_BASE_FEATURE}, {"setfcap", CAP_SETFCAP, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"setgid", CAP_SETGID, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"setpcap", CAP_SETPCAP, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"setuid", CAP_SETUID, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"syslog", CAP_SYSLOG, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_admin", CAP_SYS_ADMIN, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_boot", CAP_SYS_BOOT, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_chroot", CAP_SYS_CHROOT, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_module", CAP_SYS_MODULE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_nice", CAP_SYS_NICE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_pacct", CAP_SYS_PACCT, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_ptrace", CAP_SYS_PTRACE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_rawio", CAP_SYS_RAWIO, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_resource", CAP_SYS_RESOURCE, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_time", CAP_SYS_TIME, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"sys_tty_config", CAP_SYS_TTY_CONFIG, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, {"wake_alarm", CAP_WAKE_ALARM, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE}, apparmor-5.0.2/parser/capability.h000066400000000000000000000034361522511161100171260ustar00rootroot00000000000000/* * Copyright (c) 2020 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ #ifndef __AA_CAPABILITY_H #define __AA_CAPABILITY_H #include #include #define NO_BACKMAP_CAP 0xff #ifndef CAP_AUDIT_WRITE #define CAP_AUDIT_WRITE 29 #endif #ifndef CAP_AUDIT_CONTROL #define CAP_AUDIT_CONTROL 30 #endif #ifndef CAP_SETFCAP #define CAP_SETFCAP 31 #endif #ifndef CAP_MAC_OVERRIDE #define CAP_MAC_OVERRIDE 32 #endif #ifndef CAP_AUDIT_READ #define CAP_AUDIT_READ 37 #endif #ifndef CAP_PERFMON #define CAP_PERFMON 38 #endif #ifndef CAP_BPF #define CAP_BPF 39 #endif #ifndef CAP_CHECKPOINT_RESTORE #define CAP_CHECKPOINT_RESTORE 40 #endif typedef enum capability_flags { CAPFLAGS_CLEAR = 0, CAPFLAG_BASE_FEATURE = 1, CAPFLAG_KERNEL_FEATURE = 2, CAPFLAG_POLICY_FEATURE = 4, CAPFLAG_EXTERNAL_FEATURE = 8, } capability_flags; int name_to_capability(const char *keyword); void __debug_capabilities(uint64_t capset, const char *name); bool add_cap_feature_mask(struct aa_features *features, capability_flags flags); void clear_cap_flag(capability_flags flags); int capability_backmap(unsigned int cap); bool capability_in_kernel(unsigned int cap); #endif /* __AA_CAPABILITY_H */ apparmor-5.0.2/parser/common_flags.h000066400000000000000000000016561522511161100174530ustar00rootroot00000000000000/* * Copyright (c) 2023 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ #ifndef __AA_COMMON_FLAGS_H #define __AA_COMMON_FLAGS_H typedef int optflags_t; typedef struct optflags { optflags_t control; optflags_t dump; optflags_t warn; optflags_t Werror; } optflags; extern optflags parseopts; #endif /* __AA_COMMON_FLAGS_H */ apparmor-5.0.2/parser/common_optarg.c000066400000000000000000000165301522511161100176430ustar00rootroot00000000000000/* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) * * Copyright (c) 2010 - 2014 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical, * Ltd. */ #include #include #include #include #include "common_optarg.h" #include "parser.h" optflag_table_t dumpflag_table[] = { { 1, "rule-exprs", "Dump rule to expr tree conversions", DUMP_DFA_RULE_EXPR }, { 1, "expr-stats", "Dump stats on expr tree", DUMP_DFA_TREE_STATS }, { 1, "expr-tree", "Dump expression tree", DUMP_DFA_TREE }, { 1, "expr-simplified", "Dump simplified expression tree", DUMP_DFA_SIMPLE_TREE }, { 1, "stats", "Dump all compile stats", DUMP_DFA_TREE_STATS | DUMP_DFA_STATS | DUMP_DFA_TRANS_STATS | DUMP_DFA_EQUIV_STATS | DUMP_DFA_DIFF_STATS }, { 1, "progress", "Dump progress for all compile phases", DUMP_DFA_PROGRESS | DUMP_DFA_STATS | DUMP_DFA_TRANS_PROGRESS | DUMP_DFA_TRANS_STATS | DUMP_DFA_DIFF_PROGRESS | DUMP_DFA_DIFF_STATS }, { 1, "dfa-progress", "Dump dfa creation as in progress", DUMP_DFA_PROGRESS | DUMP_DFA_STATS }, { 1, "dfa-stats", "Dump dfa creation stats", DUMP_DFA_STATS }, { 1, "dfa-states", "Dump final dfa state information", DUMP_DFA_STATES }, { 1, "dfa-compressed-states", "Dump compressed dfa state information", DUMP_DFA_COMPTRESSED_STATES }, { 1, "dfa-states-initial", "Dump dfa state immediately after initial build", DUMP_DFA_STATES_INIT }, { 1, "dfa-states-post-filter", "Dump dfa state immediately after filtering deny", DUMP_DFA_STATES_POST_FILTER }, { 1, "dfa-states-post-minimize", "Dump dfa state immediately after initial build", DUMP_DFA_STATES_POST_MINIMIZE }, { 1, "dfa-states-post-unreachable", "Dump dfa state immediately after filtering deny", DUMP_DFA_STATES_POST_UNREACHABLE }, { 1, "dfa-perms-build", "Dump permission being built from accept node", DUMP_DFA_PERMS }, { 1, "dfa-graph", "Dump dfa dot (graphviz) graph", DUMP_DFA_GRAPH }, { 1, "dfa-minimize", "Dump dfa minimization", DUMP_DFA_MINIMIZE }, { 1, "dfa-unreachable", "Dump dfa unreachable states", DUMP_DFA_UNREACHABLE }, { 1, "dfa-node-map", "Dump expr node set to state mapping", DUMP_DFA_NODE_TO_DFA }, { 1, "dfa-uniq-perms", "Dump unique perms", DUMP_DFA_UNIQ_PERMS }, { 1, "dfa-minimize-uniq-perms", "Dump unique perms post minimization", DUMP_DFA_MIN_UNIQ_PERMS }, { 1, "dfa-minimize-partitions", "Dump dfa minimization partitions", DUMP_DFA_MIN_PARTS }, { 1, "compress-progress", "Dump progress of compression", DUMP_DFA_TRANS_PROGRESS | DUMP_DFA_TRANS_STATS }, { 1, "compress-stats", "Dump stats on compression", DUMP_DFA_TRANS_STATS }, { 1, "compressed-dfa", "Dump compressed dfa", DUMP_DFA_TRANS_TABLE }, { 1, "equiv-stats", "Dump equivalence class stats", DUMP_DFA_EQUIV_STATS }, { 1, "equiv", "Dump equivalence class", DUMP_DFA_EQUIV }, { 1, "diff-encode", "Dump differential encoding", DUMP_DFA_DIFF_ENCODE }, { 1, "diff-stats", "Dump differential encoding stats", DUMP_DFA_DIFF_STATS }, { 1, "diff-progress", "Dump progress of differential encoding", DUMP_DFA_DIFF_PROGRESS | DUMP_DFA_DIFF_STATS }, { 1, "rule-merge", "dump information about rule merging", DUMP_RULE_MERGE}, { 1, "state32", "Dump encoding 32 bit states", DUMP_DFA_STATE32 }, { 1, "flags_table", "Dump encoding flags table", DUMP_DFA_FLAGS_TABLE }, { 0, NULL, NULL, 0 }, }; optflag_table_t dfaoptflag_table[] = { { 2, "0", "no optimizations", CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE | CONTROL_DFA_MINIMIZE | CONTROL_DFA_REMOVE_UNREACHABLE | CONTROL_DFA_DIFF_ENCODE | CONTROL_DFA_STATE32 | CONTROL_DFA_FLAGS_TABLE }, { 1, "equiv", "use equivalent classes", CONTROL_DFA_EQUIV }, { 1, "expr-normalize", "expression tree normalization", CONTROL_DFA_TREE_NORMAL }, { 1, "expr-simplify", "expression tree simplification", CONTROL_DFA_TREE_SIMPLE }, { 0, "expr-left-simplify", "left simplification first", CONTROL_DFA_TREE_LEFT }, { 2, "expr-right-simplify", "right simplification first", CONTROL_DFA_TREE_LEFT }, { 1, "minimize", "dfa state minimization", CONTROL_DFA_MINIMIZE }, { 1, "filter-deny", "filter out deny information from final dfa", CONTROL_DFA_FILTER_DENY }, { 1, "remove-unreachable", "dfa unreachable state removal", CONTROL_DFA_REMOVE_UNREACHABLE }, { 4, "compress-default", "use default level compression optimizations", CONTROL_DEFAULT_COMPRESS }, { 0, "compress-small", "do slower compression optimizations", CONTROL_DFA_TRANS_HIGH }, { 2, "compress-fast", "do faster compression optimizations", CONTROL_DFA_TRANS_HIGH }, { 1, "diff-encode", "Differentially encode transitions", CONTROL_DFA_DIFF_ENCODE }, { 1, "rule-merge", "turn on rule merging", CONTROL_RULE_MERGE}, { 1, "state32", "use 32 bit state transitions", CONTROL_DFA_STATE32 }, { 1, "flags-table", "use independent flags table", CONTROL_DFA_FLAGS_TABLE }, { 1, "zstd-recompress", "Recompress if the compressed level is higher than the existing cache", CONTROL_ZSTD_FLAGS_RECOMPRESS }, { 0, NULL, NULL, 0 }, }; void print_flag_table(optflag_table_t *table) { int i; unsigned int longest = 0; for (i = 0; table[i].option; i++) { if (strlen(table[i].option) > longest) longest = strlen(table[i].option); } printf("%-*s \t%s\n", longest, " show", "show flags that have been set and exit"); for (i = 0; table[i].option; i++) { printf("%5s%-*s \t%s\n", (table[i].control & OPT_FLAG_CONTROL_PREFIX_NO) ? "[no-]" : "", longest, table[i].option, table[i].desc); } } void print_flags(const char *prefix, optflag_table_t *table, optflags_t flags) { int i, count = 0; printf("%s=", prefix); for (i = 0; table[i].option; i++) { int flag_set = (table[i].flags & flags) == table[i].flags; if ((table[i].control != 2 && flag_set) || (table[i].control == 2 && !flag_set)) { if (count) printf(", "); printf("%s", table[i].option); count++; } } if (count) printf("\n"); } int handle_flag_table(optflag_table_t *table, const char *optarg, optflags_t *flags) { const char *arg = optarg; int i, invert = 0; if (strncmp(optarg, "no-", 3) == 0) { arg = optarg + 3; invert = 1; } for (i = 0; table[i].option; i++) { if (strcmp(table[i].option, arg) == 0) { /* check if leading no- was specified but is not * supported by the option */ if (invert && !(table[i].control & 1)) return 0; if (table[i].control & 2) invert |= 1; if (invert) *flags &= ~table[i].flags; else *flags |= table[i].flags; return 1; } } return 0; } void flagtable_help(const char *name, const char *header, const char *command, optflag_table_t *table) { display_version(); printf("\n%s: %s[Option]\n\n" "%s" "Options:\n" "--------\n" ,command, name, header); print_flag_table(table); } apparmor-5.0.2/parser/common_optarg.h000066400000000000000000000032331522511161100176440ustar00rootroot00000000000000/* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) * * Copyright (c) 2010 - 2014 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical, * Ltd. */ #ifndef __AA_COMMON_OPTARG_H #define __AA_COMMON_OPTARG_H #include "common_flags.h" #include "libapparmor_re/apparmor_re.h" /* * flag: 1 - allow no- inversion * flag: 2 - flags specified should be masked off * flag: 4 - flags specified are the default value */ #define OPT_FLAG_CONTROL_PREFIX_NO 1 #define OPT_FLAG_CONTROL_MASK 2 #define OPT_FLAG_CONTROL_DEFAULT 4 typedef struct { int control; const char *option; const char *desc; optflags_t flags; } optflag_table_t; extern optflag_table_t dumpflag_table[]; extern optflag_table_t dfaoptflag_table[]; void print_flags(const char *prefix, optflag_table_t *table, optflags_t flags); int handle_flag_table(optflag_table_t *table, const char *optarg, optflags_t *flags); void flagtable_help(const char *name, const char *header, const char *command, optflag_table_t *table); #endif /* __AA_COMMON_OPTARG_H */ apparmor-5.0.2/parser/cond_expr.cc000066400000000000000000000100601522511161100171130ustar00rootroot00000000000000/* * Copyright (c) 2024 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ #include #include "cond_expr.h" #include "parser.h" #include "symtab.h" cond_expr::cond_expr(bool result): result(result) { } cond_expr::cond_expr(const char *var, cond_op op) { if (op == BOOLEAN_VALUE) { int boolean = str_to_boolean(var); if (boolean == -1) { yyerror("Invalid boolean : '%s' is not true or false", var); } result = boolean; } else if (op == BOOLEAN_OP) { variable *ref = symtab::get_boolean_var(var); if (!ref) { yyerror(_("Unset boolean variable %s used in if-expression"), var); } result = ref->boolean; } else if (op == DEFINED_OP) { variable *ref = symtab::get_set_var(var); if (!ref) { result = false; } else { PDEBUG("Matched: defined set expr %s value %s\n", var, ref->expanded.begin()->c_str()); result = true; } } else PERROR("Invalid operation for if-expression"); } /* variables passed in conditionals can be variables or values. if the string passed has the formatting of a variable (@{}), then we should look for it in the symtab. if it's present in the symtab, expand its values and return the expanded set. if it's not present in the symtab, we should error out. if the string passed does not have the formatting of a variable, we should treat it as if it was a value. add it to a set and return it so comparisons can be made. */ std::set cond_expr::get_set(const char *var) { char *var_name = variable::process_var(var, true); if (!var_name) { /* not a variable */ return {var}; } variable *ref = symtab::lookup_existing_symbol(var_name); free(var_name); if (!ref) { yyerror(_("Error retrieving variable %s"), var); } if (ref->expand_variable() != 0) { /* expand_variable prints error messages already, so * exit quietly here */ exit(1); } return ref->expanded; } template void cond_expr::compare(cond_op op, const T &lhs, const T &rhs) { switch (op) { case GT_OP: result = lhs > rhs; break; case GE_OP: result = lhs >= rhs; break; case LT_OP: result = lhs < rhs; break; case LE_OP: result = lhs <= rhs; break; default: yyerror("Internal error: invalid comparison operator %d", (int)op); } } bool nullstr(char *p) { return p && !(*p); } long str_set_to_long(std::set &src, char **endptr) { long converted_src = 0; errno = 0; if (src.size() == 1 && !src.begin()->empty()) converted_src = strtol(src.begin()->c_str(), endptr, 0); if (errno == ERANGE) yyerror(_("Value out of valid range\n")); if (errno == EINVAL) yyerror(_("Value is not valid\n")); return converted_src; } cond_expr::cond_expr(const char *lhv, cond_op op, const char *rhv) { std::set lhs = get_set(lhv); std::set rhs = get_set(rhv); char *p_lhs = NULL, *p_rhs = NULL; long converted_lhs = 0, converted_rhs = 0; if (op == IN_OP) { /* if lhs is a subset of rhs */ result = std::includes(rhs.begin(), rhs.end(), lhs.begin(), lhs.end()); return; } else if (op == EQ_OP) { result = lhs == rhs; return; } else if (op == NE_OP) { result = lhs != rhs; return; } converted_lhs = str_set_to_long(lhs, &p_lhs); converted_rhs = str_set_to_long(rhs, &p_rhs); if (!nullstr(p_lhs) && !nullstr(p_rhs)) { /* sets */ compare(op, lhs, rhs); } else if (nullstr(p_lhs) && nullstr(p_rhs)) { /* numbers */ compare(op, converted_lhs, converted_rhs); } else { yyerror(_("Can only compare numbers with numbers\n")); } } apparmor-5.0.2/parser/cond_expr.h000066400000000000000000000024211522511161100167570ustar00rootroot00000000000000/* * Copyright (c) 2024 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ #ifndef __AA_COND_EXPR_H #define __AA_COND_EXPR_H #include #include typedef enum { EQ_OP, NE_OP, IN_OP, GT_OP, GE_OP, LT_OP, LE_OP, BOOLEAN_OP, DEFINED_OP, BOOLEAN_VALUE, } cond_op; class cond_expr { private: bool result; public: cond_expr(bool result); cond_expr(const char *var, cond_op op); cond_expr(const char *var, cond_op op, const char *cond_id); std::set get_set(const char *var); template void compare(cond_op op, const T &lhs, const T &rhs); virtual ~cond_expr() { }; bool eval(void) { return result; } }; #endif /* __AA_COND_EXPR_H */ apparmor-5.0.2/parser/dbus.cc000066400000000000000000000200561522511161100160750ustar00rootroot00000000000000/* * Copyright (c) 2013 * Canonical, Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ #include #include #include #include #include #include #include "parser.h" #include "profile.h" #include "parser_yacc.h" #include "dbus.h" int parse_dbus_perms(const char *str_perms, perm32_t *perms, int fail) { return parse_X_perms("DBus", AA_VALID_DBUS_PERMS, str_perms, perms, fail); } void dbus_rule::move_conditionals(struct cond_entry *conds) { struct cond_entry *cond_ent; list_for_each(conds, cond_ent) { /* for now allow only '=' */ if (cond_ent->comp != cond_comp::EQ) yyerror("only \"=\" allowed in conditions of dbus rules\n"); if (list_len(cond_ent->vals) > 1) yyerror("dbus conditional \"%s\" only supports a single value\n", cond_ent->name); if (strcmp(cond_ent->name, "bus") == 0) { move_conditional_value("dbus", &bus, cond_ent); } else if (strcmp(cond_ent->name, "name") == 0) { move_conditional_value("dbus", &name, cond_ent); } else if (strcmp(cond_ent->name, "label") == 0) { move_conditional_value("dbus", &peer_label, cond_ent); } else if (strcmp(cond_ent->name, "path") == 0) { move_conditional_value("dbus", &path, cond_ent); } else if (strcmp(cond_ent->name, "interface") == 0) { move_conditional_value("dbus", &interface, cond_ent); } else if (strcmp(cond_ent->name, "member") == 0) { move_conditional_value("dbus", &member, cond_ent); } else { yyerror("invalid dbus conditional \"%s\"\n", cond_ent->name); } } } dbus_rule::dbus_rule(perm32_t perms_p, struct cond_entry *conds, struct cond_entry *peer_conds): perms_rule_t(AA_CLASS_DBUS), bus(NULL), name(NULL), peer_label(NULL), path(NULL), interface(NULL), member(NULL) { int name_is_subject_cond = 0, message_rule = 0, service_rule = 0; /* Move the global/subject conditionals over & check the results */ move_conditionals(conds); if (name) name_is_subject_cond = 1; if (peer_label) yyerror("dbus \"label\" conditional can only be used inside of the \"peer=()\" grouping\n"); /* Move the peer conditionals */ move_conditionals(peer_conds); if (path || interface || member || peer_label || (name && !name_is_subject_cond)) message_rule = 1; if (name && name_is_subject_cond) service_rule = 1; if (message_rule && service_rule) yyerror("dbus rule contains message conditionals and service conditionals\n"); /* Copy perms. If no perms was specified, assign an implied perms. */ if (perms_p) { perms = perms_p; if (perms & ~AA_VALID_DBUS_PERMS) yyerror("perms contains unknown dbus access\n"); else if (message_rule && (perms & AA_DBUS_BIND)) yyerror("dbus \"bind\" access cannot be used with message rule conditionals\n"); else if (service_rule && (perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE))) yyerror("dbus \"send\" and/or \"receive\" accesses cannot be used with service rule conditionals\n"); else if (perms & AA_DBUS_EAVESDROP && (path || interface || member || peer_label || name)) { yyerror("dbus \"eavesdrop\" access can only contain a bus conditional\n"); } } else { if (message_rule) perms = (AA_DBUS_SEND | AA_DBUS_RECEIVE); else if (service_rule) perms = (AA_DBUS_BIND); else perms = AA_VALID_DBUS_PERMS; } free_cond_list(conds); free_cond_list(peer_conds); } ostream &dbus_rule::dump(ostream &os) { class_rule_t::dump(os); os << " ( "; /* override default perms */ if (perms & AA_DBUS_SEND) os << "send "; if (perms & AA_DBUS_RECEIVE) os << "receive "; if (perms & AA_DBUS_BIND) os << "bind "; if (perms & AA_DBUS_EAVESDROP) os << "eavesdrop "; os << ")"; if (bus) os << " bus=\"" << bus << "\""; if ((perms & AA_DBUS_BIND) && name) os << " name=\"" << name << "\""; if (path) os << " path=\"" << path << "\""; if (interface) os << " interface=\"" << interface << "\""; if (member) os << " member=\"" << member << "\""; if (!(perms & AA_DBUS_BIND) && (peer_label || name)) { os << " peer=( "; if (peer_label) os << "label=\"" << peer_label << "\" "; if (name) os << "name=\"" << name << "\" "; os << ")"; } os << ",\n"; return os; } int dbus_rule::expand_variables(void) { int error = expand_entry_variables(&bus); if (error) return error; error = expand_entry_variables(&name); if (error) return error; error = expand_entry_variables(&peer_label); if (error) return error; error = expand_entry_variables(&path); if (error) return error; filter_slashes(path); error = expand_entry_variables(&interface); if (error) return error; error = expand_entry_variables(&member); if (error) return error; return 0; } void dbus_rule::warn_once(const char *name) { rule_t::warn_once(name, "dbus rules not enforced"); } int dbus_rule::gen_policy_re(Profile &prof) { std::string busbuf; std::string namebuf; std::string peer_labelbuf; std::string pathbuf; std::string ifacebuf; std::string memberbuf; std::ostringstream buffer; const char *vec[6]; pattern_t ptype; int pos; if (!features_supports_dbus) { warn_once(prof.name); return RULE_NOT_SUPPORTED; } buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_DBUS; busbuf.append(buffer.str()); if (bus) { ptype = convert_aaregex_to_pcre(bus, 0, glob_default, busbuf, &pos); if (ptype == ePatternInvalid) goto fail; } else { /* match any char except \000 0 or more times */ busbuf.append(default_match_pattern); } vec[0] = busbuf.c_str(); if (name) { ptype = convert_aaregex_to_pcre(name, 0, glob_default, namebuf, &pos); if (ptype == ePatternInvalid) goto fail; vec[1] = namebuf.c_str(); } else { /* match any char except \000 0 or more times */ vec[1] = default_match_pattern; } if (peer_label) { ptype = convert_aaregex_to_pcre(peer_label, 0, glob_default, peer_labelbuf, &pos); if (ptype == ePatternInvalid) goto fail; vec[2] = peer_labelbuf.c_str(); } else { /* match any char except \000 0 or more times */ vec[2] = default_match_pattern; } if (path) { ptype = convert_aaregex_to_pcre(path, 0, glob_default, pathbuf, &pos); if (ptype == ePatternInvalid) goto fail; vec[3] = pathbuf.c_str(); } else { /* match any char except \000 0 or more times */ vec[3] = default_match_pattern; } if (interface) { ptype = convert_aaregex_to_pcre(interface, 0, glob_default, ifacebuf, &pos); if (ptype == ePatternInvalid) goto fail; vec[4] = ifacebuf.c_str(); } else { /* match any char except \000 0 or more times */ vec[4] = default_match_pattern; } if (member) { ptype = convert_aaregex_to_pcre(member, 0, glob_default, memberbuf, &pos); if (ptype == ePatternInvalid) goto fail; vec[5] = memberbuf.c_str(); } else { /* match any char except \000 0 or more times */ vec[5] = default_match_pattern; } if (perms & AA_DBUS_BIND) { if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms & AA_DBUS_BIND, audit == AUDIT_FORCE ? perms & AA_DBUS_BIND : 0, 2, vec, parseopts, false)) goto fail; } if (perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) { if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE), audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0, 6, vec, parseopts, false)) goto fail; } if (perms & AA_DBUS_EAVESDROP) { if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms & AA_DBUS_EAVESDROP, audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0, 1, vec, parseopts, false)) goto fail; } return RULE_OK; fail: return RULE_ERROR; } apparmor-5.0.2/parser/dbus.h000066400000000000000000000046621522511161100157440ustar00rootroot00000000000000/* * Copyright (c) 2013 * Canonical, Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ #ifndef __AA_DBUS_H #define __AA_DBUS_H #include "parser.h" #include "rule.h" #include "profile.h" extern int parse_dbus_perms(const char *str_mode, perm32_t *mode, int fail); class dbus_rule: public perms_rule_t { void move_conditionals(struct cond_entry *conds); public: char *bus; /** * Be careful! ->name can be the subject or the peer name, depending on * whether the rule is a bind rule or a send/receive rule. See the * comments in new_dbus_entry() for details. */ char *name; char *peer_label; char *path; char *interface; char *member; dbus_rule(perm32_t perms_p, struct cond_entry *conds, struct cond_entry *peer_conds); ~dbus_rule() override { free(bus); free(name); free(peer_label); free(path); free(interface); free(member); }; bool valid_prefix(const prefixes &p, const char *&error) override { if (p.owner != OWNER_UNSPECIFIED) { error = "owner prefix not allowed on dbus rules"; return false; } return true; }; ostream &dump(ostream &os) override; int expand_variables(void) override; int gen_policy_re(Profile &prof) override; bool is_mergeable(void) override { return true; } int cmp(rule_t const &rhs) const override { int res = perms_rule_t::cmp(rhs); if (res) return res; dbus_rule const &trhs = (rule_cast(rhs)); res = null_strcmp(bus, trhs.bus); if (res) return res; res = null_strcmp(name, trhs.name); if (res) return res; res = null_strcmp(peer_label, trhs.peer_label); if (res) return res; res = null_strcmp(path, trhs.path); if (res) return res; res = null_strcmp(interface, trhs.interface); if (res) return res; return null_strcmp(member, trhs.member); }; protected: void warn_once(const char *name) override; }; #endif /* __AA_DBUS_H */ apparmor-5.0.2/parser/default_features.c000066400000000000000000000126461522511161100203250ustar00rootroot00000000000000/* * This file contains a set of old feature files that are used under different * circumstances. * * match_n_abi: feature abi for oldest match_file (pre features) abi. * * match_c_abi: features abi for match_file (pre features) abi that supports * create. * * match_cn_abi: features abi for match_file (pre features) abi that supports * create and network. * * default_features_abi: is the feature abi used when policy is not tagged * with an abi and no featuere-abi was specified to the * parser. */ #include "parser.h" const char *match_n_abi = "caps {mask {chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read\ }\ }\ rlimit {mask {cpu fsize data stack core rss nproc nofile memlock as locks sigpending msgqueue nice rtprio rttime\ }\ }\ capability {0xffffff\ }\ network {af_unix {yes\ }\ af_mask {unspec unix inet ax25 ipx appletalk netrom bridge atmpvc x25 inet6 rose netbeui security key netlink packet ash econet atmsvc rds sna irda pppox wanpipe llc ib mpls can tipc bluetooth iucv rxrpc isdn phonet ieee802154 caif alg nfc vsock kcm qipcrtr smc xdp\ }\ }\ file {mask {read write exec append mmap_exec link lock\ }\ }\ domain {change_profile {yes\ }\ change_onexec {yes\ }\ change_hatv {yes\ }\ change_hat {yes\ }\ }\ policy {\ v6 {yes\ }\ v5 {yes\ }\ }\ }\ "; /****************************** match_c_abi *******************************/ const char *match_c_abi = "caps {mask {chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read\ }\ }\ rlimit {mask {cpu fsize data stack core rss nproc nofile memlock as locks sigpending msgqueue nice rtprio rttime\ }\ }\ capability {0xffffff\ }\ file {mask {create read write exec append mmap_exec link lock\ }\ }\ domain {change_profile {yes\ }\ change_onexec {yes\ }\ change_hatv {yes\ }\ change_hat {yes\ }\ }\ policy {\ v6 {yes\ }\ v5 {yes\ }\ }\ }\ "; /****************************** match_cn_abi ******************************/ const char *match_cn_abi = "caps {mask {chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read\ }\ }\ rlimit {mask {cpu fsize data stack core rss nproc nofile memlock as locks sigpending msgqueue nice rtprio rttime\ }\ }\ capability {0xffffff\ }\ network {af_unix {yes\ }\ af_mask {unspec unix inet ax25 ipx appletalk netrom bridge atmpvc x25 inet6 rose netbeui security key netlink packet ash econet atmsvc rds sna irda pppox wanpipe llc ib mpls can tipc bluetooth iucv rxrpc isdn phonet ieee802154 caif alg nfc vsock kcm qipcrtr smc xdp\ }\ }\ file {mask {create read write exec append mmap_exec link lock\ }\ }\ domain {change_profile {yes\ }\ change_onexec {yes\ }\ change_hatv {yes\ }\ change_hat {yes\ }\ }\ policy {\ v6 {yes\ }\ v5 {yes\ }\ }\ }\ "; /************************** deafult_features_abi ***************************/ const char *default_features_abi = "query {label {multi_transaction {yes\ }\ data {yes\ }\ perms {allow deny audit quiet\ }\ }\ }\ dbus {mask {acquire send receive\ }\ }\ signal {mask {hup int quit ill trap abrt bus fpe kill usr1 segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg xcpu xfsz vtalrm prof winch io pwr sys emt lost\ }\ }\ ptrace {mask {read trace\ }\ }\ caps {mask {chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read perfmon bpf\ }\ }\ rlimit {mask {cpu fsize data stack core rss nproc nofile memlock as locks sigpending msgqueue nice rtprio rttime\ }\ }\ capability {0xffffff\ }\ namespaces {pivot_root {no\ }\ profile {yes\ }\ }\ mount {mask {mount umount pivot_root\ }\ }\ network {af_unix {yes\ }\ af_mask {unspec unix inet ax25 ipx appletalk netrom bridge atmpvc x25 inet6 rose netbeui security key netlink packet ash econet atmsvc rds sna irda pppox wanpipe llc ib mpls can tipc bluetooth iucv rxrpc isdn phonet ieee802154 caif alg nfc vsock kcm qipcrtr smc xdp\ }\ }\ file {mask {create read write exec append mmap_exec link lock\ }\ }\ domain {version {1.2\ }\ }\ computed_longest_left {yes\ }\ post_nnp_subset {yes\ }\ fix_binfmt_elf_mmap {yes\ }\ stack {yes\ }\ change_profile {yes\ }\ change_onexec {yes\ }\ change_hatv {yes\ }\ change_hat {yes\ }\ }\ policy {set_load {yes\ }\ versions {v8 {yes\ }\ v7 {yes\ }\ v6 {yes\ }\ v5 {yes\ }\ }\ }\ "; apparmor-5.0.2/parser/file_cache.h000066400000000000000000000024271522511161100170460ustar00rootroot00000000000000/* * Copyright (c) 2021 * Canonical, Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Canonical Ltd. */ #ifndef __AA_FILE_CACHE_H #define __AA_FILE_CACHE_H #include #include /* TODO: have includecache be a frontend for file cache, don't just * store name. */ class IncludeCache_t { public: std::set cache; IncludeCache_t() = default; virtual ~IncludeCache_t() = default; /* return true if in set */ bool find(const char *name) { return cache.find(name) != cache.end(); } bool insert(const char *name) { std::pair::iterator,bool> res = cache.insert(name); if (res.second == false) { return false; } /* inserted */ return true; } }; #endif /* __AA_FILE_CACHE_H */ apparmor-5.0.2/parser/immunix.h000066400000000000000000000154071522511161100164740ustar00rootroot00000000000000/* * Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) * * Immunix AppArmor LSM * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2 of the * License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. */ #ifndef _IMMUNIX_H #define _IMMUNIX_H #define AA_USER_SHIFT 0 #define AA_OTHER_SHIFT 14 #define MAY_EXEC_SHIFT 0 #define MAY_OTHER_EXEC_SHIFT (MAY_EXEC_SHIFT + AA_OTHER_SHIFT) #define EXEC_MMAP_SHIFT 6 #define EXEC_OTHER_MMAP_SHIFT (EXEC_MMAP_SHIFT + AA_OTHER_SHIFT) /* * Modeled after MAY_READ, MAY_WRITE, MAY_EXEC in the kernel. The value of * AA_MAY_EXEC must be identical to MAY_EXEC, etc. */ #define AA_MAY_EXEC (1 << 0) #define AA_MAY_WRITE (1 << 1) #define AA_MAY_READ (1 << 2) #define AA_MAY_APPEND (1 << 3) #define AA_OLD_MAY_LINK (1 << 4) #define AA_OLD_MAY_LOCK (1 << 5) #define AA_OLD_EXEC_MMAP (1 << 6) #define AA_EXEC_PUX (1 << 7) #define AA_EXEC_UNSAFE (1 << 8) #define AA_EXEC_INHERIT (1 << 9) #define AA_EXEC_MOD_0 (1 << 10) #define AA_EXEC_MOD_1 (1 << 11) #define AA_EXEC_MOD_2 (1 << 12) #define AA_EXEC_MOD_3 (1 << 13) #define AA_BASE_PERMS (AA_MAY_EXEC | AA_MAY_WRITE | \ AA_MAY_READ | AA_MAY_APPEND | \ AA_OLD_MAY_LINK | AA_OLD_MAY_LOCK | \ AA_EXEC_PUX | AA_OLD_EXEC_MMAP | \ AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \ AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \ AA_EXEC_MOD_2 | AA_EXEC_MOD_3) #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_CHANGE_HAT (1 << 30) #define AA_ONEXEC (1 << 30) #define AA_CHANGE_PROFILE (1U << 31) #define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE) #define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \ AA_EXEC_MOD_2 | AA_EXEC_MOD_3) #define AA_EXEC_COUNT 16 #define AA_USER_EXEC_MODIFIERS (AA_EXEC_MODIFIERS << AA_USER_SHIFT) #define AA_OTHER_EXEC_MODIFIERS (AA_EXEC_MODIFIERS << AA_OTHER_SHIFT) #define AA_ALL_EXEC_MODIFIERS (AA_USER_EXEC_MODIFIERS | \ AA_OTHER_EXEC_MODIFIERS) #define AA_EXEC_TYPE (AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \ AA_EXEC_PUX | AA_EXEC_MODIFIERS) #define AA_EXEC_UNCONFINED (AA_EXEC_MOD_0) #define AA_EXEC_PROFILE (AA_EXEC_MOD_1) #define AA_EXEC_LOCAL (AA_EXEC_MOD_0 | AA_EXEC_MOD_1) #define AA_VALID_PERMS (AA_FILE_PERMS | AA_OTHER_PERMS) #define AA_USER_EXEC (AA_MAY_EXEC << AA_USER_SHIFT) #define AA_OTHER_EXEC (AA_MAY_EXEC << 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 AA_USER_EXEC_TYPE (AA_EXEC_TYPE << AA_USER_SHIFT) #define AA_OTHER_EXEC_TYPE (AA_EXEC_TYPE << AA_OTHER_SHIFT) #define ALL_AA_EXEC_TYPE (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE) #define ALL_USER_EXEC (AA_USER_EXEC | AA_USER_EXEC_TYPE) #define ALL_OTHER_EXEC (AA_OTHER_EXEC | AA_OTHER_EXEC_TYPE) #define AA_USER_EXEC_INHERIT (AA_EXEC_INHERIT << AA_USER_SHIFT) #define AA_OTHER_EXEC_INHERIT (AA_EXEC_INHERIT << AA_OTHER_SHIFT) #define AA_USER_EXEC_MMAP (AA_OLD_EXEC_MMAP << AA_USER_SHIFT) #define AA_OTHER_EXEC_MMAP (AA_OLD_EXEC_MMAP << AA_OTHER_SHIFT) #define AA_LINK_BITS ((AA_OLD_MAY_LINK << AA_USER_SHIFT) | \ (AA_OLD_MAY_LINK << AA_OTHER_SHIFT)) #define SHIFT_PERMS(MODE, SHIFT) ((((MODE) & AA_BASE_PERMS) << (SHIFT))\ | ((MODE) & ~AA_FILE_PERMS)) #define SHIFT_TO_BASE(MODE, SHIFT) ((((MODE) & AA_FILE_PERMS) >> (SHIFT))\ | ((MODE) & ~AA_FILE_PERMS)) #define AA_LINK_SUBSET_TEST (AA_OLD_MAY_LINK << 1) #define LINK_SUBSET_BITS ((AA_LINK_SUBSET_TEST << AA_USER_SHIFT) | \ (AA_LINK_SUBSET_TEST << AA_OTHER_SHIFT)) #define LINK_TO_LINK_SUBSET(X) (((X) << 1) & AA_LINK_SUBSET_TEST) /* Pack the audit, and quiet masks into a single 28 bit field in the * format oq:oa:uq:ua */ #define PACK_AUDIT_CTL(audit, quiet) (((audit) & 0x1fc07f) | \ (((quiet) & 0x1fc07f) << 7)) #define AA_HAT_SIZE 975 /* Maximum size of a subdomain * ident (hat) */ #define AA_IP_TCP 0x0001 #define AA_IP_UDP 0x0002 #define AA_IP_RDP 0x0004 #define AA_IP_RAW 0x0008 #define AA_IPV6_TCP 0x0010 #define AA_IPV6_UDP 0x0020 #define AA_NETLINK 0x0040 enum pattern_t { ePatternBasic, ePatternTailGlob, ePatternRegex, ePatternInvalid, }; #define HAS_MAY_READ(mode) ((mode) & AA_MAY_READ) #define HAS_MAY_WRITE(mode) ((mode) & AA_MAY_WRITE) #define HAS_MAY_APPEND(mode) ((mode) & AA_MAY_APPEND) #define HAS_MAY_EXEC(mode) ((mode) & AA_MAY_EXEC) #define HAS_MAY_LINK(mode) ((mode) & AA_OLD_MAY_LINK) #define HAS_MAY_LOCK(mode) ((mode) & AA_OLD_MAY_LOCK) #define HAS_EXEC_MMAP(mode) ((mode) & AA_OLD_EXEC_MMAP) #define HAS_EXEC_UNSAFE(mode) ((mode) & AA_EXEC_UNSAFE) #define HAS_CHANGE_PROFILE(mode) ((mode) & AA_CHANGE_PROFILE) #ifdef DEBUG #include #include #define PDEBUG(fmt, args...) \ do { \ int pdebug_error = errno; \ fprintf(stderr, "parser: " fmt, ## args); \ errno = pdebug_error; \ } while (0) #else #define PDEBUG(fmt, args...) /* Do nothing */ #endif static inline int is_merged_x_consistent(int a, int b) { if ((a & AA_USER_EXEC) && (b & AA_USER_EXEC) && ((a & AA_USER_EXEC_TYPE) != (b & AA_USER_EXEC_TYPE))) { PDEBUG("failed user merge 0x%x 0x%x\n", a, b); return 0; } if ((a & AA_OTHER_EXEC) && (b & AA_OTHER_EXEC) && ((a & AA_OTHER_EXEC_TYPE) != (b & AA_OTHER_EXEC_TYPE))) { PDEBUG("failed other merge 0x%x 0x%x\n", a, b); return 0; } return 1; } /* Arbitrary max and minimum priority that userspace can specify, * internally we handle up to MAX_INTERNAL_PRIORITY and * MIN_INTERNAL_PRIORITY. Do not ever allow INT_MAX, or INT_MIN * because cmp uses subtraction and it can cause overflow. Ensure we * don't over/underflow make internal max/min one more than allowed on * rules. * * see * note on mediates_priority */ #define MIN_POLICY_PRIORITY (-1000) #define MAX_POLICY_PRIORITY (1000) /* internally we need a priority that any policy based rule can override * and a priority that no policy based rule can override. These are * used on rules encoding what abi/classes are supported by the * compiled policy. */ #define MIN_INTERNAL_PRIORITY (MIN_POLICY_PRIORITY - 1) #define MAX_INTERNAL_PRIORITY (MAX_POLICY_PRIORITY + 1) #endif /* ! _IMMUNIX_H */ /* LocalWords: MMAP */ apparmor-5.0.2/parser/io_uring.cc000066400000000000000000000070161522511161100167540ustar00rootroot00000000000000/* * Copyright (c) 2023 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Canonical Ltd. */ #include "common_optarg.h" #include "parser.h" #include "profile.h" #include "io_uring.h" #include #include #include #include void io_uring_rule::move_conditionals(struct cond_entry *conds) { struct cond_entry *cond_ent; list_for_each(conds, cond_ent) { /* for now allow only '=' */ if (cond_ent->comp != cond_comp::EQ) yyerror("only \"=\" allowed in conditions of io_uring rules\n"); if (list_len(cond_ent->vals) > 1) yyerror("io_uring conditional \"%s\" only supports a single value\n", cond_ent->name); if (strcmp(cond_ent->name, "label") == 0) { move_conditional_value("io_uring", &label, cond_ent); } else { yyerror("invalid io_uring conditional \"%s\"\n", cond_ent->name); } } } io_uring_rule::io_uring_rule(perm32_t perms_p, struct cond_entry *conds, struct cond_entry *ring_conds): perms_rule_t(AA_CLASS_IO_URING), label(NULL) { if (perms_p) { if (perms_p & ~AA_VALID_IO_URING_PERMS) { yyerror("perms contains invalid permissions for io_uring\n"); } perms = perms_p; } else { /* default to all perms */ perms = AA_VALID_IO_URING_PERMS; } move_conditionals(conds); move_conditionals(ring_conds); free_cond_list(conds); free_cond_list(ring_conds); } ostream &io_uring_rule::dump(ostream &os) { class_rule_t::dump(os); if (perms != AA_VALID_IO_URING_PERMS) { os << " ( "; if (perms & AA_IO_URING_OVERRIDE_CREDS) os << "override_creds "; if (perms & AA_IO_URING_SQPOLL) os << " sqpoll "; os << ")"; } if (label) os << " label=" << label; os << ",\n"; return os; } int io_uring_rule::expand_variables(void) { return 0; } void io_uring_rule::warn_once(const char *name) { rule_t::warn_once(name, "io_uring rules not enforced"); } int io_uring_rule::gen_policy_re(Profile &prof) { std::ostringstream buffer; std::string buf, labelbuf; if (!features_supports_io_uring) { warn_once(prof.name); return RULE_NOT_SUPPORTED; } buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_IO_URING; buf = buffer.str(); if (label) { if (!convert_entry(labelbuf, label)) goto fail; buffer << labelbuf; } else { buffer << default_match_pattern; } if (perms & AA_VALID_IO_URING_PERMS) { if (!prof.policy.rules->add_rule(buf.c_str(), priority, rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, parseopts)) goto fail; /* add a mediates_io_uring rule for every rule added. It * needs to be the same priority */ if (!prof.policy.rules->add_rule(buf.c_str(), priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto fail; if (perms & AA_IO_URING_OVERRIDE_CREDS) { buf = buffer.str(); /* update buf to have label */ if (!prof.policy.rules->add_rule(buf.c_str(), priority, rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, parseopts)) goto fail; } } return RULE_OK; fail: return RULE_ERROR; } apparmor-5.0.2/parser/io_uring.h000066400000000000000000000034271522511161100166200ustar00rootroot00000000000000/* * Copyright (c) 2023 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Canonical Ltd. */ #ifndef __AA_IO_URING_H #define __AA_IO_URING_H #include "parser.h" #define AA_IO_URING_OVERRIDE_CREDS AA_MAY_APPEND #define AA_IO_URING_SQPOLL AA_MAY_CREATE #define AA_VALID_IO_URING_PERMS (AA_IO_URING_OVERRIDE_CREDS | \ AA_IO_URING_SQPOLL) class io_uring_rule: public perms_rule_t { void move_conditionals(struct cond_entry *conds); public: char *label; io_uring_rule(perm32_t perms, struct cond_entry *conds, struct cond_entry *ring_conds); ~io_uring_rule() override { free(label); }; bool valid_prefix(const prefixes &p, const char *&error) override { if (p.owner) { error = _("owner prefix not allowed on io_uring rules"); return false; } return true; }; ostream &dump(ostream &os) override; int expand_variables(void) override; int gen_policy_re(Profile &prof) override; bool is_mergeable(void) override { return true; } int cmp(rule_t const &rhs) const override { int res = perms_rule_t::cmp(rhs); if (res) return res; return null_strcmp(label, (rule_cast(rhs)).label); }; protected: void warn_once(const char *name) override; }; #endif /* __AA_IO_URING_H */ apparmor-5.0.2/parser/lib.c000066400000000000000000000324511522511161100155450ustar00rootroot00000000000000/* * Copyright (c) 2012 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical, * Ltd. */ #include #include #include #include #include #include #include #include "lib.h" #include "parser.h" using namespace std; int dirat_for_each(int dirfd, const char *name, void *data, int (* cb)(int, const char *, struct stat *, void *)) { int retval = _aa_dirat_for_each(dirfd, name, data, cb); if (retval) PDEBUG("dirat_for_each failed: %m\n"); return retval; } int write_compressed_with_user_header(int fd, const char *compressed, size_t compressed_size, uint8_t compress_level) { struct compr_user_header uhdr = { .version = COMPR_USER_HDR_VERSION, .compress_level = compress_level, .padding = {0}, }; struct iovec iov[2] = { { .iov_base = &uhdr, .iov_len = sizeof(uhdr) }, { .iov_base = (void *)compressed, .iov_len = compressed_size }, }; size_t total = sizeof(uhdr) + compressed_size; ssize_t wsize = writev(fd, iov, 2); if (wsize < 0) return -1; if ((size_t)wsize < total) { errno = EIO; return -1; } return 0; } /** * isodigit - test if a character is an octal digit * @c: character to test * * Returns: true if an octal digit, else false */ int isodigit(char c) { return (c >= '0' && c <= '7') ? true : false; } /* convert char character 0..9a..z into a number 0-35 * * Returns: digit value of character or -1 if character is invalid */ static int chrtoi(char c, int base) { int val = -1; if (base < 2 || base > 36) return -1; if (isdigit(c)) val = c - '0'; else if (isalpha(c) && isascii(c)) val = tolower(c) - 'a' + 10; if (val >= base) return -1; return val; } /** * strntol - convert a sequence of characters as a hex number * @str: pointer to a string of character to convert * @endptr: RETURNS: if not NULL, the first char after converted chars. * @base: base of convertion * @maxval: maximum value. don't consume next char if value will exceed @maxval * @n: maximum number of characters to consume doing the conversion * * Returns: converted number. If there is no conversion 0 is returned and * *@endptr = @str * * Not a complete replacement for strtol yet, Does not process base prefixes, * nor +/- sign yet. * * - take the largest sequence of character that is in range of 0-@maxval * - will consume the minimum of @maxlen or @base digits in @maxval * - if there is not n valid characters for the base only the n-1 will be taken * eg. for the sequence string 4z with base 16 only 4 will be taken as the * hex number */ long strntol(const char *str, const char **endptr, int base, long maxval, size_t n) { long c, val = 0; if (base > 1 && base < 37) { for (; n && (c = chrtoi(*str, base)) != -1; str++, n--) { long tmp = (val * base) + c; if (tmp > maxval) break; val = tmp; } } if (endptr) *endptr = str; return val; } /** * strn_escseq - * @pos: position of first character in esc sequence * @chrs: list of exact return chars to support eg. \+ returns + instead of -1 * @n: maximum length of string to processes * * Returns: character for escape sequence or -1 if an error * * pos will point to first character after esc sequence * OR * pos will point to first character where an error was discovered * errors can be unrecognized esc character, octal, decimal, or hex * character encoding with no valid number. eg. \xT */ int strn_escseq(const char **pos, const char *chrs, size_t n) { const char *end; long tmp; if (n < 1) return -1; if (isodigit(**pos)) { tmp = strntol(*pos, &end, 8, 255, min((size_t) 3, n)); if (tmp == 0 && end == *pos) { /* this should never happen because of isodigit test */ return -1; } *pos = end; return tmp; } char c = *(*pos)++; n--; // n > 1 due to test above. switch(c) { case '\\': return '\\'; case '"': return '"'; case 'd': tmp = strntol(*pos, &end, 10, 255, min((size_t) 3, n)); if (tmp == 0 && end == *pos) { /* \d no valid encoding */ return -1; } *pos = end; return tmp; case 'x': tmp = strntol(*pos, &end, 16, 255, min((size_t) 2, n)); if (tmp == 0 && end == *pos) { /* \x no valid encoding */ return -1; } *pos = end; return tmp; case 'a': return '\a'; case 'e': return 033 /* ESC */; case 'f': return '\f'; case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; } if (strchr(chrs, c)) return c; /* unsupported escape sequence, backup to return that char */ (*pos)--; return -1; } int str_escseq(const char **pos, const char *chrs) { /* no len limit just use end of string, yes could use strlen(pos) */ return strn_escseq(pos, chrs, SIZE_MAX); } #ifdef UNIT_TEST #include "lib.h" #include "parser.h" #include "unit_test.h" static int test_oct(const char *str) { const char *end; long retval = strntol(str, &end, 8, 255, 3); if (retval == 0 && str == end) return -1; return retval; } static int test_dec(const char *str) { const char *end; long retval = strntol(str, &end, 10, 255, 3); if (retval == 0 && str == end) return -1; return retval; } static int test_hex(const char *str) { const char *end; long retval = strntol(str, &end, 16, 255, 2); if (retval == 0 && str == end) return -1; return retval; } int main(void) { int rc = 0; int retval; struct test_struct { const char *test; /* test string */ int expected; /* expected result */ const char *msg; /* failure message */ }; struct test_struct oct_tests[] = { { "0a", 0, "oct conversion of \\0a failed" }, { "00000003a", 0, "oct conversion of \\00000003a failed" }, { "62", 50, "oct conversion of \\62 failed" }, { "623", 50, "oct conversion of \\623 failed" }, { "123", 83, "oct conversion of \\123 failed" }, { "123;", 83, "oct conversion of \\123; failed" }, { "2234", 147, "oct conversion of \\2234 failed" }, { "xx", -1, "oct conversion of \\xx failed" }, { NULL, 0, NULL } }; struct test_struct dec_tests[] = { { "0a", 0, "dec conversion of \\d0a failed" }, { "00000003a", 0, "dec conversion of \\d00000003a failed" }, { "62", 62, "dec conversion of \\d62 failed" }, { "623", 62, "dec conversion of \\d623 failed" }, { "132", 132, "dec conversion of \\d132 failed" }, { "132UL", 132, "dec conversion of \\d132UL failed" }, { "255", 255, "dec conversion of \\d255 failed" }, { "256", 25, "dec conversion of \\d256 failed" }, { "2234", 223, "dec conversion of \\d2234 failed" }, { "xx", -1, "dec conversion of \\dxx failed" }, { NULL, 0, NULL } }; struct test_struct hex_tests[] = { { "0", 0x0, "hex conversion of 0x0 failed" }, { "0x1", 0x0, "hex conversion of 0x0x1 failed" }, { "1x", 0x1, "hex conversion of 0x1x failed" }, { "00", 0x0, "hex conversion of 0x00 failed" }, { "00x", 0x0, "hex conversion of 0x00x failed" }, { "01", 0x1, "hex conversion of 0x01 failed" }, { "01x", 0x1, "hex conversion of 0x01x failed" }, { "ab", 0xab, "hex conversion of 0xAb failed" }, { "AB", 0xab, "hex conversion of 0xAB failed" }, { "Ab", 0xab, "hex conversion of 0xAb failed" }, { "aB", 0xab, "hex conversion of 0xaB failed" }, { "4z", 0x4, "hex conversion of 0x4z failed" }, { "123", 0x12, "hex conversion of 0x123 failed" }, { "12M", 0x12, "hex conversion of 0x12M failed" }, { "ff", 0xff, "hex conversion of 0x255 failed" }, { "FF", 0xff, "hex conversion of 0x255 failed" }, { "XX", -1, "hex conversion of 0xXX failed" }, { NULL, 0, NULL } }; struct test_struct escseq_tests[] = { { "", -1, "escseq conversion of \"\" failed" }, { "0a", 0, "escseq oct conversion of \\0a failed" }, { "00000003a", 0, "escseq oct conversion of \\00000003a failed" }, { "62", 50, "escseq oct conversion of \\62 failed" }, { "623", 50, "escseq oct conversion of \\623 failed" }, { "123", 83, "escseq oct conversion of \\123 failed" }, { "123;", 83, "escseq oct conversion of \\123; failed" }, { "2234", 147, "escseq oct conversion of \\2234 failed" }, { "xx", -1, "escseq oct conversion of \\xx failed" }, { "d0a", 0, "escseq dec conversion of \\d0a failed" }, { "d00000003a", 0, "escseq dec conversion of \\d00000003a failed" }, { "d62", 62, "escseq dec conversion of \\d62 failed" }, { "d623", 62, "escseq dec conversion of \\d623 failed" }, { "d132", 132, "escseq dec conversion of \\d132 failed" }, { "d132UL", 132, "escseq dec conversion of \\d132UL failed" }, { "d255", 255, "escseq dec conversion of \\d255 failed" }, { "d256", 25, "escseq dec conversion of \\d256 failed" }, { "d2234", 223, "escseq dec conversion of \\d2234 failed" }, { "dxx", -1, "escseq dec conversion of \\dxx failed" }, { "x0", 0x0, "escseq hex conversion of 0x0 failed" }, { "x0x1", 0x0, "escseq hex conversion of 0x0x1 failed" }, { "x1x", 0x1, "escseq hex conversion of 0x1x failed" }, { "x00", 0x0, "escseq hex conversion of 0x00 failed" }, { "x00x", 0x0, "escseq hex conversion of 0x00x failed" }, { "x01", 0x1, "escseq hex conversion of 0x01 failed" }, { "x01x", 0x1, "escseq hex conversion of 0x01x failed" }, { "xab", 0xab, "escseq hex conversion of 0xAb failed" }, { "xAB", 0xab, "escseq hex conversion of 0xAB failed" }, { "xAb", 0xab, "escseq hex conversion of 0xAb failed" }, { "xaB", 0xab, "escseq hex conversion of 0xaB failed" }, { "x4z", 0x4, "escseq hex conversion of 0x4z failed" }, { "x123", 0x12, "escseq hex conversion of 0x123 failed" }, { "x12M", 0x12, "escseq hex conversion of 0x12M failed" }, { "xff", 0xff, "escseq hex conversion of 0x255 failed" }, { "xFF", 0xff, "escseq hex conversion of 0x255 failed" }, { "xXX", -1, "escseq hex conversion of 0xXX failed" }, { "\\", '\\', "escseq '\\\\' failed" }, { "\"", '"', "escseq '\\\"' failed" }, { "a", '\a', "escseq '\\a' failed" }, { "e", '\033', "escseq '\\e' failed" }, { "f", '\f', "escseq '\\f' failed" }, { "n", '\n', "escseq '\\n' failed" }, { "r", '\r', "escseq '\\r' failed" }, { "t", '\t', "escseq '\\t' failed" }, { NULL, 0, NULL } }; struct test_struct escseqextra_tests[] = { { "+", '+', "escseq extra conversion of \\+ failed" }, { "-", '-', "escseq conversion of \\- failed" }, { "*", '*', "escseq conversion of \\* failed" }, { "(", '(', "escseq conversion of \\( failed" }, { ")", ')', "escseq conversion of \\) failed" }, { "|", '|', "escseq conversion of \\| failed" }, { ".", '.', "escseq conversion of \\. failed" }, { "[", '[', "escseq conversion of \\[ failed" }, { "]", ']', "escseq conversion of \\] failed" }, { "^", '^', "escseq conversion of \\^ failed" }, { NULL, 0, NULL } }; /* test chrtoi */ for (int base = -1; base < 38; base++) { for (int c = 0; c < 256; c++) { int expected; int i = chrtoi(c, base); if (base < 2 || base > 36 || !isascii(c) || !(isdigit(c) || isalpha(c))) expected = -1; else if (isdigit(c) && (c - '0') < base) expected = c - '0'; else if (isalpha(c) && (toupper(c) - 'A') + 10 < base) expected = (toupper(c) - 'A') + 10; else expected = -1; if (i != expected) // printf(" chrtoi test: convert base %d '%c'(%d)\texpected %d\tresult: %d\n", base, c, c, expected, i); MY_TEST(i == expected, "failed"); } } /* test strntol */ for (struct test_struct *t = oct_tests; t->test; t++) { retval = test_oct(t->test); // printf(" oct test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == t->expected, t->msg); } for (struct test_struct *t = dec_tests; t->test; t++) { retval = test_dec(t->test); // printf(" dec test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == t->expected, t->msg); } for (struct test_struct *t = hex_tests; t->test; t++) { retval = test_hex(t->test); // printf(" hex test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == t->expected, t->msg); } /* test strn_escseq */ for (struct test_struct *t = escseq_tests; t->test; t++) { const char *pos = t->test; retval = strn_escseq(&pos, "", strlen(t->test)); // printf(" strn_escseq test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == t->expected, t->msg); } for (struct test_struct *t = escseqextra_tests; t->test; t++) { const char *pos = t->test; retval = strn_escseq(&pos, "", strlen(t->test)); // printf(" strn_escseq test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == -1, t->msg); pos = t->test; retval = strn_escseq(&pos, "*+.|^-[]()", strlen(t->test)); MY_TEST(retval == t->expected, t->msg); } for (int c = 1; c < 256; c++) { const char *pos; char str[2] = " "; if (strchr("01234567\\\"dxaefnrt", c)) /* skip chars already tested above */ continue; str[0] = c; pos = str; retval = strn_escseq(&pos, "", 2); MY_TEST(retval == -1, " strn_escseq: of unsupported char failed"); } return rc; } #endif /* UNIT_TEST */ apparmor-5.0.2/parser/lib.h000066400000000000000000000014271522511161100155510ustar00rootroot00000000000000#ifndef __AA_LIB_H_ #define __AA_LIB_H_ #include #define autofree __attribute((cleanup(_aa_autofree))) #define autoclose __attribute((cleanup(_aa_autoclose))) #define autofclose __attribute((cleanup(_aa_autofclose))) #define asprintf _aa_asprintf int dirat_for_each(int dirfd, const char *name, void *data, int (* cb)(int, const char *, struct stat *, void *)); int isodigit(char c); long strntol(const char *str, const char **endptr, int base, long maxval, size_t n); int strn_escseq(const char **pos, const char *chrs, size_t n); int str_escseq(const char **pos, const char *chrs); int write_compressed_with_user_header(int fd, const char *compressed, size_t compressed_size, uint8_t compress_level); #endif /* __AA_LIB_H_ */ apparmor-5.0.2/parser/libapparmor_re/000077500000000000000000000000001522511161100176245ustar00rootroot00000000000000apparmor-5.0.2/parser/libapparmor_re/.clangd000066400000000000000000000001361522511161100210550ustar00rootroot00000000000000CompileFlags: Add: - -std=gnu++0x - -I../../libraries/libapparmor/include apparmor-5.0.2/parser/libapparmor_re/Makefile000066400000000000000000000017671522511161100212770ustar00rootroot00000000000000# Profiling: #EXTRA_CFLAGS = -pg ifdef USE_SYSTEM # Using the system libapparmor INCLUDE_APPARMOR = else INCLUDE_APPARMOR = -I../../libraries/libapparmor/include endif TARGET=libapparmor_re.a AR ?= ar CFLAGS ?= -g -Wall -O2 ${EXTRA_CFLAGS} -std=gnu++0x CXXFLAGS := ${CFLAGS} ${INCLUDE_APPARMOR} LIB_HDRS = aare_rules.h flex-tables.h apparmor_re.h hfa.h chfa.h parse.h \ expr-tree.h policy_compat.h OTHER_HDRS = ../common_optarg.h ../common_flags.h ../immunix.h \ ../policydb.h ../perms.h ../rule.h HDRS = ${LIB_HDRS} ${OTHER_HDRS} ARFLAGS=-rcs BISON := bison all : ${TARGET} UNITTESTS = tst_parse libapparmor_re.a: parse.o expr-tree.o hfa.o chfa.o aare_rules.o policy_compat.o ${AR} ${ARFLAGS} $@ $^ expr-tree.o: expr-tree.cc expr-tree.h hfa.o: hfa.cc ${HDRS} aare_rules.o: aare_rules.cc ${HDRS} chfa.o: chfa.cc ${HDRS} policy_compat.o: policy_compat.cc ${HDRS} parse.o : parse.cc ${HDRS} parse.cc : parse.y ${HDRS} ${BISON} -o $@ $< clean: rm -f *.o parse.cc ${TARGET} *.gcda *.gcno apparmor-5.0.2/parser/libapparmor_re/README000066400000000000000000000572561522511161100205230ustar00rootroot00000000000000apparmor_re.h - control flags for hfa generation expr-tree.{h,cc} - abstract syntax tree (ast) built from a regex parse parse.{h,y} - code to parse a regex into an ast hfc.{h,cc} - code to build and manipulate a hybrid finite automata (state machine). flex-tables.h - basic defines used by chfa chfa.{h,cc} - code to build a highly compressed runtime readonly version of an hfa. aare_rules.{h,cc} - code to that binds parse -> expr-tree -> hfa generation -> chfa generation into a basic interface for converting rules to a runtime ready state machine. Notes on the compiler pipeline order ============================================ Front End: Program driver logic and policy text parsing into an abstract syntax tree. Middle Layer: Transforms and operations on the abstract syntax tree. Converts syntax tree into expression tree for back end. Back End: transforms of syntax tree, and creation of policy HFA from expression trees and HFAs. Basic order of the backend of the compiler pipe line and where the dump information occurs in the pipeline. ===== Front End (parse -> AST ================ | v yyparse | +--->--+-->-+ | | | +-->---- +---------------------------<-----------------------+ | | | | | | v | | | yylex | | | | | | ^ token match | | | | | | | +----------------------------+ | | | | | ^ | | v v | | +-<- rule match? preprocess | | | | | | early var expansion +----------+-----------+ | | | | | | | ^ v v v v | | new rule() / new ent include variable conditional | | | | | | | | v +---->-----+----->-----+----->----+ | new rule semantic check | | +-----<-----+ | ----------- | ------ End of Parse -------------------- | v post_parse_profile semantic check | v post_process | v add implied rules() | v process_profile_variables() | v rule->expand_variables() | +--------+ | v replace aliases (to be moved to backend rewrite) | v merge rules | v profile->merge_rules() | v +-->--rule->is_mergeable() | | ^ v | add to table | | +-------+--------+ | v sort->cmp()/oper<() | rule->merge() | +------------+ | v process_profile_rules | v rule->gen_policy_re() | v ===== Mid layer (AST -> expr tree) ================= | +-> add_rule() (aare_rules.{h,cc}) | | | v | rule parse (parse.y) | | | | | v | | expr tree (expr-tree.{h,cc}) | | | | v | | unique perms | (aare_rules.{h,cc}) | | | | +------ + | | | v | add to rules expr tree (aare_rules.{h,c}) | | +------+ | +------------------+ | v create_dfablob() | v expr tree | v create_chfa() (aare_rules.cc) | v expr normalization (expr-tree.{h,cc}) | v expr simplification (expr-tree.{h,c}) | +- D expr-tree | +- D expr-simplified | ==== Back End - Create cHFA out of expr tree and other HFAs ==== v hfa creation (hfa.{h,cc}) | +- D dfa-node-map | +- D dfa-uniq-perms | +- D dfa-states-initial | v hfa rewrite (not yet implemented) | v filter deny (hfa.{h,cc}) | +- D dfa-states-post-filter | v minimization (hfa.{h,cc}) | +- D dfa-minimize-partitions | +- D dfa-minimize-uniq-perms | +- D dfa-states-post-minimize | v unreachable state removal (hfa.{h,cc}) | +- D dfa-states-post-unreachable | +- D dfa-states constructed hfa | +- D dfa-graph | v equivalence class construction | +- D equiv | diff encode (hfa.{h,cc}) | +- D diff-encode | compute perms table | +- D compressed-dfa == perm table dump | compressed hfa (chfa.{h,cc} | +- D compressed-dfa == transition tables | +- D dfa-compressed-states - compress HFA in state form | v Return to Mid Layer Notes on the compress hfa file format (chfa) ============================================== The file format used 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 set to 0x1B5E783D instead of 0xF13C57B1 though, which is meant to indicate that the file format logically is not the same: the YY_ID_CHK (check) and YY_ID_DEF (default), YY_ID_BASE tables are used differently. The YY_ID_ACCEPTX tables either encode permissions directly, or are an index, into an external tables. There are two DFA table formats to support different size state machines DFA16 default/next/check - are 16 bit tables DFA32 default/next/check - are 32 bit tables DFA32 is limited to 2^24 states, due to the upper 8 bits being used as flags in the base table, unless the flags table is defined. When the flags table is defined, DFA32 can have a full 2^32 states. In both DFA16 and DFA32 base and accept are 32 bit tables. State 0 is always used as the trap state. Its accept, base and default fields should be 0. State 1 is the default start state. Alternate start states are stored external to the state machine. If the flags table is not defined, the base table uses the lower 24 bits as index into the next/check tables, and the upper 8 bits are used as flags. The currently defined flags are #define MATCH_FLAG_DIFF_ENCODE 0x80000000 #define MARK_DIFF_ENCODE 0x40000000 #define MATCH_FLAG_OOB_TRANSITION 0x20000000 Note the default[state] is used in two different ways. 1. When diff_encode is set, the state stores the difference to another state defined by default. The next field will only store the transitions that are unique to this state. Those transition may mask transitions in the state that the current state is relative to, also note the state that this state is relative might also be relative to another state. Cycles are forbidden and checked for by the verifier. The exact algorithm used to build these state difference will be discussed in another section. States and transitions on specific characters to next states ------------------------------------------------------------ 1: ('a' => 2, 'b' => 3, 'c' => 4) 2: ('a' => 2, 'b' => 3, 'd' => 5) Table format - where D in base represnts Diff encode flag ---------------------- index: (default, base) 0: ( 0, 0) <== dummy state (nonmatching) 1: ( 0, 0) 2: ( 1, D 256) index: (next, check) 0: ( 0, 0) <== unused entry ( 0, 1) <== ord('a') identical entries 0+'a': ( 2, 1) 0+'b': ( 3, 1) 0+'c': ( 4, 1) ( 0, 1) <== (255 - ord('c')) identical entries 256+'c': ( 0, 2) 256+'d': ( 5, 2) Here, state 2 is described as ('c' => 0, 'd' => 5), and everything else as in state 1. The matching algorithm is as follows. Scanner algorithm --------------------------- /* current state is in , input character */ while (check[base[state] + c] != state) { diff = (FLAGS(base) & diff_encode); state = default[state]; if (!diff) goto done; } state = next[base[state] + c]; done: /* continue with the next input character */ 2. When diff_encode is NOT set, the default state is used to represent all none matching transitions (ie. check[base[state] + c] != state). The dfa build will compute the transition with the most transitions and use that for the default state. ie. if we have 1: ('a' => 2) ("[^a]" => 0) then 0 will be used as the default state if we have 1: ("[^a]" => 2) ('a' => 0) then 2 will be used as the default state, and the only state encoded in the next/check tables will be for 'a' The combination of the diff-encoded and non-diff encoded states performs well even when there are many inverted or wildcard matches ("[^x]", "."). Simplified Regexp scanner algorithm for non-diff encoded state (note diff encode algorithm above works as well) ------------------------ /* current state is in , matching character */ if (check[base[state] + c] == state) state = next[base[state] + c]; else state = default[state]; /* continue with the next input character */ Each input character may cause several iterations in the while loop, but due to guarantees in the build at most 2n states will be transitioned for n input characters. The expected number of states walked is much closer to n and in practice due to cache locality the diff encoded state machine is usually faster than a non-diff encoded state machine with a strict n state for n input walk. Comb Compression ----------------- The next/check tables of states are only used to encode transitions not covered by the default transition. The input byte is indexed off the base value, covering 256 positions within the next/check tables. However a state may only encode a few transitions within that range, leaving holes. These holes are filled by other states transitions whose range will overlap. 1: ('a' => 2, 'b' => 3, 'c' => 4) 2: ('a' => 2, 'b' => 3, 'd' => 5) 3: ('a' => 0, everything else => 5) Regexp tables ------------- index: (default, base) 0: ( 0, 0) <== dummy state (nonmatching) 1: ( 0, 0) 2: ( 1, 3) 3: ( 5, 7) index: (next, check) 0: ( 0, 0) <== unused entry ( 0, 0) <== ord('a') identical, unused entries 0+'a': ( 2, 1) 0+'b': ( 3, 1) 0+'c': ( 4, 1) 3+'a': ( 2, 2) 3+'b': ( 3, 2) 3+'c': ( 0, 0) <== entry is unused, hole that could be filled 3+'d': ( 5, 2) 7+'a': ( 0, 3) ( 0, 0) <== (255 - ord('a')) identical, unused entries Regexp tables comb compressed ------------- index: (default, base) 0: ( 0, 0) 1: ( 0, 0) 2: ( 1, 3) 3: ( 5, 5) index: (next, check) 0: ( 0, 0) ( 0, 0) 0+'a': ( 2, 1) 0+'b': ( 3, 1) 0+'c': ( 4, 1) 3+'a': ( 2, 2) 3+'b': ( 3, 2) 5+'a': ( 0, 3) <== entry was previously at 7+'a' 3+'d': ( 5, 2) ( 0, 0) <== (255 - ord('a')) identical, unused entries Out of Band Transitions (oobs) --------------------------------- Out of band transitions (oobs) allow for a state to have transitions that can not be triggered by input. Any state that has oobs must have the OOB flag set on the state. An oob is triggered by subtracting the oob number from the the base index value, to find the next and check value. Current only single oob is supported. And all states using an oob must have the oob flag set. if ((FLAG(base) & OOB) && check[base[state] - oob] == state) state = next[base[state]] - oob] oobs might be expressed as a negative number eg. -1 for the first oob. In which case the oob transition above uses a + oob instead. If more oobs are needed a second oob flag can be allocated, and if used in combination with the original, would allow a state to have up to 3 oobs 00 - none 01 - 1 10 - 2 11 - 3 Diff Encode & Spanning Tree ============================================ State differential encoding is a technique to compress the dfa by reducing the number of transition states that need to be stored for a give state. Finding an optimal differential encoding is an np-complete problem, instead apparmor uses an algorithm that is expexted to be nlogn but does have a worst case of O(n^2). When the differential code approximates nlogn, and if it removes enough transitions, it can speed up the compile time because it reduces the number of transitions that need to be considered by the O(n^2) comb compression. In the worst case when differential encoding approximates O(n^2), and if it doesn't eliminate may transitions it can slow the backend of the state machine build down by approximately 2x. The algorithm used to do the differential encoding makes sure the encode is done in such a way that to provide runtime guaratees that no more that for an input of length n, no more than 2N states will be traversed for an input of length N. Understanding the Diff Encode ----------------------------- To reduce the number of transitions a state has to encode states can be made to encode their transitions as a differential to a "default" state. If a transition is not represented in the current state the default state is entered and the process is repeated until the transition is found or the state is not marked to be encoded relative to it default. An example: Lets say we have two states A and B witht the following transitions. State A State B default -> NULL default -> NULL 'a' -> X 'a' -> X 'b' -> Y 'b' -> Y 'c' -> Z diff encoding will not change state A but changes state B State A State B (diff*) default -> NULL default -> A 'a' -> X 'c' -> Z 'b' -> Y matches for state A do not change, but performing a match for state B might. Matching input 'c' for state B has no difference the match finds that Z is the transition for input 'c' and transitions just as it would for the state machine not using diff encoding. However the match for inputs 'a' and 'b' do change. For inputs 'a' and 'b' the match will not find a matching transition so the default transition will be used which has become state A instead of null, in addition the state is detected to be diff encoded, so instead of stopping the match on the state transition, that match is rerun against State A. For all inputs besides 'c' (which does not get passed to state A, the transitions are the same as the original state B, so after the match is run against state A, the match is now in the same state it would have been without diff encoding. The diff encode reduced the number of transitions stored in the state machine by 2, at the cost of requiring a run time cost of transitioning through 2 states to find the state for a single input. Chaining diff state diff encoding The diff encode is not limited a single state transition. Diff encoded states can be chained to obtain further savings. Eg. 2 State A State B State C default -> NULL default -> NULL default -> NULL 'a' -> W 'a' -> W 'a' -> W 'b' -> X 'b' -> X 'b' -> X 'c' -> Y 'c' -> Y 'd' -> Z becomes State A State B (diff*) State C (diff*) default -> NULL default -> A default -> B 'a' -> W 'c' -> Y 'd' -> Z 'b' -> X The match for State B is similar to example 1, transitioning to State A to find the rest of its transitions. State C will match input 'd' but for other inputs transition to state B, which can directly match input 'c' but will transition to state A for the rest of its transitions. This time the diff encode has manged to remove 5 transitions from memory at the cost of having to match against up to 2 states from state B, and up to 3 states from State C. Masking transitions The diff encode is not limited to just encoding against states with a strict subset of transitions as was done in example 1 and 2. It can also add transitions to mask transitions down the chain, allowing a broader set of states to be used when setting up diff encode chains. Eg. 3 State A State B default -> NULL default -> NULL 'a' -> W 'a' -> X 'b' -> Y 'b' -> Y 'c' -> Z can be diff encoded as State A State B (diff*) default -> NULL default -> A 'a' -> W 'a' -> X 'b' -> Y 'c' -> Z In this case only 1 transition is removed because the 'a' transition is kept to override/mask the 'a' transition in state A. When doing a transition from state B, its transitions will be consulted first the correct transition to state X for input 'a' will be done. Eg. 4. State A State B default -> X default -> X 'a' -> W 'b' -> Y 'b' -> Y can be encoded as State A State B (diff*) default -> NULL default -> A 'a' -> W 'a' -> X 'b' -> Y in this case the diff encode doesn't save any transitions so it is not worth doing, but a similar situation with transitions could result in savings. Loops and diff encode termination The diff encode if done wrong could create an infinite loop for a single input. To avoid this the diff-encode must be built and verified so every diff encoded state chain ends on a non-diff-encoded state. Building the Diff Encode ------------------------- State differential encoding can be quite effective in reducing the number of states but it can increase both compile and matching time (as multiple states must be traversed for a single match). However if the states to encode against are chosen carefully, then both the encoding time and matching time can be bounded. Differential encoding can even result in a faster HFA as it can reduce the data needed to be cached, resulting in improved cache line reuse. The requirements AppArmor uses to choose the states to encode against are • The state must have been previously matched while walking the dfa (it will be hot in the cache then) • or the state must be at the same level in a DAG decomposition of the dfa, sharing a common ancestor (more on this below) The first requirement was primarily for performance concerns but in practice works out well for compression too, as states that are close to each other often have similar transitions. The second allows expanding the reach of the compression to a few more likely options while keeping a potentially common hot path, without breaking the other property of only referencing previously matched states. In practice requirement 1 can not be met as each match string takes a different path through the dfa. It can however be approximated by converting the dfa into a directed acyclic graph (DAG) with the start state as the root. The DAG provides a good approximation for requirement 1 and at the same time it limits how many states have to be considered for compression (only backwards in the DAG). It also provides guarentees on how many states will be walked at run time (at most 2n). Eg. TODO: ??? simple dfa to spanning tree graph Converting the HFA into DAG for compression does have a limitation in that it removes many of a states immediate neighbours from consideration. In a DAG a states neighbours can be broken into five classes, immediate predecessor, predecessor on another branch, sibling on another branch, immediate successor, successor on another branch. The immediate predecessor and immediate successor cases are covered by the predecessor differential compression scheme described above (successor as the current state is the successor state predecessor, and thus will be considered when the successor is differentially encoded). However the successor and predecessor on another branch and sibling cases are not covered, and they maybe the more optimal path for encoding, and may be the hot path the match came through. TODO: ??? diagram showing the 5 classes To help account for this, AppArmor also compares to the immediate successors of the state being consider if there are transitions between the states. Sibling states are also considered if there are transitions between the state and the sibling is differentially encoded against a predecessor (not another sibling), or not differentially encoded. This broadens the set of states considered but limits it to states that were potentially matched against and thus in the cache. It also has the property of looking backwards in the DAG thus keeping the maximum number of states that are required to be transitioned to in a match to a linear constant. If only branch predecessor where used then the limit could be kept at 2n but because immediate siblings can be used iff they transition to a predecessor the limit is bounded to a slightly higher value of 5/2n. When considering which state to differentially encode against AppArmor computes a weighted value and chooses the best one. The value is computed as follows. • For each defined transition in the state +0 to candidate state weight - if the transition is undefined in the candidate state (the transition must be represented in the current state) +1 to candidate state weight - if the candidate state has the same transition to the same state (the transition can be eliminated from the current state) -1 to candidate state weight - if the transition is defined in the candidate state and it is not the same transition (current state must add a transition entry to override candidate transition) • For each undefined transition in the state +0 to candidate state weight - if the transition is undefined in the candidate state -1 to candidate state weight - if the transition is defined in candidate state (current state must add an entry to override the candidate transition) The current state will be differentially encoded against the candidate state with the largest weight > 0. If there is no weighting > 0 then no differential encoding for the state will be done as their is no benefit to doing so. Note: differential encoding can case reduce a state to 0 stored transitions. This can happen when two states have the exact same transitions but belong in different partitions when minimized. This would happen for example when one state was an accept state and the other a none accepting state. Other wise if states have the same transitions they are redundant and removed during state minimization. DFA WELDING ----------- * TODO * DFA SET OPERATIONS ------------------ If two DFAs can be aligned on partitioning scheme the set operations of union, intersecions and differences can be used to create a new DFA that represents the results of the operation. * TODO * DFA ALIAS REWRITE ----------------- * TODO * apparmor-5.0.2/parser/libapparmor_re/aare_rules.cc000066400000000000000000000244071522511161100222640ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2013 Canonical Ltd. (All rights reserved) * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Wrapper around the dfa to convert aa rules into a dfa */ #include #include #include #include #include #include "aare_rules.h" #include "expr-tree.h" #include "parse.h" #include "hfa.h" #include "chfa.h" using namespace std; aare_rules::~aare_rules(void) { if (root) root->release(); unique_perms.clear(); expr_map.clear(); } bool aare_rules::add_rule(const char *rule, int priority, rule_mode_t mode, perm32_t perms, perm32_t audit, optflags const &opts) { return add_rule_vec(priority, mode, perms, audit, 1, &rule, opts, false); } void aare_rules::add_to_rules(Node *tree, Node *perms) { if (reverse) flip_tree(tree); Node *base = expr_map[perms]; if (base) expr_map[perms] = new AltNode(base, tree); else expr_map[perms] = tree; } static Node *cat_with_null_separator(Node *l, Node *r) { return new CatNode(new CatNode(l, new CharNode(0)), r); } static Node *cat_with_oob_separator(Node *l, Node *r) { return new CatNode(new CatNode(l, new CharNode(transchar(-1, true))), r); } bool aare_rules::add_rule_vec(int priority, rule_mode_t mode, perm32_t perms, perm32_t audit, int count, const char **rulev, optflags const &opts, bool oob) { Node *tree = NULL, *accept; int exact_match; if (regex_parse(&tree, rulev[0])) return false; for (int i = 1; i < count; i++) { Node *subtree = NULL; if (regex_parse(&subtree, rulev[i])) goto err; if (oob) tree = cat_with_oob_separator(tree, subtree); else tree = cat_with_null_separator(tree, subtree); } /* * Check if we have an expression with or without wildcards. This * determines how exec modifiers are merged in accept_perms() based * on how we split permission bitmasks here. */ exact_match = 1; for (depth_first_traversal i(tree); i && exact_match; i++) { if ((*i)->is_type(NODE_TYPE_STAR) || (*i)->is_type(NODE_TYPE_PLUS) || (*i)->is_type(NODE_TYPE_ANYCHAR) || (*i)->is_type(NODE_TYPE_NOTCHARSET)) exact_match = 0; } if (reverse) flip_tree(tree); accept = unique_perms.insert(priority, mode, perms, audit, exact_match); if (opts.dump & DUMP_DFA_RULE_EXPR) { const char *separator; if (oob) separator = "\\-x01"; else separator = "\\x00"; cerr << "rule: "; cerr << rulev[0]; for (int i = 1; i < count; i++) { cerr << separator; cerr << rulev[i]; } cerr << " -> "; tree->dump(cerr); // TODO: split out from prefixes class cerr << " priority=" << priority; if (mode == RULE_DENY) cerr << " deny"; else if (mode == RULE_PROMPT) cerr << " prompt"; cerr << " (0x" << hex << perms <<"/" << audit << dec << ")"; accept->dump(cerr); cerr << "\n\n"; } add_to_rules(tree, accept); rule_count++; return true; err: delete tree; return false; } /* * append_rule is like add_rule, but appends the rule to any existing rules * with a separating transition. The appended rule matches with the same * permissions as the rule it's appended to. If there are no existing rules * append_rule returns true. * * This is used by xattrs matching where, after matching the path, the DFA is * advanced by a null character for each xattr. */ bool aare_rules::append_rule(const char *rule, bool oob, bool with_perm, optflags const &opts) { Node *tree = NULL; if (regex_parse(&tree, rule)) return false; if (opts.dump & DUMP_DFA_RULE_EXPR) { cerr << "rule: "; cerr << rule; cerr << " -> "; tree->dump(cerr); cerr << "\n\n"; } /* * For each matching state, we want to create an optional path * separated by a separating character. * * When matching xattrs, the DFA must end up in an accepting state for * the path, then each value of the xattrs. Using an optional node * lets each rule end up in an accepting state. */ tree = new CatNode(oob ? new CharNode(transchar(-1, true)) : new CharNode(0), tree); if (expr_map.size() == 0) { // There's nothing to append to. Free the tree reference. delete tree; return true; } PermExprMap::const_iterator it; for (it = expr_map.cbegin(); it != expr_map.cend(); it++) { if (with_perm) expr_map[it->first] = new CatNode(it->second, new AltNode(it->first, tree)); else expr_map[it->first] = new CatNode(it->second, tree); } return true; } /* create a chfa from the ruleset * returns: buffer contain dfa tables, @size set to the size of the tables * else NULL on failure, @min_match_len set to the shortest string * that can match the dfa for determining xmatch priority. */ CHFA *aare_rules::create_chfa(int *min_match_len, vector &perms_table, optflags const &opts, bool filedfa, int permstable32_version) { bool extended_perms = permstable32_version >= 2; /* finish constructing the expr tree from the different permission * set nodes */ PermExprMap::const_iterator i = expr_map.cbegin(); if (i != expr_map.cend()) { if (opts.control & CONTROL_DFA_TREE_SIMPLE) { Node *tmp = simplify_tree(i->second, opts); root = new CatNode(tmp, i->first); } else root = new CatNode(i->second, i->first); for (i++; i != expr_map.end(); i++) { Node *tmp; if (opts.control & CONTROL_DFA_TREE_SIMPLE) { tmp = simplify_tree(i->second, opts); } else tmp = i->second; root = new AltNode(root, new CatNode(tmp, i->first)); } } *min_match_len = root->min_match_len(); /* dumping of the none simplified tree without -O no-expr-simplify * is broken because we need to build the tree above first, and * simplification is woven into the build. Reevaluate how to fix * this debug dump. */ label_nodes(root); if (opts.dump & DUMP_DFA_TREE) { cerr << "\nDFA: Expression Tree\n"; root->dump(cerr); cerr << "\n\n"; } if (opts.control & CONTROL_DFA_TREE_SIMPLE) { /* This is old total tree, simplification point * For now just do simplification up front. It gets most * of the benefit running on the smaller chains, and is * overall faster because there are less nodes. Reevaluate * once tree simplification is rewritten */ //root = simplify_tree(root, opts); if (opts.dump & DUMP_DFA_SIMPLE_TREE) { cerr << "\nDFA: Simplified Expression Tree\n"; root->dump(cerr); cerr << "\n\n"; } } CHFA *chfa = NULL; try { DFA dfa(root, opts, filedfa); if (opts.dump & DUMP_DFA_UNIQ_PERMS) dfa.dump_uniq_perms("dfa"); if (opts.dump & DUMP_DFA_STATES_INIT) dfa.dump(cerr, NULL); /* since we are building a chfa, use the info about * whether the chfa supports extended perms to help * determine whether we clear the deny info. * This will let us build the minimal dfa for the * information supported by the backed */ if (!extended_perms || ((opts.control & CONTROL_DFA_FILTER_DENY))) { dfa.apply_and_clear_deny(); if (opts.dump & DUMP_DFA_STATES_POST_FILTER) dfa.dump(cerr, NULL); } if (opts.control & CONTROL_DFA_MINIMIZE) { dfa.minimize(opts); if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS) dfa.dump_uniq_perms("minimized dfa"); if (opts.dump & DUMP_DFA_STATES_POST_MINIMIZE) dfa.dump(cerr, NULL); } if (opts.control & CONTROL_DFA_REMOVE_UNREACHABLE) { dfa.remove_unreachable(opts); if (opts.dump & DUMP_DFA_STATES_POST_UNREACHABLE) dfa.dump(cerr, NULL); } if (opts.dump & DUMP_DFA_STATES) dfa.dump(cerr, NULL); if (opts.dump & DUMP_DFA_GRAPH) dfa.dump_dot_graph(cerr); map eq; if (opts.control & CONTROL_DFA_EQUIV) { eq = dfa.equivalence_classes(opts); dfa.apply_equivalence_classes(eq); if (opts.dump & DUMP_DFA_EQUIV) { cerr << "\nDFA equivalence class\n"; dump_equivalence_classes(cerr, eq); } } else if (opts.dump & DUMP_DFA_EQUIV) cerr << "\nDFA did not generate an equivalence class\n"; if (opts.control & CONTROL_DFA_DIFF_ENCODE) { dfa.diff_encode(opts); if (opts.dump & DUMP_DFA_DIFF_ENCODE) dfa.dump_diff_encode(cerr); } //cerr << "Checking extended perms " << extended_perms << "\n"; if (extended_perms) { //cerr << "creating permstable\n"; dfa.compute_perms_table(perms_table); // TODO: move perms table to a class if (opts.dump & DUMP_DFA_TRANS_TABLE && perms_table.size()) { cerr << "Perms Table size: " << perms_table.size() << "\n"; perms_table[0].dump_header(cerr); for (size_t i = 0; i < perms_table.size(); i++) { perms_table[i].dump(cerr); cerr << "accept1: 0x"; cerr << ", accept2: 0x"; cerr << "\n"; } cerr << "\n"; } } chfa = new CHFA(dfa, eq, opts, permstable32_version); if (opts.dump & DUMP_DFA_TRANS_TABLE) chfa->dump(cerr); if (opts.dump & DUMP_DFA_COMPTRESSED_STATES) dfa.dump(cerr, &chfa->num); } catch(int error) { return NULL; } return chfa; } /* create a dfa from the ruleset * returns: buffer contain dfa tables, @size set to the size of the tables * else NULL on failure, @min_match_len set to the shortest string * that can match the dfa for determining xmatch priority. */ void *aare_rules::create_dfablob(size_t *size, int *min_match_len, vector &perms_table, optflags const &opts, bool filedfa, int permstable32_version) { char *buffer = NULL; stringstream stream; try { CHFA *chfa = create_chfa(min_match_len, perms_table, opts, filedfa, permstable32_version); if (!chfa) { *size = 0; return NULL; } chfa->flex_table(stream, opts); delete (chfa); } catch(int error) { *size = 0; return NULL; } stringbuf *buf = stream.rdbuf(); buf->pubseekpos(0); *size = buf->in_avail(); buffer = (char *)malloc(*size); if (!buffer) return NULL; buf->sgetn(buffer, *size); return buffer; } apparmor-5.0.2/parser/libapparmor_re/aare_rules.h000066400000000000000000000072701522511161100221250ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2012 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Wrapper around the dfa to convert aa rules into a dfa */ #ifndef __LIBAA_RE_RULES_H #define __LIBAA_RE_RULES_H #include #include #include "../common_optarg.h" #include "apparmor_re.h" #include "chfa.h" #include "expr-tree.h" #include "../immunix.h" #include "../perms.h" #include "../rule.h" class UniquePerm { public: int priority; rule_mode_t mode; bool exact_match; uint32_t perms; uint32_t audit; bool operator<(UniquePerm const &rhs)const { if (priority < rhs.priority) return priority < rhs.priority; if (mode >= rhs.mode) { if (exact_match == rhs.exact_match) { if (perms == rhs.perms) return audit < rhs.audit; return perms < rhs.perms; } return exact_match; } return true; // mode < rhs.mode } }; class UniquePermsCache { public: typedef std::map UniquePermMap; typedef UniquePermMap::iterator iterator; UniquePermMap nodes; UniquePermsCache(void) { }; ~UniquePermsCache() { clear(); } void clear() { for (iterator i = nodes.begin(); i != nodes.end(); i++) { delete i->second; } nodes.clear(); } Node *insert(int priority, rule_mode_t mode, uint32_t perms, uint32_t audit, bool exact_match) { UniquePerm tmp = { priority, mode, exact_match, perms, audit }; iterator res = nodes.find(tmp); if (res == nodes.end()) { Node *node; if (mode == RULE_DENY) node = new DenyMatchFlag(priority, perms, audit); else if (mode == RULE_PROMPT) node = new PromptMatchFlag(priority, perms, audit); else if (exact_match) node = new ExactMatchFlag(priority, perms, audit); else node = new MatchFlag(priority, perms, audit); std::pair val = nodes.insert(std::make_pair(tmp, node)); if (val.second == false) { delete node; return val.first->second; } return node; } return res->second; } }; typedef std::map PermExprMap; class aare_rules { Node *root; void add_to_rules(Node *tree, Node *perms); UniquePermsCache unique_perms; PermExprMap expr_map; public: int reverse; int rule_count; aare_rules(void): root(NULL), unique_perms(), expr_map(), reverse(0), rule_count(0) { }; aare_rules(int reverse): root(NULL), unique_perms(), expr_map(), reverse(reverse), rule_count(0) { }; ~aare_rules(); bool add_rule(const char *rule, int priority, rule_mode_t mode, perm32_t perms, perm32_t audit, optflags const &opts); bool add_rule_vec(int priority, rule_mode_t mode, perm32_t perms, perm32_t audit, int count, const char **rulev, optflags const &opts, bool oob); bool append_rule(const char *rule, bool oob, bool with_perm, optflags const &opts); CHFA *create_chfa(int *min_match_len, std::vector &perms_table, optflags const &opts, bool filedfa, int permstable32_version); void *create_dfablob(size_t *size, int *min_match_len, std::vector &perms_table, optflags const &opts, bool filedfa, int permstable32_version); }; #endif /* __LIBAA_RE_RULES_H */ apparmor-5.0.2/parser/libapparmor_re/apparmor_re.h000066400000000000000000000051571522511161100223140ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2012 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __LIBAA_RE_APPARMOR_RE_H #define __LIBAA_RE_APPARMOR_RE_H #include "../common_flags.h" #define CONTROL_DFA_EQUIV (1 << 0) #define CONTROL_DFA_TREE_NORMAL (1 << 1) #define CONTROL_DFA_TREE_SIMPLE (1 << 2) #define CONTROL_DFA_TREE_LEFT (1 << 3) #define CONTROL_DFA_MINIMIZE (1 << 4) #define CONTROL_DFA_FILTER_DENY (1 << 6) #define CONTROL_DFA_REMOVE_UNREACHABLE (1 << 7) #define CONTROL_DFA_TRANS_HIGH (1 << 8) #define CONTROL_DFA_DIFF_ENCODE (1 << 9) #define CONTROL_RULE_MERGE (1 << 10) #define CONTROL_DFA_STATE32 (1 << 11) #define CONTROL_DFA_FLAGS_TABLE (1 << 12) #define CONTROL_ZSTD_FLAGS_RECOMPRESS (1 << 13) #define CONTROL_DEFAULT_COMPRESS (1 << 14) #define DUMP_DFA_DIFF_PROGRESS (1 << 0) #define DUMP_DFA_DIFF_ENCODE (1 << 1) #define DUMP_DFA_DIFF_STATS (1 << 2) #define DUMP_DFA_MIN_PARTS (1 << 3) #define DUMP_DFA_UNIQ_PERMS (1 << 4) #define DUMP_DFA_MIN_UNIQ_PERMS (1 << 5) #define DUMP_DFA_TREE_STATS (1 << 6) #define DUMP_DFA_TREE (1 << 7) #define DUMP_DFA_SIMPLE_TREE (1 << 8) #define DUMP_DFA_PROGRESS (1 << 9) #define DUMP_DFA_STATS (1 << 10) #define DUMP_DFA_STATES (1 << 11) #define DUMP_DFA_GRAPH (1 << 12) #define DUMP_DFA_TRANS_PROGRESS (1 << 13) #define DUMP_DFA_TRANS_STATS (1 << 14) #define DUMP_DFA_TRANS_TABLE (1 << 15) #define DUMP_DFA_EQUIV (1 << 16) #define DUMP_DFA_EQUIV_STATS (1 << 17) #define DUMP_DFA_MINIMIZE (1 << 18) #define DUMP_DFA_UNREACHABLE (1 << 19) #define DUMP_DFA_RULE_EXPR (1 << 20) #define DUMP_DFA_NODE_TO_DFA (1 << 21) #define DUMP_RULE_MERGE (1 << 22) #define DUMP_DFA_STATE32 (1 << 23) #define DUMP_DFA_FLAGS_TABLE (1 << 24) #define DUMP_DFA_STATES_INIT (1 << 25) #define DUMP_DFA_STATES_POST_FILTER (1 << 26) #define DUMP_DFA_STATES_POST_MINIMIZE (1 << 27) #define DUMP_DFA_STATES_POST_UNREACHABLE (1 << 28) #define DUMP_DFA_COMPTRESSED_STATES (1 << 29) #define DUMP_DFA_PERMS (1 << 30) #endif /* APPARMOR_RE_H */ apparmor-5.0.2/parser/libapparmor_re/chfa.cc000066400000000000000000000365301522511161100210430ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2012 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Create a compressed hfa from and hfa */ #include #include #include #include #include #include #include #include "hfa.h" #include "chfa.h" #include "flex-tables.h" using namespace std; void CHFA::init_free_list(vector > &free_list, size_t prev, size_t start) { for (size_t i = start; i < free_list.size(); i++) { if (prev) free_list[prev].second = i; free_list[i].first = prev; prev = i; } free_list[free_list.size() - 1].second = 0; } /** * new Construct the transition table. * * TODO: split dfaflags into separate control and dump so we can fold in * permtable index flag */ CHFA::CHFA(DFA &dfa, map &eq, optflags const &opts, int permstable32_version): eq(eq) { bool permindex = permstable32_version >= 2; bool supports_permstable_accept2 = permstable32_version >= 3; if (opts.dump & DUMP_DFA_TRANS_PROGRESS) fprintf(stderr, "Compressing HFA:\r"); chfaflags = 0; if (dfa.diffcount) chfaflags |= YYTH_FLAG_DIFF_ENCODE; if (dfa.oob_range) chfaflags |= YYTH_FLAG_OOB_TRANS; if (eq.empty()) max_eq = 255; else { max_eq = 0; for (map::const_iterator i = eq.cbegin(); i != eq.end(); i++) { if (i->second > max_eq) max_eq = i->second; } } /* Do initial setup adding up all the transitions and sorting by * transition count. */ size_t optimal = 2; multimap order; vector > free_list; for (Partition::const_iterator i = dfa.states.cbegin(); i != dfa.states.cend(); i++) { if (*i == dfa.start || *i == dfa.nonmatching) continue; optimal += (*i)->trans.size(); if (opts.control & CONTROL_DFA_TRANS_HIGH) { size_t range = 0; if ((*i)->trans.size()) range = (*i)->trans.crbegin()->first.c - (*i)->trans.cbegin()->first.c; size_t ord = ((dfa.max_range - (*i)->trans.size()) << dfa.ord_range) | (dfa.max_range - range); /* reverse sort by entry count, most entries first */ order.insert(make_pair(ord, *i)); } } /* Insert the dummy nonmatching transition by hand */ next_check.push_back(make_pair(dfa.nonmatching, dfa.nonmatching)); default_base.push_back(make_pair(dfa.nonmatching, 0)); num.insert(make_pair(dfa.nonmatching, num.size())); /* minimum size is 2 */ accept.resize(max(dfa.states.size(), (size_t) 2)); if (!permindex || (dfa.filedfa && supports_permstable_accept2)) /* currently only using accept2 for owner cond in the * file dfa */ accept2.resize(max(dfa.states.size(), (size_t) 2)); if (permindex) { accept[0] = dfa.nonmatching->idx; accept[1] = dfa.start->idx; /* accept2 owner flag doesn't matter for nonmatching and * start state */ } else { uint32_t accept3; dfa.nonmatching->perms->map_perms_to_accept(accept[0], accept2[0], accept3); dfa.start->perms->map_perms_to_accept(accept[1], accept2[1], accept3); } next_check.resize(max(optimal, (size_t) dfa.max_range)); free_list.resize(next_check.size()); first_free = 1; init_free_list(free_list, 0, 1); start = dfa.start; insert_state(free_list, dfa.start, dfa); num.insert(make_pair(dfa.start, num.size())); int count = 2; if (!(opts.control & CONTROL_DFA_TRANS_HIGH)) { for (Partition::const_iterator i = dfa.states.cbegin(); i != dfa.states.cend(); i++) { if (*i != dfa.nonmatching && *i != dfa.start) { uint32_t accept3; insert_state(free_list, *i, dfa); if (permindex) { accept[num.size()] = (*i)->idx; /* set owner conditional */ if (dfa.filedfa && supports_permstable_accept2) accept2[num.size()] = 1; // TODO: Define this flag } else { (*i)->perms->map_perms_to_accept(accept[num.size()], accept2[num.size()], accept3); } num.insert(make_pair(*i, num.size())); } if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) { count++; if (count % 100 == 0) fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r", count, dfa.states.size()); } } } else { for (multimap::const_iterator i = order.cbegin(); i != order.end(); i++) { if (i->second != dfa.nonmatching && i->second != dfa.start) { uint32_t accept3; insert_state(free_list, i->second, dfa); if (permindex) { accept[num.size()] = i->second->idx; if (dfa.filedfa && supports_permstable_accept2) accept2[num.size()] = 1; // TODO: Define this flag. } else { i->second->perms->map_perms_to_accept(accept[num.size()], accept2[num.size()], accept3); } num.insert(make_pair(i->second, num.size())); } if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) { count++; if (count % 100 == 0) fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r", count, dfa.states.size()); } } } if (opts.dump & (DUMP_DFA_TRANS_STATS | DUMP_DFA_TRANS_PROGRESS)) { ssize_t size = 4 * next_check.size() + 6 * dfa.states.size(); fprintf(stderr, "\033[2KCompressed trans table: states %zd, next/check %zd, optimal next/check %zd avg/state %.2f, compression %zd/%zd = %.2f %%\n", dfa.states.size(), next_check.size(), optimal, (float)next_check.size() / (float)dfa.states.size(), size, 512 * dfa.states.size(), 100.0 - ((float)size * 100.0 /(float)(512 * dfa.states.size()))); } } /** * Does fit into position of the transition table? */ bool CHFA::fits_in(vector > &free_list __attribute__ ((unused)), size_t pos, StateTrans &trans) { ssize_t c, base = pos - trans.cbegin()->first.c; if (base < 0) return false; for (StateTrans::const_iterator i = trans.cbegin(); i != trans.cend(); i++) { c = base + i->first.c; /* if it overflows the next_check array it fits in as we will * resize */ if (c >= (ssize_t) next_check.size()) return true; if (next_check[c].second) return false; } return true; } /** * Insert of into the transition table. */ void CHFA::insert_state(vector > &free_list, State *from, DFA &dfa) { State *default_state = dfa.nonmatching; ssize_t base = 0; int resize; StateTrans &trans = from->trans; ssize_t c; ssize_t prev = 0; ssize_t x = first_free; if (from->otherwise) default_state = from->otherwise; if (trans.empty()) goto do_insert; c = trans.cbegin()->first.c; repeat: resize = 0; /* get the first free entry that won't underflow */ while (x && ((x < c) || (x + c < 0))) { prev = x; x = free_list[x].second; } /* try inserting until we succeed. */ while (x && !fits_in(free_list, x, trans)) { prev = x; x = free_list[x].second; } if (!x) { resize = dfa.upper_bound - c; x = free_list.size(); /* set prev to last free */ } else if (x + (dfa.upper_bound - 1) - c >= (ssize_t) next_check.size()) { resize = ((dfa.upper_bound -1) - c - (next_check.size() - 1 - x)); for (size_t y = x; y; y = free_list[y].second) prev = y; } if (resize) { /* expand next_check and free_list */ ssize_t old_size = free_list.size(); next_check.resize(next_check.size() + resize); free_list.resize(free_list.size() + resize); init_free_list(free_list, prev, old_size); if (!first_free) first_free = old_size;; if (x == old_size) goto repeat; } base = x - c; for (StateTrans::const_iterator j = trans.cbegin(); j != trans.cend(); j++) { next_check[base + j->first.c] = make_pair(j->second, from); size_t prev = free_list[base + j->first.c].first; size_t next = free_list[base + j->first.c].second; if (prev) free_list[prev].second = next; if (next) free_list[next].first = prev; if (base + j->first.c == first_free) first_free = next; } /* these flags will only be set on states that have transitions */ if (c < 0) { base |= MATCH_FLAG_OOB_TRANSITION; } do_insert: /* While a state without transitions could have the diff encode * flag set, it would be pointless resulting in just an extra * state transition in the encoding chain, and so it should be * considered an error * TODO: add check that state without transitions isn't being * given a diffencode flag */ if (from->flags & DiffEncodeFlag) base |= DiffEncodeBit32; default_base.push_back(make_pair(default_state, base)); } /** * Text-dump the transition table (for debugging). */ void CHFA::dump(ostream &os) { map st; for (map::const_iterator i = num.cbegin(); i != num.cend(); i++) { st.insert(make_pair(i->second, i->first)); } os << "size=" << default_base.size() << " (accept, accept2, default, base): {state} -> {default state}" << "\n"; for (size_t i = 0; i < default_base.size(); i++) { os << i << ": "; os << "(" << accept[i] << ", "; if (accept2.size() > 0) os << accept2[i]; else os << "---, "; os << num[default_base[i].first] << ", " << default_base[i].second << ")"; if (st[i]) os << " " << *st[i]; if (default_base[i].first) os << " -> " << *default_base[i].first; os << "\n"; } os << "size=" << next_check.size() << " (next, check): {check state} -> {next state} : offset from base\n"; for (size_t i = 0; i < next_check.size(); i++) { if (!next_check[i].second) continue; os << i << ": "; if (next_check[i].second) { os << "(" << num[next_check[i].first] << ", " << num[next_check[i].second] << ")" << " " << *next_check[i].second << " -> " << *next_check[i].first << ": "; size_t offs = i - base_mask_size(default_base[num[next_check[i].second]].second); if (eq.size()) os << offs; else os << (transchar) offs; } os << "\n"; } } /** * Create a flex-style binary dump of the DFA tables. The table format * was partly reverse engineered from the flex sources and from * examining the tables that flex creates with its --tables-file option. * (Only the -Cf and -Ce formats are currently supported.) */ static inline size_t pad64(size_t i) { return (i + (size_t) 7) & ~(size_t) 7; } string fill64(size_t i) { const char zeroes[8] = { }; string fill(zeroes, (i & 7) ? 8 - (i & 7) : 0); return fill; } template size_t flex_table_size(Iter pos, Iter end) { return pad64(sizeof(struct table_header) + sizeof(*pos) * (end - pos)); } template void write_flex_table(ostream &os, int id, Iter pos, Iter end) { struct table_header td = { 0, 0, 0, 0 }; size_t size = end - pos; td.td_id = htons(id); td.td_flags = htons(sizeof(*pos)); td.td_lolen = htonl(size); os.write((char *)&td, sizeof(td)); for (; pos != end; ++pos) { switch (sizeof(*pos)) { case 4: os.put((char)(*pos >> 24)); os.put((char)(*pos >> 16)); /* Fall through */ case 2: os.put((char)(*pos >> 8)); /* Fall through */ case 1: os.put((char)*pos); /* Fall through */ } } os << fill64(sizeof(td) + sizeof(*pos) * size); } template void flex_table_serialize(CHFA &chfa, ostream &os, uint32_t max_size) { const char th_version[] = "notflex"; struct table_set_header th = { 0, 0, 0, 0 }; /** * Change the following two data types to adjust the maximum flex * table size. */ typedef uint32_t trans_t; if (chfa.default_base.size() >= (max_size)) { cerr << "Too many states (" << chfa.default_base.size() << ") for " "type state_t\n"; exit(1); } if (chfa.next_check.size() >= (trans_t) - 1) { cerr << "Too many transitions (" << chfa.next_check.size() << ") for " "type trans_t\n"; exit(1); } /** * Create copies of the data structures so that we can dump the tables * using the generic write_flex_table() routine. */ vector equiv_vec; if (chfa.eq.size()) { equiv_vec.resize(256); for (map::const_iterator i = chfa.eq.cbegin(); i != chfa.eq.cend(); i++) { equiv_vec[i->first.c] = i->second.c; } } vector default_vec; vector base_vec; for (DefaultBase::const_iterator i = chfa.default_base.cbegin(); i != chfa.default_base.cend(); i++) { default_vec.push_back(chfa.num[i->first]); base_vec.push_back(i->second); } vector next_vec; vector check_vec; for (NextCheck::const_iterator i = chfa.next_check.cbegin(); i != chfa.next_check.cend(); i++) { next_vec.push_back(chfa.num[i->first]); check_vec.push_back(chfa.num[i->second]); } /* Write the actual flex parser table. */ /* TODO: add max_oob */ // sizeof(th_version) includes trailing \0 size_t hsize = pad64(sizeof(th) + sizeof(th_version)); th.th_magic = htonl(YYTH_REGEX_MAGIC); th.th_flags = htons(chfa.chfaflags); th.th_hsize = htonl(hsize); th.th_ssize = htonl(hsize + flex_table_size(chfa.accept.cbegin(), chfa.accept.cend()) + (chfa.accept2.size() ? flex_table_size(chfa.accept2.cbegin(), chfa.accept2.cend()) : 0) + (chfa.eq.size() ? flex_table_size(equiv_vec.cbegin(), equiv_vec.cend()) : 0) + flex_table_size(base_vec.cbegin(), base_vec.cend()) + flex_table_size(default_vec.cbegin(), default_vec.cend()) + flex_table_size(next_vec.cbegin(), next_vec.cend()) + flex_table_size(check_vec.cbegin(), check_vec.cend())); os.write((char *)&th, sizeof(th)); os.write(th_version, sizeof(th_version)); os << fill64(sizeof(th) + sizeof(th_version)); write_flex_table(os, YYTD_ID_ACCEPT, chfa.accept.cbegin(), chfa.accept.cend()); if (chfa.accept2.size()) write_flex_table(os, YYTD_ID_ACCEPT2, chfa.accept2.cbegin(), chfa.accept2.cend()); if (chfa.eq.size()) write_flex_table(os, YYTD_ID_EC, equiv_vec.cbegin(), equiv_vec.cend()); write_flex_table(os, YYTD_ID_BASE, base_vec.cbegin(), base_vec.cend()); write_flex_table(os, YYTD_ID_DEF, default_vec.cbegin(), default_vec.cend()); write_flex_table(os, YYTD_ID_NXT, next_vec.cbegin(), next_vec.cend()); write_flex_table(os, YYTD_ID_CHK, check_vec.cbegin(), check_vec.cend()); } void CHFA::flex_table(ostream &os, optflags const &opts) { if (opts.control & CONTROL_DFA_STATE32 && default_base.size() > (1 << 16) - 1) { // TODO: implement support for flags in separate table // if (opts.control & CONTROL_DFA_FLAGS_TABLE) { // if (opts.dump & DUMP_FLAGS_TABLE) // cerr << "using flags table\n"; // flex_table_serialize(os, uint32_t, (1 << 32) - 1); // } else { /* only 24 bits available */ if (opts.dump & DUMP_DFA_STATE32) cerr << "using 32 bit state tables, embedded flags\n"; flex_table_serialize(*this, os, (1 << 24) - 1); } else { if (opts.control & CONTROL_DFA_FLAGS_TABLE) { cerr << "Flags table specified when using 16 bit state\n"; exit(1); } if (opts.dump & DUMP_DFA_STATE32) cerr << "using 16 bit state tables, embedded flags\n"; flex_table_serialize(*this, os, (1 << 16) - 1); } } apparmor-5.0.2/parser/libapparmor_re/chfa.h000066400000000000000000000041531522511161100207010ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2012 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Create a compressed hfa (chfa) from an hfa */ #ifndef __LIBAA_RE_CHFA_H #define __LIBAA_RE_CHFA_H #include #include #include "hfa.h" #include "../perms.h" #define BASE32_FLAGS 0xff000000 #define DiffEncodeBit32 0x80000000 #define MATCH_FLAG_OOB_TRANSITION 0x20000000 #define base_mask_size(X) ((X) & ~BASE32_FLAGS) typedef std::vector > DefaultBase; typedef std::vector > NextCheck; class CHFA { public: CHFA(void); CHFA(DFA &dfa, std::map &eq, optflags const &opts, int permstable32_version); void dump(ostream & os); void flex_table(ostream &os, optflags const &opts); void init_free_list(std::vector > &free_list, size_t prev, size_t start); bool fits_in(std::vector > &free_list, size_t base, StateTrans &cases); void insert_state(std::vector > &free_list, State *state, DFA &dfa); // private: // sigh templates suck, friend declaration does not work so for now // make these public std::vector accept; std::vector accept2; DefaultBase default_base; NextCheck next_check; const State *start; Renumber_Map num; std::map eq; unsigned int chfaflags; private: transchar max_eq; ssize_t first_free; }; #endif /* __LIBAA_RE_CHFA_H */ apparmor-5.0.2/parser/libapparmor_re/expr-tree.cc000066400000000000000000000416541522511161100220600ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2013 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Functions to create/manipulate an expression tree for regular expressions * that have been parsed. * * The expression tree can be used directly after the parse creates it, or * it can be factored so that the set of important nodes is smaller. * Having a reduced set of important nodes generally results in a dfa that * is closer to minimum (fewer redundant states are created). It also * results in fewer important nodes in the state set during subset * construction resulting in less memory used to create a dfa. * * Generally it is worth doing expression tree simplification before dfa * construction, if the regular expression tree contains any alternations. * Even if the regular expression doesn't simplification should be fast * enough that it can be used with minimal overhead. */ #include #include #include "expr-tree.h" #include "apparmor_re.h" using namespace std; /* Use a single static EpsNode as it carries no node specific information */ EpsNode epsnode; ostream &transchar::dump(ostream &os) const { const char *search = "\a\033\f\n\r\t|*+[](). ", *replace = "aefnrt|*+[](). ", *s; if (this->c < 0) os << "-0x" << hex << -this->c << dec; else if (this->c > 255) os << "0x" << hex << this->c << dec; else if ((s = strchr(search, this->c)) && *s != '\0') os << '\\' << replace[s - search] << " 0x" << hex << this->c << dec; else if (!isprint(this->c)) os << "0x" << hex << this->c << dec; else os << (char)this->c << " 0x" << hex << this->c << dec; return os; } ostream &operator<<(ostream &os, transchar tc) { const char *search = "\a\033\f\n\r\t|*+[](). ", *replace = "aefnrt|*+[](). ", *s; short c = tc.c; if (c < 0) os << "\\d" << "" << tc.c; else if ((s = strchr(search, c)) && *s != '\0') os << '\\' << replace[s - search]; else if (!isprint(c)) os << "\\x" << hex << c << dec; else os << (char)c; return os; } /** * Text-dump a state (for debugging). */ ostream &operator<<(ostream &os, const NodeSet &state) { os << '{'; if (!state.empty()) { NodeSet::iterator i = state.begin(); for (;;) { os << (*i)->label; if (++i == state.end()) break; os << ','; } } os << '}'; return os; } ostream &operator<<(ostream &os, Node &node) { node.dump(os); return os; } /** * hash_NodeSet - generate a hash for the Nodes in the set */ unsigned long hash_NodeSet(NodeSet *ns) { unsigned long hash = 5381; for (NodeSet::iterator i = ns->begin(); i != ns->end(); i++) { hash = ((hash << 5) + hash) + (unsigned long)*i; } return hash; } /** * label_nodes - label the node positions for pretty-printing debug output * * TODO: separate - node labels should be separate and optional, if not * present pretty printing should use Node address */ void label_nodes(Node *root) { int nodes = 1; for (depth_first_traversal i(root); i; i++) i->label = nodes++; } /** * Text-dump the syntax tree (for debugging). */ void Node::dump_syntax_tree(ostream &os) { for (depth_first_traversal i(this); i; i++) { os << i->label << '\t'; if ((*i)->child[0] == 0) os << **i << '\t' << (*i)->followpos << endl; else { if ((*i)->child[1] == 0) os << (*i)->child[0]->label << **i; else os << (*i)->child[0]->label << **i << (*i)->child[1]->label; os << '\t' << (*i)->firstpos << (*i)->lastpos << endl; } } os << endl; } /* * Normalize the regex parse tree for factoring and cancellations. Normalization * reorganizes internal (alt and cat) nodes into a fixed "normalized" form that * simplifies factoring code, in that it produces a canonicalized form for * the direction being normalized so that the factoring code does not have * to consider as many cases. * * left normalization (dir == 0) uses these rules * (E | a) -> (a | E) * (a | b) | c -> a | (b | c) * (ab)c -> a(bc) * * right normalization (dir == 1) uses the same rules but reversed * (a | E) -> (E | a) * a | (b | c) -> (a | b) | c * a(bc) -> (ab)c * * Note: This is written iteratively for a given node (the top node stays * fixed and the children are rotated) instead of recursively. * For a given node under examination rotate over nodes from * dir to !dir. Until no dir direction node meets the criterial. * Then recurse to the children (which will have a different node type) * to make sure they are normalized. * Normalization of a child node is guaranteed to not affect the * normalization of the parent. * * For cat nodes the depth first traverse order is guaranteed to be * maintained. This is not necessary for altnodes. * * Eg. For left normalization * * |1 |1 * / \ / \ * |2 T -> a |2 * / \ / \ * |3 c b |3 * / \ / \ * a b c T * */ static Node *simplify_eps_pair(Node *t) { if (t->is_type(NODE_TYPE_TWOCHILD) && t->child[0] == &epsnode && t->child[1] == &epsnode) { t->release(); return &epsnode; } return t; } static void rotate_node(Node *t, int dir) { // (a | b) | c -> a | (b | c) // (ab)c -> a(bc) Node *left = t->child[dir]; t->child[dir] = left->child[dir]; left->child[dir] = left->child[!dir]; left->child[!dir] = t->child[!dir]; // check that rotation didn't create (E | E) t->child[!dir] = simplify_eps_pair(left); } /* return False if no work done */ int TwoChildNode::normalize_eps(int dir) { if ((&epsnode == child[dir]) && (&epsnode != child[!dir])) { // (E | a) -> (a | E) // Ea -> aE // Test for E | (E | E) and E . (E . E) which will // result in an infinite loop Node *c = simplify_eps_pair(child[!dir]); child[!dir] = child[dir]; child[dir] = c; return 1; } return 0; } void CatNode::normalize(int dir) { for (;;) { if (normalize_eps(dir)) { continue; } else if (child[dir]->is_type(NODE_TYPE_CAT)) { // (ab)c -> a(bc) rotate_node(this, dir); } else { break; } } if (child[dir]) child[dir]->normalize(dir); if (child[!dir]) child[!dir]->normalize(dir); } void AltNode::normalize(int dir) { for (;;) { if (normalize_eps(dir)) { continue; } else if (child[dir]->is_type(NODE_TYPE_ALT)) { // (a | b) | c -> a | (b | c) rotate_node(this, dir); } else if (child[dir]->is_type(NODE_TYPE_CHARSET) && child[!dir]->is_type(NODE_TYPE_CHAR)) { // [a] | b -> b | [a] Node *c = child[dir]; child[dir] = child[!dir]; child[!dir] = c; } else { break; } } if (child[dir]) child[dir]->normalize(dir); if (child[!dir]) child[!dir]->normalize(dir); } //charset conversion is disabled for now, //it hinders tree optimization in some cases, so it need to be either //done post optimization, or have extra factoring rules added #if 0 static Node *merge_charset(Node *a, Node *b) { if (dynamic_cast(a) && dynamic_cast(b)) { Chars chars; chars.insert(dynamic_cast(a)->c); chars.insert(dynamic_cast(b)->c); CharSetNode *n = new CharSetNode(chars); return n; } else if (dynamic_cast(a) && dynamic_cast(b)) { Chars *chars = &dynamic_cast(b)->chars; chars->insert(dynamic_cast(a)->c); return b; } else if (dynamic_cast(a) && dynamic_cast(b)) { Chars *from = &dynamic_cast(a)->chars; Chars *to = &dynamic_cast(b)->chars; for (Chars::iterator i = from->begin(); i != from->end(); i++) to->insert(*i); return b; } //return ???; } static Node *alt_to_charsets(Node *t, int dir) { /* Node *first = NULL; Node *p = t; Node *i = t; for (;dynamic_cast(i);) { if (dynamic_cast(i->child[dir]) || dynamic_cast(i->child[dir])) { if (!first) { first = i; p = i; i = i->child[!dir]; } else { first->child[dir] = merge_charset(first->child[dir], i->child[dir]); p->child[!dir] = i->child[!dir]; Node *tmp = i; i = tmp->child[!dir]; tmp->child[!dir] = NULL; tmp->release(); } } else { p = i; i = i->child[!dir]; } } // last altnode of chain check other dir as well if (first && (dynamic_cast(i) || dynamic_cast(i))) { } */ /* if (dynamic_cast(t->child[dir]) || dynamic_cast(t->child[dir])) char_test = true; (char_test && (dynamic_cast(i->child[dir]) || dynamic_cast(i->child[dir])))) { */ return t; } #endif static Node *basic_alt_factor(Node *t, int dir) { if (!t->is_type(NODE_TYPE_ALT)) return t; if (t->child[dir]->eq(t->child[!dir])) { // (a | a) -> a Node *tmp = t->child[dir]; t->child[dir] = NULL; t->release(); return tmp; } // (ab) | (ac) -> a(b|c) if (t->child[dir]->is_type(NODE_TYPE_CAT) && t->child[!dir]->is_type(NODE_TYPE_CAT) && t->child[dir]->child[dir]->eq(t->child[!dir]->child[dir])) { // (ab) | (ac) -> a(b|c) Node *left = t->child[dir]; Node *right = t->child[!dir]; t->child[dir] = left->child[!dir]; t->child[!dir] = right->child[!dir]; right->child[!dir] = NULL; right->release(); left->child[!dir] = t; return left; } // a | (ab) -> a (E | b) -> a (b | E) if (t->child[!dir]->is_type(NODE_TYPE_CAT) && t->child[dir]->eq(t->child[!dir]->child[dir])) { Node *c = t->child[!dir]; t->child[dir]->release(); t->child[dir] = c->child[!dir]; t->child[!dir] = &epsnode; c->child[!dir] = t; return c; } // ab | (a) -> a (b | E) if (t->child[dir]->is_type(NODE_TYPE_CAT) && t->child[dir]->child[dir]->eq(t->child[!dir])) { Node *c = t->child[dir]; t->child[!dir]->release(); t->child[dir] = c->child[!dir]; t->child[!dir] = &epsnode; c->child[!dir] = t; return c; } return t; } static Node *basic_simplify(Node *t, int dir) { if (t->is_type(NODE_TYPE_CAT) && &epsnode == t->child[!dir]) { // aE -> a Node *tmp = t->child[dir]; t->child[dir] = NULL; t->release(); return tmp; } return basic_alt_factor(t, dir); } /* * assumes a normalized tree. reductions shown for left normalization * aE -> a * (a | a) -> a ** factoring patterns * a | (a | b) -> (a | b) * a | (ab) -> a (E | b) -> a (b | E) * (ab) | (ac) -> a(b|c) * * returns t - if no simplifications were made * a new root node - if simplifications were made */ Node *simplify_tree_base(Node *t, int dir, bool &mod) { if (t->is_type(NODE_TYPE_IMPORTANT)) return t; for (int i = 0; i < 2; i++) { if (t->child[i]) { Node *c = simplify_tree_base(t->child[i], dir, mod); if (c != t->child[i]) { t->child[i] = c; mod = true; } } } // only iterate on loop if modification made for (;; mod = true) { Node *tmp = basic_simplify(t, dir); if (tmp != t) { t = tmp; continue; } /* all tests after this must meet 2 alt node condition */ if (!t->is_type(NODE_TYPE_ALT) || !t->child[!dir]->is_type(NODE_TYPE_ALT)) break; // a | (a | b) -> (a | b) // a | (b | (c | a)) -> (b | (c | a)) Node *p = t; Node *i = t->child[!dir]; for (; i->is_type(NODE_TYPE_ALT); p = i, i = i->child[!dir]) { if (t->child[dir]->eq(i->child[dir])) { Node *tmp = t->child[!dir]; t->child[!dir] = NULL; t->release(); t = tmp; continue; } } // last altnode of chain check other dir as well if (t->child[dir]->eq(p->child[!dir])) { Node *tmp = t->child[!dir]; t->child[!dir] = NULL; t->release(); t = tmp; continue; } //exact match didn't work, try factoring front //a | (ac | (ad | () -> (a (E | c)) | (...) //ab | (ac | (...)) -> (a (b | c)) | (...) //ab | (a | (...)) -> (a (b | E)) | (...) Node *pp; int count = 0; Node *subject = t->child[dir]; Node *a = subject; if (subject->is_type(NODE_TYPE_CAT)) a = subject->child[dir]; for (pp = p = t, i = t->child[!dir]; i->is_type(NODE_TYPE_ALT);) { if ((i->child[dir]->is_type(NODE_TYPE_CAT) && a->eq(i->child[dir]->child[dir])) || (a->eq(i->child[dir]))) { // extract matching alt node p->child[!dir] = i->child[!dir]; i->child[!dir] = subject; subject = basic_simplify(i, dir); if (subject->is_type(NODE_TYPE_CAT)) a = subject->child[dir]; else a = subject; i = p->child[!dir]; count++; } else { pp = p; p = i; i = i->child[!dir]; } } // last altnode in chain check other dir as well if ((i->is_type(NODE_TYPE_CAT) && a->eq(i->child[dir])) || (a->eq(i))) { count++; if (t == p) { t->child[dir] = subject; t = basic_simplify(t, dir); } else { t->child[dir] = p->child[dir]; p->child[dir] = subject; pp->child[!dir] = basic_simplify(p, dir); } } else { t->child[dir] = i; p->child[!dir] = subject; } if (count == 0) break; } return t; } int debug_tree(Node *t) { int nodes = 1; if (!t->is_type(NODE_TYPE_IMPORTANT)) { if (t->child[0]) nodes += debug_tree(t->child[0]); if (t->child[1]) nodes += debug_tree(t->child[1]); } return nodes; } static void count_tree_nodes(Node *t, struct node_counts *counts) { if (t->is_type(NODE_TYPE_ALT)) { counts->alt++; count_tree_nodes(t->child[0], counts); count_tree_nodes(t->child[1], counts); } else if (t->is_type(NODE_TYPE_CAT)) { counts->cat++; count_tree_nodes(t->child[0], counts); count_tree_nodes(t->child[1], counts); } else if (t->is_type(NODE_TYPE_PLUS)) { counts->plus++; count_tree_nodes(t->child[0], counts); } else if (t->is_type(NODE_TYPE_STAR)) { counts->star++; count_tree_nodes(t->child[0], counts); } else if (t->is_type(NODE_TYPE_OPTIONAL)) { counts->optional++; count_tree_nodes(t->child[0], counts); } else if (t->is_type(NODE_TYPE_CHAR)) { counts->charnode++; } else if (t->is_type(NODE_TYPE_ANYCHAR)) { counts->any++; } else if (t->is_type(NODE_TYPE_CHARSET)) { counts->charset++; } else if (t->is_type(NODE_TYPE_NOTCHARSET)) { counts->notcharset++; } } #include "stdio.h" #include "stdint.h" #include "apparmor_re.h" // maximum number of passes to iterate on the expression tree doing // simplification passes. Simplification may exit sooner if no changes // are made. #define MAX_PASSES 1 Node *simplify_tree(Node *t, optflags const &opts) { bool update = true; int i; if (opts.dump & DUMP_DFA_TREE_STATS) { struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; count_tree_nodes(t, &counts); fprintf(stderr, "expr tree: c %d, [] %d, [^] %d, | %d, + %d, * %d, . %d, cat %d\n", counts.charnode, counts.charset, counts.notcharset, counts.alt, counts.plus, counts.star, counts.any, counts.cat); } for (i = 0; update && i < MAX_PASSES; i++) { update = false; //default to right normalize first as this reduces the number //of trailing nodes which might follow an internal * //or **, which is where state explosion can happen //eg. in one test this makes the difference between // the dfa having about 7 thousands states, // and it having about 1.25 million states int dir = 1; if (opts.control & CONTROL_DFA_TREE_LEFT) dir = 0; for (int count = 0; count < 2; count++) { bool modified; do { modified = false; if (opts.control & CONTROL_DFA_TREE_NORMAL) t->normalize(dir); t = simplify_tree_base(t, dir, modified); if (modified) update = true; } while (modified); if (opts.control & CONTROL_DFA_TREE_LEFT) dir++; else dir--; } } if (opts.dump & DUMP_DFA_TREE_STATS) { struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; count_tree_nodes(t, &counts); fprintf(stderr, "simplified expr tree: c %d, [] %d, [^] %d, | %d, + %d, * %d, . %d, cat %d\n", counts.charnode, counts.charset, counts.notcharset, counts.alt, counts.plus, counts.star, counts.any, counts.cat); } return t; } /** * Flip the children of all cat nodes. This causes strings to be matched * back-forth. */ void flip_tree(Node *node) { for (depth_first_traversal i(node); i; i++) { if ((*i)->is_type(NODE_TYPE_CAT)) { CatNode *cat = static_cast(*i); swap(cat->child[0], cat->child[1]); } } } void dump_regex_rec(ostream &os, Node *tree) { if (tree->child[0]) dump_regex_rec(os, tree->child[0]); os << *tree; if (tree->child[1]) dump_regex_rec(os, tree->child[1]); } void dump_regex(ostream &os, Node *tree) { dump_regex_rec(os, tree); os << endl; } apparmor-5.0.2/parser/libapparmor_re/expr-tree.h000066400000000000000000000712031522511161100217130ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2013 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Functions to create/manipulate an expression tree for regular expressions * that have been parsed. * * The expression tree can be used directly after the parse creates it, or * it can be factored so that the set of important nodes is smaller. * Having a reduced set of important nodes generally results in a dfa that * is closer to minimum (fewer redundant states are created). It also * results in fewer important nodes in a the state set during subset * construction resulting in less memory used to create a dfa. * * Generally it is worth doing expression tree simplification before dfa * construction, if the regular expression tree contains any alternations. * Even if the regular expression doesn't simplification should be fast * enough that it can be used with minimal overhead. */ #ifndef __LIBAA_RE_EXPR_H #define __LIBAA_RE_EXPR_H #include #include #include #include #include #include "../perms.h" #include "apparmor_re.h" /* * transchar - representative input character for state transitions * * the transchar is used as the leaf node in the expr tree created * by parsing an input regex (parse.y), and is used to build both the * states and the transitions for a state machine (hfa.{h,cc}) built * from the expression tree. * * While the state machine is currently based on byte inputs the * transchar abstraction allows for flexibility and the option of * moving to a larger input in the future. It also allows the ability * to specify out of band transitions. * * Out of band transitions allow for code to specify special transitions * that can not be triggered by an input byte stream. As such out of * band transitions can be used to separate logical units of a match. * * eg. * you need to allow an arbitrary data match (.*) followed by an arbitrary * string match ([^\x00]*), and make an acceptance dission based * on both matches. * * One way to do this is to chain the two matches in a single state * machine. However without an out of band transition, the matche pattern * for the data match (.*) could also consume the input for the string match. * To ensure the data pattern match cannot consume characters for the second * match a special character is used. This prevents state machine * generation from intermixing the two expressions. For string matches * this can be achieved with the pattern. * ([^\x00]*)\x00([\x00]*) * since \x00 can not be matched by the first expression (and is not a * valid character in a C string), the nul character can be used to * separate the string match. This however is not possible when matching * arbitrary data that can have any input character. * * Out of band transitions replace the \x00 transition in the string * example with a new input transition that comes from the driver * code. Once the first match is done, the driver supplies the non-input * character, causing the state machine to transition to the second * match pattern. * * Out of band transitions are specified using negative integers * (-1..-32k). They llow for different transitions if needed (currently * only -1 is used). * * Negative integers were chosen to represent out of band transitions * because it makes the run time match simple, and also keeps the * upper positive integer range open for future input character * expansion. * * When a chfa is built, the out of band transition is encoded as * a negative offset of the same value specified in the transchar from the * state base base value. The check value at the negative offset will * contain the owning state value. The chfa state machine is constructed * in such a way that this value will always be in bounds, and only an * unpack time verification is needed. */ class transchar { public: short c; transchar(unsigned char a): c((unsigned short) a) {} transchar(short a, bool oob __attribute__((unused))): c(a) {} transchar(const transchar &a): c(a.c) {} transchar(): c(0) {} bool operator==(const transchar &rhs) const { return this->c == rhs.c; } bool operator==(const int &rhs) const { return this->c == rhs; } bool operator!=(const transchar &rhs) const { return this->c != rhs.c; } bool operator>(const transchar &rhs) const { return this->c > rhs.c; } bool operator<(const transchar &rhs) const { return this->c < rhs.c; } bool operator<=(const transchar &rhs) const { return this->c <= rhs.c; } transchar &operator++() { // prefix (this->c)++; return *this; } transchar operator++(int) { // postfix transchar tmp(*this); (this->c)++; return tmp; } ostream &dump(ostream &os) const; }; class Chars { public: // https://stackoverflow.com/questions/32869247/a-container-for-integer-intervals-such-as-rangeset-for-c std::set chars; typedef std::set::iterator iterator; iterator begin() { return chars.begin(); } iterator end() { return chars.end(); } Chars(): chars() {} bool empty() const { return chars.empty(); } std::size_t size() const { return chars.size(); } iterator find(const transchar &key) { return chars.find(key); } std::pair insert(transchar c) { return chars.insert(c); } std::pair insert(char c) { transchar tmp(c); return chars.insert(tmp); } }; ostream &operator<<(ostream &os, transchar c); /* Compute the union of two sets. */ template std::set operator+(const std::set &a, const std::set &b) { std::set c(a); c.insert(b.begin(), b.end()); return c; } /** * When creating DFAs from regex trees, a DFA state is constructed from * a set of important nodes in the syntax tree. This includes AcceptNodes, * which indicate that when a match ends in a particular state, the * regular expressions that the AcceptNode belongs to match. */ class Node; class ImportantNode; typedef std::set NodeSet; /** * Text-dump a state (for debugging). */ ostream &operator<<(ostream &os, const NodeSet &state); /** * Out-edges from a state to another: we store the follow-set of Nodes * for each input character that is not a default match in * cases (i.e., following a CharNode or CharSetNode), and default * matches in otherwise as well as in all matching explicit cases * (i.e., following an AnyCharNode or NotCharSetNode). This avoids * enumerating all the explicit tranitions for default matches. */ typedef struct Cases { typedef std::map::iterator iterator; iterator begin() { return cases.begin(); } iterator end() { return cases.end(); } Cases(): otherwise(0) { } std::map cases; NodeSet *otherwise; } Cases; ostream &operator<<(ostream &os, Node &node); #define NODE_TYPE_NODE 0 #define NODE_TYPE_INNER (1 << 0) #define NODE_TYPE_ONECHILD (1 << 1) #define NODE_TYPE_TWOCHILD (1 << 2) #define NODE_TYPE_LEAF (1 << 3) #define NODE_TYPE_EPS (1 << 4) #define NODE_TYPE_IMPORTANT (1 << 5) #define NODE_TYPE_C (1 << 6) #define NODE_TYPE_CHAR (1 << 7) #define NODE_TYPE_CHARSET (1 << 8) #define NODE_TYPE_NOTCHARSET (1 << 9) #define NODE_TYPE_ANYCHAR (1 << 10) #define NODE_TYPE_STAR (1 << 11) #define NODE_TYPE_OPTIONAL (1 << 12) #define NODE_TYPE_PLUS (1 << 13) #define NODE_TYPE_CAT (1 << 14) #define NODE_TYPE_ALT (1 << 15) #define NODE_TYPE_SHARED (1 << 16) #define NODE_TYPE_ACCEPT (1 << 17) #define NODE_TYPE_MATCHFLAG (1 << 18) #define NODE_TYPE_EXACTMATCHFLAG (1 << 19) #define NODE_TYPE_DENYMATCHFLAG (1 << 20) #define NODE_TYPE_PROMPTMATCHFLAG (1 << 21) #define NODE_TYPE_NULLABLE (1 << 31) /* An abstract node in the syntax tree. */ class Node { public: Node(): type_flags(NODE_TYPE_NODE), label(0) { child[0] = child[1] = 0; } Node(Node *left): type_flags(NODE_TYPE_NODE), label(0) { child[0] = left; child[1] = 0; } Node(Node *left, Node *right): type_flags(NODE_TYPE_NODE), label(0) { child[0] = left; child[1] = right; } virtual ~Node() { if (child[0]) child[0]->release(); if (child[1]) child[1]->release(); } /** * firstpos, lastpos, and followpos are used to convert the syntax tree * to a DFA. * * firstpos holds nodes that can match the first character of a string * that matches the syntax tree. For the regex 'a*bcd', firstpos holds * the 'a' and 'b' nodes. firstpos is used to determine the start state * of the DFA. * * lastpos is the same as firstpos for the last character. For the regex * 'a*bcd', lastpos holds the 'd' node. lastpos is used to determine the * accepting states of the DFA. * * followpos holds the set of nodes that can match a character directly * after the current node. For the regexp 'a*bcd', the followpos of the * 'a' node are the 'b' node and the 'a' node itself. followpos is used * to determine the transitions of the DFA. * * nullable indicates that a node can match the empty string. It is used * to compute firstpos and lastpos. * * See the "Dragon Book" 2nd Edition section 3.9.2 for an in-depth * explanation. */ virtual void compute_nullable() { } virtual void compute_firstpos() = 0; virtual void compute_lastpos() = 0; virtual void compute_followpos() { } /* * min_match_len determines the smallest string that can match the * syntax tree. This is used to determine the priority of a regex. */ virtual int min_match_len() { return 0; } /* * contains_oob returns if the expression tree contains a oob character. * oob characters indicate that the rest of the DFA matches has an * out of band transition. This is used to compute min_match_len. */ virtual bool contains_oob() { return false; } virtual int eq(Node *other) = 0; virtual ostream &dump(ostream &os) = 0; void dump_syntax_tree(ostream &os); virtual void normalize(int dir) { if (child[dir]) child[dir]->normalize(dir); if (child[!dir]) child[!dir]->normalize(dir); } /* return false if no work done */ virtual int normalize_eps(int dir __attribute__((unused))) { return 0; } NodeSet firstpos, lastpos, followpos; /* child 0 is left, child 1 is right */ Node *child[2]; /* * Bitmap that stores supported pointer casts for the Node, composed * by the NODE_TYPE_* flags. This is used by is_type() as a substitute * of costly dynamic_cast calls. */ unsigned type_flags; bool is_type(unsigned type) { return type_flags & type; } unsigned int label; /* unique number for debug etc */ /** * We indirectly release Nodes through a virtual function because * accept and Eps Nodes are shared, and must be treated specially. * We could use full reference counting here but the indirect release * is sufficient and has less overhead */ virtual void release(void) { delete this; } }; class InnerNode: public Node { public: InnerNode(): Node() { type_flags |= NODE_TYPE_INNER; }; InnerNode(Node *left): Node(left) { type_flags |= NODE_TYPE_INNER; }; InnerNode(Node *left, Node *right): Node(left, right) { type_flags |= NODE_TYPE_INNER; }; }; class OneChildNode: public InnerNode { public: OneChildNode(Node *left): InnerNode(left) { type_flags |= NODE_TYPE_ONECHILD; }; }; class TwoChildNode: public InnerNode { public: TwoChildNode(Node *left, Node *right): InnerNode(left, right) { type_flags |= NODE_TYPE_TWOCHILD; }; int normalize_eps(int dir) override; }; class LeafNode: public Node { public: LeafNode(): Node() { type_flags |= NODE_TYPE_LEAF; }; void normalize(int dir __attribute__((unused))) override { return; } }; /* Match nothing (//). */ class EpsNode: public LeafNode { public: EpsNode(): LeafNode() { type_flags |= (NODE_TYPE_EPS | NODE_TYPE_NULLABLE); label = 0; } void release(void) override { /* don't delete Eps nodes because there is a single static * instance shared by all trees. Look for epsnode in the code */ } void compute_firstpos() override { } void compute_lastpos() override { } int eq(Node *other) override { if (other->is_type(NODE_TYPE_EPS)) return 1; return 0; } ostream &dump(ostream &os) override { return os << "[]"; } }; /** * Leaf nodes in the syntax tree are important to us: they describe the * characters that the regular expression matches. We also consider * AcceptNodes import: they indicate when a regular expression matches. */ class ImportantNode: public LeafNode { public: ImportantNode(): LeafNode() { type_flags |= NODE_TYPE_IMPORTANT; } void compute_firstpos() override { firstpos.insert(this); } void compute_lastpos() override { lastpos.insert(this); } virtual void follow(Cases &cases) = 0; virtual int is_accept(void) = 0; }; /* common base class for all the different classes that contain * character information. */ class CNode: public ImportantNode { public: CNode(): ImportantNode() { type_flags |= NODE_TYPE_C; } int is_accept(void) override { return false; } }; /* Match one specific character (/c/). */ class CharNode: public CNode { public: CharNode(transchar c): c(c) { type_flags |= NODE_TYPE_CHAR; } void follow(Cases &cases) override { NodeSet **x = &cases.cases[c]; if (!*x) { if (cases.otherwise && c.c >= 0) *x = new NodeSet(*cases.otherwise); else *x = new NodeSet; } (*x)->insert(followpos.begin(), followpos.end()); } int eq(Node *other) override { if (other->is_type(NODE_TYPE_CHAR)) { CharNode *o = static_cast(other); return c == o->c; } return 0; } ostream &dump(ostream &os) override { return os << c; } int min_match_len() override { if (c < 0) { // oob characters indicates end of string. // note: does NOT currently calc match len // base on NULL char separator transitions // which some match rules use. return 0; } return 1; } bool contains_oob() override { return c < 0; } transchar c; }; /* Match a set of characters (/[abc]/). */ class CharSetNode: public CNode { public: CharSetNode(Chars &chars): chars(chars) { type_flags |= NODE_TYPE_CHARSET; } void follow(Cases &cases) override { for (Chars::iterator i = chars.begin(); i != chars.end(); i++) { NodeSet **x = &cases.cases[*i]; if (!*x) { if (cases.otherwise && i->c >= 0) *x = new NodeSet(*cases.otherwise); else *x = new NodeSet; } (*x)->insert(followpos.begin(), followpos.end()); } } int eq(Node *other) override { if (!other->is_type(NODE_TYPE_CHARSET)) return 0; CharSetNode *o = static_cast(other); if (chars.size() != o->chars.size()) return 0; for (Chars::iterator i = chars.begin(), j = o->chars.begin(); i != chars.end() && j != o->chars.end(); i++, j++) { if (*i != *j) return 0; } return 1; } ostream &dump(ostream &os) override { os << '['; for (Chars::iterator i = chars.begin(); i != chars.end(); i++) os << *i; return os << ']'; } int min_match_len() override { if (contains_oob()) { return 0; } return 1; } bool contains_oob() override { for (Chars::iterator i = chars.begin(); i != chars.end(); i++) { if (*i < 0) { return true; } } return false; } Chars chars; }; /* Match all except one character (/[^abc]/). */ class NotCharSetNode: public CNode { public: NotCharSetNode(Chars &chars): chars(chars) { type_flags |= NODE_TYPE_NOTCHARSET; } void follow(Cases &cases) override { if (!cases.otherwise) cases.otherwise = new NodeSet; for (Chars::iterator j = chars.begin(); j != chars.end(); j++) { NodeSet **x = &cases.cases[*j]; if (!*x) *x = new NodeSet(*cases.otherwise); } /* Note: Add to the nonmatching characters after copying away * the old otherwise state for the matching characters. */ cases.otherwise->insert(followpos.begin(), followpos.end()); for (Cases::iterator i = cases.begin(); i != cases.end(); i++) { /* does not match oob transition chars */ if (i->first.c >=0 && chars.find(i->first) == chars.end()) i->second->insert(followpos.begin(), followpos.end()); } } int eq(Node *other) override { if (!other->is_type(NODE_TYPE_NOTCHARSET)) return 0; NotCharSetNode *o = static_cast(other); if (chars.size() != o->chars.size()) return 0; for (Chars::iterator i = chars.begin(), j = o->chars.begin(); i != chars.end() && j != o->chars.end(); i++, j++) { if (*i != *j) return 0; } return 1; } ostream &dump(ostream &os) override { os << "[^"; for (Chars::iterator i = chars.begin(); i != chars.end(); i++) os << *i; return os << ']'; } int min_match_len() override { /* Inverse match does not match any oob char at this time * so only count characters */ return 1; } bool contains_oob() override { for (Chars::iterator i = chars.begin(); i != chars.end(); i++) { if (*i < 0) { return false; } } return true; } Chars chars; }; /* Match any character (/./). */ class AnyCharNode: public CNode { public: AnyCharNode() { type_flags |= NODE_TYPE_ANYCHAR; } void follow(Cases &cases) override { if (!cases.otherwise) cases.otherwise = new NodeSet; cases.otherwise->insert(followpos.begin(), followpos.end()); for (Cases::iterator i = cases.begin(); i != cases.end(); i++) /* does not match oob transition chars */ if (i->first.c >= 0) i->second->insert(followpos.begin(), followpos.end()); } int eq(Node *other) override { if (other->is_type(NODE_TYPE_ANYCHAR)) return 1; return 0; } ostream &dump(ostream &os) override { return os << "."; } }; /* Match a node zero or more times. (This is a unary operator.) */ class StarNode: public OneChildNode { public: StarNode(Node *left): OneChildNode(left) { type_flags |= (NODE_TYPE_STAR | NODE_TYPE_NULLABLE); } void compute_firstpos() override { firstpos = child[0]->firstpos; } void compute_lastpos() override { lastpos = child[0]->lastpos; } void compute_followpos() override { NodeSet from = child[0]->lastpos, to = child[0]->firstpos; for (NodeSet::iterator i = from.begin(); i != from.end(); i++) { (*i)->followpos.insert(to.begin(), to.end()); } } int eq(Node *other) override { if (other->is_type(NODE_TYPE_STAR)) return child[0]->eq(other->child[0]); return 0; } ostream &dump(ostream &os) override { os << '('; child[0]->dump(os); return os << ")*"; } bool contains_oob() override { return child[0]->contains_oob(); } }; /* Match a node zero or one times. */ class OptionalNode: public OneChildNode { public: OptionalNode(Node *left): OneChildNode(left) { type_flags |= (NODE_TYPE_OPTIONAL | NODE_TYPE_NULLABLE); } void compute_firstpos() override { firstpos = child[0]->firstpos; } void compute_lastpos() override { lastpos = child[0]->lastpos; } int eq(Node *other) override { if (other->is_type(NODE_TYPE_OPTIONAL)) return child[0]->eq(other->child[0]); return 0; } ostream &dump(ostream &os) override { os << '('; child[0]->dump(os); return os << ")?"; } }; /* Match a node one or more times. (This is a unary operator.) */ class PlusNode: public OneChildNode { public: PlusNode(Node *left): OneChildNode(left) { type_flags |= NODE_TYPE_PLUS; } void compute_nullable() override { // The nullable bit is only ever set so no need for else branch if (child[0]->type_flags & NODE_TYPE_NULLABLE) { type_flags |= NODE_TYPE_NULLABLE; } } void compute_firstpos() override { firstpos = child[0]->firstpos; } void compute_lastpos() override { lastpos = child[0]->lastpos; } void compute_followpos() override { NodeSet from = child[0]->lastpos, to = child[0]->firstpos; for (NodeSet::iterator i = from.begin(); i != from.end(); i++) { (*i)->followpos.insert(to.begin(), to.end()); } } int eq(Node *other) override { if (other->is_type(NODE_TYPE_PLUS)) return child[0]->eq(other->child[0]); return 0; } ostream &dump(ostream &os) override { os << '('; child[0]->dump(os); return os << ")+"; } int min_match_len() override { return child[0]->min_match_len(); } bool contains_oob() override { return child[0]->contains_oob(); } }; /* Match a pair of consecutive nodes. */ class CatNode: public TwoChildNode { public: CatNode(Node *left, Node *right): TwoChildNode(left, right) { type_flags |= NODE_TYPE_CAT; } void compute_nullable() override { /* * To check that both childs are nullable, we can bitwise-AND * both of the type_flags together and then check if the * NODE_TYPE_NULLABLE bit is set on the result. * * The nullable bit is only ever set so no need for else branch */ if (child[0]->type_flags & child[1]->type_flags & NODE_TYPE_NULLABLE) { type_flags |= NODE_TYPE_NULLABLE; } } void compute_firstpos() override { if (child[0]->type_flags & NODE_TYPE_NULLABLE) firstpos = child[0]->firstpos + child[1]->firstpos; else firstpos = child[0]->firstpos; } void compute_lastpos() override { if (child[1]->type_flags & NODE_TYPE_NULLABLE) lastpos = child[0]->lastpos + child[1]->lastpos; else lastpos = child[1]->lastpos; } void compute_followpos() override { NodeSet from = child[0]->lastpos, to = child[1]->firstpos; for (NodeSet::iterator i = from.begin(); i != from.end(); i++) { (*i)->followpos.insert(to.begin(), to.end()); } } int eq(Node *other) override { if (other->is_type(NODE_TYPE_CAT)) { if (!child[0]->eq(other->child[0])) return 0; return child[1]->eq(other->child[1]); } return 0; } ostream &dump(ostream &os) override { child[0]->dump(os); child[1]->dump(os); return os; } void normalize(int dir) override; int min_match_len() override { int len = child[0]->min_match_len(); if (child[0]->contains_oob()) { // oob characters are used to indicate when the DFA transitions // from matching the path to matching the xattrs. If the left child // contains an oob character, the right side doesn't contribute to // the path match. return len; } return len + child[1]->min_match_len(); } bool contains_oob() override { return child[0]->contains_oob() || child[1]->contains_oob(); } }; /* Match one of two alternative nodes. */ class AltNode: public TwoChildNode { public: AltNode(Node *left, Node *right): TwoChildNode(left, right) { type_flags |= NODE_TYPE_ALT; } void compute_nullable() override { /* * To check that either child is nullable, we can bitwise-OR * both of the type_flags together and then check if the * NODE_TYPE_NULLABLE bit is set on the result. * * The nullable bit is only ever set so no need for else branch */ if ((child[0]->type_flags | child[1]->type_flags) & NODE_TYPE_NULLABLE) { type_flags |= NODE_TYPE_NULLABLE; } } void compute_lastpos() override { lastpos = child[0]->lastpos + child[1]->lastpos; } void compute_firstpos() override { firstpos = child[0]->firstpos + child[1]->firstpos; } int eq(Node *other) override { if (other->is_type(NODE_TYPE_ALT)) { if (!child[0]->eq(other->child[0])) return 0; return child[1]->eq(other->child[1]); } return 0; } ostream &dump(ostream &os) override { os << '('; child[0]->dump(os); os << '|'; child[1]->dump(os); os << ')'; return os; } void normalize(int dir) override; int min_match_len() override { int m1, m2; m1 = child[0]->min_match_len(); m2 = child[1]->min_match_len(); if (m1 < m2) { return m1; } return m2; } bool contains_oob() override { return child[0]->contains_oob() || child[1]->contains_oob(); } }; class SharedNode: public ImportantNode { public: SharedNode() { type_flags |= NODE_TYPE_SHARED; } void release(void) override { /* don't delete SharedNodes via release as they are shared, and * will be deleted when the table they are stored in is deleted */ } void follow(Cases &cases __attribute__ ((unused))) override { /* Nothing to follow. */ } /* requires shared nodes to be common by pointer */ int eq(Node *other) override { return (this == other); } }; /** * Indicate that a regular expression matches. An AcceptNode itself * doesn't match anything, so it will never generate any transitions. */ class AcceptNode: public SharedNode { public: AcceptNode() { type_flags |= NODE_TYPE_ACCEPT; } int is_accept(void) override { return true; } }; class MatchFlag: public AcceptNode { public: MatchFlag(int priority, perm32_t perms, perm32_t audit): priority(priority), perms(perms), audit(audit) { type_flags |= NODE_TYPE_MATCHFLAG; } ostream &dump(ostream &os) override { return os << "< 0x" << std::hex << perms << std::dec << '>'; } int priority; perm32_t perms; perm32_t audit; }; class ExactMatchFlag: public MatchFlag { public: ExactMatchFlag(int priority, perm32_t perms, perm32_t audit): MatchFlag(priority, perms, audit) { type_flags |= NODE_TYPE_EXACTMATCHFLAG; } }; class DenyMatchFlag: public MatchFlag { public: DenyMatchFlag(int priority, perm32_t perms, perm32_t quiet): MatchFlag(priority, perms, quiet) { type_flags |= NODE_TYPE_DENYMATCHFLAG; } }; class PromptMatchFlag: public MatchFlag { public: PromptMatchFlag(int priority, perm32_t prompt, perm32_t audit): MatchFlag(priority, prompt, audit) { type_flags |= NODE_TYPE_PROMPTMATCHFLAG; } }; /* Traverse the syntax tree depth-first in an iterator-like manner. */ class depth_first_traversal { std::stackpos; void push_left(Node *node) { pos.push(node); while (node->is_type(NODE_TYPE_INNER)) { pos.push(node->child[0]); node = node->child[0]; } } public: depth_first_traversal(Node *node) { push_left(node); } Node *operator*() { return pos.top(); } Node *operator->() { return pos.top(); } operator bool() { return !pos.empty(); } void operator++(int) { Node *last = pos.top(); pos.pop(); if (!pos.empty()) { /* no need to dynamic cast, as we just popped a node so * the top node must be an inner node */ InnerNode *node = (InnerNode *) (pos.top()); if (node->child[1] && node->child[1] != last) { push_left(node->child[1]); } } } }; struct node_counts { int charnode; int charset; int notcharset; int alt; int plus; int star; int optional; int any; int cat; }; extern EpsNode epsnode; int debug_tree(Node *t); Node *simplify_tree(Node *t, optflags const &opts); void label_nodes(Node *root); unsigned long hash_NodeSet(NodeSet *ns); void flip_tree(Node *node); class NodeVec { public: typedef ImportantNode ** iterator; iterator begin() { return nodes; } iterator end() { iterator t = nodes ? &nodes[len] : NULL; return t; } unsigned long hash; unsigned long len; ImportantNode **nodes; NodeVec(NodeSet *n) { hash = hash_NodeSet(n); len = n->size(); nodes = new ImportantNode *[n->size()]; unsigned int j = 0; for (NodeSet::iterator i = n->begin(); i != n->end(); i++, j++) { nodes[j] = *i; } } NodeVec(NodeSet *n, unsigned long h): hash(h) { len = n->size(); nodes = new ImportantNode *[n->size()]; ImportantNode **j = nodes; for (NodeSet::iterator i = n->begin(); i != n->end(); i++) { *(j++) = *i; } } ~NodeVec() { delete [] nodes; } unsigned long size()const { return len; } bool operator<(NodeVec const &rhs)const { if (hash == rhs.hash) { if (len == rhs.size()) { for (unsigned int i = 0; i < len; i++) { if (nodes[i] != rhs.nodes[i]) return nodes[i] < rhs.nodes[i]; } return false; } return len < rhs.size(); } return hash < rhs.hash; } }; class CacheStats { public: virtual ~CacheStats() {} unsigned long dup, sum, max; CacheStats(void): dup(0), sum(0), max(0) { }; void clear(void) { dup = sum = max = 0; } virtual unsigned long size(void) const = 0; }; struct deref_less_than { bool operator()(NodeVec * const &lhs, NodeVec * const &rhs)const { return *lhs < *rhs; } }; class NodeVecCache: public CacheStats { public: std::set cache; NodeVecCache(void): cache() { }; ~NodeVecCache() override { clear(); }; unsigned long size(void) const override { return cache.size(); } void clear() { for (std::set::iterator i = cache.begin(); i != cache.end(); i++) { delete *i; } cache.clear(); CacheStats::clear(); } NodeVec *insert(NodeSet *nodes) { if (!nodes) return NULL; std::pair::iterator,bool> uniq; NodeVec *nv = new NodeVec(nodes); uniq = cache.insert(nv); if (uniq.second == false) { delete nv; dup++; } else { sum += nodes->size(); if (nodes->size() > max) max = nodes->size(); } delete(nodes); return (*uniq.first); } }; #endif /* __LIBAA_RE_EXPR */ apparmor-5.0.2/parser/libapparmor_re/flex-tables.h000066400000000000000000000015371522511161100222110ustar00rootroot00000000000000#ifndef __LIBAA_RE_FLEX_TABLES_H #define __LIBAA_RE_FLEX_TABLES_H #include #include enum { YYTH_REGEX_MAGIC = 0x1B5E783D, }; enum { YYTH_FLAG_DIFF_ENCODE = 1, YYTH_FLAG_OOB_TRANS = 2, }; struct table_set_header { uint32_t th_magic; /* YYTH_REGEX_MAGIC (in network byte order) */ uint32_t th_hsize; uint32_t th_ssize; uint16_t th_flags; /* char th_version[]; char th_name[]; char th_pad64[];*/ } __attribute__ ((packed)); enum { YYTD_ID_ACCEPT = 1, YYTD_ID_BASE = 2, YYTD_ID_CHK = 3, YYTD_ID_DEF = 4, YYTD_ID_EC = 5, YYTD_ID_META = 6, YYTD_ID_ACCEPT2 = 7, YYTD_ID_NXT = 8, }; enum { YYTD_DATA8 = 1, YYTD_DATA16 = 2, YYTD_DATA32 = 4, }; struct table_header { uint16_t td_id; uint16_t td_flags; uint32_t td_hilen; uint32_t td_lolen; /* char td_data[]; char td_pad64[];*/ } __attribute__ ((packed)); #endif apparmor-5.0.2/parser/libapparmor_re/hfa.cc000066400000000000000000001407671522511161100207100ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2012 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Base of implementation based on the Lexical Analysis chapter of: * Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman: * Compilers: Principles, Techniques, and Tools (The "Dragon Book"), * Addison-Wesley, 1986. */ #include #include #include #include #include #include #include #include #include "expr-tree.h" #include "hfa.h" #include "policy_compat.h" #include "../immunix.h" #include "../perms.h" using namespace std; ostream &operator<<(ostream &os, const CacheStats &cache) { /* dump the state label */ os << "cache: size="; os << cache.size(); os << " dups="; os << cache.dup; os << " longest="; os << cache.max; if (cache.size()) { os << " avg="; os << cache.sum / cache.size(); } return os; } ostream &operator<<(ostream &os, const ProtoState &proto) { /* dump the state label */ os << '{'; os << proto.nnodes; os << ','; os << proto.anodes; os << '}'; return os; } ostream &operator<<(ostream &os, const State &state) { /* dump the state label */ os << '{'; os << state.label; os << '}'; return os; } ostream &operator<<(ostream &os, State &state) { /* dump the state label */ os << '{'; os << state.label; os << '}'; return os; } ostream &operator<<(ostream &os, perms_t &perms) { perms.dump(os); return os; } ostream &operator<<(ostream &os, const std::pair &p) { /* dump the state label */ if (p.second && (*p.second)[p.first] != (size_t) p.first->label) { os << '{'; os << (*p.second)[p.first]; os << " == " << *(p.first); os << '}'; } else { os << *(p.first); } return os; } /** * diff_weight - Find differential compression distance between @rel and @this * @rel: State to compare too * Returns: An integer indicating how good rel is as a base, larger == better * * Find the relative weighted difference for differential state compression * with queried state being compressed against @rel * * +1 for each transition that matches (char and dest - saves a transition) * 0 for each transition that doesn't match and exists in both states * 0 for transition that self has and @other doesn't (no extra required) * -1 for each transition that is in @rel and not in @this (have to override) * * @rel should not be a state that has already been made differential or it may * introduce extra transitions as it does not recurse to find all transitions * * Should be applied after state minimization */ int State::diff_weight(State *rel, int max_range, int upper_bound) { int weight = 0; int first = 0; if (this == rel) return 0; if (rel->diff->rel) { /* Can only be diff encoded against states that are relative * to a state of a lower depth. ie, at most one sibling in * the chain */ if (rel->diff->rel->diff->depth >= this->diff->depth) return 0; } else if (rel->diff->depth >= this->diff->depth) return 0; if (rel->trans.begin()->first.c < first) first = rel->trans.begin()->first.c; if (rel->flags & DiffEncodeFlag) { for (int i = first; i < upper_bound; i++) { State *state = rel->next(i); StateTrans::iterator j = trans.find(i); if (j != trans.end()) { if (state == j->second) weight++; /* else 0 - keep transition to mask */ } else if (state == otherwise) { /* 0 - match of default against @rel * We don't save a transition but don't have * to mask either */ } else { /* @rel has transition not covered by @this. * Need to add a transition to mask it */ weight--; } } return weight; } unsigned int count = 0; for (StateTrans::const_iterator i = rel->trans.cbegin(); i != rel->trans.cend(); i++) { StateTrans::iterator j = trans.find(i->first); if (j != trans.end()) { if (i->second == j->second) weight++; /* } else { 0 - keep transition to mask */ count++; } else if (i->second == otherwise) { /* 0 - match of default against @rel * We don't save a transition but don't have to * mask either */ } else { /* rel has transition not covered by @this. Need to * add a transition to mask */ weight--; } } /* cover transitions in @this but not in @rel */ unsigned int this_count = 0; if (count < trans.size()) { for (StateTrans::const_iterator i = trans.cbegin(); i != trans.cend(); i++) { StateTrans::iterator j = rel->trans.find(i->first); if (j == rel->trans.end()) { this_count++; if (i->second == rel->otherwise) /* replaced by rel->cases.otherwise */ weight++; } } } if (rel->otherwise != otherwise) { /* rel default transitions have to be masked with transitions * This covers all transitions not covered above */ weight -= (max_range) - (rel->trans.size() + this_count); } return weight; } /** * make_relative - Make this state relative to @rel * @rel: state to make this state relative too * @upper_bound: the largest value for an input transition (256 for a byte). * * @rel can be a relative (differentially compressed state) */ int State::make_relative(State *rel, int upper_bound) { int weight = 0; int first = 0; if (this == rel || !rel) return 0; if (flags & DiffEncodeFlag) return 0; if (rel->trans.begin()->first.c < 0) first = rel->trans.begin()->first.c; flags |= DiffEncodeFlag; for (int i = first; i < upper_bound ; i++) { State *next = rel->next(i); StateTrans::iterator j = trans.find(i); if (j != trans.end()) { if (j->second == next) { trans.erase(j); weight++; } /* else keep transition to mask */ } else if (otherwise == next) { /* do nothing, otherwise transition disappears when * reassigned */ } else { /* need a new transition to mask those in lower state */ trans[i] = otherwise; weight--; } } otherwise = rel; return weight; } /** * flatten_differential - remove differential encode from this state * @nonmatching: the nonmatching state for the state machine * @upper_bound: the largest value for an input transition (256 for a byte). */ void State::flatten_relative(State *nonmatching, int upper_bound) { if (!(flags & DiffEncodeFlag)) return; map count; int first = 0; if (next(-1) != nonmatching) first = -1; for (int i = first; i < upper_bound; i++) count[next(i)] += 1; int j = first; State *def = next(first); for (int i = first + 1; i < upper_bound; i++) { if (count[next(i)] > count[next(j)]) { j = i; def = next(i); } } for (int i = first; i < upper_bound; i++) { if (trans.find(i) != trans.end()) { if (trans[i] == def) trans.erase(i); } else { if (trans[i] != def) trans[i] = next(i); } } otherwise = def; flags = flags & ~DiffEncodeFlag; } static void split_node_types(NodeSet *nodes, NodeSet **anodes, NodeSet **nnodes ) { *anodes = *nnodes = NULL; for (NodeSet::iterator i = nodes->begin(); i != nodes->end(); ) { if ((*i)->is_accept()) { if (!*anodes) *anodes = new NodeSet; (*anodes)->insert(*i); NodeSet::iterator k = i++; nodes->erase(k); } else i++; } *nnodes = nodes; } State *DFA::add_new_state(optflags const &opts, NodeSet *anodes, NodeSet *nnodes, State *other) { NodeVec *nnodev, *anodev; nnodev = nnodes_cache.insert(nnodes); anodev = anodes_cache.insert(anodes); ProtoState proto; proto.init(nnodev, anodev); State *state; try { state = new State(uniq_perms, opts, node_map.size(), proto, other, filedfa); } catch(int error) { /* this function is called in the DFA object creation, * and the exception prevents the destructor from * being called, so call the helper here */ cleanup(); throw error; } pair x = node_map.insert(proto, state); if (x.second == false) { delete state; } else { states.push_back(state); work_queue.push_back(state); } return x.first->second; } State *DFA::add_new_state(optflags const &opts, NodeSet *nodes, State *other) { /* The splitting of nodes should probably get pushed down into * follow(), ie. put in separate lists from the start */ NodeSet *anodes, *nnodes; split_node_types(nodes, &anodes, &nnodes); State *state = add_new_state(opts, anodes, nnodes, other); return state; } void DFA::update_state_transitions(optflags const &opts, State *state) { /* Compute possible transitions for state->nodes. This is done by * iterating over all the nodes in state->nodes and combining the * transitions. * * The resultant transition set is a mapping of characters to * sets of nodes. * * Note: the follow set for accept nodes is always empty so we don't * need to compute follow for the accept nodes in a protostate */ Cases cases; for (NodeVec::iterator i = state->proto.nnodes->begin(); i != state->proto.nnodes->end(); i++) (*i)->follow(cases); /* Now for each set of nodes in the computed transitions, make * sure that there is a state that maps to it, and add the * matching case to the state. */ /* check the default transition first */ if (cases.otherwise) state->otherwise = add_new_state(opts, cases.otherwise, nonmatching); else state->otherwise = nonmatching; /* For each transition from *from, check if the set of nodes it * transitions to already has been mapped to a state */ for (Cases::iterator j = cases.begin(); j != cases.end(); j++) { State *target; try { target = add_new_state(opts, j->second, nonmatching); } catch (int error) { /* when add_new_state fails, there could still * be NodeSets in the rest of cases, so clean * them up before re-throwing the exception */ for (Cases::iterator k = ++j; k != cases.end(); k++) { delete k->second; } throw error; } /* Don't insert transition that the otherwise transition * already covers */ if (target != state->otherwise) { state->trans[j->first] = target; if (j->first.c < 0 && -j->first.c > oob_range) oob_range = -j->first.c; } } } /* WARNING: This routine can only be called from within DFA creation as * the nodes value is only valid during dfa construction. */ void DFA::dump_node_to_dfa(void) { cerr << "Mapping of States to expr nodes\n" " State <= Nodes\n" "-------------------\n"; for (Partition::iterator i = states.begin(); i != states.end(); i++) cerr << " " << (*i)->label << " <= " << (*i)->proto << "\n"; } void DFA::process_work_queue(const char *header, optflags const &opts) { int i = 0; while (!work_queue.empty()) { if (i % 1000 == 0 && (opts.dump & DUMP_DFA_PROGRESS)) { cerr << "\033[2K" << header << ": queue " << work_queue.size() << "\tstates " << states.size() << "\teliminated duplicates " << node_map.dup << "\r"; } i++; State *from = work_queue.front(); work_queue.pop_front(); /* Update 'from's transitions, and if it transitions to any * unknown State create it and add it to the work_queue */ update_state_transitions(opts, from); } /* while (!work_queue.empty()) */ } /** * Construct a DFA from a syntax tree. */ DFA::DFA(Node *root, optflags const &opts, bool buildfiledfa): root(root), filedfa(buildfiledfa) { diffcount = 0; /* set by diff_encode */ max_range = 256; upper_bound = 256; oob_range = 0; ord_range = 8; if (opts.dump & DUMP_DFA_PROGRESS) fprintf(stderr, "Creating dfa:\r"); for (depth_first_traversal i(root); i; i++) { (*i)->compute_nullable(); (*i)->compute_firstpos(); (*i)->compute_lastpos(); } if (opts.dump & DUMP_DFA_PROGRESS) fprintf(stderr, "Creating dfa: followpos\r"); for (depth_first_traversal i(root); i; i++) { (*i)->compute_followpos(); } nonmatching = add_new_state(opts, new NodeSet, NULL); start = add_new_state(opts, new NodeSet(root->firstpos), nonmatching); /* the work_queue contains the states that need to have their * transitions computed. This could be done with a recursive * algorithm instead of a work_queue, but it would be slightly slower * and consume more memory. * * TODO: currently the work_queue is treated in a breadth first * search manner. Test using the work_queue in a depth first * manner, this may help reduce the number of entries on the * work_queue at any given time, thus reducing peak memory use. */ work_queue.push_back(start); process_work_queue("Creating dfa", opts); max_range += oob_range; /* if oob_range is ever greater than 256 need to move to computing this */ if (oob_range) ord_range = 9; /* cleanup Sets of nodes used computing the DFA as they are no longer * needed. */ for (depth_first_traversal i(root); i; i++) { (*i)->firstpos.clear(); (*i)->lastpos.clear(); (*i)->followpos.clear(); } if (opts.dump & DUMP_DFA_NODE_TO_DFA) dump_node_to_dfa(); if (opts.dump & (DUMP_DFA_STATS)) { cerr << "\033[2KCreated dfa: states " << states.size() << " proto { " << node_map << " }, nnodes { " << nnodes_cache << " }, anodes { " << anodes_cache << " }\n"; } /* Clear out uniq_nnodes as they are no longer needed. * Do not clear out uniq_anodes, as we need them for minimizations * diffs, unions, ... */ nnodes_cache.clear(); node_map.clear(); } DFA::~DFA() { cleanup(); } State *DFA::match_len(State *state, const char *str, size_t len) { for (; len > 0; ++str, --len) state = state->next(*str); return state; } State *DFA::match_until(State *state, const char *str, const char term) { while (*str != term) state = state->next(*str++); return state; } State *DFA::match(const char *str) { return match_until(start, str, 0); } void DFA::dump_uniq_perms(const char *s) { cerr << "Unique Permission sets: " << s << " (" << uniq_perms.size() << ")\n"; cerr << "----------------------\n"; for (std::set::iterator i = uniq_perms.begin(); i != uniq_perms.end(); i++) { cerr << " allow:" << hex << (*i)->allow << " deny:" << (*i)->deny << " audit:" << (*i)->audit << " quiet:" << (*i)->quiet << " prompt:" << (*i)->prompt << dec << "\n"; } } // make sure work_queue and reachable insertion are always done together static void push_reachable(set &reachable, list &work_queue, State *state) { work_queue.push_back(state); reachable.insert(state); } /* Remove dead or unreachable states */ void DFA::remove_unreachable(optflags const &opts) { set reachable; /* find the set of reachable states */ reachable.insert(nonmatching); push_reachable(reachable, work_queue, start); while (!work_queue.empty()) { State *from = work_queue.front(); work_queue.pop_front(); if (from->otherwise != nonmatching && reachable.find(from->otherwise) == reachable.end()) push_reachable(reachable, work_queue, from->otherwise); for (StateTrans::iterator j = from->trans.begin(); j != from->trans.end(); j++) { if (reachable.find(j->second) == reachable.end()) push_reachable(reachable, work_queue, j->second); } } /* walk the set of states and remove any that aren't reachable */ if (reachable.size() < states.size()) { int count = 0; Partition::iterator i; Partition::iterator next; for (i = states.begin(); i != states.end(); i = next) { next = i; next++; if (reachable.find(*i) == reachable.end()) { if (opts.dump & DUMP_DFA_UNREACHABLE) { cerr << "unreachable: " << **i; if (*i == start) cerr << " <=="; if ((*i)->perms->is_accept()) (*i)->perms->dump(cerr); cerr << "\n"; } State *current = *i; states.erase(i); delete(current); count++; } } if (count && (opts.dump & DUMP_DFA_STATS)) cerr << "DFA: states " << states.size() << " removed " << count << " unreachable states\n"; } } /* test if two states have the same transitions under partition_map */ bool DFA::same_mappings(State *s1, State *s2) { /* assumes otherwise is set to best choice, if there are multiple * otherwise choices this will fail to fully minimize the dfa * if we are not careful. Make sure in cases with multiple * equiv otherwise we always choose the same otherwise to avoid */ if (s1->otherwise->partition != s2->otherwise->partition) return false; StateTrans::const_iterator j1; StateTrans::const_iterator j2; for (j1 = s1->trans.cbegin(), j2 = s2->trans.cbegin(); j1 != s1->trans.cend() && j2 != s2->trans.cend(); /*inc inline*/) { if (j1->first < j2->first) { if (j1->second->partition != s2->otherwise->partition) return false; j1++; } else if (j1->first == j2->first) { if (j1->second->partition != j2->second->partition) return false; j1++; j2++; } else { if (s1->otherwise->partition != j2->second->partition) return false; j2++; } } for ( ; j1 != s1->trans.end(); j1++) { if (j1->second->partition != s2->otherwise->partition) return false; } for ( ; j2 != s2->trans.end(); j2++) { if (j2->second->partition != s1->otherwise->partition) return false; } return true; } int DFA::apply_and_clear_deny(void) { int c = 0; /* TODO: update to remove perms that are no longer in use */ for (Partition::iterator i = states.begin(); i != states.end(); i++) c += (*i)->apply_and_clear_deny(uniq_perms); return c; } ostream &DFA::dump_partition(ostream &os, Partition &p) { /* first entry is the representative state */ for (Partition::const_iterator i = p.cbegin(); i != p.cend(); i++) { os << **i; if (i == p.begin()) os << " : "; else os << ", "; } os << "\n"; return os; } ostream &DFA::dump_partitions(ostream &os, const char *description, list &partitions) { size_t j = 0; os << "Dumping Minimization partition mapping: " << description << "\n"; for (list::const_iterator p = partitions.cbegin(); p != partitions.cend(); p++) { os << " [" << j++ << "] "; os << (*(*p)->begin())->perms << ": "; (void) dump_partition(os, **p); os << "\n"; } os << "\n"; return os; } typedef map PermMap; /* minimize the number of dfa states */ void DFA::minimize(optflags const &opts) { PermMap perm_map; list partitions; /* Set up the initial partitions * minimum of 3: 1 trap (nonmatching), 1 start, and 1 non accepting or 1 accepting */ int accept_count = 0; int final_accept = 0; for (Partition::iterator i = states.begin(); i != states.end(); i++) { if (*i == start) { Partition *part = new Partition(); part->push_back(*i); partitions.push_back(part); (*i)->partition = part; continue; } PermMap::iterator p = perm_map.find((*i)->perms); if (p == perm_map.end()) { Partition *part = new Partition(); part->push_back(*i); perm_map.insert(make_pair((*i)->perms, part)); partitions.push_back(part); (*i)->partition = part; if ((*i)->perms->is_accept()) accept_count++; } else { (*i)->partition = p->second; p->second->push_back(*i); } if ((opts.dump & DUMP_DFA_PROGRESS) && (partitions.size() % 1000 == 0)) cerr << "\033[2KMinimize dfa: partitions " << partitions.size() << "\tinit " << partitions.size() << " (accept " << accept_count << ")\r"; } if (opts.dump & DUMP_DFA_MIN_PARTS) (void) dump_partitions(cerr, "Initial", partitions); /* perm_map is no longer needed so free the memory it is using. * Don't remove - doing it manually here helps reduce peak memory usage. */ perm_map.clear(); int init_count = partitions.size(); if (opts.dump & DUMP_DFA_PROGRESS) cerr << "\033[2KMinimize dfa: partitions " << partitions.size() << "\tinit " << init_count << " (accept " << accept_count << ")\r"; /* Now do repartitioning until each partition contains the set of * states that are the same. This will happen when the partition * splitting stables. With a worse case of 1 state per partition * ie. already minimized. */ Partition *new_part; int new_part_count; do { new_part_count = 0; for (list::iterator p = partitions.begin(); p != partitions.end(); p++) { new_part = NULL; State *rep = *((*p)->begin()); Partition::iterator next; for (Partition::iterator s = ++(*p)->begin(); s != (*p)->end();) { if (same_mappings(rep, *s)) { ++s; continue; } if (!new_part) { new_part = new Partition; list::iterator tmp = p; partitions.insert(++tmp, new_part); new_part_count++; } new_part->push_back(*s); s = (*p)->erase(s); } /* remapping partition_map for new_part entries * Do not do this above as it messes up same_mappings */ if (new_part) { for (Partition::iterator m = new_part->begin(); m != new_part->end(); m++) { (*m)->partition = new_part; } } if ((opts.dump & DUMP_DFA_PROGRESS) && (partitions.size() % 100 == 0)) cerr << "\033[2KMinimize dfa: partitions " << partitions.size() << "\tinit " << init_count << " (accept " << accept_count << ")\r"; } } while (new_part_count); if (partitions.size() == states.size()) { if (opts.dump & DUMP_DFA_STATS) cerr << "\033[2KDfa minimization no states removed: partitions " << partitions.size() << "\tinit " << init_count << " (accept " << accept_count << ")\n"; goto out; } if (opts.dump & DUMP_DFA_MIN_PARTS) (void) dump_partitions(cerr, "Pre-remap", partitions); /* Remap the dfa so it uses the representative states * Use the first state of a partition as the representative state * At this point all states with in a partition have transitions * to states within the same partitions, however this can slow * down compressed dfa compression as there are more states, */ if (opts.dump & DUMP_DFA_MIN_PARTS) cerr << "Partitions after minimization\n"; for (list::iterator p = partitions.begin(); p != partitions.end(); p++) { /* representative state for this partition */ State *rep = *((*p)->begin()); if (opts.dump & DUMP_DFA_MIN_PARTS) cerr << *rep << " : "; /* update representative state's transitions */ rep->otherwise = *rep->otherwise->partition->begin(); for (StateTrans::iterator c = rep->trans.begin(); c != rep->trans.end(); ) { Partition *partition = c->second->partition; if (rep->otherwise != *partition->begin()) { c->second = *partition->begin(); c++; } else /* transition is now covered by otherwise */ c = rep->trans.erase(c); } /* clear the state label for all non representative states, * and accumulate permissions */ for (Partition::iterator i = ++(*p)->begin(); i != (*p)->end(); i++) { if (opts.dump & DUMP_DFA_MIN_PARTS) cerr << **i << ", "; (*i)->label = -1; /* merging perms is only necessary if partitioning doesn't * completely separate base on unique perms. * atm this should be the case. Code to handle case is * only to document what should be done if this is allowed * in the future */ if ((rep->perms != (*i)->perms) && (*rep->perms != *(*i)->perms)) { throw std::runtime_error("Minimization different permissions in same partion"); /* perms_t tmp = *rep->perms; tmp.add((*i)->perms, filedfa); rep->perms = uniq_perms.insert(tmp); */ } } if (rep->perms->is_accept()) final_accept++; if (opts.dump & DUMP_DFA_MIN_PARTS) cerr << "\n"; } if (opts.dump & DUMP_DFA_STATS) cerr << "\033[2KMinimized dfa: final partitions " << partitions.size() << " (accept " << final_accept << ")" << "\tinit " << init_count << " (accept " << accept_count << ")\n"; /* make sure nonmatching and start state are up to date with the * mappings */ { Partition *partition = nonmatching->partition; if (*partition->begin() != nonmatching) { nonmatching = *partition->begin(); } partition = start->partition; if (*partition->begin() != start) { start = *partition->begin(); } } /* Now that the states have been remapped, remove all states * that are not the representative states for their partition, they * will have a label == -1 */ for (Partition::iterator i = states.begin(); i != states.end();) { if ((*i)->label == -1) { State *s = *i; i = states.erase(i); delete(s); } else i++; } out: /* Cleanup */ while (!partitions.empty()) { Partition *p = partitions.front(); partitions.pop_front(); delete(p); } } /* diff_encode helper functions */ static unsigned int add_to_dag(DiffDag *dag, State *state, State *parent) { unsigned int rc = 0; if (!state->diff) { dag->rel = NULL; if (parent) dag->depth = parent->diff->depth + 1; else dag->depth = 1; dag->state = state; state->diff = dag; rc = 1; } if (parent && parent->diff->depth < state->diff->depth) state->diff->parents.push_back(parent); return rc; } static int diff_partition(State *state, Partition &part, int max_range, int upper_bound, State **candidate) { int weight = 0; *candidate = NULL; for (Partition::const_iterator i = part.cbegin(); i != part.cend(); i++) { if (*i == state) continue; int tmp = state->diff_weight(*i, max_range, upper_bound); if (tmp > weight) { weight = tmp; *candidate = *i; } } return weight; } /** * diff_encode - compress dfa by differentially encoding state transitions * @opts: flags controlling dfa creation * * This function reduces the number of transitions that need to be stored * by encoding transitions as the difference between the state and a * another transitions that is set as the states default. * * For performance reasons this function does not try to compute the * absolute best encoding (maximal spanning tree) but instead computes * a very good encoding within the following limitations. * - Not all states have to be differentially encoded. This allows for * multiple states to be used as a terminating basis. * - The number of state transitions needed to match an input of length * m will be 2m * * To guarantee this the ordering and distance calculation is done in the * following manner. * - A DAG of the DFA is created starting with the start state(s). * - A state can only be relative (have a differential encoding) to * another state if that state has * - a lower depth in the DAG * - is a sibling (same depth) that is not relative * - is a sibling that is relative to a state with lower depth in the DAG * * The run time constraints are maintained by the DAG ordering + relative * state constraints. For any input character C when at state S with S being * at level N in the DAG then at most 2N states must be traversed to find the * transition for C. However on the maximal number of transitions is not m*m, * because when a character is matched and forward movement is made through * the DFA any relative transition search will move back through the DAG order. * So say for character C we start matching on a state S that is at depth 10 * in the DAG. The transition for C is not found in S and we recurse backwards * to a depth of 6. A transition is found and it steps to the next state, but * the state transition at most will only move 1 deeper into the DAG so for * the next state the maximum number of states traversed is 2*7. */ void DFA::diff_encode(optflags const &opts) { DiffDag *dag; unsigned int xcount = 0, xweight = 0, transitions = 0, depth = 0; /* clear the depth flag */ for (Partition::iterator i = states.begin(); i != states.end(); i++) { (*i)->diff = NULL; transitions += (*i)->trans.size(); } /* Prealloc structures we need. We know the exact number of elements, * and once setup they don't change so we don't need the flexibility * or overhead of stl, just allocate the needed data as an array */ dag = new DiffDag [states.size()]; /* Generate DAG ordering and parent sets */ add_to_dag(&dag[0], nonmatching, NULL); add_to_dag(&dag[1], start, NULL); unsigned int tail = 2; for (unsigned int i = 1; i < tail; i++) { State *state = dag[i].state; State *child = dag[i].state->otherwise; if (child) tail += add_to_dag(&dag[tail], child, state); for (StateTrans::iterator j = state->trans.begin(); j != state->trans.end(); j++) { child = j->second; tail += add_to_dag(&dag[tail], child, state); } } depth = dag[tail - 1].depth; /* calculate which state to make a transitions relative too */ for (unsigned int i = 2; i < tail; i++) { State *state = dag[i].state; State *candidate = NULL; int weight = diff_partition(state, state->otherwise->diff->parents, max_range, upper_bound, &candidate); for (StateTrans::iterator j = state->trans.begin(); j != state->trans.end(); j++) { State *tmp_candidate; int tmp = diff_partition(state, j->second->diff->parents, max_range, upper_bound, &tmp_candidate); if (tmp > weight) { weight = tmp; candidate = tmp_candidate; } } if ((opts.dump & DUMP_DFA_DIFF_PROGRESS) && (i % 100 == 0)) cerr << "\033[2KDiff Encode: " << i << " of " << tail << ". Diff states " << xcount << " Savings " << xweight << "\r"; state->diff->rel = candidate; if (candidate) { xcount++; xweight += weight; } } /* now make transitions relative, start at the back of the list so * as to start with the last transitions and work backwards to avoid * having to traverse multiple previous states (that have been made * relative already) to reconstruct previous state transition table */ unsigned int aweight = 0; diffcount = 0; for (int i = tail - 1; i > 1; i--) { if (dag[i].rel) { int weight = dag[i].state->make_relative(dag[i].rel, upper_bound); aweight += weight; diffcount++; } } if (opts.dump & DUMP_DFA_DIFF_STATS) cerr << "Diff encode states: " << diffcount << " of " << tail << " reached @ depth " << depth << ". " << aweight << " trans removed\n"; if (xweight != aweight) cerr << "Diff encode error: actual savings " << aweight << " != expected " << xweight << "\n"; if (xcount != diffcount) cerr << "Diff encode error: actual count " << diffcount << " != expected " << xcount << " \n"; /* cleanup */ for (unsigned int i = 0; i < tail; i++) dag[i].parents.clear(); delete [] dag; } /** * flatten_differential - remove differential state encoding * * Flatten the dfa back into a flat encoding. */ void DFA::undiff_encode(void) { for (Partition::iterator i = states.begin(); i != states.end(); i++) (*i)->flatten_relative(nonmatching, upper_bound); diffcount = 0; } void DFA::dump_diff_chain(ostream &os, map &relmap, Partition &chain, State *state, unsigned int &count, unsigned int &total, unsigned int &max) { if (relmap[state].size() == 0) { for (Partition::iterator i = chain.begin(); i != chain.end(); i++) os << **i << " <- "; os << *state << "\n"; count++; total += chain.size() + 1; if (chain.size() + 1 > max) max = chain.size() + 1; } chain.push_back(state); for (Partition::iterator i = relmap[state].begin(); i != relmap[state].end(); i++) dump_diff_chain(os, relmap, chain, *i, count, total, max); chain.pop_back(); } /* Dump the DFA diff_encoding chains */ void DFA::dump_diff_encode(ostream &os) { map rel; Partition base, chain; for (Partition::const_iterator i = states.cbegin(); i != states.cend(); i++) { if ((*i)->flags & DiffEncodeFlag) rel[(*i)->otherwise].push_back(*i); else base.push_back(*i); } unsigned int count = 0, total = 0, max = 0; for (Partition::const_iterator i = base.cbegin(); i != base.cend(); i++) dump_diff_chain(os, rel, chain, *i, count, total, max); os << base.size() << " non-differentially encoded states\n"; os << "chains: " << count - base.size() << "\n"; os << "average chain size: " << (double) (total - base.size()) / (double) (count - base.size()) << "\n"; os << "longest chain: " << max << "\n"; } /** * text-dump the DFA (for debugging). */ void DFA::dump(ostream &os, Renumber_Map *renum) { for (Partition::const_iterator i = states.cbegin(); i != states.cend(); i++) { if (*i == start || (*i)->perms->is_accept()) { os << make_pair(*i, renum); if (*i == start) { os << " <== "; (*i)->perms->dump_header(os); } if ((*i)->perms->is_accept()) (*i)->perms->dump(os); os << "\n"; } } os << "\n"; for (Partition::const_iterator i = states.cbegin(); i != states.cend(); i++) { Chars excluded; bool first = true; for (StateTrans::const_iterator j = (*i)->trans.cbegin(); j != (*i)->trans.cend(); j++) { if (j->second == nonmatching) { excluded.insert(j->first); } else { if (first) { first = false; os << make_pair(*i, renum) << " perms: "; if ((*i)->perms->is_accept()) (*i)->perms->dump(os); else os << "none"; os << "\n"; } os << " "; j->first.dump(os) << " -> " << make_pair(j->second, renum); if ((j)->second->perms->is_accept()) os << " ", (j->second)->perms->dump(os); os << "\n"; } } if ((*i)->otherwise != nonmatching) { if (first) { first = false; os << make_pair(*i, renum) << " perms: "; if ((*i)->perms->is_accept()) (*i)->perms->dump(os); else os << "none"; os << "\n"; } os << " ["; if (!excluded.empty()) { os << "^"; for (Chars::iterator k = excluded.begin(); k != excluded.end(); k++) { os << *k; } } os << "] -> " << make_pair((*i)->otherwise, renum); if ((*i)->otherwise->perms->is_accept()) os << " ", (*i)->otherwise->perms->dump(os); os << "\n"; } } os << "\n"; } /** * Create a dot (graphviz) graph from the DFA (for debugging). */ void DFA::dump_dot_graph(ostream & os) { os << "digraph \"dfa\" {" << "\n"; for (Partition::const_iterator i = states.cbegin(); i != states.cend(); i++) { if (*i == nonmatching) continue; os << "\t\"" << **i << "\" [" << "\n"; if (*i == start) { os << "\t\tstyle=bold" << "\n"; } if ((*i)->perms->is_accept()) { os << "\t\tlabel=\"" << **i << "\\n"; (*i)->perms->dump(os); os << "\"\n"; } os << "\t]" << "\n"; } for (Partition::const_iterator i = states.cbegin(); i != states.cend(); i++) { Chars excluded; for (StateTrans::const_iterator j = (*i)->trans.cbegin(); j != (*i)->trans.cend(); j++) { if (j->second == nonmatching) excluded.insert(j->first); else { os << "\t\"" << **i << "\" -> \"" << *j->second << "\" [" << "\n"; os << "\t\tlabel=\""; j->first.dump(os); os << "\"\n\t]" << "\n"; } } if ((*i)->otherwise != nonmatching) { os << "\t\"" << **i << "\" -> \"" << *(*i)->otherwise << "\" [" << "\n"; if (!excluded.empty()) { os << "\t\tlabel=\"[^"; for (Chars::iterator i = excluded.begin(); i != excluded.end(); i++) { i->dump(os); } os << "]\"" << "\n"; } os << "\t]" << "\n"; } } os << '}' << "\n"; } /** * Compute character equivalence classes in the DFA to save space in the * transition table. */ map DFA::equivalence_classes(optflags const &opts) { map classes; transchar next_class = 1; for (Partition::iterator i = states.begin(); i != states.end(); i++) { /* Group edges to the same next state together */ map node_sets; for (StateTrans::iterator j = (*i)->trans.begin(); j != (*i)->trans.end(); j++) { if (j->first.c < 0) continue; node_sets[j->second].insert(j->first); } for (map::iterator j = node_sets.begin(); j != node_sets.end(); j++) { /* Group edges to the same next state together by class */ map node_classes; bool class_used = false; for (Chars::iterator k = j->second.begin(); k != j->second.end(); k++) { pair::iterator, bool> x = classes.insert(make_pair(*k, next_class)); if (x.second) class_used = true; pair::iterator, bool> y = node_classes.insert(make_pair(x.first->second, Chars())); y.first->second.insert(*k); } if (class_used) { next_class++; class_used = false; } for (map::iterator k = node_classes.begin(); k != node_classes.end(); k++) { /** * If any other characters are in the same class, move * the characters in this class into their own new * class */ map::iterator l; for (l = classes.begin(); l != classes.end(); l++) { if (l->second == k->first && k->second.find(l->first) == k->second.end()) { class_used = true; break; } } if (class_used) { for (Chars::iterator l = k->second.begin(); l != k->second.end(); l++) { classes[*l] = next_class; } next_class++; class_used = false; } } } } if (opts.dump & DUMP_DFA_EQUIV_STATS) fprintf(stderr, "Equiv class reduces to %d classes\n", next_class.c - 1); return classes; } /** * Text-dump the equivalence classes (for debugging). */ void dump_equivalence_classes(ostream &os, map &eq) { map rev; for (map::const_iterator i = eq.cbegin(); i != eq.cend(); i++) { Chars &chars = rev.insert(make_pair(i->second, Chars())).first->second; chars.insert(i->first); } os << "(eq):" << "\n"; for (map::iterator i = rev.begin(); i != rev.end(); i++) { os << i->first.c << ':'; Chars &chars = i->second; for (Chars::iterator j = chars.begin(); j != chars.end(); j++) { os << ' ' << *j; } os << "\n"; } } /** * Replace characters with classes (which are also represented as * characters) in the DFA transition table. */ void DFA::apply_equivalence_classes(map &eq) { /** * Note: We only transform the transition table; the nodes continue to * contain the original characters. */ for (Partition::iterator i = states.begin(); i != states.end(); i++) { map tmp; tmp.swap((*i)->trans); for (StateTrans::iterator j = tmp.begin(); j != tmp.end(); j++) { if (j->first.c < 0) continue; (*i)->trans.insert(make_pair(eq[j->first], j->second)); } } } void DFA::compute_perms_table_ent(perms_t * const perms, size_t pos, std::vector &perms_table, idxmap_t &idxmap) { uint32_t accept1, accept2, accept3; // until front end doesn't map the way it does perms->map_perms_to_accept(accept1, accept2, accept3); idxmap.insert(make_pair(perms, pos)); if (filedfa) { perms_table[pos] = compute_fperms_user(accept1, accept2, accept3); perms_table[pos + 1] = compute_fperms_other(accept1, accept2, accept3); } else { perms_table[pos] = compute_perms_entry(accept1, accept2, accept3); } } void DFA::compute_perms_table(vector &perms_table) { idxmap_t idxmap; size_t mult = filedfa ? 2 : 1; size_t pos = filedfa ? 2 : 1; assert(states.size() >= 2); perms_table.resize(uniq_perms.size()*mult); compute_perms_table_ent(nonmatching->perms, 0, perms_table, idxmap); nonmatching->idx = 0; start->idx = 0; for (perms_t_Cache::const_iterator i = uniq_perms.cbegin(); i != uniq_perms.cend(); i++) { if (*i == nonmatching->perms) continue; compute_perms_table_ent(*i, pos, perms_table, idxmap); pos += mult; } // nonmatching and start need to be 0 and 1 so handle outside of loop for (Partition::iterator i = states.begin(); i != states.end(); i++) { if (*i == nonmatching || *i == start) continue; idxmap_t::iterator j = idxmap.find((*i)->perms); if (j == idxmap.end()) { perms_t_Cache::iterator k = uniq_perms.find((*i)->perms); if (k == uniq_perms.end()) throw std::runtime_error("permission not in permission table map"); else throw std::runtime_error("permission not in idx table map"); } (*i)->idx = j->second; } } #if 0 typedef set AcceptNodes; map dominance(DFA & dfa) { map is_dominated; for (States::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) { AcceptNodes set1; for (State::iterator j = (*i)->begin(); j != (*i)->end(); j++) { if (AcceptNode * accept = dynamic_cast(*j)) set1.insert(accept); } for (AcceptNodes::iterator j = set1.begin(); j != set1.end(); j++) { pair::iterator, bool> x = is_dominated.insert(make_pair(*j, set1)); if (!x.second) { AcceptNodes & set2(x.first->second), set3; for (AcceptNodes::iterator l = set2.begin(); l != set2.end(); l++) { if (set1.find(*l) != set1.end()) set3.insert(*l); } set3.swap(set2); } } } return is_dominated; } #endif static inline int diff_qualifiers(perm32_t perm1, perm32_t perm2) { return ((perm1 & AA_EXEC_TYPE) && (perm2 & AA_EXEC_TYPE) && (perm1 & AA_EXEC_TYPE) != (perm2 & AA_EXEC_TYPE)); } // only applied if filedfa static void add_implied_ix_mmap(optflags const &opts, vector &priority, perm32_t &mask) { // ix implies EXEC_MMAP if ((mask & AA_MAY_EXEC) && (mask & AA_EXEC_INHERIT)) { //USER_EXEC_MAP = 6 if (priority[EXEC_MMAP_SHIFT] <= priority[MAY_EXEC_SHIFT]) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << "[6]<=" << priority[EXEC_MMAP_SHIFT] << " < " << priority[MAY_EXEC_SHIFT] << " adding implied m to " << hex << "mask: " << mask << dec; mask |= AA_USER_EXEC_MMAP; priority[EXEC_MMAP_SHIFT] = priority[MAY_EXEC_SHIFT]; } else if (opts.dump & DUMP_DFA_PERMS) cerr << " " << "[6]>" << priority[EXEC_MMAP_SHIFT] << " > " << priority[MAY_EXEC_SHIFT] << " skipping adding implied m to " << hex << "mask: " << mask << dec; } // ix implies EXEC_MMAP if ((mask & AA_OTHER_EXEC) && (mask & AA_OTHER_EXEC_INHERIT)) { //OTHER_EXEC_MAP = 20 = 6 (EXEC_MMAP_SHIFT) + 14 (AA_OTHER_SHIFT) if (priority[EXEC_OTHER_MMAP_SHIFT] <= priority[MAY_OTHER_EXEC_SHIFT]) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << "[20]<=" << priority[EXEC_OTHER_MMAP_SHIFT] << " < " << priority[MAY_OTHER_EXEC_SHIFT] << " adding implied m to " << hex << "mask: " << mask << dec; mask |= AA_OTHER_EXEC_MMAP; priority[EXEC_OTHER_MMAP_SHIFT] = priority[MAY_OTHER_EXEC_SHIFT]; } else if (opts.dump & DUMP_DFA_PERMS) cerr << " " << "[20]>" << priority[EXEC_OTHER_MMAP_SHIFT] << " > " << priority[MAY_OTHER_EXEC_SHIFT] << " skipping adding implied m to " << hex << "mask: " << mask << dec; } } /* update a single permission based on priority * - only called if match->perm | match-> audit bit set */ static int pri_update_perm(optflags const &opts, vector &priority, int i, MatchFlag *match, perms_t &perms, bool filedfa) { perm32_t xmask = 0; perm32_t mask = 1 << i; perm32_t amask = mask; // scaling priority *4 int pri = match->priority<<2; /* use priority to get proper ordering and application of the type * of match flag (rule type). * * Note: this is the last use of priority, it is dropped and not * used in the backend. */ if (match->is_type(NODE_TYPE_DENYMATCHFLAG)) pri += 3; // for exec permission bits and their audit control exact match // has higher priority else if (match->is_type(NODE_TYPE_EXACTMATCHFLAG) && (mask & AA_EXEC_BITS)) pri += 2; else if (!match->is_type(NODE_TYPE_PROMPTMATCHFLAG)) pri += 1; // else prompt +0 if (priority[i] > pri) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " > " << pri << " SKIPPING " << hex << (match->perms) << "/" << (match->audit) << dec << "\n"; return 0; } // drop once we move the xindex out of the perms in the front end if (filedfa) { if (mask & AA_USER_EXEC) { xmask = AA_USER_EXEC_TYPE; amask = mask | xmask; } else if (mask & AA_OTHER_EXEC) { xmask = AA_OTHER_EXEC_TYPE; amask = mask | xmask; } } if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " vs. " << pri << " mask: " << hex << mask << " xmask: " << xmask << " amask: " << amask << dec << "\n"; if (priority[i] < pri) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " < " << pri << " clearing " << hex << "mask: " << amask << " from " << (perms.allow) << "/" << (perms.audit) << " -> " << dec; priority[i] = pri; perms.clear_bits(amask); if (opts.dump & DUMP_DFA_PERMS) cerr << hex << (perms.allow) << "/" << (perms.audit) << dec << "\n"; } // the if conditions in order of permission priority if (match->is_type(NODE_TYPE_DENYMATCHFLAG)) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << pri << " deny " << hex << (match->perms & amask) << "/" << (match->audit & amask) << dec << "\n"; // deny x never implies mmap so not used here perms.deny |= match->perms & amask; perms.quiet |= match->audit & amask; perms.allow &= ~amask; perms.audit &= ~amask; perms.prompt &= ~amask; } else if (match->is_type(NODE_TYPE_EXACTMATCHFLAG)) { /* exact match only asserts dominance on the XTYPE */ if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << pri << " exact " << hex << (match->perms & amask) << "/" << (match->audit & amask) << dec << "\n"; if (filedfa && xmask && (perms.allow & amask) && !is_merged_x_consistent(perms.allow, match->perms & amask)) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << pri << " exact match conflict" << "\n"; return 1; } // dominance is only done for XTYPE so only clear that // note xmask only set if setting x perm bit, so this // won't clear for other bit types perms.allow &= ~xmask; perms.audit &= ~xmask; perms.prompt &= ~xmask; perms.allow |= match->perms & amask; perms.audit |= match->audit & amask; // can't specify exact prompt atm } else if (!match->is_type(NODE_TYPE_PROMPTMATCHFLAG)) { // allow perms, if exact has been encountered will // already be set if overlaps x here, don't conflict, // because exact will override if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << pri << " allow " << hex << (match->perms & amask) << "/" << (match->audit & amask) << dec << "\n"; if (filedfa && xmask && (perms.allow & amask) && !is_merged_x_consistent(perms.allow, match->perms & amask)) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << pri << " allow match conflict" << "\n"; return 1; } perms.allow |= match->perms & amask; perms.audit |= match->audit & amask; } else { // if (match->is_type(NODE_TYPE_PROMPTMATCHFLAG)) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << pri << " prompt " << hex << (match->perms & amask) << "/" << (match->audit & amask) << dec << "\n"; if (filedfa && xmask && (perms.allow & amask) && !is_merged_x_consistent(perms.allow, match->perms & amask)) { if (opts.dump & DUMP_DFA_PERMS) cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << pri << " prompt match conflict" << "\n"; return 1; } perms.prompt |= match->perms & amask; perms.audit |= match->audit & amask; } return 0; } /** * Compute the permission flags that this state corresponds to. If we * have any exact matches, then they override the execute and safe * execute flags. */ perms_t::perms_t(optflags const &opts, NodeVec *state, bool filedfa) { int error = 0; // scaling priority by *4 std::vector priority(sizeof(perm32_t)*8, MIN_INTERNAL_PRIORITY*4); // 32 but wasn't tied to perm32_t clear(); if (!state) return; if (opts.dump & DUMP_DFA_PERMS) { cerr << "Building Perms"; if (filedfa) cerr << " (file)"; cerr << "\n"; } for (NodeVec::iterator i = state->begin(); i != state->end(); i++) { if (!(*i)->is_type(NODE_TYPE_MATCHFLAG)) continue; MatchFlag *match = static_cast(*i); perm32_t bit = 1; perm32_t check = match->perms | match->audit; if (filedfa) check &= ~ALL_AA_EXEC_TYPE; for (int i = 0; check; i++) { if (check & bit) { error = pri_update_perm(opts, priority, i, match, *this, filedfa); if (error) goto out; } check &= ~bit; bit <<= 1; } } if (filedfa && (allow & AA_EXEC_BITS)) { add_implied_ix_mmap(opts, priority, allow); } if (opts.dump & DUMP_DFA_PERMS) { cerr << " computed: "; dump(cerr); cerr << "\n"; } out: if (error) { fprintf(stderr, "profile has merged rule with conflicting x modifiers\n"); throw error; } } apparmor-5.0.2/parser/libapparmor_re/hfa.h000066400000000000000000000273741522511161100205500ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2012 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Base of implementation based on the Lexical Analysis chapter of: * Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman: * Compilers: Principles, Techniques, and Tools (The "Dragon Book"), * Addison-Wesley, 1986. */ #ifndef __LIBAA_RE_HFA_H #define __LIBAA_RE_HFA_H #include #include #include #include #include #include #include #include "expr-tree.h" extern int prompt_compat_mode; #define DiffEncodeFlag 1 class State; typedef std::map StateTrans; typedef std::list Partition; #include "../immunix.h" ostream &operator<<(ostream &os, const State &state); ostream &operator<<(ostream &os, State &state); class perms_t { public: perms_t(void): allow(0), deny(0), prompt(0), audit(0), quiet(0) { }; perms_t(optflags const &opts, NodeVec *match, bool filedfa); bool is_accept(void) { return (allow | deny | prompt | audit | quiet); } void dump_header(ostream &os) { os << "(allow/deny/prompt/audit/quiet)"; } ostream &dump(ostream &os) { os << "(0x " << std::hex << allow << "/" << deny << "/" << "/" << prompt << "/" << audit << "/" << quiet << ')' << std::dec; return os; } void clear(void) { allow = deny = prompt = audit = quiet = 0; } void clear_bits(perm32_t bits) { allow &= ~bits; deny &= ~bits; prompt &= ~bits; audit &= ~bits; quiet &= ~bits; } void add(perms_t &rhs, bool filedfa) { deny |= rhs.deny; if (filedfa && !is_merged_x_consistent(allow & ALL_USER_EXEC, rhs.allow & ALL_USER_EXEC)) // different x modifier in same partition throw 1; if (filedfa && !is_merged_x_consistent(allow & ALL_OTHER_EXEC, rhs.allow & ALL_OTHER_EXEC)) // different x modifier in same partition throw 1; allow |= rhs.allow; prompt |= rhs.prompt; audit |= rhs.audit; quiet |= rhs.quiet; } /* returns true if perm is no longer accept */ bool apply_and_clear_deny(void) { if (deny) { allow &= ~deny; prompt &= ~deny; /* don't change audit or quiet based on clearing * deny at this stage. This was made unique in * accept_perms, and the info about whether * we are auditing or quieting based on the explicit * deny has been discarded and can only be inferred. * But we know it is correct from accept_perms() * audit &= deny; * quiet &= deny; */ deny = 0; return !is_accept(); } return false; } void map_perms_to_accept(perm32_t &accept1, perm32_t &accept2, perm32_t &accept3) const { accept1 = allow; accept2 = PACK_AUDIT_CTL(audit, quiet); accept3 = prompt; } bool operator<(perms_t const &rhs)const { if (this == &rhs) return false; if (allow != rhs.allow) return allow < rhs.allow; if (deny != rhs.deny) return deny < rhs.deny; if (prompt != rhs.prompt) return prompt < rhs.prompt; if (audit != rhs.audit) return audit < rhs.audit; if (audit != rhs.audit) return audit > rhs.audit; return quiet < rhs.quiet; } bool operator==(perms_t const &rhs)const { if (this == &rhs) return true; if (allow != rhs.allow) return false; if (deny != rhs.deny) return false; if (prompt != rhs.prompt) return false; if (audit != rhs.audit) return false; return quiet == rhs.quiet; } bool operator!=(perms_t const &rhs)const { return !(*this == rhs); } perm32_t allow, deny, prompt, audit, quiet; }; struct deref_less_than_perms { bool operator()(perms_t * const &lhs, perms_t * const &rhs)const { return *lhs < *rhs; } }; // a dedup cache for permissions class perms_t_Cache: public CacheStats { std::set cache; public: typedef std::set::iterator iterator; iterator begin() { return cache.begin(); } iterator end() { return cache.end(); } typedef std::set::const_iterator const_iterator; iterator cbegin() { return cache.cbegin(); } iterator cend() { return cache.cend(); } iterator find(perms_t * const &val) { return cache.find(val); } perms_t_Cache(void): cache() { }; ~perms_t_Cache() { clear(); }; virtual unsigned long size(void) const { return cache.size(); } void clear() { for (iterator i = cache.begin(); i != cache.end(); i++) { delete *i; } cache.clear(); CacheStats::clear(); } // will delete perms if not inserted into cache perms_t *insert(perms_t *perms) { if (!perms) return NULL; std::pair uniq; uniq = cache.insert(perms); if (uniq.second == false) { delete perms; dup++; } return (*uniq.first); } perms_t *insert(const perms_t &perms) { perms_t *tmp = new perms_t(perms); return insert(tmp); } perms_t *insert(optflags const &opts, NodeVec *match, bool filedfa) { perms_t *tmp = new perms_t(opts, match, filedfa); return insert(tmp); } }; /* * ProtoState - NodeSet and ancillery information used to create a state */ class ProtoState { public: NodeVec *nnodes; NodeVec *anodes; /* init is used instead of a constructor because ProtoState is used * in a union */ void init(NodeVec *n, NodeVec *a = NULL) { nnodes = n; anodes = a; } bool operator<(ProtoState const &rhs)const { if (nnodes == rhs.nnodes) return anodes < rhs.anodes; return nnodes < rhs.nnodes; } unsigned long size(void) { if (anodes) return nnodes->size() + anodes->size(); return nnodes->size(); } }; /* Temporary state structure used when building differential encoding * @parents - set of states that have transitions to this state * @depth - level in the DAG * @state - back reference to state this DAG entry belongs * @rel - state that this state is relative to for differential encoding */ struct DiffDag { Partition parents; int depth; State *state; State *rel; }; /* * State - DFA individual state information * label: a unique label to identify the state used for pretty printing * the non-matching state is setup to have label == 0 and * the start state is setup to have label == 1 * audit: the audit permission mask for the state * accept: the accept permissions for the state * trans: set of transitions from this state * otherwise: the default state for transitions not in @trans * partition: Is a temporary work variable used during dfa minimization. * it can be replaced with a map, but that is slower and uses more * memory. * proto: Is a temporary work variable used during dfa creation. It can * be replaced by using the nodemap, but that is slower */ class State { public: State(perms_t_Cache &cache, optflags const &opts, int l, ProtoState &n, State *other, bool filedfa): label(l), flags(0), idx(0), trans() { perms = cache.insert(opts, n.anodes, filedfa); if (other) otherwise = other; else otherwise = this; proto = n; }; State *next(transchar c) { State *state = this; do { StateTrans::iterator i = state->trans.find(c); if (i != state->trans.end()) return i->second; if (!(state->flags & DiffEncodeFlag)) return state->otherwise; state = state->otherwise; } while (state); /* never reached */ assert(0); return NULL; } ostream &dump(ostream &os) { os << *this << "\n"; for (StateTrans::iterator i = trans.begin(); i != trans.end(); i++) { os << " " << i->first.c << " -> " << *i->second << "\n"; } return os; } int diff_weight(State *rel, int max_range, int upper_bound); int make_relative(State *rel, int upper_bound); void flatten_relative(State *, int upper_bound); bool apply_and_clear_deny(perms_t_Cache &cache) { perms_t *tmp = new perms_t(*perms); bool res = tmp->apply_and_clear_deny(); perms = cache.insert(tmp); return res; } int label; int flags; int idx; perms_t *perms; StateTrans trans; State *otherwise; /* temp storage for State construction */ union { Partition *partition; /* used during minimization */ ProtoState proto; /* used during creation */ DiffDag *diff; /* used during diff encoding */ }; }; class NodeMap: public CacheStats { public: typedef std::map::iterator iterator; iterator begin() { return cache.begin(); } iterator end() { return cache.end(); } std::map cache; NodeMap(void): cache() { }; ~NodeMap() override { clear(); }; unsigned long size(void) const override { return cache.size(); } void clear() { cache.clear(); CacheStats::clear(); } std::pair insert(ProtoState &proto, State *state) { std::pair uniq; uniq = cache.insert(std::make_pair(proto, state)); if (uniq.second == false) { dup++; } else { sum += proto.size(); if (proto.size() > max) max = proto.size(); } return uniq; } }; typedef std::map Renumber_Map; typedef std::map idxmap_t; /* Transitions in the DFA. */ class DFA { void dump_node_to_dfa(void); State *add_new_state(optflags const &opts, NodeSet *nodes, State *other); State *add_new_state(optflags const &opts,NodeSet *anodes, NodeSet *nnodes, State *other); void update_state_transitions(optflags const &opts, State *state); void process_work_queue(const char *header, optflags const &); void dump_diff_chain(ostream &os, std::map &relmap, Partition &chain, State *state, unsigned int &count, unsigned int &total, unsigned int &max); /* temporary values used during computations */ NodeVecCache anodes_cache; NodeVecCache nnodes_cache; NodeMap node_map; std::list work_queue; void cleanup(void) { anodes_cache.clear(); nnodes_cache.clear(); for (Partition::iterator i = states.begin(); i != states.end(); i++) { delete *i; } states.clear(); } public: DFA(Node *root, optflags const &flags, bool filedfa); virtual ~DFA(); State *match_len(State *state, const char *str, size_t len); State *match_until(State *state, const char *str, const char term); State *match(const char *str); void remove_unreachable(optflags const &flags); bool same_mappings(State *s1, State *s2); void minimize(optflags const &flags); int apply_and_clear_deny(void); void clear_priorities(void); void diff_encode(optflags const &flags); void undiff_encode(void); void dump_diff_encode(ostream &os); void dump(ostream &os, Renumber_Map *renum); void dump_dot_graph(ostream &os); void dump_uniq_perms(const char *s); ostream &dump_partition(ostream &os, Partition &p); ostream &dump_partitions(ostream &os, const char *description, std::list &partitions); std::map equivalence_classes(optflags const &flags); void apply_equivalence_classes(std::map &eq); void compute_perms_table_ent(perms_t * const perms, size_t pos, std::vector &perms_table, idxmap_t &idxmap); void compute_perms_table(std::vector &perms_table); unsigned int diffcount; int oob_range; int max_range; int ord_range; int upper_bound; Node *root; perms_t_Cache uniq_perms; State *nonmatching, *start; Partition states; bool filedfa; }; void dump_equivalence_classes(ostream &os, std::map &eq); #endif /* __LIBAA_RE_HFA_H */ apparmor-5.0.2/parser/libapparmor_re/parse.h000066400000000000000000000017171522511161100211150ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2010 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Parsing of regular expression into expression trees as implemented in * expr-tree */ #ifndef __LIBAA_RE_PARSE_H #define __LIBAA_RE_PARSE_H #include "expr-tree.h" int regex_parse(Node **tree, const char *rule); #endif /* __LIBAA_RE_PARSE_H */ apparmor-5.0.2/parser/libapparmor_re/parse.y000066400000000000000000000106651522511161100211400ustar00rootroot00000000000000/* * (C) 2006, 2007 Andreas Gruenbacher * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) * Copyright 2009-2010 Canonical Ltd. * * The libapparmor library is licensed under the terms of the GNU * Lesser General Public License, version 2.1. Please see the file * COPYING.LGPL. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * * Parsing of regular expression into expression trees as implemented in * expr-tree */ %{ /* #define DEBUG_TREE */ #include "expr-tree.h" using namespace std; %} %union { char c; Node *node; Chars *cset; } %{ void regex_error(Node **, const char *, const char *); #define YYLEX_PARAM &text int regex_lex(YYSTYPE *, const char **); static inline Chars *insert_char(Chars* cset, transchar a) { cset->insert(a); return cset; } static inline Chars* insert_char_range(Chars* cset, transchar a, transchar b) { if (a > b) swap(a, b); for (transchar i = a; i <= b; i++) cset->insert(i); return cset; } %} %define api.pure /* %error-verbose */ %lex-param {YYLEX_PARAM} %parse-param {Node **root} %parse-param {const char *text} %define api.prefix {regex_} %token CHAR %type regex_char cset_char1 cset_char cset_charN %type charset cset_chars %type regex expr terms0 terms qterm term /** * Note: destroy all nodes upon failure, but *not* the start symbol once * parsing succeeds! */ %destructor { $$->release(); } expr terms0 terms qterm term %destructor { delete $$; } charset cset_chars %% /* FIXME: Does not parse "[--]", "[---]", "[^^-x]". I don't actually know which precise grammar Perl regexs use, and rediscovering that is proving to be painful. */ regex : /* empty */ { *root = $$ = &epsnode; } | expr { *root = $$ = $1; } ; expr : terms | expr '|' terms0 { $$ = new AltNode($1, $3); } | '|' terms0 { $$ = new AltNode(&epsnode, $2); } ; terms0 : /* empty */ { $$ = &epsnode; } | terms ; terms : qterm | terms qterm { $$ = new CatNode($1, $2); } ; qterm : term | term '*' { $$ = new StarNode($1); } | term '+' { $$ = new PlusNode($1); } ; term : '.' { $$ = new AnyCharNode; } | regex_char { $$ = new CharNode($1); } | '[' charset ']' { $$ = new CharSetNode(*$2); delete $2; } | '[' '^' charset ']' { $$ = new NotCharSetNode(*$3); delete $3; } | '[' '^' '^' cset_chars ']' { $4->insert('^'); $$ = new NotCharSetNode(*$4); delete $4; } | '(' regex ')' { $$ = $2; } ; regex_char : CHAR | '^' { $$ = '^'; } | '-' { $$ = '-'; } | ']' { $$ = ']'; } ; charset : cset_char1 cset_chars { $$ = insert_char($2, $1); } | cset_char1 '-' cset_charN cset_chars { $$ = insert_char_range($4, $1, $3); } ; cset_chars : /* nothing */ { $$ = new Chars; } | cset_chars cset_charN { $$ = insert_char($1, $2); } | cset_chars cset_charN '-' cset_charN { $$ = insert_char_range($1, $2, $4); } ; cset_char1 : cset_char | ']' { $$ = ']'; } | '-' { $$ = '-'; } ; cset_charN : cset_char | '^' { $$ = '^'; } ; cset_char : CHAR | '[' { $$ = '['; } | '*' { $$ = '*'; } | '+' { $$ = '+'; } | '.' { $$ = '.'; } | '|' { $$ = '|'; } | '(' { $$ = '('; } | ')' { $$ = ')'; } ; %% #include "../lib.h" int regex_lex(YYSTYPE *val, const char **pos) { int tmp; val->c = **pos; switch(*(*pos)++) { case '\0': (*pos)--; return 0; case '*': case '+': case '.': case '|': case '^': case '-': case '[': case ']': case '(' : case ')': return *(*pos - 1); case '\\': tmp = str_escseq(pos, "*+.|^$-[](){}"); if (tmp == -1) { /* bad escape sequence, just skip it for now, that * is output \\ followed by the invalid esc seq * TODO: output error message */ val->c = '\\'; (*pos)--; } else val->c = tmp; break; } return CHAR; } void regex_error(Node ** __attribute__((unused)), const char *text __attribute__((unused)), const char *error __attribute__((unused))) { /* We don't want the library to print error messages. */ } apparmor-5.0.2/parser/libapparmor_re/policy_compat.cc000066400000000000000000000157701522511161100230070ustar00rootroot00000000000000/* * Copyright (c) 2022 * Canonical, Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ /* * This is a set of functions to provide convertion from old style permission * mappings, to new style kernel mappings. It is based on the kernel to * as the kernel needs this for backwards compatibility. This allows the * userspace to convert to the new permission mapping without reworking * the internal dfa permission tracking. * * In the future this code will be converted to go the reverse direction * i.e. new mappings into old, which the parser will need for backwards * compat with old kernels. */ #include #include "policy_compat.h" #include "../perms.h" extern int prompt_compat_mode; /* remap old accept table embedded permissions to separate permission table */ static uint32_t dfa_map_xindex(uint16_t mask) { uint16_t old_index = (mask >> 10) & 0xf; uint32_t index = 0; if (mask & 0x100) index |= AA_X_UNSAFE; if (mask & 0x200) index |= AA_X_INHERIT; if (mask & 0x80) index |= AA_X_UNCONFINED; if (old_index == 1) { index |= AA_X_UNCONFINED; } else if (old_index == 2) { index |= AA_X_NAME; } else if (old_index == 3) { index |= AA_X_NAME | AA_X_CHILD; } else if (old_index) { index |= AA_X_TABLE; index |= old_index - 4; } return index; } /* * map old dfa inline permissions to new format */ static inline uint32_t dfa_user_allow(uint32_t accept1) { return (accept1 & 0x7f) | (accept1 & 0x80000000); } static inline uint32_t dfa_user_xbits(uint32_t accept1) { return (accept1 >> 7) & 0x7f; } static inline uint32_t dfa_user_audit(uint32_t accept1 __attribute__((unused)), uint32_t accept2) { return accept2 & 0x7f; } static inline uint32_t dfa_user_quiet(uint32_t accept1 __attribute__((unused)), uint32_t accept2) { return (accept2 >> 7) & 0x7f; } static inline uint32_t dfa_user_xindex(uint32_t accept1) { return dfa_map_xindex(accept1 & 0x3fff); } static inline uint32_t dfa_other_allow(uint32_t accept1) { return ((accept1 >> 14) & 0x7f) | (accept1 & 0x80000000); } static inline uint32_t dfa_other_xbits(uint32_t accept1) { return ((accept1 >> 7) >> 14) & 0x7f; } static inline uint32_t dfa_other_audit(uint32_t accept1 __attribute__((unused)), uint32_t accept2) { return (accept2 >> 14) & 0x7f; } static inline uint32_t dfa_other_quiet(uint32_t accept1 __attribute__((unused)), uint32_t accept2) { return ((accept2 >> 7) >> 14) & 0x7f; } static inline uint32_t dfa_other_xindex(uint32_t accept1) { return dfa_map_xindex((accept1 >> 14) & 0x3fff); } /** * map_old_perms - map old file perms layout to the new layout * @old: permission set in old mapping * * Returns: new permission mapping */ static uint32_t map_old_perms(uint32_t old) { uint32_t perm = old & 0xf; if (old & AA_MAY_READ) perm |= AA_MAY_GETATTR | AA_MAY_OPEN; if (old & AA_MAY_WRITE) perm |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE | AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN; if (old & 0x10) perm |= AA_MAY_LINK; /* the old mapping lock and link_subset flags where overlaid * and use was determined by part of a pair that they were in */ if (old & 0x20) perm |= AA_MAY_LOCK | AA_LINK_SUBSET; if (old & 0x40) /* AA_EXEC_MMAP */ perm |= AA_EXEC_MMAP; return perm; } static void compute_fperms_allow(struct aa_perms *perms, uint32_t accept1) { perms->allow |= AA_MAY_GETATTR; /* change_profile wasn't determined by ownership in old mapping */ if (accept1 & 0x80000000) perms->allow |= AA_MAY_CHANGE_PROFILE; if (accept1 & 0x40000000) perms->allow |= AA_MAY_ONEXEC; } struct aa_perms compute_fperms_user(uint32_t accept1, uint32_t accept2, uint32_t accept3) { struct aa_perms perms = { }; perms.allow = map_old_perms(dfa_user_allow(accept1)); perms.prompt = map_old_perms(dfa_user_allow(accept3)); perms.audit = map_old_perms(dfa_user_audit(accept1, accept2)); perms.quiet = map_old_perms(dfa_user_quiet(accept1, accept2)); perms.xindex = dfa_user_xindex(accept1); compute_fperms_allow(&perms, accept1); perms.prompt &= ~(perms.allow | perms.deny); return perms; } struct aa_perms compute_fperms_other(uint32_t accept1, uint32_t accept2, uint32_t accept3) { struct aa_perms perms = { }; perms.allow = map_old_perms(dfa_other_allow(accept1)); perms.prompt = map_old_perms(dfa_other_allow(accept3)); perms.audit = map_old_perms(dfa_other_audit(accept1, accept2)); perms.quiet = map_old_perms(dfa_other_quiet(accept1, accept2)); perms.xindex = dfa_other_xindex(accept1); compute_fperms_allow(&perms, accept1); perms.prompt &= ~(perms.allow | perms.deny); return perms; } static uint32_t map_other(uint32_t x) { return ((x & 0x3) << 8) | /* SETATTR/GETATTR */ ((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */ ((x & 0x60) << 19); /* SETOPT/GETOPT */ } struct aa_perms compute_perms_entry(uint32_t accept1, uint32_t accept2, uint32_t accept3) // don't need to worry about version internally within the parser // uint32_t version) { struct aa_perms perms = { }; perms.allow = dfa_user_allow(accept1); perms.prompt = dfa_user_allow(accept3); perms.audit = dfa_user_audit(accept1, accept2); perms.quiet = dfa_user_quiet(accept1, accept2); if (accept1 & AA_COMPAT_CONT_MATCH) perms.allow |= AA_CONT_MATCH; /* * This mapping is convulated due to history. * v1-v4: only file perms, which are handled by compute_fperms * v5: added policydb which dropped user conditional to gain new * perm bits, but had to map around the xbits because the * userspace compiler was still munging them. * v9: adds using the xbits in policydb because the compiler now * supports treating policydb permission bits different. * Unfortunately there is no way to force auditing on the * perms represented by the xbits */ perms.allow |= map_other(dfa_other_allow(accept1)); // v9 encoding never rolled out. AA_MAY_LOCK needed to fix // non fs unix locking see kernel commit // 1cf26c3d2c4c apparmor: fix apparmor mediating locking non-fs unix sockets //if (VERSION_LE(version, v8)) perms.allow |= AA_MAY_LOCK; //else // perms.allow |= map_xbits(dfa_user_xbits(dfa, state)); /* * for v5-v9 perm mapping in the policydb, the other set is used * to extend the general perm set */ perms.prompt |= map_other(dfa_other_allow(accept3)); perms.audit |= map_other(dfa_other_audit(accept1, accept2)); perms.quiet |= map_other(dfa_other_quiet(accept1, accept2)); //if (VERSION_GT(version, v8)) // perms.quiet |= map_xbits(dfa_other_xbits(dfa, state)); return perms; } apparmor-5.0.2/parser/libapparmor_re/policy_compat.h000066400000000000000000000020741522511161100226420ustar00rootroot00000000000000/* * Copyright (c) 2022 * Canonical, Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ #ifndef __LIBAA_RE_POLICY_COMPAT_H #define __LIBAA_RE_POLICY_COMPAT_H #include struct aa_perms compute_fperms_user(uint32_t accept1, uint32_t accept2, uint32_t accept3); struct aa_perms compute_fperms_other(uint32_t accept1, uint32_t accept2, uint32_t accept3); struct aa_perms compute_perms_entry(uint32_t accept1, uint32_t accept2, uint32_t accept3); #endif /* __AA_POLICY_COMPAT_H */ apparmor-5.0.2/parser/mount.cc000066400000000000000000000746531522511161100163160ustar00rootroot00000000000000/* * Copyright (c) 2010 * Canonical, Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical * Ltd. */ /** * The mount command, its mix of options and flags, its permissions and * mapping are a mess. * mount [-lhV] * * mount -a [-fFnrsvw] [-t vfstype] [-O optlist] * * mount [-fnrsvw] [-o option[,option]...] device|dir * * mount [-fnrsvw] [-t vfstype] [-o options] device dir * *---------------------------------------------------------------------- * Mount flags of no interest for apparmor mediation * -a, --all * -F fork for simultaneous mount * -f fake, do everything except that actual system call * -h --help * -i, --internal-only * -n mount without writing in /etc/mtab * -O limits what is auto mounted * -p, --pass-fd num * -s Tolerate sloppy mount options * -U uuid * -V --version * --no-canonicalize * *---------------------------------------------------------------------- * what do we do with these * -l list? * -L

;~?)^0t\ɸ2߹;Pi`A' HM0UŝiӺ'4U`d_1N/RNds{bq Ht6\ |ᄿP{,ЫoF&ϻ8\M,ݻqfq"wHIJY9}X\BFy gk;<7\Glv97>SSc5!hX1f*y!ԉirI F[ʪۖاi 9/?z{v&K4LT"GG`bS> 2f~b0qa/!.n =p{Xo 'Q^ `pOyBtˈXTHАXHshf&b6aNivviy1LJ(mS(1ةz"䎮%1i"3>Mv^偢EHR7,)n1- tu)Gog7K|=\ d:)OsETZ^lQ2p»)]-a@:WC_|U `m=|m/BK)\6T)-`]"3̙V ᓤ~x3Nd5"+G߫XhKo%i fVF^8<ˏYڻ]L8z+yOovb @ &Eݮ2v(-&3mLbu;3ִE԰ds_6>R4|nfw*ͤ~v:b" ;y9XR<@ gb{Ή[B}IޔvuoM+Cr Z YUYyªKerbmo7R_a~ fBQup4hO{`7)/#yհthFo/P3W[ZG_*J15tkU6/"5|}c?مrc'7MlLo5ಘ&yA <=;.hd"Bޗ1*%m~2r%ۘgMq҇>yp2&҈Fk;!䁾J7;BP^#SRa qqx 2(޳@_콄}R.]+) [;*<76;T9Q)*%hK2#BC$*Y t| ( M/Z.4Zg&JަH8hƘ]\F?gvfdyGbbm6v:sP(2uJ$h~NI&Kم+U(ZSl&ϴ z\}@t(b 8V0R' ~TeP,:`_xItb0hkcCƨKAk#ߺr FDm8Kɩxࣘv~㉈_+#H'xC;顇YNG\ >!]Y3y7CoD^@kg9@|`G{慙 k?03*.WnlPih(F°bҴP4~JHt׿G,]bc6BiB.vW^ W&0%%MlIg계c]]:Wk"ӶB}gM rz^YGv/MAS3mh hE \0kAUQ6<;N!w2#!ēALuu{ N2)r@I6fbSuCx a^Qz~!4mz?pLH@"\7M_x GKì/D[S@QZ$;AJ@0L\6Ҋrn<9d <y\o%TuPm# :[wM0/!OalQ!R%$U̅U>ɹ qp›( GG ˗+;48@1dxQYWJc`rupO.MO" *0aف1_/UEVҴ<}GۥJfu*єˌ?xpJzkL56ڱVr.hL+H+bDCbH]EL/Zk#]Ϝ7o`6Ij(# lGK{ǡtT8|0anK!42ѐGad(€I uٹ1؉Ƅno-{ 3`}CM>rpWn:j XcN3p5V2RǪڹ>a=Z ?%6'[BI8ƌ$ ~(ND~jq~YَuBj$%]iKbKX,쪜#t5b7tI"__YYV'ݴ!n#r`EFP .Z|#)Wj}/?m[5lQӡi񼽤T[[YdqSjGs+0^1&vc'ǝz+!L{4vTOṏlIhR2 )#S Ƣ2L(T yws<9XK\U=+݂Q舝Y7_lM)ui(|dn ڌ/UI#}\pq!:*xR8whXv?. SekQ ]jwEl^'ȕ~(o-;VTV.k 85=9g' >A A,Y{\+}rLU\/Qxhzų30RVlّEݖ5 \EP42C*ҲQW!+J/&-3\dl4NZqѺQH ]{E$ܳgz1iq~khD޷xc\fǽjɚX%*}Cx&^]|}+FrT2t$m{}Wo`PFr+8ncMfW/t$ Foc9e )08PA_^/ܳX|ݹ1|D;i]W!fM*t(8z4Gۧ5K,ܿua0WZY3]'▸TFB5*uCYvM 4I6$ 3`T_%bwCաժM=9saS֑'`ЯBpkIgZf6uP<ܡ|ڔ'L'2e4'` Ϣٗ (S#⻰]G}nͿ=i_`t2V`sq gӒO`<\ 0"XW>J-Jm7yNm[~킅QIs(=[eMv яzJ&nޖy'+WxQu<|}@Aaɣٽ5"c20 IC>b 1 8sbߧKWY`CM0H_vl?K@- L4,iK8|?J-'7%dx on" YZ_v.s2/aAh P\/V@ qxN쑧e, VTo|ӲCȩ nr@ܼfe|B}%=ly N h!>?w_yM(x.aL;:rHl/bW] +A@dT=X4b%Ɯk)Y#rgܜh@o[859 Z-vbBǯ>~3ݮ-w5~WFo8&7hm3U{fŞ/nXM~rTYGй5#I}T_d {J/jO C/]T-vJ . :V8;&l~Z}Ttbοj9asQ㔗@ҡ>uù%*>l"eҔ,/&#5A hy@wds+;? zǙ #èp+L{yً̯&|?}TLծHmCp>k wui|H6 OtrXo9nstSKLz̚$<u䲓9n<7;&Qķq[ zᅤ0d0>85nNږ|)O&BoJ 6۳3hN dKB_37_P!l@5Y<1_it@2{^UfxWth:w6smIEspnȟn(lE`AC-.a]V-=!_7 ZGDx`e9x!(@+zոW/K+R&YzAZ91&:{SCo.PNd5}nIoR!Ԇd3 2)CcREӴZ@a%T!AP'HֆFТ,èzg`e3z^twg-5qKrd6\_'MP#k#Y_k ,t-OL?nL&M>լV|[t8=c. FXWٟSeEe>mdY^j U+_ lR>)<^'b}Q)~y<ֿbI(y-tmex S\)g$G>##]GXGpUd(|ښanmTeT8衒QUo_Bic=k]़Gu:unv|J3(b.4Û:1FN}~yqr/C}8-O4.agy؀F(E} 9+ko<ܩh;Ooؗ2vO֤PB`i}oƗʨe,s+֮?h,[j2(k34ur;ʀW]/VE| L@PyŒ/RYL%,gkd6[4;þ[-ej^T뗳7^0;"yHVznM{o A?`[#e^멑6A83`eVVW"S.m._rs6vQt#}ԃc*+$Fmʬ nլSOr"(mNOBMM֐]{̄ƨ[?|(t1; N};{Wr^κqϔj,=Y۾/羺#8y~P,?I덽V܌KЭT]B`,DBlԨ|ԕ<l[*1W0- {hs=۸} `#}ǁsitl@@.芬,T!r4[ڃ·@@&aT^ }#֤T2# _bSN:*+vY1NᄯG^݉4M{e>{EcXi#N?w=9RGy{ 'km4Bwθ(BS\}FwmK iC+/s HV$mBGQ͍ ~3#2^0(Ѐs0Tއ霿5Oy}|W_idf-~&%(3bI=Χ5+|dQe{9c܂/<--i,{DlHTZvuI@Am|I# ?C6֚GɫuvWRW<\v1pq9&f9{JS ܔ[q{8{h?L{WE.(JoH~Nd>?{v$8'Q_pe_Lzަ:8ns}cꋾ߅y ::+6+LcI2gԳ.cMfN|;A0LJ<)'d "\m##;"Yټ! ?Neآ`>چC؀j44w/E'Бm; o*!  MOx Y?8`Ze'/1 T|p<s/5Zj:gݒ/aA>.t!M(wbFt,W𕟣YnR7~o^Kƴ%2%PH3`RZ=,Fvt !P$+]J[0aJFmn?< W_󡃮#q#'+[Yj pS9~؈Ň5l@~]޹-Bl|ì_z RU4Y᧝|f~ /w>*ƙy.􆋮˂?Pq2MZl84@xeƞ/;2!GŶ K"q<~h UAM?]m5qNo%[}18+1:̫5p$zM"S3\odD_Iu<9/cY+œ]@tY ? 3j>tg}jIE/LKΌK?_ ڬxH.w%6cgN-F.Pi.̸/icm| ;4eD;L8AyQd/{cϡS=3g~ Rvp CZٻ3t$u\TҩXD!9Vt98e[*ɔ$YrJT"T5-M9>V׿37+jK_%feN 5߀Ǐ?9wJ 3{( ڟD!6" z"k'f6%XAuWɸDӷċO/j AԭYq} SzYENŕ]+|Ԫ<n^{hsPiͻx<̃JC݃t#Vٹmjڏ 5GmE_\owqx;dTAa2YQo0Cѡ<9 \1nYZuar_'/ yՔN z|' vsrhlaqV]uk=].+tɽ;}fg)e }?g&SJL"mM{lń˂cqƦLVnMB?=;guﳧ0/ƩI7e2p:?tR$fᬃg_ĊL]*=Wy5\Bl` y!=|RNJtBC;V~R_-jvjH|Yt6?ZI~RXe6Sjck p97.*_[*ӮY_߰4^~ Skb5 } zsJF[b-';%/Nj?W﹤cxgqsi)h.[G&NMp>M_'<.dhf_OƦ/D h=+sD 29s.̜%cfЖyu~f;Kwhf L|Iw^7Z@J&}[zj\߹%h|# p6et s6DΖry62p-G5'7bQNB{~zA!~CMK6q~/yA_ciTf=f"zhoGb|w;$ksQtjDhb|F1$qJ{Rw1 'Mav@G^h*7uߜZϙ4K\*oC9_=? <'GI+;}i~vC"JPs?y4b+'[A?xz'[eoT L]mgՠ<`{ǰ㺕 /] KsD7G_RƐ}L 2dTUtf%\v]S-@>[c5w7qʃ8T^=6Nv[04h#>gc]HuEEzx헶~s t剄J*ւ}MV6//1pw[,ɉ/-B5HGu[4t oW ;NuY@NƆ_P"Uہᷟ%\0 Tṿ9ٲ1t϶S5TJ)dʷ([j~0TU?≍)kpzx$ݓ˸}`EЂ oxq+@Z7B8a2\;*Aʵ!hokZȫ`z!JZ6~\4"LАseѹk!C_~zο_`o4WߨI ,@lȾ DW[CBd;XXXEAb}t?Ӹ:əm3ADkoʼnpM/|eJ=:Uy674S $y7j hePn .;> v3`H0;Pڈre$~Ug tO9_LݾpajvMY{;0NU>?BYnw2vd۰t59G{w0alO?eMbd?9Q(/LWSp a*?6;Cg(Oƒ"۶{P{s!X^װ:hGf8kBI9 ub-ouGCrޗoX[^t$DRt]0^Si(^w ՜!]5egK?A3C{5@;m_FJqwTrYxafF´&ɉa9?^s8aFK F/kj-"%xhZ*]e/qCHzG #s# wH.cWqsa(np=kl k=4k5k_{*1iIƁgt#/Zцoƹ('`u*HٹcjCFjdŖ;̻fz'&E=붨;ކ5 ͛?wr3kMA%\qs:x'~۳Hֲ{jyXVLtȱp=ăs-5*qesXO۬0-cix?ki By7Lx@csYwIj6-"4z8zILYb2oOLnZLrߦfuEȸ-nG$Pɰ7yGjC+BCk#vθ7'.L}]:, ɣg:,=.@մؑ45%tT]S>$DZ+Z_aH{g]Qך S.1tP'{;afW(jVP w%WOJX|Kt%f%{OC([uAM8,|pr͚uwn@~zLh1y8'mA _d1:i0R 3LV.;:$g{=dl|LL# E-O\m>cܘnώ߈ p/ZL¡ek9(L%#}.O]o kFLtݳÇ5~jrM=~ }I[hN"D2Ҡn1eo#nu3`,p [¶x})0$3EcNuhDѦ8>~,ڿ.׍^13둩gODiXEum~䶄m> _vvIʶ,ܺ~PS>xqk{73ЌGŧ|%U SS˧Vlt^.jntk4spP12D ]~Qv BGùrip&sɌZ- 4:w oykwBv7iӠFysH+.:s;x,,fŏ^lܚuh6jm5/ J/Dm0U=ۻ վ@^XM ;nn;+wkB ]eZ>4\z,u+WiV䕯撷jz1 +ɆE^Vxb ½ X'GN\Y^ 6m>3◟FTwIS7-Z[T[j^*#kԉkv]s!gtB-N2bK!7g^wVT(|n5AGiW#-M" (cU{B5{Ms6Q펞7dYm?U3y#?25AJcϸ8{KEivSu#9 ofA)ɖnIYtcsfsN w"t ZA5֓@i ƃn*J^?S h .sYbP;xy3M'E/o{&yt6rDbS晸{g;`ye5hZ8`6#UPM[CRq {OΪ/bn%{s[Q#ѹsY2#h fûCFΟyp, A%q@ۖU@Svت]`@ 'NCVuz5[-z{.wEbS|Vd3t('3+&ۃ#YK)+Ο|%z̜VN PnUbqI[UW3E h̷6نPoGAVzD_pB`oַtS,S k22b.G=%ێC@% O; 4gOijCP-ަ𕇵Я+$o;U:iFAQ] U R/cz_O`þBow]-AڸZ-x.<xVTO!+O%.4]ݣO1]vSpǽ5>}@go#-PCmğ}gz m_G#Q[ڏBExJxVߗ ӄ,pHß%>*s*:'d8Kc,r*$918pYͽ|>gzߪK,Sjɭ OSmmVsV#3ǎ($o7τθ"+{^YԸfSu1Yu+w/qQјG2/oOow H tJ,{95螓Frgg1Oy8KZ@wǽ] n>{ wtl&О[R{xc?YY.tdh>T>5!7)p?/q(~ƹ9mW)Bm"QN>b ײ6);C`4_JyLԈF>GB ]Ezk0{BF\fYwtsmaCc$mJ$l?XǩK׏rwqn>fpUYzaWVg,ɺ{2("tFIwkj?>̋0*/%'X"^O;$e@vxۄX|)pxG'd]3a[>{}x!텔yR1F{5h;XGۺ6Ҭw[{WG[W`F4]q?Q0冃_uv-wΐ„/X"xOM:ʹcSd%~xX;yNXԳA7 0;%AEkڻK[JNE#]E1$٧O*"t^N-Afa2"0^?A8by,seITY3T'A@B6\y(mRe}l<"E2wɗm` +^Lr4PrhX [ GWB2uS$v3WS>8q*H @ĕ9tT6l43_'Hs>\ ',leTz \;gҷδ'c]yYT8Y@_Ӌ4OY1$6;yg:h@sa7б쯦lj5`ШwM?QY +Vڤl$Y QO S: ;'['O0̽"B^rиڝ}ae $zFbfqK  y~ʲ_O78K~=Ä,^ L=VH =>\vP/Rh'I3"2?NT(x.m9d )kA˝ϟI5aTk D /oS+.{0,ݐ'3ژTWF;7j"]iHc䐗"o g9K[AhWzuuq%ÝE 0qJz%F@9b 7+ *EqO4"&dfS†\K< ۀO2ovi&+YQ\1LRCwt {}lǁǩAkq!)P:Js?]t7|D1Ѩ9;{84c ?V ݕؘxycwH`]7@-YJNOz Ʈ. I,LRԫh%9]!0ATXĔ&s%lr}P׈zbh=hbRH5_/EFT3Q$lN yvShM (16t/Vڢ53MµAX%沙q6sg_p)dµ=DׇD1b W;nxdTʫI'!?5FDFso-7I(O;qygg:oNjb:'ujGK"uy*5U}&z|㌱诎 ӈiMҋḽ.:={6~ni)΂AG}B~pVG:YNyU-g1B7A ˣ~uF|wYZ0`Yc8hPxDkٻ518J!.Ul%&yk0MykAƕGqijiEbSѧN"DnN@l),nT_r(sK|Ɨ}^\-P&vR:I{Fneܾ߈[v,Kn1X_1|ѝo@4A% B2GGWsC#mcnz ,l'opF#6ˀï&gLuyzQ.mhϵdij$134 [\CWO1ܶS}6wt-fֻr>rYh"CPntޒVۙPX  ᬔuQzA;73vqו$ۊ|bo|@.Z^ hY%sKJ~oJb&nY$?Qm#ڋDwOʯWyU,spsymt+  :- >TΜfF4FUiG;XJP4L)"{u>'I:z|תt)8w,\?~-1?3(+z]tZ-*&7Wo߳Q,,h_&d]֢=E+u1wI5:"bNwޗԫ`icDku>ES3PUzvfߺ/30$Pt1+"{uw*Q2t2O kBhSl몛ny^gA΅:3g㫃8eF>^xy0˺ܨ38thֺ fu(afFXo*Zǡ{ܳCWsÜcJо7c }'EzRoF2n6">=ĨRNg%6v'Kf݇>eM ȃiա2#wRw#M!';((1t؂*R釂QizM a\2fX~@j܇9<ifՍx1S/~Vx|3q쫯*sJiv5JG ?sXW2k%n0UFUMW*|t^eS~6 ^> 9mb8^П:)Y?f51:a lEޗ`戁&l5 "HnZ@ְ7#(3Qo`57&MegQw;F2f"vyM瀬\ᫌ;r6{N8sLYF@ebYvfR rR,Ngb5GBv]2@-ǮV,ɖglTytB@S(gQh ?ntB$g6TXM"\r92Wy.(do?',,FUF@1Fnl_UdKq"( / w~{DKk+ r-4 ?9@Mzw LfzDūh]X9sVCۗcRcS :=%3I.߱=N~ }|ugSDlbnxW/OwF'׳{Qj]s 4GTK@d4ɯgW0%/rbKtH֯rn?Sb8[U- )76w2+j'3å~-| pיK͝LC*=JT$|nCi"]^ھ{EE'#Ex'>F*a;=}.s.oX~C"sHչf+g )_|ρ ᕍhn%UioxdljOz]8.E7wXVUuD6UO/Zӏw%^z+`wqarc,uցwqF DEOH%n{9oyn$oj{4 y_l%~`b ]KHC4io+gB|\sF1#h5,͙ zyf9 =W5/%Rj% o݉44I==2ٳ}yO @ȶ"X?;;%h̯2U7?br&et9]㜫IV,m"gZt?4]Pn~gy]hFf=jx25SP{wK?~ wU,[z819Jl4<;]DN(y\YCw[MQz FOZ?PƱOs1E9#GX `jze.flFwY.LsZ pmJN`13_&݅^ŏ^JP{N{yܔ_&ó:BP!)S+l9'f>_ni5rZ<Ҏ | eϒG7 oz['UxP\~fQ%Wl\5XQESlO4}vjAGgAN2RH.HŦܠ Ie ;`ə@!ҹ^1BryOnb䆁H{% G#ث9o1[H!x O fl+n[BkP?-xKƸ7٦|K_Ɨ ʋqHEQ}dצAryʹs ?qa6eo7̷>BCjd{=}"rVSxpO9Wـ\sNVr?*$9$WB؏g[ ]wC-l[5#+"f_vo*?ȰqG )c~OC8 o} ÐˌZNL\ 1 H.IkEV~)*~EgzS;&>@8`A1BC ڒʹI"0vQ:\&@ AVK& 8 Qnz<\iQ=((v\U`$"h  DPWa3wbSNCC] :틯JCp* yP=fGI '$eZC"ABg2)LvOi(z/$Q' 8W򀗬+jߧC{T &TJ&0О@zl&$iAAQnE'fmnK_nzYl%nk n 8ѩNԳ2n˝rܣ0 3עj hs.v<1u s$)\)p}vyBqK)CU-UC92SWQc#Kʯy Nވ. 2p9^LI9_gt}N.9 t \ ,;&(Ob-Z VX"AaimpqG,TiEBfPDۍCD *6*BX.7u^&H۪tnjX ܰVhƮ018->BV9 {gHkb,:EeӅ 6-䙬0[Z 㔟ҧ+P(EtVwtYe^~ECs|m|}l-Bz?h~8Q_t!Ƽm, ֻ ;"/zdm.f>np$uhݿ 4i; ?däu'?ƙF:@xvQ,+na6l̃RwԾb"0i}N9GE'3rŅhxP<6Ŋz9m Yb&<ُ {vuoA9oM[[4u!tL=\sh.-}!tObxy}0UVobv:N5/m ulL;Ϲɓӡ.{SsO3Jui{tݦd= <,̴1w):;SzP NQbcM?D=z 6&*cю R1H~ui56>-C]i0X4s*gOU MT6fIvtw"mT. gwn gӂűo"'0܄;QJ.{KmQVW<8ipA,jysvI4m$P]'}ΐ;j)EO}ʃga:nDo -Y 5k] vNG4/#mj2ܯ9̍y0,w*OD#V4>G*E'F~λ+휧|O'.*JBQZvjx̌E.2R3 ~kę>^keryxи)d 2#[9.oz)NÒq npFX8jȿ] uK퍦\0}J3t+m$c)2E>MMlЧl=_q{a՛t>^BfZ hu- )<-J,6q/Q9MNd@]?^J!E)x=̭J8o,37NU \1.ۈ%"{ځjexkLK^8) eUcv?_yIbX9"I#=]2Wa }!6W~'q^|ʒ>ZJﰛ.lZFnJ zZ,.RM,Y}a?ke2"߮QYC豙⚰.J9rdVZ;zgm{NM 5.šH,F >snsL`[Lw6p86yW`,_\=vRWh1EѣVZe4=f4 V63%;9.r?oFG*!^1paK#ęs]zkmHM},#՜@Ub%CS1Q_H2[jVá}"ysL ?,]qsuM~;ZIDN-McGЍc +SY5СǐڟHޠhɳӧe(?HOɯ6AA9ǫ O⨙ɋf^[ ()Ggc~Wc,|Yp\m'1}x Ek|jǼv'C%KqǟKy`E~X!=4j'{vA0ַCu;\" ˢFr|'lg:U]Q)P>sjS4aH$s|B 0<=U3{H1=_G:X'۝;PbN3QVKgB6a4ViI*jC1u޽|=ku@HJ8H!- SiL,U|pF97b q%*& L&M@Cl&zbU,匎W$*DnL +!/!7jPER l>uQ'%#Rh (g8L˧VI]{Y^<]pLHMpcR!j#&]=ae(6y( Ujc0×Ua:؝@!jA]qN`cTqFFm<.R~ԛV_!N^gM  "yVc0ed ȝ_& I@ h꫇_gpQp)5' {|98⩲G*c{S+pEב9K%'Mj-䝘b8mX"`weoY.,[>P#[ rӼCN4r-FX[ζ0Hh)tː67{(u75 T{TܐiHEvFK @jhyU ڶMd8p9cېm~ ZI* 26tZ*(NBAI aw6˸$Foy.>xo) 8!d6|Jpc&%{۠^~ӡpd/lellBD}LaYO o{ih'p)aZ'eS@yK1&%ahPW` \ ŏf֡zG8ch?$UESHp? | j_HSPP+2wd/`|`GW^ H h yrD'bjzs(<ZPBbH|S#c?z4DdAi$i&y5"PYЃ8Sw74$pnc=(\z \gIɰ9F}g8>>LK&u.a2!1ʸEd}F$G* Br*+( JQ֒OY J #peRl=;|\'Q($ IcLxc8bF$䒓R vW*T(6ȭYS7{Ibrnya@1[bT@ʌ Q?D>2N'~4=!LE23գ }2":ȎH)/7Vf4`(aG o6h`LGީb* ݂Ǵ*z>I&IRYԣD%9 4OJ} q'G{8.`7rW%hbXA0 t5HIV7bn~!/s/揍#NWZF8wҺH9)?gHG )+Z`fS鄼ShDI'Թv<&1 9;<Ȑoݝ>U:nm??잴K~L#>3LWM`> ZnLpȻNc#uxc sr_Cr?P@}7F q!k+w+KI2!o;4o&k DWH^ ϼȯDͤV (>c:2j*An:32Rm@?]-F.*7N0m{*T$Æ&4̚?J3򢏝iO[T-~q[On`s) dzoQ..'(icu|(UA*+x=C/ 0ͺYEYwJǚ" @Ϋ8Wʢ<#pݲ?'g/< ׈[bꞠ#gL{RH壯wv*99^2 vHԖj5?(`9$J͙ v`W^Zo3>^0X`wG8Bn|ˍ8gxEn>=#x$_[JI ^9u :Z7e|+C/ϢUfwFБh9>|s8{/fl aD}"Ԭɬk[4FMӻzvpF+v[07 ~ahNaK.ɴ`su/.]4hV\$ôsψm*A/~[j~IV1/IsG,M@ߦRj6߻PlkNSONmsr_lN)=8 HL2|0oܔk.2'wo<^4-Om*n*ikT]luu*Io;v_Md R=:t}b)mQʟuX '#mWE $3bud3*L5v,Jldʚߋi SuWSUumrrQڋ̽jܭnQb7hpy;Pڱ˰lx~Q[msx z.L1\+-' ܛRZ^"NPYW)غBz sY"J浇2)%lѼ\xnl]u3Wq9I#~gIIʽ6AEӚU~󵔦{\8q{%&}s˕# AKyQNJ1}"wSܰrOU֠68"j[W`s}AY&$T# Dzc%_u8[nԂ?NQ, URxS1GCK^ufܒXѶlGf t5mRPl )+Vok{G`~1alIcj3U2ft6(mȈۭ>B4ǠP?(5 ˋ?lm%DEY_o<..>yQ 'ٌ#:g>=tU)h7Q(v*:szy2|w6s8EJ&تDI B;gxjkڴpS\g:(18>Ŝ k\j]Tffn$`z̐vI5czWV>=SgEZf;3ihIf/F_Bk(3;# =x 0_b}5uـrz^0J6sA 7C3Y@.7@y$A3ظwIz bgyŠ\-`` &YB<:Q#v?.N3ҟwZ-M|o"/3g:DuN JqMcQ p J7n_c ĸS`qn,=)jT XRsJYYGy dŁ1afof &Sg , &HY1v/(&5Չ-2׷_g͞Lt)[pH51" Մ"|r̨H J%yہ }ʼF}0١.mj}z!3f0Gʦ?cek3nDRD.o$ ;Koi?>y5"kf{ǫdLZ9ߪ EHE^BU~V8&us1q]}yr].`k%\/vT{QKWɆr- cɯiANX<OJ 3椋w+7:ۚzO's½ʹ馐TUBٯa8ǐ6RNnw0+y3T~8*Uʂ k1l'@^A/0hovX4ۮ=\ߏ%)J7-|8-2+.'ƗĖ9jjo/;iX)dqlvq)4$NUZe'NZŎn]pItss'Bc}e#){qj*7V%}9ôճbsU*8!?NQaqbj٩Gi)2@`gKEgpXn׾ J~tn# r|(o|{@-{8@wg{ c&=28aŶ#-9d;E';*CAi%!"Vayq5`gִ;^VHհsq&޳m̆R26Sy~$w^^|A5@t c%cI %\eo)qC;q穫66IIV xE*`3׶npް_%Ț/4ͻ̜MR 7S=ggQrv&R1Eܓ{7yU(9"Q> (?OQ#X#ui ;dNuZwBd@d3JfcsKz[e︀aɣ\~ZЖT(O@*fxO~B1Dd.u4O.4$c[4< =Pȳ;glYSyd\n䙆ycLFQVo/`!QEOq_Ύ_(P?Y!T- "mfrQze/u1~ls k)GbV: iFkO:G ;n-/nr=+F~;{kjYAxN±-odH$=A3eo6`mS[##N/~)RJRgߋorSiI{N ߞu]d"0U1C@(_%poQ'dwUu= /^k#.G$當]m0'[ [ VY;gM}I~ W޹Ih&^{N)u"g} dz`RZל.k厙;Y Z9 aEk}=s984`5+Y(:OA@j/}j'0U*&SoN.E ww![̓ n~#N#4@E 9=)j447*R8hba(5X[5?D/T3J!'%hE]Z//i2ⵌn&p뻊_-C^Ja!Q):DRNN9U%CBrā?E3e[ > 7. 2J ZۙBM ~șZw{2["-|&X8EspGh;#~m膐M1i_|r~tcYݞC.Cx[>)D 9B&;pNNvE|qħ@5vCA fۺֱfJCGRRϫQ">ejFfٷ;^SRVZ[oOϻqQ<' ڝ^6\L;y+' V.hr+I3mE]̙f}9 lPȆ1c': 'DHV05")RdZ%{GC3|*K*v@ W[(zG}ZK9[bH#T}qI-04}@5h89DK]C?R8Vu2ni-NR7fyST7@KndFE}xQ0g^R,irVE~v6=_~EZz'H&@\14u,/ H&_rn@fP7z3JoMP7Ga:s!fZ썑NA5Ie>:e7Z#[n +罰Sgs~|JUp_J><&Xo;&(ZhJ=@郾6O Qt˴߼TbV)@~G&[Q}0 =994j7U(o\F7w)@pb\̠!*?)=@z] {+ N#c0iF )ta1rt#֭/"G yZ] *^z \oa}s ~2 Anw>ZĨ g Vcq2@=a1/cYNE\U?AI &}8NstBp$$va?fFtƗ:1Hv(;7e/ 8v£vhrZ2Ct\~[ZʑyJeE˚e88x+BB;&A{ELOfc.$v[ѩTԏJUiMub$$5FL_<#{aC.nFs0Yy;URbE9>tgc#l%4ЏTc8k1M?~&f7LHջt5-ȹCe}g2ږ2]FE# erh=Yn#A~(:r6%OT ,lEh I A9rwnWm?0O/S>"9n[ԉ)ݱWaHе%/樋律3],嗵qBYZ{QVI^P?g^J^GXajS"f;)RE,/V$z鱌Yaf)Œh;>*m=4A ;3>Vkۯh)Mfڊ[tu2 N߷#&'7ix=f8KX>Ka3޲UsAil7m^~"r|]ԖIϐw]+tgtmf_DʞD-j& "s9:Q[xTG59WM}c6W^|9}ڽ5_)jwƺeәCs,/5]w 6x1|)3̗9m4_†f _گә^35tI)b3x0|V=ZrSspqǣ |;Sjo5(ݫekͪ$ d 5Ymkp;iբGNY:Pj xO:D;m/^ìM 9+K+A} fb~AUGBfSAl!XpImoύuLT5fhHa3z'1XRZtj^X/YeK>)ȜG1b)bM;;x=[5ry5f6]4%`vdqOŮR #t a:rRVLP߱5sgĮK2뱶#F/o&5&TG"[ \T^֋9YV#8bǮ.Y7yrOܭOqDĒD|oAPC ">~DގjڴD3*t$%̆xosȨ~5eaW^Ab5G\ QzzknL!$ =.џxu8Y4b כf st /M`ӟw R-/ɝO/4tIی%Zƨӹ0"( *}KKL^<^ҶG&7#>F]š4URw?s.,Ɀ4Au{OVdgOKǫ"UUŹǬp5_M핕@p5Nx|\jP?%;<9 KJ y9Sq< nJn]%Hx*9yy>+P؆7E/GFu8Q%li򋹀 ^c9= P'YVt( d"EqDdo#PJnnM3[3v|O84TӗJ|i< (qb:f̾%rx7@ZRkX׺43^9yjl]RtZv[>6[oZfѽӀ/Ap!N$g+GoF k]>.m1iΙo>I7c^Mj;.qBR8N6q>s^#}c- *|t[մl.h2W˸CǛ_ƨkT q@o@!̟@/\/髲a@kBqͧ)%7oH@TO8I8g^Rh--/aՃMc /% ^ чqo؉mwhz9+5Y/llT;mRuk.R'HOus8_] &HI=-VyGRlaupi9WMNSCG8Oۇ-ݻK[:%q5)qDeU?:`欻dr]58Z]9f~l`>AG[ә\Iz 0<TJ.No&U`}3EVV$\|eJ=gJU?8GrX@:™3f}K+0 H|1 V^.ӷ"21WZ]ɁKGĹ03.8:˃17tfa͸#]:t,b0߃;LsRo1g |Sʱet(C+upA;&[cIZc0@q A_ 3Tf]{PpzCHfKX\< 2YEځqz/-[?R5|_^y37lAȴJe'i/͟e[ڮEbTQDn2ߚ۬8#~7GdOZ7,_;8`+0:,(l}ݸ1e•QNņR]9~JND$V́) X[zVF H+]+'(>#JpZ 4im]qxyT,sg&G'#&ׄ`/I48<MDm*׈xJ|2̅r\Z8ĵq⮜g>@t$:M.` 7<.:vZ Rabv3Wdբ s T{$A <4đu,޼R'Wf#R{8y."[kRs[@Nm4Hv͵%c>^gmk ӌZ7F2ESyBrl1XK!_-7xͲ?_~7 G<_S8bDs*'l}w[ݛ2bc+=ܫvjw?>:cnj}7aV; <<}~Jh״!eF44|hqA)-:ŝy-t:޽%lW ܍Ӻ6-B4߼8<5O夡ۆup_- w(T> ml_;3S AZm}Bk+?-r\=ߒ,|*16I}oǘuq˽ 62]W3 w-] ydP'foiG^% гڈQI*O8㈽LlhghKC/)WKF,4)&ܜ]jG煽uVSEVc:tRɫDx2#gn}]$P26 9OH=ࢵl LY^]r_ȡWD~9܄Y{~ 3܉uMZ;b3_YR7ٓw,1 h#iZZ}/йxy6Qh@߀\"@:ubT+ڮϳ8́Q l`;xVbWwcP҃&G';Lg ߒO$Ál`*w^lg+O= dڹr_$X5]w'J[a[í;v_sDe  {xlj>לB%?/ ׈1ֻ, =2ԒYk7#t['3~=_0l+]TQ)|ֽ*YڶE ߭O4ߚc`ϫۺ+~Fj8$K%nP).ugOK 9eŊ]U+0;laN&*p Q$,fٱ (wiثO%]J@ 7EYp˴WωA,L>+]C))QhPoJmM14 (fWK{nϹdv5w8El }wa/ُȥ/ g(6zylP١}Tm᭖(yY[Bɪ.Lڂ5O ";e:HVWϓd I5<H5 1J#eM_9!('gʽnD uB&l)oLMM+=N߸\{bMbg *g X9k +akDqž ,l8D+S6)wf>OKӌ8RMlowdF=I\M6ʹQE,qrll5Ollx 1Ʉ(HlzN)"8ug^ZБ_f\Ҿ)l p%Z|Þp@.|Ӑ7H^6-2|8S :~WhBt+?<`E:_:qE4U"⦾#=ӏ/+8.ՍyWs[%"|]7Oh8Nq#1lh`EAGbj%#oVkIU`G[H!XǽiQڭaE[gtDGG6 ۹wX0#җß\ vU c y0]hY[HSum:j$g?6G̹qDJTBγKkFS[愒>Z ^'1ᥟI!fUFLC1Q?ep]{1O&oL-`SWU?ʨ׆|GNb2 +C=3+6P@Yߴ˚"rqE@.ēJU(Td\+U'5gm@Z-@MpމAV[g<q䪇r{ʪֽ m2p(kҫdO5;r`Zs:0!ⷲ@Mivkg8 -9FQkRNY$S٨D%[ HZz ū+-=KgL #NwBh~qD<`g02 7G.99Dg]v>L֌ΰ@+N?;Am ;3zσ^^~+qSgƢtb, (jwq~QǬ⠢Ud@^-mYNm:)ug8l. #= : q | ̄t~H8 <^q!^ BAP4pQsג!PWȾ:64 PfN̫7q^8{4 _9U -ԉ;wTpуa#?йx뫗ǁ<I֣ڗd)K­'5=>וa_i@j$3Kt)ym~ݞ{><WqKmQbm"u ȱgLrt=CCOģ|DQa_:%at]|Gݞ|hzUmͩHmlie%x٨_lxqq3(p3f*>3 @~Yz҆ !2z#:{-@\/2wnT#']O]](V >)OEw1Lj +O̐)jM׋>!Keo$sCCd'j\}eBU rȨVMt' d/~٬OE%?»x@&͓@yCo}s, y1p=R3d\\z<+TpO$[EXAؼN:X삛# TVt_{])%֯#?GXzҶj! A)Y*/RWzSZ{e"SLo=C^+!( ]X`^'ugjk.UgBU:+7J4 О&b7C\<.9}̠<µ?S f=b|*||o,J@!#ӷ`I NFG*iɗgTIvO~_~x%;?t@P5ឥG@=DN~ԈZmCUO&8.LM%69p?@Hc?*Vq{g8[r(xc %ϩ p7/o^r|XڿOד1˺/ ek^C|RSI+ ~}6OkW3#ʈuI"%CRj-$ X@}+efmLIٗfEqWj X+Y>e(C{ƎಛL*v{F*h{݃5wB81)˱@Y[cITv^(2%U|bzTw+ Üϖ%s wڂV#Oe0>Eo:.9CӋߔgFHo]xNQSwa FӰ:ޔg[7&c?=MG!r~Q~4d +!#o1V oT٧uJ)-PQ3#xAV>AȩeFh_1|?}i\[YU!_}bpF1%d72V"'I*`"M^T*S5cظZHia{ŝіǐsb &KgUk'_f& slj>dk|,ngw-7г毮+*2ǝ<#lIfT}ᡢ~WrZvb`#O5 2p` A\>v;GԖS}k 獤&g,4O3*Mԟ_$U" Jα3oc-tSD]_<x]?mxWd4鐂*'9Qǿν{G{lSLzR?`\k_XLiE'Ek1N׻c{dݻCzSvV>U2o{N `mo9Jz Ǿln&ܡ-@טPT׫N^vsYj4E@k'bN2}%ȔM&{aʇq{#.x啍-?EJ^;E)p?r'0qrYc3Q1tXEz(b facA޾ 㠘$I$aQbT0& =e_{qz׾1j/5>9 );>?qz[Y{L-]􉻤drb43+LpR -U2h8o9H1ób֙ D̄bVC170w0:qu cL(PC` ^ C-D=/O-qxHĄ(PLȯ|R3d@몤Sn]ts}vYP:5H" ġtlq#Іӓ@H?iȂ/-ϙUi h-͵wDtȩ%r~Cz+ %qCQ2bϱԪqV2YYиYb4 շDH+:kH泷i|ULàZ!xIw .\*!wެcbJ+Mo~/ˤ_s?ł[A#h/Q"̾+@Cv'e@I4bй vR)*:]Ԅ1"͵ $ngu,q&87)Ic:63, տuy?4LvfUG>o9fL>9DH#d31QyQ]m}ʟ3l&clb#Q2/H?<OislrqΦEFcZ!lsAr'wt.9>&1 s ^I>)-w>: -us#MWXX>}"cUK07e2Xڋ.o:S:_TGCp_'07L&Zгmu{y8 :2I_R'' \L[ s-WV!t['y0ގKnS._f-3u( 00QuThwU'|OM"x/чwJߟݣ0lii2EXБGfR-JL |Z9EGT⿁XeTJAR12syw ӏw+M 68FJrZgbC@@䇗j5;ۻ ;, +g]lVv&p, WNd_6r!ON 1`p_Y!TqL<Ԏ"Ml 6P(51r7 t*, ݌ LJ"GӶԘ=8;9G̋G$t1Į:CXR)?_a]'Ɏt5K]-;S-H^IS.@.^HҵSܵtCfnt¢[tgp-T_d Jes\ߤܜh߈h6aז`3?Kœ<}=Ram9>+cfs hm+,AO47 4@m &OqaVk뛲ӻ–`44:mj '{OI *-ݠI ki8ZqRǒwr !e!K0`gX}"S/Pl&F Y]efw;i$ ɺe0Y"թ[I<=qPhQآ؏LK{/^P`hnN֒HUhM˙} ֔V +b íg 3 K/7K3rhV!"K1+Sg7P8ȝf\P ̮k\=33-v>W#mah%m};cypbn`|ǔ>v Rʃ}~_ŧqO_9; : ]'~ Lt>LWg sqVÝf HUYn'߿`M,}K1=O$r yRQ7DT$N@Z/Di:;ok Fq dw׈?bUG ~0*)0܉Ļz}PLQQуS%Oj2H_.9 n<ߑ[]hxLtnOXy";L:>b ,,+@P4RNH1(6Eö6:&v,{PYݣ@}eߙ@2\1Mq%\eq*볘q(r`&x1tqI-z gJ { qAIj)7~+.&@5) &kX=dHn~o_/zώ4}!Ԍ)f}]098BO)nv[.ۮA1krzϥ悍u4; :rg6ŐF::$zu-;T/UCvF * N}&ӝrs\ 1ZY5aX!Fu mc6Kנ=7G?5IDxM8Q=,B z7Uu!z&Es37s}y񲖬v Bٙ6LDoǕ`p2߅֋-V>lq"Q[a LAa :v/ Su%fUP޵ۓ݋_)zk>qOAgVW[.1NLqqQr K5 )2:=ՂmE TY]U[H#ri` jML'٢59zrsĻ[ku_v1w9ȭ,pʐCcnU ?1Δ1´lW_xO8ϔݳ˩:.؃0)H6TM^ޓ'~hߓ]2" fE/?YYټ ~p|gĬ*A{8KV6'秇ł)+U A_uhnLfqaU\ Jq–LU?tizUofrK'r(XW, zCv0s;AFD멾T Y} +!l=׭3Gog6Gx˗.DDFݰ!rx_D}Qa27ٜ \}Vh[y)X(m+(pIYyư(vԙ_ 2wc3qruфIv- soYrgq):)#u*6$*o+ ׍i#E|QZ; .:x=|CL﫡M(,m=IXt-,t4aFļ1n\\z$ HqzcsXJ!q,ލy'?BܔdywJ\3򝆽&#ZUI㾏/$OK5-CޅH(BVI~6HM=}J?`ك!#Ie7RY qDdΡ)흓(&qV1.;fwW[G$.6:ܔC0ԕ}tj,Ft#^"7A CcGŏ>>d2n ˯k"z%⟒ĿbpF:D>]b^:*.*3G?:?`ۨY/l B7:b[:tx# TB~[zY jldWKRq]A>Jd˪.|:Y`;("ckhHiXbΊ+ zf^d/l֣ RL0x9ɕ@qM1xĻۮڠ0jmŬ8,tEQGݪ0 '@ 䪾*ku/ W"KI!ͥUC~:+Yfz8pnAP*DsF)ߟw]M嶘O?92G3}b1=3V0> P@Jxefݚ Ԯ:.DGoP6@S>0*i 0@r25"Te]+z+*@;KX+{AHqEW+SvӰ2xT 3?I~.SS+ծ޶q|- IㆤJ5!v@>zd+o\"zõF#\U/gzx:t[8(0J.tq2$N-̍6J3!q5B Z`\55O' !$$%/ǔ w X2O7WAs ;ζJA )7QQй//RF΋&x[wkLh5:VCflBRJu-ZR=ERM[g6V3 0[ɦk/dPf< #i^vQ((r?"rYw\ͪ:x)=u̍)W^NG׺־"<\ jDBw~{~)ȀZ!\CHeM[j捌Q~-7yґ"+izfdH;4>)n˗;)kt,Vm>[{ʔՀz#|گiBJuw=0S12d2鲎y},.nF³y:*CS pU|#]=.& ˃i'~..e{qϋvHYݯM-I+: ݋NӰc$?2 C" 1B P܅>L !l*v,pc 44l8H's r. $-m"icv :SƤγ춉?'0Sm[?jRGr+i.aVVC+X &۹RK j҅JGtL  +7"Lò \?]!Cn/YeV,ܐ8%N&Ĕ5,y44_s*Q8"m;" m0#znJzi_"$/l0"(Hq=ښ?jSe4KT bE縁zFaX`?jO}K?/]sbv0f>p@`'"bqOYyՇՐF_K#4}NnIĶh.3"&= ̹՘m;J: F'}C|rr@f;TkaPېD^?J7JY\2aS fe#xTAb<1]3N#8ءeGzSy1JU(iCo%S(-S0Z\h"OEX1p7f:^9{*^U V@s WAu=ز>C}G`>1`(\vrm|s|}S@|}/T'L y%eFnm;$dݑBNg$p[Ia4͟fuWMAL3XI(2Ocf?tL1Ā F\m*P<"FoYw85>B!tF뻫4睲oY lXdܙ$ d E83^>s}wejOcJq۰xHwNt@; k,[!nh4D EU1yrewL3rbH^'uP<\95dC?|1c9AHP3faVFhb+e@IXzDzPq_1k hA}1 Mln n`ytTUiK[Fm"3%4Xt auuCU-B ߔRM} E_Vtv.hIH&˗YUa:Km~WeZЃ.)hm\@@6Hz,>h >bTW?J#L΋NKIUm3#}Ѽ @PLB,~?)f Rn[;HLѿr-`uwSRi^0~$V!$7JwղP??`p* j%wۿa/àbgp'Q%96KT(h S5ԝ~%GG$lEmTJ~R\Ci\SFgޏԟ."EH.rș5p x]6S#YL PP|Dvipi}7aKJd?9保jK@,6RD>2.U"EGʌstB2 v[EJjCl}xmO%.VN)K}`7όϟSdnmң&$}QKlIU=ff y&1W_|6-_;zfN?)b"YeQ7 ɨgG{` vm Ԗ+0QcԤL*uĹ率 {ϴ䘼m ~E.%twAָAsr}Ӱhݠ+7 2 D@tiuLV?00FRvO$? qa_¨+d3+p`wOh O]ݽi˜l`sy>$UrvYU.:Tv}4򞚆`'6-̷ZZ{=s8RDsHm79G*) 9ҫmUWH>*Y1Y/mLe y}lT1k3Ձn$0-& XNPoH-X̻D0C$kgu%Ww<x**r:Mlg9ϯܳ2!Vy8O+}mI8:!iT٫-djoP&$=FV-aNϬ]d4Z}vƔV2ᠨ 64(5>%L2Uw0 i~Efo<*S'e0b6cq;O/ ɒIDS?Z#:ϋv z5%zK=rE^rIzB3t/GZ҆82wv3e.Hӱan$f={2a gsy`?vWTnM̙,_xtkkScIWt 0$,fwՏl{|m +gZ]o:t}Ƨ &I]ѤSSOW! Am9 :r( yՏdP74J.oEPިkLYuA albe@}YE2v~@&tw$& I`KSN(e|z8(6;ͦlo)ك8(TG#ʼ%J;C$mP{$/k8'(_WVyw1{T8 YĊ g%_F.Ƀ Y'ïW|_Z1FS ȚViu5.%aPUWHH~#jϧf`~ LU l{:YǖED1x{q!.:P!eWkl@g@w&\,hlz hq\وy՗z,+pzh]ezrݦ7AɅlF̃Y[xsN|`bčbV~=^u( y"j `thIl6(h;VJB! k>1 l$M}`"=7sV)#?w! a,z vs *clm<"IĦ^fh`ոK9D;π٬:u>&(-3U];tCN[f)n^Z1(׊d-X:TSR{$!s z:hUZNQV7h5%SC/_CzGkѶI!V6I+SR7@<_倽^O)?`3K_>c& LLZ\eȹNA< }P_{~53JO| c:t{t! `D'z=+_l&p=AI)3ꆨ0gl.ݪt%-t."ܗ"֪GSp۾e9Qy}y&VJe0UK7ԳpЁ:e.cc[mV39^܂k#OUj#J=aت|4*sJcBOSZX`MM7:>zH m0$SJdj">wk߅21k apq;/3~S6W(&˞ak-e)QwL)r.u$mMjTKșѦA!1E|׃Dɩ/,7XVv'qǎ.[~WIwV'd,Cv{ϝM)=G+hs4?Ltioj7DJ[{דz]6^ڃ-; 0F Zp;DmrQBjĐgBjWK||h#ٮ01.⬽w d:lܬq׫ @uזkPNWpT{!3rr\`>]416!#n`=(4m[f&gM#v>in>W"'wb?LxTG/_ J1Yz~Dta"=Yeڑ1^pi$9X/{ ~=2k UHxaU=|naQ{~+1/ pv?\s xQv뵖\Zr!Kţr(qbp5;XMՍҲA@w5 F9 aM Xp)=H⣕MňN FZsWn+V E=vN 7]'rk&nnv_;X,VbF瘉ی[Z 2Z VSd٭AʑSFLuB/FeF4w!F%ICLH aXX3'RaxjGÖo0 _5'*>#W$}{$6kXBɅRtҭ9 ϙrڧI&rߎ}S jc_aǩ\j#Da})ɚ 15";G.Meh4f7K͗^xk- fp>X. 鞁"39AE՞>{$ 0O?z1;XlrZ/:ủzW ˵[i{R)pOXB誩X dد,):ii$y8B.6Jܖ,DfF3&p-lڮ{J>gXk,z/ZP} OonshTw5e,~YWl5#ZR> o*PoȖ:Cw9pV{NJ W7A#Sj4>(h'b#-@ WHL`ҳ]RS"G/K`rیwۖ谟',HA {n,P4Za %O$Io)f^(fL7=}e1J3Q 30dʟ@wtY|QB#֚ɵiЭ\/Any'4ha !ZZ:G#Ry:ysN^fhO(N[Fden #6)O.9 UὈ\ƁK?WNϬ% 9ܒdwʂhyc >N0O&lt(#nVR5 Tt{jυ% }9/e:Vk.}oYyZ(tS ERτ2Ͷ6$m#: %)mYejfcL{]l{4Lt>AuwYۘBKꝊ_m.DO- c-Ջ_U,d8 V095݊ms] S:EŮ0 2__Qy,|`M)һmFQ»P$.3f*&y>б H.o\g91@S[LC;5nNkZ􍂇>Bp8ۄ )6hy/ I]Uc4peQZn6 Q*%c0J4?7U\m΢G|Vr𱢯e*# Dd+/@0uTDLm-43@[xGpUG2~E³/pGa 4g>YW Cg͊,-ꇥv9lHE=MU.ho D8;*%"$ٝu=+DI$}sV4I E-[Q|&[1A n $ qg,>7(mG,㻱̡oH}h=-,*~Bp DX3/]WeJ WˀJ6-Ϗqgd2a$PO0iq І~Ȗʒ ?9[h<ȶY=KM۠ףaRȆ\;YJ~_7ؤc$ٞߋ>N~xmw=cw'Dm^N 8lvq];Ba9l؅9I;mD1e 7#6O Aݫ 0X^m 7" ƗͫY-h{a@J($6f 둁ޟH2 PK-@plE[!#%lS= W']huL0Fw`cСj$BR&2U_RJӹ~n{H|v\@bg *o М9NA̞ ]x\SscC51,w"`Ii2*~qTɅ>;z d%ʬБĂ$3ߠs. +/Cx撫AR>f`/?i ]$t %dt][TAtmVgwXie"$k꣕uf>CxA"C͔Up: 7'񂲽Z-YenC9c$RX_ TO ukޠϨ#{AވJ7c7&.{֞e; )6aL@MzS/>{>DtB5(k=)I'=88wImn(-)ff/fa; B#eVY]Z}ꕇA`\Ly Y5sJ(F!*c/Ljɳ&zM*MDOʳh +j5qp_ԃ r@&?u;4Ytt30!?HѮ6tsWGs CJ{S6XDBM!q#f B{ҽI6$4%]7T5u}9YX-nճ(CW kNtsK(y"Ks\ǡ +d& g`r ]yp5P97Duke*C`nܲE'/v{Wԩ:0Z.qGv;jѶ1ۼEknu@ҥ) _TjdiUɮ>C=_ؤSm L9wT Ћ`#j+&]W-{>vc9XV}wJek>@rs?ni |AG>[׎Va󉝀pT|NtvWV$^^,?_X8jzZ'^Ej%ds7m՜G?YBj߄s» N5N+75CCn mgj~ic|=7veؐ5U; 83925 +#r5ݸI3-d8ί7Ot[Ɔt|;U'}4!׊<8 7@5A(kfokF=pKMv1 RŢąh"w>٠\@p&{V^W>fa/9 yBSrcKr~rԵ1!= RmS똠nqcR/? l=FV"\,_Ypvb(ƐXzB`uwnN._/6!7=|n|Z|r'0Rk (w}FEgƽ:-eJ-i&?'Ćbj)[h]+ٓvq9gmus,qFXx/|*lCi?&PJX-=~;tG[UQwok0>YQG/K Mz@:*v7]VzZ us,g=c!9"SNvεI}#q_e(w\7T*)`3\kmF>hdhSܰjOHfljU]R@Ycm1*&j5̃~:Ok5vRI}„YL~k6|9x-ra `\nZq01Zq\ {}}]˵SLٟXk9ձ=%?"~/%M o_Tz17XȏJ Yw)Ke#@\Am/Neh^t 63qڎXQ i-TΫ ߉ i+܏/;Բ"5ۮxL{qd3ݒU_L $9qT6^+,jO@ n9Rۨɑ`io6l^H'#Vđmq0Rm|2Bx71e"@dWGId C ȹP!à`nD MlWL jsL'(k5 PBfhuz'*rkGFiOڷ 7=+8=|,*h+|#2'礛˞/u^AceeNm_zYL,\[ckzO n10~Y[qi ~ z~|&~nk_x p}~AGVŗEn2vKϮxGZ#B̼7)Ͼ~;ɸS?28d0NN缩2Z߬_(G=kmاش2!Ns> 34%J5&r]?L"Q(P@<{Dj^ՓYʲV7kl0%eK5;XD!Y9. l&ù,fn~'*P ֳ?rw=jf`Uxr*jxHt@b]PIbȬdڥ{{##^=@UrJ$C}\9#BnªFr%XXC<w pT ?a4{6Fas̡GP&Ln¶b- NL`b`hېKvsPg|BAFϦh%mf-{!b׆9|ok(J(xYEvX?X5.n]0αZpWI;S:|iDramCX%;rBر-=ISfYg`٫]ND Mf`CzЉjw97Gol~־V^ȼWAB4<%7@}LY~#E?jcmMZ*NHhg {nB@NT= O+O>3f?8Y\v'531dr4pˀ/?C8!:i\:xdNMH_;XqQaA.GgjIa}iqRF˵qD%ز6[-ZHh_[}sh혚6?.#Ibd*`v'Bg_lLJ/ ǧR-k^5{8i)39G)PTkośLmqy!̷l .<]}7„1Q_!(]YtY>٬5F"f] 1 U  yn < {`CU8xM^il^ q&_t2 ,o6g5;gs;tc\Žg|jSуw1*>(M,ڙ{] cB5-&O^{@^;9QAIS?_na~j=.;[ڎP@Ъ}i Iw= ԈjepL2U[ w~b7Ly|!^|j^ź}7PpQ(2!oZtqg|_nc/BԾ|$1{~fL OrT8HI@ܧ5ۭ7>"¯wpMZ˓9. id"KJ:fCZL#@<@pkpͣNߧ.RN7i_ #,ܭa꺈P^)(]t-'iXZ~9JN3Ϻ1po[H˝tUW,4`FaF󚇄.4J)UiX@283b8vdMЫ@jX!]nsx%|s3G cOko]ֲdg`#cqs8^rd+xEt8e_h7HPU_F;sx}qzÒIei# r<2?~;'6XVf%Yb W믟Ych@@h luNXFX)nk*8 Ç:M6C_`Eǹ~6I 6>@hj1|04ƣp.;`G{;w<g`-w: Qt >C{1:.N z }ܝnv8l7&_/QfB/rgm(T#b,[E_(6VAפi@:A"Z)Z9T(xЫPi r@,3AϦQR%Ъ xIO朼x44vE"Fn-h<ӵ1ɔiaFb1ўH@M)nu\^(`Guc{^ RW-̹&;U%w^DmU6iɹJ1"pHz[ǮYAˢ6pUV)E],Tf^Q3kP; `buZH od0!.D0Uc*v9u]ڥ:qY u6RhM~)28 Up >4<8 zXEf,ˊ:>hv+8=*"{9I\f+o!'~LPQܷUv w s #3|驴p~|7Gd.[ٷg^-6>&w9܇K=X hߜ27U!Y3ZIhʖ47+ZF)gޜtrZ@H0Azx"ΔAkQ\IB$%DNXZS/^yYuڼ:{)͈#nI뿎e*)鬔 gQ? q}*l$3.%o"KK?yVN&8wIinU ^gVX&-ໟ4~cՓ;+歆$=.-#?r{gk frmtyeb*z:aԷw w^C[Q]Rm5חo0nU~_YB3O;, ]ݺ؉h^mHo[ɈUf%US3%^6xw r .$dZ-L',[\wZW7p '1mMѕXz9r9a!-)<|[9(,Nn[o.q4v,E#ݫ}x/ 7àaN|eQȞP m\4r'ˊ&~A-tdkJA0D$ndxs$y :JA{L~o+[D֓pN-FqKQ #=P's B_\$g i,.c@(Z0s`M6U.B:B pļD~}Hm ?<6*XgN{)vcbןc @&TE@$^hʡ>h Ru ͢_\?i;ƌR礶Sfw2}p!7W9Oky= IvjI9>"R,g[U!+0 ?Jo䡼3k|ژBF F"eN¯ D;rv-OAhXuN1*Xj+֮V 7Rעo]pJ3ɀnկjѵnA'#p&J V>ԬJAi"obtVZH#/.P~YL? ,}4`c=!ڨFݟ[]t|9,sQϑ>&upcs,>qocCDtbBkwkŮaգ+HFU\H=>xm| %YN_9^ħνUSBy$O'(eHtfv!'}@Hm=/v':ŪY8o #}l0"[W<1 D^)@tVsV矊E ħqDxP)s,hF!qO~mf4#pܫ+||B'-u9 1iH|#(zۻx3ؗ c # ){nD'q"h36C;|K9foo܊~} 7mL*U}n@H.iTA+##9894͞C\I "7lK*#xk($tR6O=)qmעA~*@4r' ][=5=W^c dUC^V%4%MHF.ݓ&ÌՑ gn\r@kaC[fyl݂Mmbc̍=HÞ/vD ͅ9 g$8j*6II. ;¬d? j ?D-w+~u}fʏ\FCfbF<0 vS>f!MSoA;[e a;_qj>BciD|63*Vk"Ɛ.0E^1\8)"_X9$b{4`hE&6z]$roQw5q; 2|Yqҧa0" N#+ B| rJK Bq[XWI^aC]Y L6{8Ж%6o%(rhnH 7gɒqqb9rD6xjdgzNƇt![Er~@eW/N"aks rr.eKq {H:nPkH{A@2G~" VЀEu֍( ` bx>Lvt\ֵ@qjy4}ɓg6қd44[h8 w$rA %!gݺmIutP0g7)nkasEC'y}~ZV0g,!RewC BYD҃X0rv' W8ŏ8a#+6M2:wUEsrg$ZbQ4n=Pz=#gHDfl[S%F:Y06ăM QyvKxzK8}\2+3Fűķе`@|AN-A;4VB?ʴIdή[{~E;43AmpKIܕd*d(mbV3 //H}%;!/V5Bp2ks gp_IAA-YE>6g3V0rVl7W]]zԋ_Mb (C{3%~ IG" 'NQ1!0Ôqhw;pp g6K[>"?}L~:ǀ_^G:L;Y=O"=lq]rV9?GxUir@PaհlR `KV~oE Alh +=+Mu<;zJf60&~P+@Yq F9BNuVq$31Y2<D !3ߓh V`3WG5=b-2y;B1DiG4PeI`|ߡ%!^#گ_ ]YDDX <`tB"7P#BzH@M~mP56*lrh>!픕/p {f`Frdm~!쨸+~6%N Ԓ7ۖ=le*SeJrC$[p('ɹx%A3*g*FfzUY<hh<=xS'o*h;,HGF*8_EZi?+>q"_Q&J  *q-b ߸>N.Ն])WJ1lxwLhcWL@A^ҿkf.NpVL0}QՍ@hY"bd {E#܏d? `M/2ٴ8cfiW,=QXW-*!6' |H?<=yA=ЎQ՗ؘ;NZs>1!"Wv,TƩ1TC0I hwV۬а3S_c0tYT2ޣ=W [4ng4H&+ U|I%qL]H+ݰ栴+%r+uE89=(\YRq +%"7`yC~^yoڅM/=2æ }}r>&'GhX`u`ۭ *h]D)݅ CjY`dVMD_,Wdځf] )ׯJRW}pyg$cOS,/sw>ђV[ߐ7%C5KO^ Ю}DtR 'v\t%D5*VE hjՐ'^/:dsxwPsrLĿX+O#Yʕ#܈3WI;/b+ GYHhZ-x [!~@2 p֚ x"jQxËeX@ۭ1۞+t=ID;pHG uf|'R]֜ %ufbxTJ"wJqvv> 2.M=WXמɤpB$Ƥ`ʋ$sS u:,O&<,|V؍9*9\\e<'gKA8q6ǘZ'OmiֽƬD+sESGRy Av[DݮYsˉ7_tx^[8`ǰE4J(I4E/Y(a<,T׼,ûH`/P֙=?aM: MOjR׎z[5ЌneA,H;x;@\ȱ6<:%!/?;%v>{h%e)й}T'4b)ηE(}d5-%f8leeuisK& UO}3mcbZhI *sղATZ@R Z405L>j %A4 |HL5G5֞4)\a |U{9‘ۻjߤMY9) ,洭dzcg Dm\NY Lr)0i2x?5;u$#w\NϨEZõoNjfsy۩6fW[Y awGNoICY3i/:C-,*9zw\T#CZݯ,KX8𰔜<{YlQC-oPwbl@yyv+|2 58m3LI:Mڄ󺋱6 DS5˟HpY\=$ 9h܋K7d¯α48e =4qMw}q[ @Zvs,8 J Pnm/óA5 qa4c<`9urpfri\zܵF:Nȯ[ =g<%Л|J b^dٵÞt c\`C[&?vK5?˷L3wz;/Mm|D98 )BZ}e7e Tlkd:v hy\cdAav`ywdHږdel5:[5of~PpN!\=0y'9LBS_픻+j!W`3LvgAAQPajlc30;C/(GLkbl8Pm^ĴN2%EjM]8f.'@ÇwnP"C1d^ϲ%Ӌe\ gc !Q}{ QzyN%q篙Q=`Dm?|$ؿa 7X0H,k?*Tʪk]]}H֮vhmn͛6F DtX_7; MCPfihʊʎ֡20goZ`<#y`.w J($vHVid[}ThjFfn<9V UKd(d"H*_bFCLh\r 7}"!2rrY3#˲XZS .4[hѕ(.MhjYV -*Ү]R Rߎٙh' _?pi$IC7pR@Ď"_ݐ]Y30~|V.]0^ղwb`WDwW&Kvx/&jVBd Rbt%Z6|QO\#0ns305gh/)'TV͂p؁s9*D `Ֆ'Z[uG֌ ZUjkdIzУ0٩]ȏGS%~RǭqYݚ>)k/"zTwfzؿZF"#١-{xUm?c1hEE&QV/@PyXv $W3s=F%KחJ}9஬i:uQ29e]$tذX4KS.U qV{10#ƽ9"k([E{CU݁fsݦ,iTe:Ie16N} P!B#5s@ēh;w緓ٜR6`/ ΝΪYv䯆nshP^U~!P:d@6o]k}OGt\:xai? z`o#Ou8}־BmMt-bʧIe#*@Ec֬΋DDY?畇GHP|<bIU~DT:8 Ϯ-B@JS[:PCHd3Z {Ige@mlk WEgb+3sO교%zP./-> pWP|RfDPUPqa;ܻ:g oShwAD%D\_"BI"3RіÆ$Rg`HKI٣c[pGơ#|%aBn[KR9amszmZ_sKȄ6e>XS4eDwGg`'#˹27rIͮQvuD6+I}ՌOG'!b" 5|6e+I\: 9':G>&6QB]ȯDls#=O/%1&7w&oA  8?b3pcᄰ)3#!@f1`wV`ةVJ)A<51A[gHzJy?%h-ˆQr_١DO]G`y>yWt_1cmsCw7ٿ`Ƈ7tr?0Es5u"'>(iSPEpdԏbp/#sXS(3ŦF1BIN<6]JmpuD:f/Ob:xJj=37 ZPGȟ2r>g6jJ {4q&2&*eJ/`#[ӘhӐ]pA}ԃ Wz!60;65A$*ѣ4BCF2@7N.tP;&x3|xإQ`8v3,Ӱ7ypNL機bXيxzEBfPB[_ L魅.sWu)(+!} Co&L@AU*!"+A?QMalX%WZy>3QaF0 [H~". '&g`N2WA]/v͝"2H%`,񰂌V3y4DTw{3(FH*Z8XnYAص0JDN]طY}u۱n`f:FQOU|2G|?f{ T!nqF:d5Jax,6=$: ODӼSې65D%PIȮ@?Rv8! !ԟN/9Z]4Z6(%h1m[ wum:n0#lLieb X?_o5g] 1Wl)e+ꅟϪ&v(4SC iRKuEyq71I^@&3qni!Q5/ KT/pM^Kǡ^Wl: BSc&].>űd7=ͣ2C|N35s)4lJ * \5auXgʗI-8]>)=ֻJϞ륳d-wUf[IWJڢoٜwbzb!dA#6 k__]ML7g/|aӴl7cuR[N__߿f[^lk=sW1YzusNyx[_)z)Y'\YU4պl[sqct՟7}pR,ϛ/(p6oVm66^}/VdOy+pBu's|?T _ ܄y ])(qI}y m^Oכ_~_³M_.7\~}+^5N{#2x0`fyi:Шf੖aw /Te$mޭ~5#"ͼkg܊WtsYPK<|QPKѬ3C-Pictures/2000003D000068DE00000DEB7AA6CAC1.svmyt'M5m*[JNUI$miK7DApE+(Eٔ*("  (Sd+. 7CG\xgMfy:O2߶UrIIWt_t=CH#x܁gѮ{qYt;,lW(ʇ(ցO>𸿢,2ph5+J98 [uX17#9`>8 s օ*`OCcX{!8 |L{aK-X f*'HQ (- ƂI`B @p%l g)p l`q [$\ v 7cy ƀm1i't^>p t>*AĿ%|@}{gx5%㷃oAk08Lm\ .guv3YG0}z <,/[gh,~92|7 ۃNg/Lg`0xGa2p78<n_\ F^e- rA;*c }6^?֕Em`4x`8KV/8*#_π.yx6=&n8D'} h0Nj,W7)4xl_!KN`glYYO]`u V VO4l ⸥x\Qy-[|7Kʞi cXH04Q.Cr ߧ7q]/p8S_M:0|| ~ff7Kޫt VJ!g*`k fj=:+]/9`Q~.q쀮1[C9I+/ 8X#\$pJl~Q=ki0Zo;q_zZ ڴ ٧e ۾ޥ7-d ؛Wt=Gw3̬Վ^$~ {CGߖ Xr!gZm"XX{Z IcGYk%^I?jx/iUw[}y}>MLO5w~͠3?cgo]VrQ* Myk>N1bH~ZpM=q#"س ;[_|[ww`;es`(|=MVp,Ksɣ@$wa8:~0~X8뎇B9nq(yrke]Vo˭+ v ݋a#~N s#)= Cc3?=+<< 6Wa ^xx V Kec"6;a+8Ga )Sڔj;ש=&+kĵ-`.k=0VK-ۋeID}'2׃u4͐KgT/]`.[0OjppڣE4nE;(}g 7$XWcX߉q8 ￀x˦ΦWە]:aunCvn&r&< hOh:3?!uঢ়Q`S "8A)NmOD`Wv^v7'{|p3hF6)ÂxOF)kEx0Zvqؚ nsZÉv!U#%&w8HA`-lw@ b+^I4X;veϣw+h`2x7\V3,Otp7V4[Px8bw+pU`|r'b*tu>Cǀ`2.7u6Fd0LW\FXbj݃W%:߇`4\u޻-_Z7 \.oQFꟻTkY=Y1,U|<<<2,~.Kud.Raa0eTeTSYg1XY,ս,) LjLjT&K,,W2X``9)ahA, ,w2XfT3Y#3Y2Y'2&#X'X#Y'.9,գ9,Ձ,Os6ay,՞y,ջX XTTY|Ph۽bR+gRb'z :,:/ljA6KU,LL,?2XLX,UsXsYoc~R|_TvW5J(UTKws!(wt4R5]|}G)8>Q2 4a \TpdhpRw'O8XSa*8Tp4TpRqJMd mj Z@GhTpc;F)`;;yv*8RTptSNG3;M|$ENGG;6;=Tp٩Nd;-SN); D]@ǘ*PUQ(URM#AA |IP';0b.*5$؇*||RW8Tpus&*8=Tp|KEGC^*:~RqKEGHWQѦO;zsJ/Y^ƣO=Yd$SI٦JZVQvsLoUQVMՔjo5 aΨn` qj({@ӵ=UZ%QzUQ Lh%YJ)5-9Ŝ>[ul+Nsq%Khǂuv:}/RmO)ZF>XF?ח匃͔R=WN\Q9ez~,t2ڕQ2nTIL L JGS(ӳ:2=mlM˚镔ZI-U,N˒PFwa([QE*(ӻT )9wK[_OjP)_[J{hh2GQ\)*N2#]=]Aۏq86Ͽ5Z:aP[jdt8H<ᥞ/~uFq~9ۃ!YQz˧60ׂf`N!y^c: .;&H"y0  !$_7p5x8 9AQC_ S n{`X|F~/f&w3skx||wo6|X8o is+ʟﰋ'kL3u0ӟҀĿ1f;L xc|>cw%~ϗg9_ݫ6xN Ͽ3/LxS8~T+צr= ROR^i4M/44'F?d'mBlcm^=bc1ZAK~/r䏌'$K~|o2䟬GQ')|K=b+.K??R/ԏSI+%zKI<%>R/WԯSY+]ws$Rj&VSy|.NS9V(q~Nӹ{WF2ѯsW;~WBm`AqŠ u`K,k؋1π'o\s`hti>8 ~(Y|~;` 73u P{|*+o3|$doK8ޣI?#}g%r=D/ ]ߗ@{/}ix?.p?-鯯7q8Ox1>b߶XXwm,:sxwzX8Q7ǟs=?' G{d=ľ~Əߠ7⟥&ksv&BoUE 1Ia|Pr0WHc?Za>k9."KVO2 M%e=>WY|$^_A!! y-#zJ}XM-g=J<>%>R/_ԳS껷%ZsR"|V<0w``2zX]Nȳo"B_` #,:k'm:dh-Xx~I>~i"kʎ\ >OԂ)6v\<OO@z 'w, vg-. =!GB[B蟟C6_PPW? W22> P/x^ae|Xoy>*}[xe ϗwpT/Hr=i\_4wD׿(J}VͶlTFglgt7'.M3'%twM:v:/%䇜/"KxO2'&Kz$e}^WY#,I~bCA#!zI?ĿR_o7ԟCQ#)zIJ<%RzOsѺp|n5$rDh3x'ǵw.͖7;'Y뉤hc wx.S9V(:&ҟ􌝓s2vN9;'cd윌SPKTs33ePKѬ3C-Pictures/100000000000004000000040D3C0D57C.jpgzw\SKIBB #`U%!PUWQ !@( %tŎWD@&](("EQ@( *Irs}{';Y~ham@.̣ؕ`cCHA,!k~U(0L Ȁ% e`9~  s`(7{SPvt=ܟ":jlHd*YM=M&ibvӶs@@`_9}DoVg/K&.OH֘6[U|Z$')- ‡D  Yf"^&"c @ 0spl˷!Fp V GsprqC(}. l gGpaH( b&$!t^,B8%TN^7N+l_']!qf=9/W_,Ǽݱ Tw.SVR(D޶ d gN,J~U<5) ❝2@ —0-ʨGcAMݤvx;44W(.#U'nULD> }b?kPN|vxV>@@ b9@v|Ua}zE탋.i^jF\AL뜡!o;ò> e#GPkGIc7z=saxӺw)GC[8fxգ3ttg/qwD;O'VHXeϨtֈ=>,'Kv) N⤓&n[q5]8ś^ ;1é1-$>'z\ M̎` ėaێPE.W"%'M:wgl|vx q_ oE ɋEMQHۭ51۾Iǧف ɌE~j#ŮM;=G`ѷϖnU8uM`jmæZ ?S^]`VYY2ޟ-baUO4..uUjb}qf]6qi #Vprp,8W9xVaEݟ S_V,åTsE YjG`,ǏyP㕲V}I?c_84P;<+@_*||ZDPkР)Ow܁|7 :{7)_ zje?:",cB(\+LSb0~XDX{Mw)DLQVN6 ;wʜX wO ;]/)"T1D;~Dz= 40!`Ncs2!pʯ /\7:_QuW) hf)dcՑ,?zf[LI- N!ZsLxI Ap&@Dgl+;r!?A W'1u2=X#Q CH@ M3CMh튷Ѵ !RVa-N@h Z/:8$ hzцhVz dkQ,F!zEHԱX56ǩXm* Iiazx=- j||,V ѫÊR©40X ,V P 1Hjp=QaNC1$0#c?/66^58v8<Ùi%FGRh }?چk>d J%T c~\*#ޟ_waP}}\)*%L=b30utEdfӆhS5M& \UP}c*Qѳ3H4擁!S>zxm.oƧ M05ct1s 3 L{[3:99lKOmW*HA]>d=_zX0 1j0ɏBC~̵+O}+ߚH R@A)ڜVêc(3M "1Cw3P"VaL0R)wQAñ-")fT?J8VuB*RbuZx&X0 8U*Nǂy`?:`uuu<( N '5,pDBW\K4`Xs,t3є 73*8Aqik*q3 Ja9qm-U-M]mM`W0Y8Ǭh2[05-9Z& cY Stp˘ºLLda cYZ,gaV[ЭZ-PZ,aabYºL NFgamaa 3utuY:Ʋ0XՖi xk *AUWt bp-UprA%* XP0@B;ICGa -ΤVhU < 4`&}`i6tB$= -Li!p0y d  ͠D3Ow"=$u܂R&C=eE 0jvऐ|PDzp+cf<+j֞5g?ӣa<,;}?yڎ_#ʌ_gQ)duCTԙpl |dC3ZQj ۡ@Q' eT84*W_65Ba/uCMlB (͜.s]z%Y'gd& N|"c.$@Pf1`ll' ^ 8ҁl,e@P\nw6^$0KH@B !Xb qCv@ 4Hd'd?$ ɅA*!W 7! g!8d "Pi"TJ@7A۠~Ph,4ބAC_B's0 `f `DX,Vkºa/aSl6!6>3----6[7 78\ Qx" #QBapG Iӈr D'b1.ݐݖ`O`b>c-ÂÃƱ#:Gk%N~NN=N[Nd|Q%..%.C.'\\e\-\\hn]n{n*L CܟH$鉌@C o !gyxxyyT\M.pTPMb-6vοe{}畍Wj_-&{-Q1:חbnܘws٫ۭlj{έV֦wӻw>~]v[m;AC퇵:t5tt^2m}ǺǝO<}gAE-$>BE/̆ڇG#_}=03z77-Ml \J>VwgfF߇_4+1[Qc܋O ?>..^%뺯 6}X^^cG=ПC~M_?X Zs l0.(Oz p̏zcZEpF@06 B] p`Mr"$yC^qbn匢VNҐyfN:degWɪ>i(ou#FYYo5?j@dTtLCN-TGq*EW4 ^gJ΁,gIȿ(}s eKW_/~~d[faݧo>ZScg]lF}nh2QCm.^\6;ͤiWEn]V㲎Gbuo|dw”O4.;Kj;t}xLGpOt:Q<C 7/uESxWlxFx"Y37eXU ԃWh79o/r㱳  ]Q~‘[S[m7x]N;2?QάJHUONf ={v P%pmXE907 v "O~hJl蜫Cb\@4J¦L+)=BWih/j.O;ϋ,Ǧ2a/'W>bΣ21?rQn_"ToT;t996Skq j_tKsg9ұ>̆4Y iH{V}Owr}jfOjl􋴈wqcc/+:%RnlyayKІ>NvrT=RqU\˟<ٖ/wI!zQ U˝tC\%#niZ}mcoR9qd^>:;IѱswU*U=/mpʎsF<8I٢aZW3nAۭɒ$AWUOL^렘߾Q{G_6Ix8K鵹TjnT\GxEc앯7Eǎ[~mKNO%8w_`K@oZȶi+ɏxy\r @ڥ니yNI]%%|Kr=:זLY=&iܻpcbtʸ[c-ml؏}5n (xQA&eTk+]IQӴ'^_6|7洅bs6=q`` 3>%c )awQX͵|20>Z>[P3i7<8]}gx"eg 7Gp\ߥwN}Үƭ.Ozn)m0꘍86!ܻƦrqKv^Nn/V6&0~Ux-qxs)>h5\9ҙoBwrk2=&t: KHd/*)>c Lta"_9sX]I8t1L^zfwUWSP_Ц`Q˥ZY) #N. [_|NH6$+}^N{o s+˩ sa.mgŴM.< DgULU26^"Ll8qT07KWhQY~lp1Pt׵Alt՛G#Ɏ!-,^|3+KIu~sS9u2kjuGFnx6J% QKi0FE/9섳ACrKoݲgdb[[oMbm}FhN Q'-"DZ]L:$!a;iAhrB}P~FxJwZj:'˓ y4M(6QV4c9g}ª_|LoO!(w^cgiMV8T ˿1p H]-V ʻ@}^} s]7{la63uSWS5䶠ù\o+0&ۛy&1~It %Jy-#āb.I=XDvuC&s~;/.ļMt$+9ؘƠw:܅KJCKpz(_ymO5vGF8\Ɵx۠t۹7Jߪ]F:Sd%g~x8Ko8R4]9|) NfǞ_Ӂ.߱~!]4XJY k胛C*c-c z~- 6j\$AngIakN"_Ӂ(*j[dD9E9 d'w+ #bwUk<߲$&%.vpln_ B%Cωni*ԑ(KRIHo9*LS]yUN[+e%|hONlix9}7 ȼiyb@P%æ9bh ZIp`߮J@oTN-yO>նo˫W5VVqΡ[T%3veu_ّCKM$F%o.*]>Q.'^-ǫ!.W LA!ЂUw*/y묳H=ip3sGS qVAxE\RZZL@uEeSe ޽e6/Je1ˁ&WUl9yzJEU*zTl\rbCeQ}\X_b?\2 l`o'P,Rq8m,hNvmۖe`KNE1:4m*qxNEM`W(BÀCkT.S"b'Q*&vk+mE8_%lq)o;U{|Q[D-=\s~0֎y |Oa*Qϵt|45R/7fIܿ(F>p'/$ޮVmMbϏS]l~QWNv}_̶Ғ?퓓U|!'ROw/?PK=l(BPKѬ3C-Pictures/2000003D000068DE00000DEB7F4B225A.svmyt'M5m*[JNUI$miK7DApE+(Eٔ*("  (Sd+.7CG\xgMfy:O2߶UrIIWt_t´Chx܁ gѮ{Q8R1ݎ39;>=uS&<(K| ZẘRrqܪ(-(p.p,ur;t-8n|_S>R > V RlA0v7*J{l #`nЧ7`88\ǀ }\ Xwh*p8ׁG`%8p>1`[uFg 0k ]`φJ.f#( >qi?@_E^`2xg Ǚ^ n ['xkc*8 <> &A[07p}0cTLH&# 'Y04 pMCk `kp &11 dp!׀Ѿ_Zx% Ͽ f툛olPc #vpxF7;xo |\[WVrn\q0B_W&b0u%G#`+p; ^y9RU0m IȺW b(MI۠< #GK*$8Zաp5x<^WwRmVSp ne` [m8n)x< ׃{m`l^n飰͒:gZX/V0`qka\׽KA/Βqo%x __)?=*,yݽ3{0Z7#{p/R˿`oG?d;/sP~c9lN< > H{00\æqZێ`ܫ26mzzYkp#wi= 25+e]ϑL3k$ɽސ·>j'Gn4]Mk-=݁coǃON$}FW Sſa t*9];wm>&tz?YQIϿ;Qf?cg37._+(]O5CClg_[$|?O-^8`` vD>m;[ز9so0N&|8S[ W1 N?vP`} ?,cu'BN8B鵲`+uҎVF;aÙ0N?ߎӏPՄq⡱⏟pFF1Fpf ^$R٘Sp&+$.D >u={]:.x6:ujI*+qxg+X˚`eAp"tYQovku? @]v0 |#Y U.KإGV'?Ӣ< h,"hEѤNE+oYs~< `.X1wbN#u)ǹ)1vehuh:.됝I驜3e`8;G+ &!ig4TAPGQ> #g׀M\ S1 vʰ`8SQ:Q</xf\p"t(~hH 1N*eX >ŝ~Ђt|= p w=΀{`kzp? 7KS:8';iة_#? $o5&I5&]Ga .1lزGMl b:Lf8?c`|#qjD5"πF׃˰b{׿K]k0a>?KR5J(Rswՙ|jM>KG>K5&R=RݘR]ǰUTTeesY9,9,ՔlljJ6K,sBK5=/:5aRRd##rKo,! P0[#% M3H5:1vK jv&KuT&Kuu&Kd&dTKTKuT6Kd6ӥ6z,:0I.&,3zwKuaKi}D_x_߱s B>M8ࢂQ.*8TpI䓺*8V9Tm D */TpܢRaS8U@>}V@ǀ*8PZ@a;o۩xΧ|J7NNG ;TpSNG;"kQpSNNG;ev*8S1Χ~KTpSqNG>rP1 eTp|YmT*8JU*8fTplWpPuP1An"vrR1IkN*8L.>5t"㈋J ?0)Tpyn*89 7 jy_yt⥂RQc8*J&pO1RQ異㰗Ttꥢ#+hSħӼ|9KEG,/˧ *:P]0CBO?Bco!OR)&3uA.*:R\TttR񎓊)N>5rRIEԡv:xAEXe*:8tUHT*:$DJEU*:ƪ%VHSkT*:T*:U*:TtTtTtU*:T֙EGJEU#RJEGJEGJE]SJ;T*:&Tt,PXRѱKʯ:!*:;X렢㈃VN*:߉cOW:IEGʋ]Tt<㢢㐋B*:)B*:DrS1ME7kP1/{p/^*:TtR1-/|Z{:]DEG"*:"Q񓗊C^*:>R!SǼTtwRqçUaPAc?h4~Sr }ZA9Y|%I#+)7\IY*n*ª)_r@״!55 =(rgjP ?\* A0K)%"E1ǽsg\יb3r XXBNNG_=/]_%~RJ-S2=q⡱rRg)+*Lϯ2Uen\F^2vR7ҭ*V}PA]UA)W({ez~W[^2uiY3_*)sӲ2^ՔiY[Cܼ.~Cez+(Ӣ_\E^tezʂ!1ӢN ciQ *kKiM{]]F(+|P9tQiQw2 ckezOUP~g`?o)|au2|6;Y8](N=u}#m74R;Ox}_x_zlHV G`{ǁHWCXtIl!'t|Lǀ`9x \^NJPAg`iATp8:…`&K yo2H mz"-\!w??ɟT>v7؟dpe1fS7L~χ~o=˷{uIwE)oO ozrTga*q*+`@9Ѿqi4_F|FDTџlX_dc<޴1>[lWgc<1Gm6_^K>q9_E$d|/OMH>$?e~_GY{%~ԃGC%"J=Ro?ԣGS%*xJ=K||?un_DJ7֊p?TR|*5 z9i:y_WF2NJ~jU 9".5b4Sl b;.\p څ,~ZNolmyVpp @!#~gf ,p x8<,oF93ʊ <>)ےy$HH|_'rى\; Hz'p&bxڻ6?Ox88sJ;$G1!qXk_,=|7]xep̟\‘j;i7o7hͿgc&=xLV;ƫ\a(g7'VZCK?擌'u|$d~GY䧬OU+0131>wW_zH}}CEnKğR_"zKYOԫKW',wVܹaE.03U}>~i̿VS.t[C%=:$:>qp xl&A 8:_l`Ț` , ]<:' /K t s /iODB{!M(#1C5ǡOoy: 2WX6V[JczV)<^")/*K|3S9G\Op=/N=hF{m&1F_mY.IKatttoi::]xk9.!K?2䓌/%IGY䧬WU/+H>}b//zH}^R?O'ԗ[M/'zH}J^%~ROgw޵\ \'*l[jwͭ Ev7<r}qmƝ|eIZ")5X<iTkrNgɬ='=cd윌s2vN9;'cԸs?PKϛ 63ePKѬ3C styles.xml]]8z0b[鏳YdN4d(6бcMl+99s۹eۢ 4dIIQ%SEh;HJyo~y}r_iz7rKo_~Wkwܯi1x:*?ޓw'Gxw>X{gO?E/Z<̜.8qLY+~ZE?7^zu_mp?<==)OmXLԤ$o\:cljh8 yUڟv_;)|爲~YE2rXrcY9UUbewv)|%swUfZr3ItydU՜r?fSٗٗv @C 1v>.{n+-$ޭ =KuU_Bk;@']=26n[I6,vyCfN8 1^oЌ-yeV:^SZOo\<ʡNȐhM}&켗>x_2ފk5é3xھQ8׵oI;_˟ϿA^}s_Η/7_~}×ߍ?h˿Ofh(ǿv>Q=8}`BJr|rk_~rwYߡiHzs/]{0!,ⓍRzߟT7;wIi~pF4"cVEu4գٸhop/|"%k`Xbf:h4BPl J!UךSZds h*r_TU2s3csT`JSqQvRH_UV'U Bc okmPn,VbD檼T=w'Å:4kGOk`*O+=gc>kqw~sVPI^qaSg<5{{e7NڞT]WNw{NR_8qzu9laiOvGwDx_ܩ#u#C%W/vz6𳍮tj6:ykaۇF|6k ={rl˥K$ۧ;l|dM2v28'|7˞oݽ3~#J Ayx8x͛>'[pE굽=:IZJvQ=4?r2*0u϶d?Gɴ#7kѵs~mL^?NiD^@fmL2~2IݸMQ2}9Fcl_ꎽu2#~uWdGȾ+|.wǑFjARP1pQ:}(>&2ߒ85n$O.zv·6ƨ/hJxmuS}ܤR{ܺͩc4`9lsnbܤ^غw5ٶ'gK2NۭH"~1>q^rJ"6uwϻ/a3Iv0Q굚JʄY{ 5ņ Sv~PěDDмYM!d?|z:OϹ!dt^pDrȺ:qIe*o#LB1I>%D$, oR^Sկ§!rnIBL+Sw J +l|!Xa>GBY{hg ]5HL/N>l?)(~_i1b롈i5J O'(J~7U YB!]](E^\,!(u9>$/C/~t 8sdd&c7u( I|%~YR7I 48 g4?G7,Rh!M'p8T߇p._C<⊮ak?ӯwBj'ۨ僷z;hɝՂU?h8NZW"-[-d$o#`Ǯl;tddȸIZm*l=.&7RLxWMձ L=g5u~J$楍 {Hqc8|0ap;=?"_ӃGIoF̈* |z\`'Jz?yX#xrq=~DdHGVth4%LeߙďTbGU-F1+p%D•H)#Bp%D•H-Bp%+U8_e]?ELڸjrLyOj.oVvd vj9VpgC/Js-Q~wN,ʠ\R|>mى-, GvxJVUY܉7χdhWvwY^>:'%.)pIK \R2".)pIK \RK"Q3uryLF8hujި|oFrQ}?]GW^~| ;K_Ņ*,Ns@]MO7b bd;f;6 b b Njc GJ**(Ѷ!XFvA-{]&9?d!̒_e4.+G*pb T碾ōUZ,D,=<.΃^[a jE;X&JyUau986zDK 1V9;gx<ʼnQV9ſWT$H)jR0FdI1ψEٟG6Zg5 RgT`ry? >głѰ {/&M+x%wEwy!.ޣW %I'_d.qGwu6;\&wo9uGA3?g9<'x8)#B'x8 !y8k\TH ac'x jN'&[l µyɇ*Qj"~]dj|ƒULɫ8HJi5򊊔c?*P5+yhC^GLU0ǹ `sYtAtx4 @LÍ` EQe0PkX2Xv]h `6(̷Y|ⵖj C@U `}ؗU .(Ⱦ%†b_V0 V }Ye肂+X(l(eU `ؗU,.(Ⱦ†b_V0 V }kjfoS3S1uS'4Ա!1uS1uS1udCL7Iy]7i:P}@՗!@T?߉0W}&DC%z}=S+J!VZ {CТb- ʦ[£i^5=EnIOP[T8κ%=AnIOP[T8޺%r"nV1XgiMoowͷ j1xͪ[BoWCۗU.(ȾbuKc_V -}YuK肂+Vd0e- XݒؗU.(ȾbuKc_V -}YuK肂+V)A0&@03A0lGĆ3A03A03Iw L-rL@x= ߤ@_FH͓4' &MjԜ757kFD:C[IqwTF*-mQ'Tsh,sR9ɇ̉2'W\l7q ZMl&<& n"ù L/* n&m l@nہI@.6 ]oYDk5vd˪mBd^N&7/ ]Py9I ܼ&tAAl2p ˪kBd^N&7/ ]Py9ɚ ܼ&tAAj2T fd IgobC ⛤;a^/x)]7i:P}@՗!@T?A`Y_$H|P H@w$:#&莈v _}jseѴtG,V[κ#=AHOP;T8κ#=AHOP;Tx Kۺ#17:k 8~@3ɫ|c.aU.7BlEx8X+qAzf] U/YL肂,,TdHf0 P !YUƄ.(BeLdaV% U2YL肂,,TdHf3 P=AX]6#xqNqNjCBqNqN$89ɭCܔ(o/T>P}T>P}7O$M[%MLj,iR4ɇ& i&WtA$Mx4ϒ& =E$MZA$MZA$MZA$MZA$MZA$MZA$MZA$M"IgL3C9atH9) ]o՚IoB鬺#tAA.P}2"T>P}@o{uyn;wf@ۡyE{&1m,Qȇ=D;A=>550育{hZwqx gyYޣ'p *=z gyYޣ'p *==@C,y|N*AJ7^C҈i] u!X:肂L֝X&c 2YwUz.(dIon2V u!X4肂L֝&cˠ 2YwzҚ*]6dx2+2B B+2+2+#*ʀ,ƭB썔DZ(o/T>P}T>P}7O]^YmS<CyE{&1k,Qȇ, d1D;Ad1>5#育bhZwr\z+Y'p *e1z gYY'p *e1z oY ai[Cri4~@u3*f]Z h8ht814ؼFSa*u3Nͤfj%tAA݌Z t3) ]PP7㤰Lnƪ C8@7*u3NJ6ͤf;tAA݌t3)b]PP7ݬnVS.xA<G}G.@#B(u}[.*GR^Gy{A4>P}@ˈP}@տyDHipHmy] "_Py}M=D7D*yh"C^GiHȠHWAtA4x4 4@I2_rQM$DjO_T@ 4D*خ߲m+kR(b|U.7(pޞP8T+r/]P} ¾>tAA3 W ˪dCd_J6/ ]P}K ¾-tAA2WhJ[S.xK<&a&َ! !a&a&a B@VADczIӁTT>PiNJ*ÂHEx|4QP֗߫u|)ҥ񶨗2 D6FcxTT+RFxfICS˚{cU &ꠡ)K3;%h]E%.@uk]Q8޴pFn(V -e%֝JU \B`j2n$} ʾ 6%oߨw9yj@>r~/DK?,)E1"/Ogkr4~x[4M)o;_~:P+mcaK I?gh/+|HQ*mZ&ݥP1TZWNPS6MeT4lU+RfY{^p%KiE#7pq{J8As_=Nּ}$q6Vzm}(Iq6$ADοj\_նW*M~&#m4éC!o]oS,]( ?R/~J t>zV+|#sy(~xy{(~>?Mb0_'SabLIm2}K/Kq8i Z"!=LH/7,gyKTeD)zNZxsN'ABUKf-4h< J Ĵs6VlnHV2ܱ/x} ?`(S8 G~GHG~G~D~GT׏(O lf6۰ٖ!lf6۰نͶf;%V(%)#!٦= \g_݌a\exS5JZ^IW2Je4ӠJ|H[wt}ߢo*0_Hef#z,C<[5SԲvƚ1MPԙ5+ɞ" hZ5ij+&Vs=QoTk.5q?#K ~Ǧ~ h'nO& I3xe^^B;E}i-fYL8i^};{ m21:UϤYbڴКÅ(ˇgiG,'n߷me;Lco[EN|{E4RO'Lăs;u(X$(hs)Ѐ .>O R BQѥԥ0˻GySp/^·lnߢpmu/z|M~Q GtfEzzӴWaLsy}֙C9*$?ѮSmv@$H$#=*h I"ďv. OU$I$k:Ѝ (녾{YV}?h ~ES9zcFvo٘dKCQ$תݙvm^tgT(7ղGJFP&`jq¸APnCjqZɯGTZ6!jMk&TRL+s86fakMkr95.V 5 i?p-Pґk՛eZqd ε* %]Zf\+/k.5g\ZVϢXJSa~uG7rVn0 6(wvq]3]NUtfRL?oFPv-CV3JADFoTx;*<! P (0'! #t 3R$a"DlfNFO#$ WLg*~3{b77ɯ#7@k %$y1`In$\[>k5͠YdqBi Ϫ2#?*UypcC¯)笩̌GTzm ,;N=5G=y!@{!#tؚp2y8i'?PK c0'PKѬ3CConfigurations2/statusbar/PKѬ3CConfigurations2/toolpanel/PKѬ3CConfigurations2/progressbar/PKѬ3CConfigurations2/menubar/PKѬ3CConfigurations2/toolbar/PKѬ3CConfigurations2/floater/PKѬ3C'Configurations2/accelerator/current.xmlPKPKѬ3CConfigurations2/popupmenu/PKѬ3CConfigurations2/images/Bitmaps/PKѬ3CMETA-INF/manifest.xmlUMs Wd5'&cԞ{h%$#d_$GL35{YVv8[D=ڲNFf1I >?|͔N=')̀dU*`*4mZ&W 5IO)(̷56u=nr,؊޷,mkNih+W /mǔ.{jy0]^+K͕Z XZ1jӨ߾ T1WuhNBg {y~|28dyccK|l\4r7soˆPjfæCt??>Ⱦh8= ͿPKkuPKѬ3C3&//mimetypePKѬ3CӛLHssUThumbnails/thumbnail.pngPKѬ3Ccmeta.xmlPKѬ3CWC8* settings.xmlPKѬ3CLCWELO= content.xmlPKѬ3C'n'-kPictures/100002010000013300000189A474126B.pngPKѬ3C`_꽱-Pictures/100002000000089D000005E47842EDEB.pngPKѬ3C\v]56- Pictures/1000000000000589000003CE7AC8834D.jpgPKѬ3C<|Q-Pictures/10000201000005AB00000006CE524E21.pngPKѬ3CTs33e-ӪPictures/2000003D000068DE00000DEB7AA6CAC1.svmPKѬ3C=l(B-6Pictures/100000000000004000000040D3C0D57C.jpgPKѬ3Cϛ 63e-Pictures/2000003D000068DE00000DEB7F4B225A.svmPKѬ3C c0' _styles.xmlPKѬ3C+Configurations2/statusbar/PKѬ3C+Configurations2/toolpanel/PKѬ3C7,Configurations2/progressbar/PKѬ3Cq,Configurations2/menubar/PKѬ3C,Configurations2/toolbar/PKѬ3C,Configurations2/floater/PKѬ3C'-Configurations2/accelerator/current.xmlPKѬ3Cj-Configurations2/popupmenu/PKѬ3C-Configurations2/images/Bitmaps/PKѬ3Cku-META-INF/manifest.xmlPK/apparmor-5.0.2/presentations/LSS_apparmor-userspace-2013.odp000066400000000000000000060415161522511161100236660ustar00rootroot00000000000000PKx3C3&//mimetypeapplication/vnd.oasis.opendocument.presentationPKx3CU33Thumbnails/thumbnail.pngPNG  IHDRd*12IDATx} \TQ]TPAEr)̬LMnu_ZݬZneW wQA@}g{ΜFtx:}y ^!@@OkkkkkkkkkxkqiCbSm %莛#yc }7WJ-{n?6u$j\W`{^ÔĭzB / 1z}4,|;-R7XU/r[\<{Ma{kLfTk<B}}'oJ1:~)hjҶޜqFI!7/LA*Nor;imF&쵽q?]jzBيuP]# mǿY2tHvlF8ݾ/&+vIG4z/O[>~שXv_Bk=LrjK]6}KO=#BZ 4urC[WAX*KQOt6%7+F}`OcvM7ڦLD0wإ]<>i침Q12t!º];R_EFTZZƤ§}A&'^n5wY?܍zmDA097V O"}G~CNF48g4z@, j2-7;r'B5*Gۗ|7!?ڊ|[]AsŃbF7=CR(1T6GEcOo8iI`_F1w'NuNR< 'y"«O\8s&FX'/n;|l6哠#@~IՋ>}x" pK"aC@< {kkkkkkkkkk, me^Aʁ%*Էux~{ԁ3suoʹ3v-9~,>NE@Sݺ '#ŋHvdߖ;f{txuAz)1c<.$Va i<ܱ[sDzKTgI!E}T=^Սf:wlXļJfv~ӭxt\R:TrEiOuY#fRFhIwp$M_2jzzC}<-j]f6WyQԌzH]04\Sju yt 1JHO߻/GI_ؾ'AT8oT,I:]Vvdwg)ϙӛNh[Y&O7RSu7Z|_L{}kpC)3q#}j\2Q`,>MnalIpt5QSOw8N\N`⇭]"Zg@ Z@H[|eDOJ\ % $u҂'|m0vA]83IS]optj/ow&nr[ycŮ^RMp0+_xA[9#cWVy^S=A'8ڦʻC\Ŝ)U~D <дVRu9F( %<\>yI?*b T"l?hҒ]'+d[PYϏ /=D޳E1)9vyXd'0i{8!xX2{,[z|ζl$/Aԡ ʐ֪y~IU@#2onk܏[a"rR).c.7gb~|bqˍ ðrMk+⠫[nHt#;{if,z_F ҾtMO|:+uI?QsB)>0$fk?QƋӰG}n `Č,z~Kk ;zɤ΅'-5]$yon:`^rR*@BnBTo Q@ 1r^5[.b5}c@hZ6}]qV8zjHZJu ^ ȹC\-P0 O?"OF<)(3ƥ3^ݗ"ވ4"OZDW>'VFs^.Ϛ'+p$؈>3kOԬ{[=:6AЏ;aV \u'M}%:t^^䳯bKd#c_}-y#s0 ̭W A|#FDzm}p]zcD]Ö@$=4uZ=lDho2Mtjɺ3u>3gJr(1S&L 4Ls!^y:Yxl5/ڐ?H-_Ee9&B/}aʍoMZ^zZ_$cgnʈ~snx}O,I+_'[Y82wf WƐgIaD@i*mK uL0uZ{NcNA@,]"bqf+dfr%@옱9?kkkkk>"'-iO͚Y wHDnf;LʠgJtkuyyN=ZH03p/Nrlo]i&~/L?{U7Y:{{ĩOlKO`G{̡Ap"uI4aGAEZ5>,J֙rOcXyiN^˽ TBi/݈DfuCv9TߒR]uy,3h`%DH卋1qɳƺM1߭kEnuuv1߀扳i -KLN5 F1~P{f3H!,!f??:lpJ11R[!jQi̍TtгSn|}]Y* I }+m- _`Nf<% UX7b f ?Jv@<ԗ"A"eH?nW1j`}{"1SwIX_6 0s=5rCI H 2ULwV=P7,x~Pl7M\LϛJD Z//St `"MB<,~2RӾƘ6y.r2*F{䗉b@ o ?532wQ/b~} dIA@h=@#Jr# wfUj"i@tPAޟܒ=}#OavD1!puF$@)耩 @2 S(tDaR ]%8hj{b>J`.ɇ'«gD@jp!|<182ڥs᭵fQ2~:_AG7Fꢿ aLm&IK'=AKHm ߴ`5րa`$)[x[ sF{AYc@?%"{,j>cy*:TrWv1MAcR91fp_\TSG6NmI‹ BjW'NRn2*ǒ5 ϫ1 xgUCGX|>F/c-)k}mMی4kN_5F.M(ДRl)GJemq7T/AQ4Q_܁[olOb thz{dfkV",zĂ:m9Yp$|A,./ko:sEEr%*Y!XFA ?݇$ݔ;`s|MWq'(aI.Hhkҏ&/L2D1OeH}0 -CFV֪"}Df@D F2vbe&Kt')`S Y$#jSQݏ5.3w?ba| q®®®p@&d+wj]^S޴ &F{[k^-וRM*b- A,Y?_ί5f#+YE(|?4 "tgi33ZTGڬHKej$FRY I;= 7a3/n mB 6nUkF\8w6 6X0l|H@X=Wki! EdZ23 D% os`1~`!Ȏ1a#k 0Gc«Vq :b%h.y :GsԶɑq3G9 ÈѹK{9^S`< įari,3oMˌP묗(&S;247`жɑȋ;`ѕ\*Y"{vJA"2CwZ=sbZd<G_CmG3l1wň7'=R'cEdͦ1oT>"b`(cr|(uwbdowir]ZLKQJn2_Nj1zپGh`CM0vͲHhN#daD̳hP򶝭*Cڈ㻷3\PD#Fǎ9ћRe34S_^.i~d]EԐ+iogm/7䏏5HƳbYS)e E+4HLI.Kp0j|kXaT! 29pe.c3\* YOt8]֐&MXjc91G$::2(דs]_E\bƅ9vRRji, R|GKOvMd&ͼ e i2 ֙PgJPIf:Ys >$I$dhl릂6YY0Tl@ HJՂd֟;֚9c,]Tez)u2±FbfS٫ ~ꠥR3w\_}#WN,!/04ǀa׀a׀a׀ƕQCkl`Pr.xElwgTOnE5wc|"lnj$S[ө:#L: oFOكR1ޥ?͚3 B'v-ۭF$GqO.%,=ثJAݩߝWsfUJSCخE"T6{TK/ݖh- r-BQN= ʛ?crOw#5ӱv7YW/vIH3z=" o?v #ϳD;q @8!`ob)bD$|e.K:q FE#DLSR YFR zΚszУ[!ퟝl'ͻw@υܭR} 0}BIy$Ony2~So0FRA&T"WK $`P>H/9!/2Zeyg[ |׺O:91au5A 9O}%j2%J4-54.= q0>g #W{Na- -`@5`@5`@5~ԃ򇞀*{M|ޖ_ Q[D]^Jp+/7gܱ"Ob XTo6?#|-l , *֝2`Hy} #|M^V^| a4աgaoJ,IcQn]z'ސn0]d?NVc8>!8!ls-<>Utg}[2{REQ3"%c& cLh5 L۾z3scSdW΁CJfv7dV2|_)}:`O~fdl+Aw.A?I[qEQK]_Ӝ`R4v {g.zK~tA#i^X-.jjCnMZ;kpia\wErz6)'D-7x`ݕ'q~ J6H9.eEd7oVr6,h7Eu t)g DvNs tCŴ:QOgjGű{joXO? *lNk]1wMoa4 Sܝ#(~@ToRsk8G3dNp5j,Mh ,%wa]EFd\z E i_x~aU8w, .( aG;=tw6M [C8<®\?`UtceUM)O# VPE;ܛx;Qߜ=^/LN̘y"0q[:sh44kǞBae<*W@1K(s7E ߰;bQΉ?P=OWbS:dľO^n.$#<kkQD g$sN'2 oiE41֐Y:QFH54cחPgq;bQQ[f)bSbL՚֞f "t>hrD;Q*5p5&aXBAf @^΄a2HN:K%M1fRZTgUL)i1FDΪلYr{i3pُ^g+ 3$e   ƃvLQ'zmZUK`͐D*9nwKgy3']Ob{{¤0㪩"b5}&n^WWX+82y :QC__k S:QΪ`ze>` y M@g͖&(~FSdRc|j7)EXUJ5NӸ36Rg "7g7IP޸ƴ 9H/જ w^qgq]$ĥ Tp^l؅#Kq]ޣk9|6cTZetmBƉMe[ٝI#u5}zB@NKo 5m\mԎ[Òm ';X} ӏU?:GBih0!ĢH^ iqΒ-w!\dw^pfoW*f%]uۺ`%FH9ޟl%GL&mqoF}=fr݉:[ pDG  uB`$fO3j-E2AWJWH u=9(uEI&lςO/*-9dnͷR^칩 g A}UK X}WavF>!QPm{׉ڨ- "%Hf$q.#5f$y-/۔+55gAyF]xVJkjN'gw'S:Y)?xrT8&,?+a׀a׀a׀a׀axJ{8XRHxL_|JD}!"tm<2ՖzDB:!XV\t, vpd! }ㅶlMqIF!VZS @ ֛W%םϧ)1 AhH>r4ȵbR~i{j%F4k)!KA -KLNd( a,RecpQf \A;v%HXnvB@] U_ϾwY,;1.b\H9;Q3SIr[̲3ϰ?"8]b_MIPdӄ}Mb݅%IK烻4תZ Q~oǛ4)$Rߜ!K&&4+eoБ%y 0K˛t6q?]$C/sOf;)ax;:-1 Bn;! Ti5ײvX~bBRLYB26 5à.,;{Sa:ŀ JKzfM ehJr|mBb4x3ała#hn-]P}~ږ)@`@U` 8d.tH>P"d"]`ZNOq/v p0YH)F 9$bmxϱcA^vKM "/{ ܒ6*V ֽ'pS-g1>\rWtIj%e^}oe4tZ%F-zۋӱ_M͈RIhX <1B:9/Tw2rT:1馋-ZEhU,GEh5=?cB\ᝑ{iKk ۮqmp ԹkKQf{(;e*JpvWN] Sr'ƓN:M: e;0S9眚ax_?PKRcPKx3C settings.xmlZ[sH~_1enm91`Mێ[ %64A뷻LF1q@$}ӧ}.叕> t +ȟ'mx~W0m'׆ؑ1-R6$Ħ㰲+DajWnZ ۴w~Ts|󒬪jI ByKkEٌ~;O,LHR7:\t.[3g;]zGH^f1p*gSt)|6s xo=yϱܐ+@ kuW1awM/KGCIW7"Q|E/z/؁+r8yfoav i,pBNf:p!\(Y:z;40A Hbxjz=C6֘;K(Qv 5uM<`nG ޞPGb@aP[5P%Kttc-u&h^P1i2KsLuLYG;cΞwC1%{ 5mT< ۦ! hm0x\>-tM;'n Jօ,+ cF<^e!hD)r+ӑ>pI @(5hr4!d"e ͠ JO7$6W;;,XF@ְ"4R> pQyszF"_s85B;U?ݫG8.K}i~-X>_̾k!~mCQˬD0)o ^Qy{$,ʧpAO,`^NKӅg'G. rym&8*G\cDa}A^^E=;C.s/ɜe<--C0: >.*7{ hPg<ˆ ްú5KjSߥ|wmhmd{5DzZ=a/#O{IQVf{{-م.8bg?޼.y_~o-JjO6m>Gwʆ8O]%Oi[>_vɃ[e|6(2}8f+e_gחVٗc뛭1* ݼ}vvx^ |>w6S:$Ɯ C qafAҬQ\%1[8t܇DnUZVlV׊]ZQQ"iw7ܤ{+{S-|6*ַɞoYV@*&;%WCwm%vq5qU1Ԁ&i0'E>D'qn;Zz&e^@7W1q6Qz UL(೉c0dr\dkn0@"$Nvڲ?[ (2]Ĥ;muF4!ӧT˰ҀV [nnoMV7til6ʳ a\{a/.o%k }dmUw6m[ ceVIH) ǵ[.hG. Jz |mG~*M%xJGL ^ep^pLoF?ž%6P@R._|`?@L-UME6wW^i( Q[.9|/أmh4p-(cC76=,6Hܰ}=0X+ E҈8wgscm_0w+̆)ΆHf6Fe?Nz6:  W-3X;9{UlQuچ( tVWs4Ne5-f,Rd[>UC1zP֥ sJSξ^Oz3tJN&QcbЩ$9^SK/Ț/—xk$ y J>кb^7}Audp˽xHry%P^cz1{ ?^yJxir[ }np纫*R|3𰴩{ʁe$w.`_$n;5襸#)>d v/C_A-`I+L˥D݋gzÊB sY0xiR6f斗6Y!\!tcӟDVyw]#796h2?*Muhv@L*?%R6Yɠ-M%N)KYs0eAI>'/y2Dv^a4E~"N,anճ̈́aN+S 4r(Kkd-qy.'`YK]>aMC ha ZYX+/gg2W6%չ{Ƨ9Mِ_6;:BI8%>wN]twNYBe'd(@)+`׮C<utVg' ا#Tk]<+#xTΧ]*HU+#s6f@Wbw֑L;BQAT+:ҤZZ%q eۘ1?vA%%'K xq^IYX?W/YUJ6JR&[XtfЗb nXw (/GWYM9?v?^۔h .npKlS7Wю'flXьi+W5L輏9өC5aٮ6TeʰXwi'{{92maך, H"FNѡ QESqyyjӖ6O)!y?u5U:竫R"zp#:RrDD ukU*_y\<g/U1g_t-v<)L'A~u[Uw{2o m%^+9/ ~ JDĥ9COOӏgV2>$<16[`Vj2K)tFt $եF拆T3E|L@|nہ>]~U@.\XM,)8.T¨8WC1W;e|%o_t?˯/~en#sX[mB+BA6?ٗNIL>Otu=] #Ͼ=d,G-grl*V=ATCB _͍L3]Y;'}s76jXyXx;ZSzFw7JI(%EmUo:6>D2~mLWb}QήT].ʛ2 += b?nIanwLOG:6]2n"7uM6ٞOFG1KGbI&#jdHpNFCvSƧDAHFAȠ|d6ĉl:6J|i_2De<>yxESq2ޒ렏(# )/- $UUƏfI'ʒ^5;&T|%TL=[,ܽ wq3`q&^qdZvW@]#{ +* D:ב҄:Sbwh`AM+-M`sCY;ӓ25`OCRkx,94nCD~|!,3m?ggqh|zz"SOcwK,?/E]NLIX48L [TmE˕&h|a/WJ$}\iϗ+M0r N>_4Ɵ[*a)͂q<콼|7xZ}rX`>X`}@,ВCK ]{݃P{݃l`9ԃ`{;{ckzckXڧ͡k#bm!w&\i9 Lrܙp,3JYpg•΄+-g WZ΂;wlgqkc~bFFClQOCqb-?\e(3ב,Cy?3gAtC3痞O=,ӞWZ>%y8|W8Cc |{{!rCJ=A#SNEzHC=$!)q=$NB⡵\*y\kn8XXc&1]x2>bQ .rk ?np/m?r,˼DMyq$~?krtGcǿ++^׶Ս7AI?#Z!" X6km}oZ5 &m0/$GHGr۵ejw^ȝ#_be<  h1/<<2?d wn"!GG.MZ'kmrtg< tZ **ӵܮ]wt8#/-x"seH.H5fStE-1EhUfa*|tePc~6ŧL:A.rh~(O=!l3:>S~)&t Kby!Ƽ0f2l^[Ft}009Ȃ@! (ƙ\b#.1,bx',5amRXE ˸@U80m+:(f\֭c誻 SG/dߚɻ欭grh%>emm(9c0J qla/ ,Whkst={1&XdWiݕ 8oqenCUG⇕c\<0&֊)a2A6Ndf-![brXu8vK}p9_wK;)guNQ|qdJZ\a7+a5 nmdrl #hgZ6h[t$FFa-?^ym0{]&MLJF1 ń)< `/ԵHQFESGBF؁32QrGupT!ݲ9a(TЇfpְԮ5\ջ.S5'l^l,$戟McYĐiIw^XS:(  J̙=_Tl<#?ѱt ?\]/v]h[p" ֝- mc;ݶL:cYu\;?Gxm#,n0#+U;ݏwDߵ⬻m鸠T{m`mYsmm>MSNvيhA6su lG0{;FYc=)3[gnUioE%ytkg@AGnXSItG;M8F3竲Ӳ"}mD:g[xuLTx)eX-qYu'Byh@hF"(Xz)%T,>ve?[7HX纍[YtRi:ɵb[ ԂӍp; l>.3G aJ]q&'Rv՞Cam@wX\]CYX6VF^x-ydћ!;cu祿65)VGTC߲ ӆAYўjvqփub j)i{d`P>ʢB!@atH"z܂y^J f7d<;Z]{,TZnQحznEΥ& N,V6 ՆGNVxx@YKuX Sˢ EkSN||GuccVB7zkYԊUB >)4+aOwjF:XV/'W/dy,U87JH&ђa*Uj{=eK/1\8<9;'JkRuމ#;j.dRlx}ޖzv< ;ǴɭI7OXy4L](6H&&❫7Yh-]3w-i,7`IM)s2dDԈ.l殜[60a N] } c! `޹iKӝ*,Y|}l*|Hl6]XCc"0w{EЋI#Am6ts{B[#CYD3*YofLz%STwkƬb?3evebd 14y8Pn*G/^{|zR]I@6Y: #~*K zW&a.'RAQ() q3 ̢_Ea>{}AXSJb.JLH/LcVl6T~\ ecw4:6˔'\+JX₱Cš܈705&Z<}Ɏ.YJ71OHʶl_d1`iR\ * |l f9ҟs_u֩''>6}F_{:K\(wxP !ߍwt7KV _D^&p:&/K tᛥ7Zlj'ij:X<0΄_ O`Tr‹]B{A=K4oq_y` tBo7z)@uG=z<64w(OױulcOTfKr"mKB$%LQ /'aTP.CaUlۺO*mazkDkN ~$'{dw*bqN!b≓P`{E%eW{ *Wa0A*h1F, ME%Q[YA2&ON;c7[Q]tʚQYDFHgiMJbA⺢S)1BC'2*W%Wre\JxFO{:7Skam)x(>SֵQ#?:51Ys| f0,8 )/#,Jz6II#%( &Ym %+VI8ʽш=! :H[@yg Gbl˜n+;VӍfO(GvSүy];Ħ! @rq(03(ϯ̸ְqGKu<)ڭDM;Kg_Q -6pk @nrrQ0p${屳ۗf35QBQHC~:bKk8ĝD,S{} L>51Ȋ?JXM Z>ԇYDE5w+\S]fP/Pfo1eCmMC8TًkIO 'czjm 9qk=l^!`Ì/l~"Hty 2Z& WapsoeDAmbA1]X vv[UsR Y<p ҆Pi; .: ;c9eV09 0ײ )+p^q^*cHmVbYLPC'+0rlᕖ&asuʤZmJdzs՛qZL`[z/Sه")A AL}Vm? rM@*;W7ox%&ny DԐD]f3rl-1xDv˦xzKK9g,L'DlNԝT}PYx<`VPK@7{Pi P -@qe&4iY7t,󫘫Z4=; ;ߕ,ro-g t]ׇda')d9#x:4KXXֆ.c3]m"` Ģ,T TXl#* ͮ&^#3^"KJqȴ¸aD@kv|q؄OFr ,A DmѰ ,Gm{UakaX,;[{w<$ }))QIh^e$Y!#G.^xqO#hR;'Y9rO-}{]jIDɍO/9Z}τ6zmijTʈ {f啱4~5$>"kw#̡}nd ) zbtnL!ʮ9Y&G&Xql%9mEp1/";랹<ؿa2,+- 72#`X5߆v! h_sӈp3 L̜Rf <4k|G"g%f7Ey܄yKU-I0T mSԔ9[cLF>QjzR'|K#ii}j\Xdh(a6ʼ0T638Q@,8&9Bnf{6@`~㔗f5&I% Ŋ`N K%hSH(W#O`=)!rP ͷ4Ii PEU3j>䒓)9n3~0e愥U[&SqG3'{fN9ŷF`NOSpظ1,#0Twy0PO1R?p u7 xUjHO$DF!=J7b:ϭq'cەAhyA3^G@!r{9 ub,8U)Yl#xX< mnxlSZ3 TftjFyoٖVB4M(pKSϩ=5 >N}V,"Ug I_ ќ i&(>~cz^[>=4գP.@4@g; }w؊mك;b |@k5Ec{k^ڿҘ>SW\lL?+*+@͇=5-_Ġ/PhB$4grث؆<54"@j D.HC[A_CG=E^{BTH~{z/+^ p؍h][Rn̏"^+ST"7ԭ-T죣P>@-YihJD{Ъ-Z_I㘕4xy'}&rz5h%Qzaps FkiQ7X"G P0fH`b|G5k}R"/f=b~bEh9ťjǕF ,?#dϪ?#1}}3׷<`!+xZF@'@ڝ0=67J}d%Iffe\92N@N8*Mq $梋9S'$NFe/*TuQ 1J,p^Ҭ8>7e~>&uDt)=^NM1?"lX6y>Ž>B؇dN* $KnBӥba1¥$qԒ]i4#|UcVHq))E=3qH߇H1ۖN.py) @xL`̮)W!f΀BAYP4$N 3>ױ Ҙ)fyyB@!\ )Hs/$ {%GWa$6X ,Hb Obi&hVOHLu)#d/sp$Z#5璴&ܱZL&ڬa6V0^E c%M#4XA$)C:y?IdV@H-RfIj􉦔֯}CxUܱV+c,꯭q.gVlî.\Y ;e7ŵ4A?i5'r X%Vaz*GK[&e @&'H@rKvcSy:A&Ԅ|9ܧ(0΅T'=^·/qREٺ [r'+gHnp/p44Uwfܤ ؁4CPp/ aAbE| 1Tyns_2!%m1?T~`E XPgZ_PIM RϬF)oMZT$嘟wT6mwZZ4Zro>Ľ'ČaOͻ!>Sy75>9k1C s-N6Gf nGSmq8uMGd&Ww>UAאM!E:Ap)II,1BMV-yk(*X=(JZ\VEI,`IiOXmAd{JЊ,){;(^Y:y۔zdDN/_:@}EHFAŪ=^(+Ϸ Qk@FJ-0#> q}+M0sGeF eR݃j~VN~M̰J儚޶JtPK80=uK% cpkZ&Q!:rjgwfPDGu<[ M!X `ejuz>4esr*y͸N;PwUfqUng3 -{5\ڄJuW\rWjLf KX7'@"gt@R{ t#Z!E颿).,hCܵ~O:!vhZ2(  P(IB՛ 4 Uo{ܢETݡ1P/1uIӎ`MҰl!̃ aJ_ 6Y[di5gܛ8Sɫӆ]DX# YMGVp%MQ6) WB@\E7*nBDNlLСl囲 Pb4fR~[ orwq'.Xk紻#DC MtmRnoR? };@yGS5+DŽNͺJpMXK6 S-jW;-soꩇ>sT/<;ZRټlּ¨R B`=K3aaR lQ^{x}x-5Ncbqg;pc206S8p?i]|=Gצl~Cuhc5oz B>77} b=ЁqCW @܂@^&[T6j tfFYrӒG>B5f=kQY[Cv sIG#i"zYִc%8`r Uq~i[tLā1p7N]סJ ,SlX5IDL(GPH gh(K8Yy07XR*KWS~w`[L:>?P8ч"-TmL0TKbg ώBRXǭLj;;ڐt<:*?+:ڲ#T8d>:g%Qx:xI<^/ #JԬ'Qq$jv4d.aє8Y^ A=ytm%P&`LNxns=YY[.pKV/)Ņ'-h='}Ҋw o)¡W[*TE]쇩(E+ǽ;ε9x3-nKLpsϤ뀮nLA@߂pE=3 -]%M-~;[󷸹7!X [9 伒Ɣ{@]yh!PN7ކQy-q[tv,7 I$V?A_r6j kZհBYGViҠ1KPܥ8,Z&wJUk^V \-BV010 S7̌(t ci ŵ vP B8"F cr3^1 e!/@YKm9릫+)w9Ch;,*v0/cA/rNcw!Ԅf]7l00vXuHu*#l'XrDSݢI!* [TX kkFlCXRޒ-\K%[ԝ]L,q"TR{%ÛIͽI;;ȝ GzF(M& P0LIkfL+&:-"6 Iw cjBl,&IWL8NI(H6;E`'CsB?mɨnK9iHN1%4Z?2_x~|C34ZΩ%_1=Y[#V"lxB3-5fqZl{ܛRXhdc*7a%X`XQ#Dյ~U+oZ:䚘\k_4okp, G ^iKb8~D}+0n\ [SӇ`?Zl )G'e4nkcC#v6f΀ot>}-I=)ӆ;!\%?iψ9~^q\ZyPj{U ?_igqoٷϧɊUv.Ctv'=hW)ܕ:E^/cy|rw Q^(6v_ik203,OCdV'q9ba1Yi??;yJX^mh`"_/߾yOo~yR+o'Vъ}bъOd+8(8ec}Wöԁ 1<y߅y]v%޷ɟv:=ԟ%'i xNe?0M$ĶL7yk)eu?C}fQ~?2Opu[$e*87Bz#@Ufd&m 'V}Eյdj70/qYcˎF|WyNmZ4N.R `1nnH,hjc9'o;&70~`@ WEaeDO@`ϣu됷htVYRz[f,QV5Vz2H[{9-<{xDe_/ї`H_TGShpvmxJŵ1l8fQ35&Ij׉)/(V:s`Ň&,l y3E٣QP1lic6IEPv?A V ?3|TC%>2kԜwk(*Y[d xT%*,Ebh ׆;FX!iRddbL2DȢSOtG3:3;Ka4M&E/>ėeԗ ("( "/qawe"(۞sogRۡnשTW:]u9|uk;fi~[ \ "vz`k/m-Փ@/WߕKNzvI|׎N> -/Tc- 6aLMO3{S;I~\-zymu]'n_s[:ivwy7!~ɻG+YNNX>nwppСL=>sfݓ* GZ3@Wy˭yS Ti|~9lw 7\p0Ob/w y?(@~j;PϞv_{-Y]P QKhE5YԖ,jOLEӍ}z@ Gru~]vXٛf ίYhtYP}CUvkٰtu&Z5iDqKI3k]K9Eȭ<ܓG~(X6 wAO>~݋S!<>ǜX]a<{'^0یϸeH۔K12t^ &g/(] ЏC)WaϞŋ߸OW ֘,5pHJu&E$V'$EkE&%ahddцddQOdѦDOh]gzM|:AK/.8?/g_d^PI&GqcZ߰w2j3lrsص*la'fq{8Gqxe o" u Ji% Ak\_OD0n)rbey޿k9}ds=Mk+9xe0@>Wr/%^ykݻ|-%jV<|~˘Y DrgψD+a qֹV +I+r޸eYEwr99rk 9f4$r-,SKnn "ǿk99AXW?5R29v-Y斎g>׵Ϫ :@dPY_rgψ|ه5|Y8G\>/Sq(n/iFN,SKnna>O`yπ29v,sK3ZZ}w!Z>_O*N'Tg>׵y32d KY8G\>/Sq(n|vH#Q|>y Du-~|̲!73s~sVL-*r!>GΰY?ȩ99 ['cL-%)3ZZ#gQYe'Tg>׵y3}ʷ[W9ZZ-$y[FqUxw-C|>a0b954#a~L%d7eyF~]q0ȱ|~j9>_HI_GFŔvh;'k})JWTxZƂK֚tvT wkc믆j:j5 K%Z?yVB#/ B/6E>REF^VbTxYv<(DF^ bTxY7(DSbx-BDhP"#/r5ƋaWFo ,,ϻJ/+E^-4"f0n*b, ym76n]wP UxY3Xwh1^V :z[de`yUxQ4,m6yWeѠkF^V ]%Z%B"ƭngJ/ d-*AWGo,ϻJ+]DEF^ ]%ZG"yY)3wh1^ ޻u(*b,,ϻJ/+E^-2vg<*Qc|Oܯwu5yYx3Xwh1^V 6z[he`UxY:4(nlסDpf<*byu:D=qչJ_de`yUxY4(m4q{WeѠkq dTxa0]%ZDWeՠnj(5ƋjD=qϻJ_hBwh1^֡ Fo]%ZxB#"wh1^V bTxYF8ۧ;PK9|9J PKx3C-Pictures/10000000000000C1000000D0CCCEE605.png@PNG  IHDR1IDATxu&\ͻX@0'0(SmggKg[ɖ:K>˖%V%RdXlىUtÔ-gzェzfRB(aF>}B_xU A|ktOQ@WX$,)yCTXO]V˶ LAAk8@٨=OEƇj0 QV;HZIsTiԀzHaejxlE BIV=?_J)Nߍ+gҤ4KWE7 wF]˾~Y@DH%7#ϑ?ZlxbߐaژJ^QN4[85~?,+cL݌@r(;9Z\82BSxg8({0PhX!WDJ<\qَIUnb#\䨖&TWS|M}#d i.*BJ3J9@3riBy>y :7% <) P/JmROpII>q mzH*!iDD[ITմ1E#2)0Xl4܏>|cSM.ID,'w% g`y.F" /&XõYgU/g23,Ӵ"5;PcBQʀHX:뵓gJ(Z@< U`@*Pz*H!48Bjd]]jXE(pL<lYT%ʄ!]5բaVWFh8E_Q\L`z!Q4 BG4 ł1Z\"nJ ]Tp^0Bn,XG YSu KJƠ& LCbIiӺ<,w;y(PW6߸5Cy (v6[0 SVk+5 } YB+`l[?9p1W >𣝙:(E4(}R#2ou3`yT ze@ Jܼuˑҫ@8M }*c ~9FLSlepsJ\jտym7\=333kTɯ嫟_0FaXǡ T>nk@-sb%tp}k8Hj瘦HqE@ ev0dlX6H> $:g܋qܴ)#"b,zH2A@Ԭ4,ޛoҵlS 媚Ɂ:KCZbJ g^ ߔ_9D 9&53mƦ'so7~Ƿsd zX2[Ge}PpvLx91kW<x*TWs/揿;ڷ&賌A|s( O Lg8rAQ5f@,-x+7O[`jfz]ͻsKF4.qvU-`1Q2_x9t앦Zw?͈-uyMDbTOeE 4Ď_DAK'Cեj׿؞E]Ga/ sFeUg?v(>̳=}[*#@Z3Zc\564:vwpM-..E&GWl(VqPkH1@A>ek]+֯u+VC/8ƫ{Bp(Y]OVn_ݏ}oz]w;?)`(FH ,a{IC%攡.kC`vT9Q.c⑥k`N~"$H1[1]kkN@r׌t4Vdc s_wAjm\09[bl /5sJkL+C7:A_Kp$4rDsQq$qÚdž{z6v͎_kx=[:zv٦i]Xwlέ:{44 e89uk}=z/e2r݇>/~S׎=;^{m4{hl%6T5|@ 2{H/8gw+1RP&B1 JbB8⊰v)iwUJK3o={vNNZ(/f2.캕ڮ{Fi9?ZCBb :+f;قC~S꯿!l PojC"j@h(6= ,f.2~Ͻ{Q0ɞ3sQQ\O֝?ӠfxG}лjOo7"s×(Z71Dax (lz"bIT %p5J!j3FmߓI`e0D<ߧsQ4$ n82r)* cLQ"USaL9CJujiuFGW:. ~gRtŅ-RO(.S3{9ZA3gɀ36pmjs@H|.9]Vl/g`= ^:1*Å ڠrx(Hm@ <`Ѕ@l ˁ" ,@+)@VvTzJw85|QHSe qp9G4*_<^d>4N&1u/|I53D.MOgnq& jM=ȭY QUd9lՇLr ҡq島<4a-o)l~qM5`3XqU#B$O Xľ A0kFu×=ȡ!q|`5M4 hxZw-,HQِd͔r,@+hpPDI`e?4U~pQDɅ >SiCE]3]i>8Њ;r<V O:<- (ep1yE \%J@T@J- qS (C7c~8BHlpC`$$kW[‹^Y+.^ 2?ﳾ5W\?zgE4PTq,E_Őaʹ\-kkTrL-Xx gx9EɄԑ0@>, L30 ,( /cIngvr/( e@*$ـG6p"y,L QUV=t9zG% mGq8$T\a%8 nh/$u0+*gj0f `\ra 9TAA+:\ ( 7||2sOL|gv)߆Q akW8 LR5}blzZe2cMC.>}{ۻsEGW0 (Z/eQ8A E%TԼTa嵫ZX\:ufqv ]@ 0cA-TU`>Š u@*hpxBA) Zʥ4J asrN`MulE#+^{Yn# +S.@{7D8 8#|H^8j؛oIF*0X#=@`/qiVFW@n5 SO@31*rQmHm]sj9Piak %Z!WzellvɃbҒ Ǝ#nGOϗV\!WPk; bԁ JO҈.8x 浕[+W O5 8VN:m!@fܓ!QY'(T3T/jlOOalJ^v'E!Uϗ6m`W8a҉`HTƌ;_\weA@ϹZ;~ç\993{B/"q7:P`_pwݕQ4{r5Wӫ[Սrii+Q>2﻽cύog*{y]m.=ϼ2a5Qw/ʠX:rz޷Lpork;Wn3GW>||=sm\f8XT."S?5ypٳ3SjT^ٹqW~k󶞿"ܙ.WtReKoؼ~`8齇KD #O/+nަ5C&f}{ 88;ɦBd`gw4fP'~*c7/~1P9C+g7m?L{{#ڃPϖW>ӗ߼⭯<Q3.R{oy]~O Q3/}Kn헾ȥ~wox)TN;E~oSwNM,nҹ?rrnȎ݅M@`&(N0r޺7/^Ugff^{~C_<`zzWr}߱%}rY;[>^s3XM{Ʊ陓za%K:05ٗE^89S; ,*- ?\-IB&mS#LP028O.Y>zm5=p뽛ׯm0v4J q[Vԕ(xn&L^^BB}$f&&T 5 B1~]W[\pm6 bk"ՠdNDbD1 F 놡Skhez(d /Gٙ'kNiX(bJwX5c{ep<'?8T$! @ ?z 뉇)w]u ӧ'+Vw{キ}nMQ/NM>][߽m6: kzz:d7q7w>a=t|M  /w~]- /'&@_p-u-9tPu ȿP:xեW^1Z:h\9Sy;  Wl~|;n~SM̔^ED`u߰O9vW}(n^ bQU'Wn\;q*qh}M=Nmz{j^MDÉ bZ돿uUϘ-XH7rc+ #=t0HTer+Z BW-937;=s=FFmbjnrj(~>3zQ[#cwuss/WWT0;dv7_|ze&E#X&]b?[3^onɷLupb-V_ŞBU}'_%a;v>*?:ju}vV..{Ŋ\33 ?~}kࢾYb>{'/ẽ098;pT¶O (>3w<ȷoUG3 [;t-g^,R؞չnxRVbPE*;AR޳(>C܏qwo*Ξ/{8ofFԢ+CCxk,*,Hzr IFM<^pN:ra/_T]0"9F8.YJp랠PU6mrA 3#KzvA5p0*0|0R,gOs|irTi6IU0R (!+Mȩ@AfY2b.s2B ?'jFÞ)2ye_B^ThB1\m9u]b?GKGOL1ʙ) hX#Bh0 G94Cs` ^nXU^!A *:Lv8!Éᚔd &/@ۖ$1u8g+Xu Xa$K6P&V 03(Yaۺ8 9 FP,Ś\K''OX jp u(da. IJjZkfmVF+ +R%qS]͜И_V@*2p" 6 =#a^s-W}+[@0X4 25M۶\Ma/.J!'@8`B!%Z3UGq9UTa[O@r`7]an;ASC`Aq J ~2(7pzp#xrRŏ^sJYp9tJ+<nUU`8@Tsr:C*L  gh$g@w03uA *u?OYOC)*`Ahy+izgu LlW<[N*1PRh5$qq Ғs0]$H3H# kBœRTM*^cK>Xx/_8 oEE*T)z`R3r~|q"rQ@0/rBtwp#PT ŀ/AJ1KoNK4x#95I.F6L`0XrUN"unex(yuлC'6|l ݿ&tr;*L- 凷ռ\mݟ%#y6@84@϶Fݤ"yHn |{]w܂TD Ɓp 軯[o٨uŀ)Y^\]7Ȋho՟֣/|cd\Ĉ:.ݦ>4r@}\I&u߶pHe_E["1ec4QFّ8nTZXeы-me|woウgj%ϲ*`3/8Ńo[:Nr@ knZu!1&gϼƫx5FU-K(뻍Tqvk?DF ku(ZZKlbLB)JV5JDx: ^\!ܣM>W{D1 ˙{L\w_b2杝 U©٬d9Sm%p5,3$އ R~[ ۄr:d$ېVhk-ځ7aI?uC{xrZ=*WR[ҪhL:.6 ; ѕn ý+ GB*F1 QWPJz~J \t)kb`.\DD#(Æ22D(7_J'EsD7mڀ7]9(aF5=Fm㡘%Y;G86a9|0@3I\vM;b.!1PD1ZLùH3G%lӉF #v!2_GH 1\[nlWq 1J+SJ tYIX5ͱknVvnKy( ǡ. I'.R'&4qAҩ :<&DQwD-|W<2 ;ƟD3!>b`*9rdZ8N;AB q˙t/ÿ9_-La%PS@p;k2P68%). x ٷ2֦epN]1IXEFJDNh\SUfb&[Q;OH"xI&M `ڣ"CG%<&JeGU%1 =Q_u:r܋,\ D](ur9 u'e},u{, iO.[R1@M[v '=qPW^K֘U@mCD؍U 8djM GK2p駢"7J5xleDžK〜;a0ւ149%(S4BihvLn-9[c$)jiZJq XGkHl"؁oie.=DK8*ji&|*!EMJ%$]!A)Zps\,ۆI^ FbgMDQ25A!JnDH(7?wYDqk3!rx\,b'D 2A4S1.',rwl.b8Nԅ0/D,ZrD=F2*cНsDRh%#]Ý'j%j5E9B`F? ?In TC$0T$2i8pG,=#w(%?%/UAb_Loj)? IE6&qD pA'!K@Zs'/J"M̈́qodkr7=bɝ [o'-IOzBFrl'iPI["̕)B[#.xھ%#KN"4搶pK A\OEt2Nze(K\7&Nbg?4݊+jM!=ޒ4?Etb|0M!T %W"؋\ĩD'89G0*ӑ G/MlH܉?|nJKU+L[QhεcޭZF"YgCQ(aCAԞJDO.&h|t%2jf,O?:d+>H{^"62t}yOty8=9߼:ll E/8mbuz<Wܖ$'f;C種m+e %lF,vMs@R GcI0NOPH^FWyHNzE:Jwѩ[/Fo?ưumoDqm7~3F Q3EkG+Y4l/F!DQ3QWdyZG1t7yC4* lvI!R2hM~v@XZZzP9&" !͉F%PׅS I9 JVHjq%]mNI+3OaEE:x E$J;9Gj\+ĩ4vYv:*Zh6c%{3\qZ;ʣv=}e~UTD~*z4/)l zhˆ>!nyQ ,XNb0K4t d. Eၭ3\B1+\qx楯L]ZԪ6XuX5*;qP_뫚f.Փȹ1_$y2U`d4"ϛRUciWኺL/ s(v1) f^fys¯!]jϧ20Hltkx2ta64̙T} 8:˾/_PfH9xٺC]w4l61?UUm䎷I e3ʁw/|S_ꖡ@]s@` W_xW 𽱞 @UFl9}eÑ< zPnpf˯o~]ӻ"s݂ֈx )J*y~q{D*kc4E)JJyoa|~ozj`2jrnbه~׋?̱_ط_}ylt%tgOO>o{ԇ77TWScm>vןѓ]y7yݼ{mϽ{a.EOf8ֻo;ƫOw\Ư$S zThS"Z 5osʅm̓V^xe|q .p+i.ź\x'p ?iЖ\z5uPT02+m9Tݕq w1X{JeXkzΔdh}g&F׭v[Col&j0cP@ ,Vnr  K.i I捫@/=zmחmL*- w|C?~獯pcGx]8|ܽ~=l=o؉UEq+m['.{]b@E/Mhg__wu7O<+w9i8S{ q,`KUkW+չ~nK?[hrpx|h wԊ_} c } _]3<ҋvkb6n}G+VΕݑ+<d?_3RAnҭ'9йg ^7%w6Cn1Ɉ9N\kC9|LcG!4i?=9xAlқ5UV+f=vM93<'r*2xԖL cT^Vk+޴hu3  sƏWsp7+&LI~թW^yu?;̲GY9 .OKOG\HUUsyD5Õg(̽F"=-%ȣp׃9۰i Fl?{VQ}Of&/5W-LN>X3Źy}csw 54!5W=)sRLSOu/<8d61[+Y0յD_8>4Š1,cD$ofF4T _Vs3>Z2Zglčy2fwz>:\OL iCzV8z/t>U0@LݩyO.d^\*^@r "pZԫs˻]RUTu4ED@'w_4' ]׭4A;@  5phtK=,,O /3*'@iQn"LbkՆB 5_d}L9xin3ӾwዋCW9}/>."l]L+V{zVM陙088<8#ȑdL'/f}|HN<,sbJ-ˉ|.,Kx]Ft jXU1S䎃 si"\z}Oi.`?}i^ Fy%Jͳ4(w|ZTy3-]k63l xyzF7<a֑"ܓ!0.e( ^R `| .Yir@<54Xj]yfӀƛ4Ӏx $ZeW(#ys9OAfl&8<_ g*c9 3AjA C<,ȤL@~4D0X|HuU 7ÇR}SA/AÍrcp{!-'8<8 <RhD?r$+Tvaڎ\{뮸czB _1zE^Q2뷝?vĶ GƆ375m0bYqO]QbмwVw=B=xX@ゑ23cMXs2T,A-M ضfƳFS>9A45+)I\>jOfl~x T2RKpyk&HF0j. 6BU"&-w_f`s`VPjUl9?L}k_!fS%e =ř/eXRdRvnoC{}a1E羛 Av,{ܫO>W^$E5Vq0/Xܭ6+6VPUXu`6GajK,cԝF2&Бeg۷הTVUY{y.MA7=a03ƴ}7Иiw6 Ƹ0e^YKLZSؗ/E^TG EX"aꐯ. Cr p?P@Yѻ{[&n/mfVVav )P(""вR8Y92y{,-mD[eM<4M7(*).ᰊ<84o )|x3ZʪΠL l!{R]u% +arXT!cK4r 2 ҄ 3<׹%h,0ƽ^]/?# eGݻvYZ ƈV7R(1Ht1N.Ŝb(>6eSmkR!Ȇ SwKfNiS^/(0POCn]/H"g~ȩӯϜZc3F+Οcwr{;6uw{2Zޓw:~gc佛#ӖrŹi<V}='ɭK^h9rd.BP:8&83[d""=rLˉ=),eGƄcӞ}PV! ƻO0>7rgśc%`C;zg6V ‚q>;w@ :qӫ- a)$I{z(Yxۻֺ{Rdxn`1s+jm WA}ʗܡ'ZJ)˺%p1&9%Y[c~ǯ0?\_lJ',ryC"c(tSP5IMZ% 1$&@b(f{bKbct%7 W!;ᴴ|G&Eu0ZIx=h5=8͟\|^H.@NbKGPHSnuqǛz;OK~d=sy;99sG WnRWm{}住>l*ĩT.u8717=gdCW~ƞ)t>y{4]gFDΕ O>Y̤;݇R4-KoN~' =z=wy`p 6@!͓u'Ѿ\:ܕr3m:+]V>[zқZ4xw[hj ^}8<^+Rbx*ʤKn n9rB̻oX^o^0@+ih4:26}QxKξG$S)cal.B&9vn0cF˞ _Os D$Q;59CJW7+fR8x v'AUL}ʵ'O\_}>]EsOQ1@=(ɕ ^Q6V7".RIÁϢ `O,"%&B kazMhdndGdEDfET4wD;Z]MDrLHSl<9Ҳ@IVr>J hRX"$HMrePÎʚ: i0Lj̮/'?k6=oV=өkbr!0(H`Cɢ jdSB_S/vw(v>0+䂁oj%%卟}ܟ8v0JR}e#hoo'>Q-& 0QԂaXw#z*ރB8yp?ta@F' Rt F(Vɫ ,+O%FBF? w+uAFI ͤ+ U =_:#`ֲt6u݃v t r+0oo-jJ.^eIJr ԉgf!;i9-fsfI $=cǎźۗ8ww'X^^{q0Skz$Ul؇ց?ӻ쟎Y6˅|T ny+&2XSAؤ]|ˏ>=tpʶ rYVd!rIL(114WwB % 8;b!uC3-۷{ѠF<~TgS[VAֻ};ܾ1b*Fv3]T\b[tEL,tzv3uKwsrOr2Xy F}Shmu \^7 pR,OG*%l2uVX7`h^}]s KP_{sCPfHD'oezime{{~3 O[Cbb`}wE9 Yw=.deq &'KZ6'TfeQ4T}pjfG wV_;WOm(hX'sW;,xzpF3L4NthGؚz½١Ǔ4gܹ:#1kj*ExTb# ٴ\]8@Dі #o^# #}:萄yM,g89[( AQ4lbialL/ϡy#Lݥ5p?RD[P(#ܻ;(/]Cȫ_V>Odx/9NvcЂѠDInEGM{Fe]]LP+%2q^U) "QCŨ߸˒d0\OaA!XA0Z:7pxaD`:S-*HrɱiCo2kV~%:3q/-Ś;iVS(H hFl^ɕǧ-@HMefivP7L)WC6.Ɛ2 Dp䙅ϝ2Cg}qMVmɳO?20ʵKn8MX֍XpE dY94I`T]2V`\~H݄Pla?M3XsE1BY6O!(R¹lFRHT,Mhfuc~iLL5d"Cu[pIR1XHMLl9/Ar$ fC]O 4eQ¡ J"d50P,]OSo\vbMXL7lZ )| xJȞhpAFL3ZDyT_A kO@6 CD4F(DG> l(B@VE <=z ! ,DG{;:# 3y,<[ղlև ƺY'AtGK$V *1 m䳹R۵q=A 6s4dڢA!Pa";B*khz=\}X"-[5x,/d BȒnEE}jN H;l\ աKr w全@xuj& @?؆T iܯ 5aZ "e6AZxIVH`q+Ȗ8-Ta8LʉrXFeyj c0 Gh`uF3QuyqKg ʚ ;>dJ`6E ExJ7\_JZ"lB G烙I`0+31a Bqtg;9:xYLN=b(:dJN!O)M&Ł5ey LrHJ! !uAx S^`a\=,eʹs!7 „p/7$ GI"u2LV,_B/2{cLSLY)/xUXZ?_+6m+$ 4Ƙv,b\Zmt7ߨ~{5}0q˭5 QA0!ˀ?pCMډXlӀQ4F-\ՇB$IB.9?Pp\ċJ:򆻞"t. )|8K4#\x \Tj|{laSLg)I 0s0Ŀ32o|㋿SB u Ć\9,@m=== 빕t 42) <[~vJCۓ)vh#aH1 m]yGOd G^7J;V&şՖͱOSo S!XwuEX *8y)*eʴìXM g6w{ukl>xJodjVۆjԳhC&i 4c PzI) R図˿R#놬Lhko*lI68E-d%I+64[B'3` /_mI $6< FрqO&l) 0JP)V6p K)*&_VNn1r2V2=CA4Hn @Zrj5Uu627a]7SInшJ}b(R*CLt}jy%S.̮/E@`4 KK+S9S,-B$ŶW(X#*l5QGe 4RJe4vz0z~^ޏBG;o=wéG?3b>_D ``[FE ֬DKKK?;nO/ɴi(\sFFFG}3Nnnvwu]zMdZ?Qfuic7/LA,{T,z$fO 6Q!~֑ճ\옖?>my\ afW %:*U3fGrJ*% $D^),Oʖ,L!\jCHH l6 +ۍ7cWw{g{Ӯ,"k?zuw 0T$~`\"?wW_n=_Kɦo E^45NLҹV}xޱ~×!=9ɖWo/1&fů|I<' 9EVR݅[ *=R?3 ;D%zg` ;Zv|?IwʶZ(`Skѐ% `>؝u+\,҈f`#iAPiǀavJRR M͊,EC>bxqE|Ibj"~-dȪP҂hMD,/mRά(fF%$D䢔ЎfB`VX= wo@4lHw9rJ: R$+&2J2et$钝:)bȴqI=ǔ?4 OïGzaY,iv8^W7!|\ܫҎWբ|6Io)9IԚX hU­&-!`g׌pj#dMxj|>NBvHe_ݩV+z3$K/pjghHVeQ=[.EXo;JuNœM"*[o,Qaں8f}Yx~2R:oZyoo]DZNRuTٝ}KqH9Z AzDbpC7!Vͧ6W{[݁六+y 1}lrOT}mxOՉQhj뗚9UnjDzȭumpyü%T87A ol/yVk02.]< s2¥5 ?jL[x1Uj{/߽Kn5'U;]>hv#UU]x.|6"RpUQ:uXL[=YplJ?0Y\ k>ѻ4rGvJxa3UЬ3>rޕQ0&JMc.nDTCwVPY뭴xK9jեi+{ݮ2$v%@僀Hw ڞ9VsODV+h_!}9y Xmk:|.Qd;k7Zgݖg;PlM U+#+fQѵ[M׮Tʖ`UuDNhoqSAQWzXQCa ]CD{*~:U^QU;Ǔުk,Ply C+YiP"RifvEF}B&' ߐMcԝߊKdĘkFUڍjJS"UHְyy,dv HTm~n]Rh8|~يY ɺnJ^TLE(48־wbKbvm?g}e@á=h[^Sm9)OtU).Tk]^j-WAlj .Ǝ;2?kU*-swd(z V40_?UQmlT{,TDxa](vJ,4 )- BG䫈'_{RGMI϶-U{[C:=!޷u|FoPmH^YO5UKT}1&=}jwEoK~8}i`7C Sݯg=x԰ s~-Opړ7muwXvN͆wV\/]Sb- n|[,j _MtO36~Fa۵owh;V_kݻ!φz]ݤwD%R+~PNHbe<<W[]Swy}4^0CaW6˩޻׆סML?j0_W?k7$m !_ 9xB QɖꅪagDҳ)40;74|W!" PVd;j(&vzdǞin'ާ3ww[ v}gAtEnĆܾ[P}O5mdPk5ndʎ]kF[>w~ *K'΍ZNī4D~2$Άᳳ{M6x#=a>`u_dPTj LJ0WlVfT]^\{lm[zA)b'gj4:t567m+OGi,oˎݫ9[ͦYq*DnVm/a>Ʃr6Lz*u&Ԓ8[#?Ev6ZU)R7*%|p660|?w~^Ǒw2`ߛʒNm!TzYDڿ?RTF}]vg5զJS;? - mOM;~rIeJA]a([fqt|Б QT9I`y&8`j,R;oݞ'D"ǭ=uk> 0`Y>?ʸh机?mnٔ+-SD'waQ2 2L"F)P@sXLYdYWeY71WLfpR:eh)BP PfWUFs*kfVw|᯿+:# "6qG2\!D 80JGax$ZG?_7?eƯj @n rbP>wgrY5uYƄPސ{Ohj7Ѹ% @(tC!Ehy<-Op/oA;$[㽓鼟'H`F[mWg;_/S9Mk=v}_/Iب瘶 04 Y Rp!4@P@@K PǏMl:5@f!j9#sEPTv1EZd7А9@Fw},ɤ7Z{hjnzfzzJ655g?J^ aSz/] GvMO,=ȩޕdx.\8 pFڤE5:xsq4ePۖ?UTltg燱LIpDD=ڻ,jI0x%ݓ,q QҬh,8{%U1E8r1{:f)ap}DU R:;$o"Ve-tzw:2O9؛[Mح _8ʯ|K7L~yꙑ_~kOqo}w!U0ˊ}oݵE ! ť@~?Sw_Me~W_|5֕[7 ^X^]yww_~UӴc'xbldSFJvtȀ.wgU[RB؞9o7S`jT͈2Te9"5/J1yi*$>6@&SM&ŗuY5\jjz~v,l|Tv`$ihx(8vTBV H4a `-RAR'S\-#~!LE!E-\Xʬn|@ ?/ɗ2zƟ}oN;׿/ݽr(/B*ȱxO.^Eg^?+o][w}̱PPj ?|ᣧO`yv}[W$◿?9sX8@ȿ[]a=@:uc݀;Uim+ԕ|81#CGȋP{gyM$P`#z+c%fFSwt2)TU(S6ѦpdRE臟87~W`ie_!,#ZQ6`m#ÔmU E.a@^ZNW~K/a2<ɲ2Ői9;~dSM0'ts^f )v!Q(j5hnkI1wۖh3P#X$ #獇U˖+CWVZV{NXn ۜ8Ėgmhu{aX|<xjpTaf|ȅx*5)=\Jk:h:OZ.=8C]<|+-ΏL.]IXf? ҝex)sa/ +2vI"s]'v/?b0]l;->sky%6rDЛ/g?&o^$"lhl;ÓMRSz>ۿ驏?}uc<6}lIJBn/8a6*x;gΝ~/r)k)ns>| K4)kZ> rۚi?Ee\ WuG!]'Klb\1]ʐr;.ARQp sS@■B-xq?Ev{'C"q[,,YYj= OY-nZgGt9F/<*"%~w|?,?wV/Żڗ'?G٧0I>Y(TwKJN߿9xb"M.GqNMqlX0נQp(~|{a~%OA䔻k ?0}M-LϮ})P4}'ť$_?G I dNG[6!bdSKj6g5Ug'=g**h?qXz7mÃ'RVIѭDUH 7U]3uWA f9ld7DB,I9Їh}qcezt(uqu .B(Ѩ)Wp% B>W0Dp, Sx:PG) Ӏ!+f,O5SJ9uYI a,ҹQ +#/ l9c{O16l0gW]H&g X#5U,@4A |,*2YP3 r1Dd}WFr\ uDJ+A*/oUxZ+wef/6wK@ 3] ,S U4L !ǀqC:1 =aH<:0i:8hn¿sh_[on\[&"Jwxgj]G.p-|ǧ"rpܤ~MOBf2< wX@N&zb` VzAsBԫa4ZbAC9֠iɐX/+R+}4B@Q!yWb7,wqi܈c"c/n<2d4R)< lT`!3=Lzʐ^IC-UU02aXYz-d7A 8$dJHN&rTL]]}EZhh'6h=ԈB;۳ }I',nJKG1)c <R֔,\bl35Yt~0"\ְ+)JusPT4 l]UAb|'9hB epl+*pU񐺆al,:܁L+hq, PQt^Tӂ!u Ԅ"X!(Aci9 $*ᢴG\Kh'] pȝjnr蓤f\# B524 8w-rXRiފ &Aƺr^W3T.ผ4!q tnW`I¥w=)$-ߣ9!h{jrP8icce#"\9@FZCڛ_Jj<3Hc1_-S:{=ZI5K' E)*4]%(rkx\vp=LMQYI!\:YG0ɒp c|P7ض`XA`nZD&aG;ZΡDD?# nd*b(ȸ,nsX܄aH6PUVtLp+A:`QXD=D2 bzYH+DF1! {3ri߁gJ6;11qZ,&k`xw^. 撩;_:"? F5?q)@|*r{"cJEd v{cQ:f43e&&M3 08oK>_ZLڴacaZC$ST) KI6ww* !d4bM\aށ]ZY^XZAME >+jEQ\]]͔P4i.} fgvw  çjSfaxiw6B70tuεX,q|wpq??.ݽ? 3\:RDl?Ϟ0~.XS%7^;[W._D}192:=̇SFL2\_vfBsrl} craPkHݻwc-}= 7I\uO~JYέ'WԔݻ-VHFa~~{JYIN=dRk1K@A\HVpy n6(hg H,!ar[ZFDGGiǏ^l;b)Vtr2e/NEU@_N(CGN h"U4wئ(t6y Ne]J}f!>?1{2Q,;.Ь"ˑϞ:–yVfMN8tȹ#Fz!TloO߇x.puvuevuI=w}9w7˶ a\JAN%3Lӟ+7uѦW8Z](66Ǟ9ޫ:y[=v&`ﮒLSg^jd?yS򉃏/bˊztȕm&p`Opktޡ>Mzڊ" pϙɇTL$۹oՋHɡc8F|gjn64Cx~lwws~p ϝHjF'OHJYaSH*gP|]I,WiR $ˣa9y 2m:D Bv iD:mŤ.ߝqu^-nxfgH5p^]ӭ[;߁OE)8We @GC `f7#wo2 i8pw[G3ޣ`jHR&̮X3J8::qwP!{x‘MЭÇ޺mxX`"y/+~Rr6bX{{{ׯd& 85_Qpfmmt灡V:޸{`ࠛҨy8ha9 RȮ$Ӈ9_B2:~tW/{FĖVCw DJ/pϠQ\Il.}GKTHr|KзmmmX\ZZ٦4źD4@n?y {5s &00Tƴ-6-๳÷sO x{p.Լ esiG</M2ɻvwtzcma)i188ij?(Twr[dyGd qe4}ua @P>{8_P .h2 !]zO?)7G? KMSig`nf15[`^A@J9|>KEp{@n36G{9H7b/A2USN;+ 6'O(< Y"S\K^6Ӆ C7YZP(MxO'j9\P=|pr~6$M=dFpG?Lrm&#Ǐ_Ngf ZY_>x`:8,DldY޳gϽ㐄. eébcXۻ[ IZe\@ITڵrwZnٲJ$H5LY3%9&" Xz;=$xdImaC67ܻv+̋ YSi% LחV\.bY}Yh~$ 2M[4U$?>z.J!@0qǏ?kflEsLS1O%\_ayzl >rrbfD[= |j2 Xp>'!Em4tx">ӱfT&$~GNuN̄C!XZbj+sUq\%05L ;qȅ rN Z Ex<7tu/l(Œmg6|;=)T7ԋI"dj x\ %QP╦ɑ i$7yUa2)pkDyzl!Fy4rxw LHf&ROd~ҏ<oВE$ueu荶`8;(q+ x Xz10i3 r[i%wNwu^'W7,#DS9\DunpE,ɢlK\\L !#):l* ÀtJ`_qP ƢmQ!T6uKJ#Le*HJX0Z*9jP\cCZ 8h) @b;N,>Б3%8ϰ2qfI]5nueW ҴG޻ S7..&pǥ5$\Ȇ& qǚ!mFg`,Dc2czEP ] 91xnA|װpBgqEnq|qλL87 + Sen-D -CI.F,a𖐰`Auӂ/X @ 7GPWOI ;I!j)EfטH@tkD %AeruaْuF3cn*.:V[j\@6;)PyҎ+ySeOH\"xKEdh;Ewew o WDGs)h" 7P $XǢDH().kx]`2kFcJau}hj(ڂXEw`w{r8R%x9%P4%9(\ "Śe nT >` ./4)0s~޺~cf|&Y˕uƺ%(W X.},j$rMyA]UqMtXLs`w?'rr)I鲦xʐJ3=_inxbLñx__%FdfY , +- kHLi%Z4%@won@b9z4 Ta(ʁ&0bv q_ w*O c;4ϢWm7l$g&U$Hu8 ܖ P!FO!t w+bb<6 'MZ [zr#ݶP%qٲ#Oj=*ہ`+IBbse>T æÖSN"uh.MA׆HOWxyZij Y-Ȫ40Ïjy0s#o_Bg΂Mݸ x$Uʏ~).y^Ё}{_<:Ͽzh6:u$^~7i,o4%X3oXȢI׺ hcXvm℣Dž=>,xQ'*̵wt.~jK~& 4D!'>SB< Re_ӏzృIߠxFUlacab!8Lg=H3HUվ0`3ᐣYII+Qߑ:H]Jw:(}>ER4"9r`hﻫ˧{e0Y&3"2{{e#oHCIUѲ:mM9 BRRּ#ł1+LM s#ȕ*"|H"+/8wd\wo+` x'[H47Z150Kʕ/aWM4MJsɚS,TUɑzg҃Ybw\[8g-eE " v$ܻC:%UMO ɻ$i^(fgge6e# UExzA[VX651>p˖M?Ѳx~}#@fђtU#~&p(jC]sr/5+%` n#Z!T A.v16O<({_sKd]XaLMEG,ZmFEũ~}x#F 5!iLn贙2AŸtfLK1j}T4A\`iϝ9[:9T"z$s"/۸w444siYn^ƣFќoiju"ڼy,\%\~o ܰl(IK/&4>nRhj|p#𲘤534F_c@cTMc~+b:dx[g.]ґ22V)>yǠ]17 QR" iQm^w;jbPbDlxqU j!5248|=׮\5q[/nZ4W(% W:" ;WoޥY"Z'ARolJ[*Rc!O=, ,Lc%54 6dPH'82`"UE&ī=UV"H!MZlT4 `_G6\ l: ͐$bC}(8vqs]ca61%N!%$=" ("ȩ]d "FڬDy~d:IM?FmǼ)XTu]WyE% B]Z/ ,Uy,+DW5ºH IX"jnTVgF SIE4Dd9O~?HY V$#.rFMQ#JOml"nj(0 +G ˲kq\MaOض0qtNꦅDX$Y)$u+%)ىYٸ]J`bC`ffL-#ʓ^IP 08ੲ dZDb#./x$o@ b%2xD7\q0."(Xx\ E Msg*ĸ1D^9XNLLi-0w#UI};!M#`"#Dm14A;y/vBX(X@hwFȕ FcJ)(bXUr@UrB"$8K ' )!$1R"9d;ۅ.LD1hI6NLˆuE~ bqp /,Sr gK!πN,]i"YXDvFi&(npL dՀ|N]Z"AXM!@[.8[=b~Oq33eyĈԏ烠qp o6q%S Nf9 @ QyNH.QFB'L)}97:@X`Ab7#(Fb˗q3AV">O[ q=2f 5x2% 1S&X|W W­r<,\ M hyVDO@C' #Wl)ao^ B1,PXqT'{Z2So'Ѡ'"l{SㆢٸL KF-}u9CsC.ȈBf*M+,qTۊo`D}Ac !/+ ϕI>PSxVW}Xc(kJ_ %Lf (o0C`u6Vd|58BANU/F{E 2S4B1b=C=\>䴿*gȗq`qT\)ܮDY HM\kPx7>Ɇg並FfCs̕@\ %Sq@%bꩱ*jp(C>$ɹ:ʇB,4=\3ŹbY$%..<+o)@Zު?f"0TH@U;K]!F4K|b@=GޯdQxUoTn5!O WAC~Ú'gKWu!. @Ĩn^flTPQr 2FUZr/n苰V(p5h5$*kΗö]:PUfީ3!x9[{(!& T`Ifޫ(oJ0nxQpaQ2:nAqcD``5Y#_U2 > qVP0"s6zw,}+D9.@4aSx`90߇k̗×buC!+$4Xf*g,[pCShaZ M%ri/e5O]{a/ *(:>ZN@'|Y2mvHo1 YU{F̌_lK41J*N1d*Q%xc~%d5>fӭDBA9OVЦk/wT3_B>| !ɲB$U4e_"4jUUFT =WW3,qALM{D9J1uO62A 1()]x68v˕2!Ն)qi\ȡE 3d9h^);@aQ`I+߬ t4Ǘ| vDx+2#ʐB}l Ղ2=I$P,2|>+1yvcCT,%/\Fԫ2N`!ʼn)3*@h4Hj JaЕ|q,p;65*^ɐS5on!!- 8D1%Pgw ,{bntYS4,.ZtǞ`dg'96VI Xy?%Po4HR4I"8m47$cMsu|'FggD)Q$YXr!gY-QwC"^~`1 *(ph,F$IvRcL&dEpp2HPik8k\n=o}_H0 sw~ |~̠ya;Vac[qv g=?63ㄚrn잁'?.8%`';9:na d䲹X4+*XvQ`.]nOkSLjʥҋgvxȴ#^>ʙ3ꯥi1x,RKMV/mWp*Q4^G}S~/9²w9~wRS=(؇E2l {Ν뗯jhj~lՊKۚ7m\TDӴk77;ȡ#Ϯ[[=ͨj$ck]7"[ر(ڔ]ۗ.Zzwoۿ9Cxk7~O!uQYUV ].^7O[co}[(.[vj|ص׾Kaq2?խAa3.{/7"_ 2%ըam+Y.&0SiSo`֘2G&J< vCMy#6&9\}R!NF$u/{/njju.a=>:f*W$yFU-/TU+Q)*ECtU%7nپ~{{{HI橮$n}7l7e.X7UX !L$Bzώ'v-Zm- H,| ̤g}`kr{D1QP#W|pjĕ.ܰtM霫 :$\-=wC10V,cxN0u=ņ&u_o~koE:tW=g7h_? h$ 1SjH4z̼۹XK׬zx֭[>OѡEw5w/m޷@ mٿ <35ƲlGlxd^܅djӦMQ$vݻ?::>;L-?r0`^ߒ^irBxf>l--A ኮ=V0vf{3tDͧ`ULp%G!2N::* /o7" #_ry3S7o(6tOw AHgozl+ONRSo%z f3><}TdرzO'F5z/~=DF蝷n󸨪W\HOL#:q764]8)+l\N@\IgZ\=V썭o[4wЌ=_nٴIFc^"*ױQhU/e!B7 m\<Ʃ?rv0p\6$pp[IgS]q$@x zowQpD0bUX6`컪\GsLS3XB$x줩kydÆa`3G_S:ʛ_Z/?!׷rC Bܥr$+EUna{QeENapo/ Cתemz0t~4ԬP4[kO`̼l5hDib1b~wjy|>%pN%)GӈBPrUTkUװ{m޷d9\Qe8S a!AxP (pQKz6kLKb'Dθj@ U&j8IU`B@Њ|Pa_ԸѰW8I. C^#+)Zz1_)*Rka]eelCүd}Aca/_nZqx~l`+ɳy|GeC)-O(B/P ˡR[z<&H- B4LJ*X,t}!>qNQ9 ' VA\ic ᝌ|"X< i3>hRK]" &ԏ~!͇Qԑ9LF6cHqsy ,lYcٕ'1ى[¯ k?<SqpyUMUC_^SgzoJs^4j~5[;(Iӥ9Ye|\9 e0W@Bmҷ2;\ kGARp?6gU*⧊Y 3уWj\$@j""'|epA #ʬlJά/Q//؁Epr0J!ɝ,@{T -C4n/K0ThVp+~ rl@6OFҢj.n/Yju56Kl>y}n5=aQUBӖ@=Ux!.L\à :|G,o(5.4k}~lw&!Te#葯^SqCZ6հl˖ܑKR Ӱ7U՜rwZOQQυo FXfխ'pؗ5L|B.AUnx4)$ ?I&G}ԡ0!tAU_\cC| UTxܨ˚}UFM/`FKkھ ު)a xֽ֟ƸЪ)xS <ÕܠeCNϮ0yQ9BkiZI3XvJ?/_` ?Þjg9Bg93*PATwSOi6a+sͮXꮱ5qzBx,q^EɨQrAG7 W߻9ev0ɨ4G`uXq!#Oyy(=xj7*yX&[̗]M" ?H0Ɗ«p+LvO/++ } j0&먥z8o(*֯&{xyn)Mʺ߅kIC5?=٫D: ƪWX- $)G|,8Gm[y.vL3i("%-D b,rd,[6"I U)*$uLrr|2t}`qLCg;c]R O*6RP>le V [G@FZgpE>$Hsu2U.#>v][iI&4FۋnRā-W4_hz.GSwGlٽ}Ѳto_`mY RS$!U!\ITD4W4i9*Z=w Bu9/bT I$Bdd9'eI ֤5(bI&âؑMS$EB,km'b 7 /0fi!|eze%ᣗoTE^L)Anߢr˂"%5A^\-Ѹ upD3䰳b,PQ"5u5+/onC 7#)"2M+vrĵ:p(e߿k[97;;>u?( r^Y{x 3a9e͑ F6;صmώ,>K|FVtttD"}m:N5mkok#ܼymd a'/?00V/ɏƧ3|ZR{_Ʌ|K/=w;??̌Suȫrn^c13 ;q1;Ulf:;G`q zf>]T ȯnP#bNR*45L&ܵqDh]ھW៪K 2eJ0|~ TT߱G#d5+0Hbs+bL$Er.`fQ9>o  ׮5U5\Y&rq%iǮIbx/Iq- "y;_;kWwn}_?/Ծ2ܻoSz;?ۿ;okXޱ;:961vбhcsѵmuw?9s7Z}w)$80lRS謣zP@(ȃSiU3[j^CR_i-p`8xBCbfc}]csGKM8Y)\E'60!b#-# qT*а#elb^70Y8u][/޻yV2͋g~{-%O>)  H@H$æB? К#±2m]@x*RD  7y8ΰx^eST]TOZͶiѭc ٮۦ$!={MX}%54ۓ-&Ud3쮘&ljhAm.m!6lq T.GV4VK*H&]?6 -.I7s $@VfҙMg"@qyDCIB:YR]*C8Mi?bO'\h*Dh$? " U&Ip=eaR@\-b1$IEeJѢe@B@U2eJ[-(7P⡋ Me'A4\2CA+?]kI4@Se90)F`HV"nL ; V&0 e-R,=cb(ʶc.I ;ƮAro$?(0EI5)x4#DR#$(!a*$Q)3qU nȡ F\1jEd@ .B]bᩋp̌aP"C\45b@ )ƍ`]9Qt(ƶ,rЅ݈23lGB7/8I*i"? ӥ/h YU摉iBdOJ`.o=СI*'t4OJ:ŇlQ.W(L99;skms7uKF/^6 ņHlB>J!$dh`'T ȩnI, e Yt`fyE 5ik!98[ݐ5Af 233:,FҸt4A$03|okH"-b92,p8lvX2*%3 D" hir^B$+É(HUUfj00AZEXzA\l=zͽwwH=[rc+(:dsp@b6BКm]f:ț0aƾ٩4.eSϼ۟G R(7&{z3O+E.u E Gc#Cwnw51r)K(d"C{Uàr0aWyjaP#9 QK 3)Ezԁ0#=tӢR`l*Lm"lۊ$pYCeXr`ah0hG;y!mVeyQ oz'n6\ţo\98439"[6lC4Pj BZ:.a KSg"Drٮ-[X}^\'m:qĦ=;[~1HcS/>ԉ(lA\q#c+VnAP[wofg4ϟbOC kVٹy/H' &]Yru:f LNٖi…+:o|ݻw-\kʕ+{zz&SS1{0A1L.OxZ8{rhtDk\Ivd2 @ eӛ_q^&#=s܂ ͛[3g1DUYp~Þe+7—<:yv4ūڕks3^Uzansگߺ9gVoY/EztOgfV\7=55000>:2== U/X29>G]$Lr!V=Em5)G1U-93?{N5deu?wg[יWLgKC.'咜~O$E`3]\>g 3~CЧ $"A⳰s@3 fgH'fHLuܺ~sՂMΞ_lE"Y߱də*8ӏo;}}O.[ڑy}s'"ޱoO^ۦڛΙH"6wyO˷o޼X۷ok?/a׃qgy@ |٧>:QKkӧ`Q.Xi㖱9meO~-YqơޞnP_| nX*Ţ ex`AŠHHs/`vJ1[|Fϟ=;88?Yߴz놮 .] zSjlr&==?3ϬXui:7>1qw>sPDQaͦf3%/ hjtohg#gPщ3'N߿ؑ< K'ϸtq߼z7t_wy_x@]]ݽ19S3/>Ƣ7\%L]Om$Oˑ2H\s;̉OsdJ۾{ߓ*+y=bD2=1> A*h)#ŵ152}XW0<2!|t`荁_hLD2@ B3<IT,CL.Y3HKŒHWU:W9(xXTl x Ըɔja>ds)\Ŕ'5i D,ͯ|{oR y񄰸?⊩h Zuɕ-|1Mj**="ĖJ=*Dc}csٳgag)A690KW//;.XdѢEo~rtgsi`^it=o[r"Cu爄v 'P`|"bi$u.G<`?X(;1m;%9UzH@I1g,". !VR"ѥ qU5ba'G>ך" nKsCn74gGZU%)rTn]bY-4B>NPH o"(jDb//@l>ɧN~e$sQ u6s-7_z;/ dggg%ׯ^[L&7n&&@,EΆ榭[fO5.m43> (g;w.θ577۶ 裡3O= x˱_0guٙN_y~KW'@-]tUo~Cqcӳ'R szx uzG,L': dD\A!v,r0G d"jMfBa优~Y$ReorX1V' %x%6~̜XC2jf,$<(X=}<'|ܵ0/:t:=88d0===S-mx}{ill ?p;frC}C遡A5XQQ5M[7am[=rB7$O_<;Էc&;?<:cD[\vƦ&9Tرk'<r$tohPCԝNf ( #@ " P s*VaE.`׮NvclŋQF/b?wIDtl qm5ϱdkѱ+LX4%66iN$A(FZM5o+F^ԅ(S'Oʫ]sbZ8Ù\akVx%$MϤG'F'FGkۗ9`ؠ x$ADxի.^)N$)6?aݹsرaGe΃+3,RI"?0$TЗ.YoQ'x͛7wݹx6/]hh&ݿwy?w.UuT,mk蠬_ke-&wyb洬sG7,["6&:]QDHC4qR]gffҙ|[# uƆ* 1U}[#* Ѥ} cf@igOL=]TIAVWWhޢKWSǏ~DzLv\&sM"arr"@πe}̢Osjjӟڼc;O&$AxM[:Ri~kfj0훷^8iFp3SOy lfblڥwzz-Nt"xj&ᄊy^#*10*-#"]Oo\;ʌPF71AxÁ#aػ^z 6/j#uTE.";?;p˜/}W/_Ik O/{jWQL딟X˛=lo1M1`鮅 =L}DD"x-a>q0 {Olr C {*X;)@*ҰČRʥn7`|QX,egd#dd])rš('J5z4eViI,/ȹ:Ìr!&f*"g K p@/Hǭ1 Z#72|QhK_UM6֥[/SX!Vۍ Kӧҍ ҼL6̬'Ji_JػuܰFX[+R-?/-kA,JIT]ǟfs&hl|9uv~>Tunrz2/1,{<ψQ6_a;ƹH?JZT}>?napd"!3ٓ-nZe_8' "2ķ}e<պ>mkwIENDB`PK)pPKx3C-Pictures/10000000000000BF000000E4F0C10A37.png@PNG  IHDRFbeQIDATx|W%u&m<\or]=Ax0 Ō&P̓P"G !4ڛכcgnϭ}<۬[{df%/ØGË)Z%-y|O~!^'O|wYo\;ϟ[b*gbG/=&}|=u'g?95A\?8O}[~Hӧ=y4Rr9,F?n{|'ò"#_֓K.[Ị7 }O[rp=ybo|/rvYfVOLgOc#{[dGGciՇ'g>}_x:?=>킏~F֞X%HO5o@,F|zhҟ Z 㓟jM''ɟ.Dc4:tp34p!$5tZĜ-5tV֧Uk,z)Vv@% Fb7Q츞v9'MóCz*/a[ԣhb1t,F)C.rnO+#$ltY sx!ӞQ(Ah 줔|. NF2j܍[Z*=tq}N)8qiw'mc3 ˬyG7%[㩸=ClvGլ־C@W:U0Atϛ0fº/5n Ft*iژtӓQ:dJ |V5h¼6|}M5LYMhG{R' H( (TNUEظ*o<|׉vB-Nykn+޼ѽZe^T`pԀuY֕l"@pa!XCs$>@6=S#q$g8K9 5LC61%1B2-.>rϭ=x8{aV'7KxGX~;'USYo'A5v >pyٗj׈zg?/x7?xDyqܿK0ӍKw}':?x7o9?~|WF?́ spT CZȼTὪޭv̴}HE xQ= T#TaYWWuF "m75Hդt (\+7B^̆͝^ z ˽~?}M)oseR˃dﭏv}kԣRu_@3\m ^x[,F>rO{8 !TFd~wj8Q S9whSs `(ѯ~ڭ&0ZL>4,9ˊ|NE[=+O{!-f)kgJ~R9؅YPixFI)FUةTy.&E Pڑ* ,0\ LISLHܯ/]՝rAe 4x6_vz}06k?LPz`U[ A =( Qtj5۔޼9~_N{ 3oVY*3ANBi&:$S+,%c=MÃ˯_x9!T`+piTRf)x[n\>fI\Fݡ@C#ȭT! %k]ՊFX;(Oa[ wB ;p0R70ߟNa }2S@R޽} o,ɷBY4 @GG_v'o:>%m8G>9=9e)K>~z׾ޏ~*g@B ff4Ց`B4r: (!5U1)f%n PXp9i]f5*)v[΄P@WYBj !u)[$l,(dvf3o<1't4e.T$w>PO2|xbTT]w4sDN+k3x:ڻƵNihp_zm:H ` ;g&2C2%JC;{;Q/|XJV:&~;_h! [?u7ݘ{J/:17wf-A+q,k``A rj*x#m$`D@ )f9Jr=ˍڲ4A%ʵ$WC* .N=*%O:-un6(4Y2͛71|Rg\'섌l%&UaUdlyi|>ɽ`9 B h+{ý-oZh8Lp8;•nR~~Y9 nK=vL}` % )z \R7f8 [%Ҧ0 \ 8tZ861JEaܦ$LQT& 5颺sd x0.`#ٟES}Y #3^d@ FGM6)g[Q T;%)@,%>Ab2"£^fDgx$g8vn@ WJ|=5@܈q;n=7RFnwځr L2iN4;35.܌Gd|9f& 08BdA*3t_*haY{`Wubw>v=f`6[{x<(YUUeReD2oFIK6Ɍͧu>qʜ"6^XGh l,jsX HylZL'Ʋ'> WOG{e!|K05 %m>R0T"#r=oR/pFbSV6c$`(e nIc`0)w3bAydM@42 Jֺ6(q8ݙ瞽긾&l/2iVyecmQRi]!L8fQf @KWܶQ0[#R`EhI1O!*y8.q]lvGpq:1rh] 'QuVY]U4ʹPGI3JY9M&,ɋY!6p MY}I.ܾʪ>ɏS`٢(g/7ReYƃuGBr&UTn\]aKҍ=%@eG1A4@ct? \6ot$ Q5)+r@sPH99vxa NjB4Pbb}*)XO-E)HЌ))͑~  R2Em1l0f+ZIo Tόcx(m#׫AԌN[[ncIu|T tp]~Rw]Efi]UtZ@GFyrOOdRI$kZ6(]+r A)|>B*EGK.06 ESZt%d. BLbFڹ@~)ia! !Jcs6 )ɦ֣"h<)UDMvG 99gv;@E@gdS#r9( Q9t{};_ :6o썦M;3#c:U%0`IEr/ ʑ6nϴCz^>B^?Wq4% (Rh, 3 8ڹ1_yCP&}`R Mv1_{Awn=`8Wl|  ;FS- X.Z䅌'(1AOՊ4 {CE`@a$$I3 5hGSI͇'ןw'RT]ߺj^8?ңyqI!-=upu v@bX5Oi׺#^a-6t"-*4, &-VQ`vd'=ﲶ+!<.;-5­Dc BFwoU6K*ŵeS]QJ`Bn`U*WZZ *2#1FSfe;:?\ȊϮڅ&=>wfKkM^ʥn*'Y^4!x'F,VҺM(":.`,,ddT ) V E9{_ v[qiQCt.N!/]aVi`MyOxx[9Vi{fdayr:CD3@_.y!y-4ApcaG6j41D/Z,GvXN0Lңw2 |f|:s8eldiklk+VBCtI&dfed1jxeȕP2JS"Xy*@^w|˯>oe s\&)T_ٹ(= ! UL(r!0$`⻣Z3  jC@bv ڜո,>/:W@<76~9u|E/hJm*ݨwE5OȞ5 wش0kAoQϥrɴ4|?)T:\nx!5"KF&ndUY:{[γ5tKV Aʒ#6o"Ex>4QنMt:إ-ôEV.LX6?8ݿ j4:mxG2r^* rGjՈ-*R!\VR#>dB)X- `1@"{q]t 0Q7䣛o|CƗ&1&yƯɽ +&+ TbNA4/.v[YjHu<v J*L{m`@Q-)_T:_VtEav pJ@ 9RCP[!QmZDU춚W*1Nk/_g 7*PRIjH|0dubb)"lA?9=Ut}ej0[`=v$7ҴBi;Mv9U>Jb|c{aG JG|' 5W?m@Fj_(+4j^ ufU\<͓L16RS7M*_xki['&V7偘OX* [orۆkaϞkA8htaSAj7Ts]7VX|KR -(Lc>PH^䡗w{B 8W[wN^-f5JYЋ@ȒƱJN*s ЮTمvq~sY^Zu5wg#Egl % R1V6g8PGrٱ]KH)3}R/>¶QՐ" p1%X:E80#N')1ߊ!Ȇ7 is[~k2͎lOKQ91$mˠʳY_߂ ;¦I0U ,YZdV rXݍK/ĭwHg8[(0!h{ӉRMKkTb4R -@pL`S 63l@/gIRX"JSQ? ZlDjиn;>9:4%>EI6"/sϳ jǠ$]6dVK!Y[RZ2tK<W.9[e9%q@4 e`ĶJDUHKm!2sW?63.?֖|r0@H 20&~úCT at"‹ynH6?pk2=Ǡ2{ R8,WEpr6:*Fդ(δI#ij;$<ܽH@*ۈb:`ZOZ +i[%kbJbqP0J8ϡNQd[˫WvW-9Ou}WJL"8(fcxus d;뫊%s7׷-,#B;8Lne\X1z'LԲK?=Ha9_kZgB(@!Z>r<7 k"O$x>̻MՈWBԝLŶaL9uPGIz#0--Ϛx7)26X uYL*z繿Իv,%x˫/5D;lj]CL򬚍 f'G\}t:k*8`ZKPPC-PؕYV *&RډH0 f:~µd*lQD/PY5eEK>垱`3YS%Е(=B`I:0ZJ}A^䈦n6t Eɖ2\X/͹2t/igDI{7VRIѽܕ!>wÓxrY52TP2WtPcwVX 葻,*ѣ3ohtCQ4l]zI.i>P;4⍗72GUlýw|W:llmn;r::e޺sUBZVwr:jbc;lruɳOK@K D ʗ~~^>wQntJN@ wrzĐZ6*oBz"'D[(jفHGQcR@49TTӕϙ"vѥWdzA"̲1jZth/]V$:-0ZZ¦9]Wb;­֕WȉRj4OOG{'Mj\zW˲pb6yș";O7\&W:Uyh{7 UQ 8m -f嫞wXMIm` -1R'oi*,cP`~8j8M Cl &dvx/}?箌f'7O^9~YMY_ݯ/o~|rxTZ6thOsΔk;v?Pxۖx% d)@̗32LCߵ mf@S'hmLr;BL]))]KxqmD%>7 N+ ]ʖ&BM3&:WBWHQ$I޿zShdgY~bM=BGt tlBJYUrc+rw)>ʓרեRkՕ~'j}U7{zuN㻃N'Ow>G׏ݮ [=:T@{#[y=O>|/{r,*P ,b3@1s q {_?߯- j0OVK"kv'2iCqew6$̙$y dkQV\Xv 4:. \5,glV9rv],t.|[[PV)@`u`i# vQ \yjrcAkNPh̀Pj$R59c[O>yr&>}o^6![߽-ٟkۛ,x޼]c&˽/KJ ("mdnE؃|ݻriuu5A+Yv^[{cᬇmb΅l Fn~r_o+6_p2{8Bn͛TxÃxZ h܊bu *@*)@vEɐ'̮=A-Z$0 e56 %/ U-}$ }P(-BҴSѪ)opizͱs2oE-4df.້p hapm@rgz<8/Mt!.^yiw^b~g}|s K֖zR6Go>+Y޿qUmBB|GܩdVRQw_v7nܸ|QIU[l[_gopvjbl F&sCW9?:BY)/6e>`Bu&UA<)ɕ﹝v U67&*\ԇgt6ux^>Cb,v>)`*!bA-ז•>Dl×<`MbEEw"[`0ב"ϒn;xv>ΝlN:Sb]';ӭsUnWn0.F (ҎdyH']_A7>ng8< t9UM)'g Ř83#>AK2 i}`[]FoJ+IA?%Y }q^ Ƙ2A&u/Ӗ[ΧZB麠1M^4㓸5ͪ0Ŧ8pf;NeuCXaQPH`ܒ}ƃHX1-MpLˠ յ O8:L( (xRN76d"aNF\$fL(Z[fDףGYp˴QX<ܭ+T5!]+`CFsX 82\ /aFLcg;myƖE Y|}? h(G[HF=59Pju$Ky}vV׺0J;u@@bmtTd`)jdF-3b`Cv,O[ /DV 8wn(y‚n"lucE.^[g 9L2Y^^Ms1{0赢/rUWk]5 狔ȧ'bO<ypBK_ׯ F['D${tVFҽ7M}k3Uؗ5ZYV:Sd5'Eo͋/\89^koyD9-=T;6-+ajШ!"m T)uJ0l9 Xgj|*njX!( xY]z k+р `Z 6thV5` v2GiU)Q9m;f@J}Ҏ#V)  i\B &Օy. BMHt4Zi#L&)JFbXXYN$:,X41Ul6D+ +KZ6F`+lPbwg(cVl99{b 1fqNh 6ˊ\K_,Gjk==&G;b\ @fɛ?XЊ8n:IJ0Io޾֥~K.S݃WJw%8"(svpcw5%Kv$J4F>5PNHe9$|3 Ib`- fX{65UqPڵV[7_xϜ67Q$ [x@ * 7b_|JE&X0q ,b녾}\9| N"JM^3WZV@( t _JgFA4ev5Yi?ܡT@}RԺn<B통EN "W@ps~dk5U:4C %ѬSSi2&q밨\nO*b` 1Kz;#2F{~9.QQSvZ#"|1]맽8#[#)lGben'[zZ|fXLHRDNWj:he ;2uγEN܍z5$!t&7ͦ_7@mZ=ϘnY ֮>sm:M5xtP]|w^:'x6 X,@#$j{Q biݨ4tXA3ZMgxw`܃ޤ]i '("ns ܸ9[Ь'RbNP[0=z~ dSGz$.)؋M Vּ[cw JB3 mG'[˴UQo+hsumnj"/ٺ^m=s89 ί _ؚٝȝdk@*&i-w.A+B^W-2k䦍@B,DTR0D6{PhJϝ ^kuF?/rcirX>gTq@B|Ro_}Tr]Qg :?y~#>ggus_a44Õt:oR$4D9|y/T7aa@8H2׏ZS-3Sewrn46g3>}8EQH#D_>\7V/@(RąYe];/ B(J ؙP@6c6;Mvs+}eh`ք)TM&p?{~}O/":R7t黽*?>V 6 2kt?Yxd*g1v=7(ӒY=T$~wys|NY篿֗_Nw>}w~z3|W^*T}zxa%$[ j9sow<_|##UonI-~;ǪiFE%BK uV̛ #ᆌ]N~67 P.SNIFhz.wJ2eԮ\k?<#X#oU><{pT(lAV;V?y1\\rz ϑ(fX9A_^^ۻ}<Ժ?v#wd:Gpg> w@$w;quUl(0Wߝv]^9>:|{!i kJ]r$%3U=?<ŗ?|uQRE->Dц7Ί|6KYwaVJӓW oo\GkV3Cq?fv[d乎LJڹZ%k[_ԣi=$C6yפ*DplFyC3JJ,Vg:Kc.Sս8;A`ꜺK-Fk*4D/@Xs cO+fX}ʅs훳wfd_2A瞽6\AL.%,;/]>7"=9 Dti@>9b&վ 'CmkqHcwӮ7}8O/xYΝ#A/Ʋ C@i:U)-O [ W~+f[_+VA|4@V|F%s;>KtԜ<9l-`9( qf*<9 stŃוnGAiZ]^ݽ{Y0o&})SQ8oAix,M;K+ z";y~'vEZJ1^ ,!G= 2]4SG-h+ :M+Ǎ`\̪'|V/>1_?~-KK^|7?ҝ9`?z,~0ŽO.Ӄ}tzw'>Rog3Ê8ˮrZ)n~qtMyr|~jx0ޡJΩ?WBMB@tVϞQq|,'S @ۂh_+.*y0[hSLoQtZ7>{298:UuEɁVPY$$) я^ <,mOIeu&v}}sċ9k #Af&e&-V+՟B^j!FƖ(qhQ4R$P@M93ou΍"dZ @2##_~s?;EB, Hck32AU'hn`I*b~SqeަA"ơSU[RH^ә|f%7X: Th:VNk9=(cC dK}]yT.Nqi&:߿gBZz>~ps}`TuP_87O?ϥv+/]Q.iDʚq0Py^l!`5έ j#k&aTwgvy8u|p< U1xZ[њ77//O>Gh6s ֿ<9?}&34Lb`Zh8Åߘ7wlڙskQ@HJe8 E%3z)9|a]DJ]٘Ƭ{^lܳ}w՟z;^R721쩮YU0BmU)mcۖ/'''37o3gbwU<8=q Pu8#+}T62l%MU ʒ1FwW?o>Y&$Lr}}BTw)G?.6OSp-uxK_=R}2ч"O!Kbh~~~艢-e/ D1Q׎}+?iz}RtuܬMٯG&ضvD2xn΃7J "H7EfɼO{/q CQ,VKi7gWV~ j~Q8N3q.'I8|'Vڱ"p ZQAgw߹/?i^Pmt<Ѯud!&R!d.KE@:ZcGqP[WϷw뒬׫7y cz ћU?t'b^B0Mw|"}oiz.Jr(g.kTɽ*-%sS2IHybtym?{vTiD}Du!>5]__Zލnxr acۤZ5AKZ H߀g [^ь/535 bɌsވQLgh>qg~x\tIbBWwxObfGϧL3ܣxk ΗoU˃ޙҏcq8}(WYz}z.o<6vtpbiu,^tg.ϯϾ P%V?^ r ݁rB8H,~<<^ޙ9OFZe&~ě|sҺ!8H[oʹ\\_^sGUm+{|[5D5i!Q;lv|PurՖHK 1S`&vE:wN7W7a ?˸wNV7@]W E 6wp(EQt:T@6ùY氹D\rXV2j]8uy1j.O~rJwΟ( g㣹;Q0e0Fs R*s,4$<'_sӫ$ r7_EW'! ;Imխxf}Z6{/>]w&꫿y>XB3X 5keJ[CrW,ݿ6^O/fmc 8PoCs_Zк'uU+p6ٴ/r_^7Y)HvtǍl٠q7U9y՗#Wj*oz֝⾿/ +?>Oͬ+Fe=I`ܸrKsNfx:x/ʼrAȝE5i u]Sgqcg~Σ;slusq?9oxt2cF)1,Jzå3S47uZ8MԊ:fyqX)*iJ Ɲ#%5(( ɛ"+>|xxzz0{pmƳNwoVmHp$_ o}F .&m = 6՗UuyǀU !WJ<0dMK6ՌY< c:&5o/vXpŃb~|˙L|h{NIEv87>&ll*]umW7^ZH',gR݄8_/]gx`|x~lд1/>=}YgMMqEyuTsb'~C ߮`F5ں(.AG3**ė6e~lA*ji~wuX*9iu' ҷTb2l nM[B*8&vi V5[8I7+/ts셓ӕ`d[7X< :޲ /άU7UTaWW?Wj'z~ << >p+܎-ZE HB%J+ ]w>剕ݛjE{#"T"_kr~xalvUZ=b6AG?"\WQɾ<?~mW|ugن՗MzVi_8! ?mĤoOς;x{7)3[iC妰xOdin1ҁXgm%Tf67?>t+ aj$Kj>nDy#Bƶ;ҤZהB V^D8z,"[{|O((MٙQHl@0†. j+ylKFEyy2 ri -G5?乳.}<~6۸Ut':l/}͛o$WNFCB~#ڨ-!Lŀ$C0p] 7)Nl*0Gg'UTBiEJ$R`Rڶ#ϥO,RDpM*"8wǟS+q;&@!KaPX Ż H4?&/RSIDzA7w72'y|(9N;3 R(`Я;qnmq?.9XtM`+[\Do/C%| 2MiAlLf䚉OBr@=+Z;Ju[TA]xA .4@F(c < J;nrvԴ s;?Q.]_4[˛2wGxi{gkGúc>gƐc }2Ջwf?8_eI} @ϰ>4;]HBXytH@0ό__oݺx Cn*V8F^sT 2NJ0êC:U8*Ԃ1)>v!qF3Uдd;~)Fٌ?l=d c;e:K$iǣ"("5Pm=eH]MkXdݕ^vjJka94Ȁx.zQ-jwMZ5f|WFޠd2.mVi $M!{riUk[${uK}]78[bEE]5JY }xZuNRd`N83 :eE']^ ʃ3۝tӓpw~ZJ}{ y mUhsiF8`]1>mcuꃻ@P ^WahT I SKjLN|]Krd(eQɒ9C & De$>|'b]DTgGji7A[Q.ʼn-@ $`,*Y T(λң6S2<%2.,HY8GU.)JE8n[e) |7$:L,3"2<)=ޮ $Dw(!~dbyx.7e("Ac^~LCIZ!$25.\ï"Z6x[{B[Z4ۚ+zckUXL7m_&5c) ㏿Bd iy<LJlԋ IU&1c@!2`5uk,pX#@ !P_ZR7]zs?+qH l"[H0CHSxv8 X" e;$mC#U{3wPq" LqTxҼ&o8xFaunqu@a LMLX?k"E-mULmv}8Fb-8o‰Π~8'Ȑ++fz?P( ˃3Ak+&^jZF542dz$(zK84RX?XzE56C袵c)6f] =Q4 X6 [QC^P8DocA$%/ YuwĞ`{ KaZud=XӪ _7;Q&d}g]Hl*`]T>oSIg)|9/ ITm)lvv4MA|Wۮ-v2GroW/Yب(#=ǯ_>ɋgv\HQò uuOfFILu}@ͶC)bA%bo+ pa-U,2rZlBX(-ϲ3GjGOIHY!ܡ(DAP6VxHID  $kC` ֡5,rτ |'lCnO:__; i "yC)ZZHٱ =xcQ5q%E lZlu*,iTU'2cFf~4͓| !t]%E]a!ow Ӕ3e{x?zO^֑C}i!vL]i{W9c!B~vӗɗyqM%BQ #نRJw<]=OӦ6Ǻȷ*L닭/pв% :xjܬvz5K!adV AUDo3g)a |<:H08{a-):Һ=@:+M0DLҗ`mX138BYJ 0…eXӜs5^-)a>Zw uv^U~t8{,ҥlO8iBfNX]q&(maMYХo0Mtp̖h1cqN.WWk>x2WG =t A2b/7w SNSToPΗ`^!x>PoZTZX)%: O[wg8k.kl Gm|`7[w@vtkD,2 Ŋlݠj$͑FuMHs4o8|@Easǭrn8F\垫cH([^ONjW~ 8Ⱥ6a65i=+.(^DGvʲdC N+`4mc겥AH c{ڤ i^ϧjݻUF"r19Ȯ"p^ `(gg1GpD6zw =6^Gx֋9}/;K"wZc8Ph 0#XfPJE20,HԀy3,X\v,9xv{T3Xd8c'XȉuWQ֤qiB3dCԨ|Wwy[:ZwKOK{N;^ Uw<:;={o)9-w5nQM~-D()tOP<\|fJ5r^[]பOu0w6oXBfcO^o2F=Y]4~-7W=o̎`QXGX ŅONDx>/}6雽+77@ ȱfRӺa5SPӘv=\t΁9@Sz=Q+RGp9(N4cg.UꫜZ(D>sBQ#`F@}F!@iGb jN Cx;:.#'CB\mwכ`p6aV06bny@~\2cgGf7䭘ΜDQY=0v ۷j}g/Ηgǯ?dzjwǏcz . @mM_o=@:ř^x=%eTQ;?~ˏ z{ d՞633 fk_nហ~Dϛ"'*z td֬׭kE6b1XZ>ә,YatS:jz+$Lcyoҋ>V#J>l`ĎpC7tp@h9 PU1ƈ{ e%NU0懣@$%JD!DLX*TH7ƄI ^_)׀|[ߪA!Rv_ʴ_u{&Qmt˱Iz}Y ۠&5-b;hhSv{piEW~ ܲ[(?y+yo^N<}tfpxFpE 0WX u(Z\ =;bfS ?Wίb4t]ժJ^6ZC)UKM;Gtzf@ʇBNm^ՄzmX* `Kϰ׫f}_Drv̴3+F1ʌ8*݋n;4utN#2=õӧ$cY΁+n;2`("e ^~A\Tm"P s8i\p::}Rocb@(C;S\\I^IޜMH:`R0+r VJl~a>Tܧդbb B#|o |F0{}/M,y:e ?LzSm< Q54um42RXLFWVa.|W PɷWӿůUAOD`^1X󆷃 G D!BI 08]^;ӭRC@>L-GԅQ2܆_{%3Q-N4=ni44~}|c6h{([-M^6(`&jLX*׺ClbZ5E5<\Ʌˢ\ 9Mz-jً~cXXpo{j].GF~DbqpGFͅeW 0! i ְK!%@"]vZ 5k$ '!<vyVU}.km`o5MzoWĶxum*40mifwmTGSow0d>OB'@D԰0 v\}_^U'˼/^6e2QHPWz#e|+۞Fs{xAnLܠB"We>K?BPz+ 컐 c~2@]J#d}U2oZ͵b{@·$*aɄM GN k]v6X'd4"h1NӑYY4t҈̉1h c (^(u(VդM-9|O_55Jxʋ$3q?ƉW7uؘzq0 53zT-$jS8כЄ>~02NUf;ʢɽ}ZV;,?^JwM35eGAԒO`߿M~-*'~G:2kc!Dr\ؖj B&X$'6-r]mߴ~Sv,"XykҦhIcQüȏIQ .o^TQ L"!@$qno8*2:cg}4_`Oc UagEOTGUY!9c罉5ꋍȞE8~px;nh> ξQNmoҰt1'O#V!_a 9ؐ>uC& ɈI@a<ʞ w_Z~PB*KT}Y62UxΫJ7!1IT0{tr&SHBnnouӉ oQ 7 &<< [U[ U8Ftx8~ EM¨C|QFwOW*]/[8Ƌ8f?v^#Qv (0@.7Q(y5J?xkP.+h[C>D = _1< 蕇mEV",p]x.xz3d2H|uSd7MߔR7ל>#3=3;cBO062+iTTИ΁o/q!gӅOZ5cn"yqS1̿A,z[\[a uvKe_jLvu|gki;8/,͵/~ 2Gb v7NvpEHP~8h 2vѷ#[Pp$#)AH gn4.u!QAپ襔xV~/3o7zVWY6'hflJ JAqX8J/̯"}Ng?}+aeQ, Cm1*M!W6r#hCvdHXbоN1e+Ɩ%r]zvπy{-,"+0]vyAҋ r:G ?J9Q[^V X$ y8`#VCm1byiؓimuFiho>55`4eP_;=;i/لb43<Atzq띠8s8X"lX㉑( [At+;LgK1ȟ=!Iث,qCJoǣE]@'p<U`^$7*l$XG%IXEY U:{QWv.'&阡,bQʮ#>C_ [$`ŋ'9=p,xa:sp=јQpf Hj ~P/e<<3{OÃrj8myzS6;Y5b놴 kgjjrp\F6b,ĘQm,b"@=;hz|`80M"~<~<v>9'Sn_2I . (wfVԑߞN*K^tF\rs7 ,]<\K78ZE?~yQ5:fxOѹrƂLxVj-ԭ6!Q_prsp&<>\V^aE37GMO} ܟ7q);`<{O`29\tƬ/V͖b!^c]5&e_D.k3c0et` 9+qV-JAeR7O?P,ԵȗHIE5ɒ f]0f&F#(*0!F Lz$ȥ(!it1-ImuE;,BS֒V-[W`2_S-+T,W·MM FdEQ!mL=Jɋ0)5K»و$XDv2V$NH eN֪+]!d,c3iϼii^b`gL?^i ar(SuY#"ʙMZ1Hkzj" <"W˔BrA*UT.j*EP-]s=[w e:]۟i%Qh4 : F]*w.ahIm-wӠNU Օ# :2ɗ[ ;F[}+Nz5ttL\Y&$ R.20㺔%)pPavj :a W AHX;66*"nXrh_cᆕ&Hm ԋť挣H-Jަx}UFE:P I6Y T3W4ZT͋rN$9[9|qn|WZs[fu@ F Iae  z"S3DAlF&nA>Pa.&&IijV?&rV_9(++MN(X^83s4WFbJ{=f yR2w$ 6x36 p(Ÿ*)yGD%2QJXOu)V l-m8kWW<1rBXբkm-GWr}AhkZ<65*;SffI81,D8Q2mY,OQ⁢U2SҒX킞];y4FZu%M׿C{?z\*F Jhi< /I [/uګ`_X EβWN_>wa݉ %J@6UcP P#K0&Q@N_vl(0Q44Ep3cr8"q(ܢOPd"0Uʓ1ɢXczʉI0[CxRjhk;"*)YS/$7CBLd1mП+ z<&v(&(+`ECgȆxRF2 Oho};[ZƠ>jF.H@~=Jsā'Amc͛ۨ.MWP2{K8bb A~s*+5e-ГHMb.%Nby˜WDn,,cf@j#,+9St+#N<~Q}ܫ--rVZkk4 BhT9od*I8X}:umƭ7.;KSr}%m!."&^RRrh|L5•ZۓkwH@ x෺l%dѨgtPREQjoJ ب,j ]}L`lO<";jèɄ}#Fkd.6JI ^3O%0P.Ryjk†z5^7C/3,.ie1%lQ9CN85#^e^xڬR }# n]QHɄH01,4Ս ̲:+כ~Vߌ!4(rBYX+ֲyRpRJ"xѐ%:& 4@Af]:)&^.UKk4jҴ*I\W=rFm>J\)Ea4Bh{u!n72w5lSQS,Ps%bYn>98-Y+uVw=5JyFLc4R$ ssY1ѝ$8"w;KV]/5y ka7 t"),%,VLZifA7+_0rT_j!gh樺8L;&Wd2WEU-2$,X5@Cy~bPjD+oҦjeh^P|hxMI#fkhJd0mZK"\1Ki3I0)hc$S@) 3jeY-3#/Kf ߵРƚYUŢg-ݚRx)ii?R^`Wa><΃`h_ItU`_UU'14EDUd핂Qʒz|j;:ԩɓQP= DY7 B:xqģ|AB/6MԀ3fq=ndui/A|YU-Lxzj݌g5_*ɐb1[,ND1N6GrnJ4UGFEQgjƠ":P 7A> O1Ĥ&ZTw.J;)hͣ)B9NRea%=Bxݎ dđ M !aZnaMNJ"KBa t^:4? IASF2%:[ETd;UXeݾPJʈtsô0)80#\"Q D,ғn (&iddiaL d B6X2]3507jHb9#{W x,۩wӎCW O0L8%HLδ# ͆,ɺi6b2(]V_I YB xgG 8An`ؔi[wlw zׅE_TURͳ'cEI瀴}YF Bu:8g!+o]7 Huqs/ҊCO>6}ݛ!ƙe^`c9Xn @lϸw=F+#ʤez؛~*CP]˝vvJLZo>{rw^ f#hhaq9rcǖM;vtvffތjIЁtlYO<P5缽ՋcvJIMuGo 9˂=<ގ=ry w۳g]9z(7x SO޹~t,%G{h'S/\1%-㏼o}+_-K=/^}f7Ltp&ݷ_˻-")D81%AoWc۫J8H=-n,ѷ#7we2[mspT*MvɍCf?W_%O;ۋ3vMsȞ]^{+ʕ+Wr^n_ow㶿\m1eC|kRIg빉w"3WMh,{~-|#x9 \0{ӧ&v7&]fT}?I apSo=~6|hQ 3N88]_2K?w((O?~Jl¢Y}[7,;<=Rl^(C^ 1;;wER5:K/xPe_X[y䙧^_;D&{6yGbL{"-_z䓽O zI7GPR52>tj m7Fw*MM`Z1Ȱ'[K3-O/Ưݜwg(1NH9F S&5&q@G[m?#msMŲBZ!%#G%(×$G*KGJZ d wK/~>;wwvG'MvSt~RL]^XZqV}rk[/9ST{ա~' VȢX"X0tVJ%UEY 히)Q0Bhm4vj[W8\)V8Of_{S?744U4fy޹1%x3q9wnD%4U(E0#ݨj*j1&=3qB]Emf%mJw CB2ƝSoFO,]:ۦl L"43jJx`t$86P :-Gi7(JB/ô,̤K*biw2\Ȗ4vꥋRKέ^.O~c3Kn1?\I+$7gW疖-#tg^wҸ,iWwo{o~!wt]/93kS \PcEL+DC@q" AJ/ltoPhIrx)CдŰtq*zUO0P 3A+<@EFjTt֫t-(h~&,#R,h~Q>D,gB ? xى='\ߤP (#L#1+ d {m?`8PVr)g= D=ЖEa2-fYvpӓ@5dn٨Rf/ W%| OJa-s9i7l+@ $뙺QO)oֲ+wGYq׫mm` .03l7֫6) M7_d2ӯhQd EBAő VX:<j(bяflxyny[A.\ȾCSn%\uJN KU1 ԭ*ͧm>$oK(K 3z[~722R M٤ӂ *TQ4Z. RϮk$;bJ2ޮ1kQQ3B[SRoGƪ|*%ǜޛxHR챛QCe!'-Sh[³WϿTySO>;zZZW/98Ïu ?VyG>Hsܞ2֖|S}`"kɎ=m/\M/eܐ"s׏=|~'>8𡑱W_|q|@~rZso/WzZ[?tG/?񾓄o;pxY\][xų縚u9pp 3vcr3asqc+䆼ЙN 9b:0̐k{&''W.^SOjZ0ssJLT~F)E&%pn-ˇ!hpRj̹O`~1V<0A7Ν=wk..-TviFX.gݛSߺg?А 7eA2INqQ'Wg +X7o|Z[Yϼm ԔNmqvO-T֗WN7}Sc\N82vh{/ߺ{xk|fqn;-u)~'??p~`w[׏^}eTEowԽ;$sG}4˕7/_ ݵʐf7g(O]mbh)W'[g^{C'7u! w|=eU[oL*Uk K{w+i?_?dd'ɦzWodU^[[3-h!U{?<0V>5+kZ@_cq C]~b&zrviٮQo॑c{z{{;߉Pcq1YB}o]0i-W99֊vNC#(EkFwo?yFMV*屡N:511 {"-ဥkS RHb(m܋|'uT?(II쇟|^pKKKcJ6kuq>nԪdqΟ:#L-/3h[ruE;}}xE|IwvфӠaRDAޤw2Q)Ox0+XIU]8}R UU fĞ ѨoݺuIʳ>;;;KxrW_WOݻwf3( roRazfi:YEŒIºgt"q⬺2>c>Wzd`CZfos'7V#˶U}ȉjJP`p`_;KЧj󗕘]p5 b>^]:sldO}Sg֖W+~' ]e`O?5Wws0zsddd~sY{ѵWmyͳ@sҕC#wMj5}hVghM<8=|o\n]( :yqDìO& |^=BFy G'~}+wV&F:im^\3;7udꪑ)CݞHWU%c' ^IGMoyvhd^zg3}};wHp !m[(AG'zQ}兗1卹,KKt]G9"0vs1^Ѣ 9ß "5jW.ﯞ_^,N,(3/t+9h41F4m-QQ5HiEBMàBkVB/|@ bӸSD6=0CGx}97u,7@.TBT%w3 2=t=ödA9AN5 Yh# yq$an3DtIFQM8h7"?KީYY6-)=A& 5LC?K*No\_@[nA[ RF6}QIL'FēJ 9b2-RaI53+ux"Dzd@0 ӆ"x(̱)F> !a%(DcMY$ .awG*K̠vԻld3⴨`RQeDSi/+H@1㘐v15$a 5!,XPQ)A:=|_rZYJ\e 8SQ!IDw N yIQ}.(HS% -B 4.j*]Ր&ù$ f:(#0\ YU+ Z =/&Ya1`[UGc+"k$RZVnouY#QAFz7 LjyrAa$Z4Ak(`;9d}Ƥ A%GAp!ZRr0}*f%֤m EEG6-2)ObT;; %buyzzqb)ty=.bWI/-1aZW5bޥQrvd1cؙT n_z _Y2e(hVR`(/3Td.5`&YLd`[fDd"`e!i{CbrTl94C9ˢ^IL-!IzH98" Y\Px8ay+++Ev#}CÃJ\;Yϕ1ؐL!"@dɺI$iQv(\98UKF}6CIcZu>Tp]{͞u]2HCch~_\2, - dtBhu^ &l\? /{QNVBn;OLNNι;T}륉IDMK4>ߢ)S0IM28Ӝ/aRQG?;桇jLd@Cz,AY2)ޜᜡp=1RMQ,(YE=Ad~ +_U rY k??d[EO-Ci`ׇ C-JYwffKt5rIBx'#.*‘d$}=B?(_2]k,kFF*͠ Ƨ\iQʕ8"!E0CH&ǩihD$9uRύL׾uK9 }}9H\ܘ &Ci져`z;cōf'H-"LӍΏH{O tg^"hHwG޴}魺uN $,S%[Aaᇘ渺s/\_;Sّ[%'=TF_T(hSzxbOQ#:^S-nkDoGVN JPיcP18,{/ZDcˬN웤LigZbF?(Pf,g@gO)0 T)V8, Ъqt쏲vpɚ$T0N#f<&=E4!&B "@TrP$=D( ʦg﹮{pDQ-^nW&LNTSgQYCh঩ p<v#HC Ncݬӆ-b@XmJBh8bOa#T7ȴ׽%'^j{_=wR~OÇҗ_Oկdw~fmw׾YX ,EΈ4.-( V.E $o2 DNJl!\ oZ~kPfƒtmP+gM|mW$:trJ1Һo/k G?qc>as\-$»0,mĭՄtMf( 4,ꆩ9]E5VÚqwg.^ҘV`1?(u+aFZ{I|Pбkc#Xf[1BPg>Q*O>/ꯝ<0w,fNd:e^wp# = +B\8I"'yYjLӦCX]+f&qL4ңJ6ʑ9M$͐:Ǒ 218uI!WvP(IIdžNh(v1W 6DΑ#TlsB"YJ|Z<Ɋs!EOǑPi,oμv#*ytqĀl(yl3Vs@1aE/Fw/t2x9děXtG4Us魪NJ<@n"mD%Ow/u>tK_rFk BzT0Z;:o??tYeIg郞b"?)'YѴ6C[Mڮ6FMdy{9IU[/gJYJmyb `[d;XW9b)fMioe+%0T4?mt櫧~x`G@[)@l:Fųrū{"Cn}많Mo)>z 6  {~HR#-V<<(5"-Th]L"GB3(~yW81;X]'/>dqArV[og' ͫg/*uvmm;cšnG{gle[17=;⻮Y jVgN>)b.7c~dM>yrxPZGkf4R44B+/Hݤy+pxTwMR,VO1TSzÐd1(edg" XFAdrK1%Y39!E()zQʁYi+S7WP*X\_/\ܻ{/;?ѣ9nC~Km:t{qtsz% *O?!Dl;7.W#)-lWbЭs1P!""t8 =ǿUfT BS9h6)$0?jģ }HrdŒ@DUR+"9P )$xO C:Ix\6YX&$+K* =hF0j oDĈ:b" 9}G?]*bUE[(d! G($Ґ"$vU#aD 5>tUFu˒2)UM0ɩ)\$̏$TmOBpLµKgo9ၧo=G<>ҫ1 wJRXo0lɧ94ܷ7Pt,P+W߿_QK|/..s3IDPV & ?O޳_+AA_ C% z<TInQ8F*ObTQFS=>":@/6ДB勦A7~H|Wx9ls1٢ԄQ5y כb*%ӈ<0 o⤤ƒT37pN>}mX~;qb EnBEra~6RkJjR,lgM1.4K2]Lc1S ~ #=fEWMYq*]ai_I({+om#?ٗ:ص_|S9d(,qΧ?|Wܵk{ۿR1s ͒}M+;*>-Zu.'jQFAPS3zIi'͘פbBk=}ńa4 Co`^a{3/@&r^j5_&te$ycR^C5Оw^zˡ>"1 !'.L% 4-az6Vxb@JexrC@!WK:@b)VR5C1Q[x1FЃHU2dq(me"a1Q )u͢C5u)_7NÕXrKg\[h?ñ]mo𗕞ݻ\?1#Z\/@xvR4])-tRD%qYԅ{98'c9EvߑH!1! U„Jf:LIJu:|8pLZ'hLHi sUrIuyQ4=MEUleU$ GU[FWVFؓi3eaVÝZG{}y'ίٳll-,n/UatkuOv?Ǟ̯I82VLX)oo~dcP^|>?ԧ~S_?u$zu{K?~o|rA,IAd' $\:`;'?MD^Eed1^ P#U@'zL2L٬o<{[0N,,=Ouܻ{7 |:4, E nNm_N?h9Iz1Lc 0ehi[To,.T&b.0 CGk1>0KYJ1mIP.F&[}QѲ0w;L?>YK5#LȮ]}C3.^zʮW|F፛kpn^~I92Ih5W_?|+/~TJg}>~_ƗVU#/(ryVvM3sAwËW!Z3KK1'1Y`/ A3 ѣ'yӆ\7#U;"|ZB>2/o- \?{osYI+XOcM4r1 \;MS`9~w -% c'OK9Б'y4x؆]1VZVƒ0ki~޽2w?L.ٺssçʨY`"B3% Rs7?]l/|w['%˞o~ϷY5k ;FsCɚ2ᬗH 5 ljP)ji{[ KnFV/Y$dΜ:3sW䈑epr3+ q 1<µs+jZ((˟~ggfaULIy{sL*DdD@ŕ4 &JAqeI3 G2 *ͻ/$iȄ19;m$DXl&QBH28&*bbn\t&`5v'_xKo\l4g7^?״3tMO[A,Bjͦ8)mS{g}/5NT;Ȋ#V.<;n>~tWw|㻞Wp<|'~m w6w8}{x_/6?wo=+' /ۂ}ޱ>xjWUoϼo[ Wtdw/_{!7~~m{[,+P*R}Q!+x|0{_z{a\dYFQWs̹}EMJ_wPDL["?@IKw>C]ygmȢW;hfp 9U M5[7nY{$_|љۃ==ٙ==7[|{8kկAjx[ʑ߾y7oZgݹl83T^@q2xF29zYi+}&L :7o\{u~EfB:<, #a-1V$&E KsD:kxs[$+%Wv"o(V;zuD Bx \t JIs% Ék_hn]h;{-5 ."5 QZQeUe<XNVi&7mzFu[kʹ3IjvnAQ0qXv 'qaOٷwwEae؃!8k[ÝZp^{[*cl3_ʷv߼qm6!. 悋ΗwR8Z _ lF!bڝŶdE;w! lQ֍Єp` Vm1u 4SoIuH1S"s3 3lH2-J\7MsI22ixQ Yke/ݯ))zYUU՘E3BB]_铭I/kU-1x**i-GcmwKd5s}ʫ.7ZEcj`B@;l\s =xl$MF+q"^e7jy"l<1?{k"(,éVp;ZxCgO?ulAv[EwfQnEWSsFӳ}G¯GR[C<1)N #!+΍OZ4&Y?#u5K e)Δ+Ѷ QD/tx4LBi)iEyeDQxs#+bZyibIӹ_lBu\ie`.L(h+j};Sd_QSXtmVg2:Z2l4Q҅M!qFkf S%\TatP6;dI lIu妕4ݴ\GL0!㲽=1lz7nL}z:Wommm~#{nr'fߺ+;k'SײmF0˚&)6)k;"?W.ifIh*/Pi8<<zU hECv&Iu.;y[B,L'{IͅtSy!-b$<>%/u x }D:1v?bRc+I6KilYɣ04_D.ʗrNx D!Z5ᘺmKyIR{(ۖƥʣ'fԬ Yx<8(K=kl{*9Mc N˟mc9GJ"hH9LjO| 뎦Z:HḇC}YhGعG:ui,mׅAJczH!DtxLF]-'aOEN9޴5P%Y;H2lI(K QLg +f꫃-?;2yrk¹eϼy75UK6CF{kqbe*ҸRI cffmgC^p쫟Qk`Yդ9ozu`6VHI(e=Ѩ`62<$E%m}|MT{ZAiǎ ?Xa!уʠʦl*͋+I>W[u6-({'iyp E^f7,nݽs(\җ_a3ٵ"2 Ӽud2LMRl4{*wjCTlmVX И cq xQ\!/@ɼJ߃qZ0%*f)AEU@+ lL1BRlw!`p0 p0EzȢ(%]MkY䣧n6j~N`}xsBPSHE I ﺁ:)_,: h}95Xn}Qrx+u{eBR8- Q`˂ ]%K;҆J@,,(5hU< Y'lB&Y{v߁X1xeY , Gn]jTS]/җg?ZхXa)Ӯ20c{*+v`#$TY/+(Ʉڭ:JCȓɽ}Q8Bn<>x~o.9.|MYLXb7q 葩ׯ/,_g/.~;rrf:nӟq=&Wo<~!)ynktBC")LKJt8xBo;XCK=4(^j?cޑm5s׆Wiw <ٺ۶{;eYJz>} Yn]߹H[2/|N#nw .rD fQszڔɉw]_+(}q htx(?2 %dEɁA.'VHRS7o\{3˾\5sUI+e5y;;LK>ȃ^ozzŒU ^*x뽓|RQr0(7旼ahfH2iƒKӜm.h.\وFwJ}5j4cfD)49,'X%^!*Hf4 1kHe"hO!o6]{ _z? V8)IKjH${pl$=61&&31UK`ՌJV kjbpeB`<ִ̐CX))sEoPw3gy¥7nҼ ogrĭջ 7n\{wˑHg{&`KвL;F' ҉LEg]E GKJ^݉|" >RȌڣ"',1%M=>21]xMV5. =5$DJ-@_,D(VѾAظm(XpѩcGo\|'1 t^bɀ_`q-a?%v fLQYҴj?O='r9A#?ZPhޭs1DX.į=Hut2>XXôؑ#+sǖN{LCv'_{a=ΠeQqGݹ{b/ށ772Ҕ\ ];9*U&.-kAv˪O(jp}EQփB|?! i*GWؼNVt4-:!!OkO=Χp9([U9r푩CZyZV@%GزكD?D{ȪH(r QjIvU5-%7[N.j[ca,K[};=CqԼwk'p<33$L7]!D|^x:zbϪipb{5=sԌ{J94HU;M}ͫD8in9di϶YZ%;Z|%qy?ד5íyz#A{= %\|ۅ08=/MYØ 5!G"G=s90;Meh5ug<yw6sMiӒˊwBg82s824hy,i UUURC=`L*x%I˱:"6Av4ʱe*KKw4,\Lyzwܸ"DGW_/~!taa:.2[Ӷׇg;q!5;ҫ$I_.T=85NHV+F1lS(HGlD=#8 D|I#q6Ƕ4ϱ<"u\h -o]?U[F6kzEI3O/<\On]rz^5XlXYbEUتe6sgs(W)9fU#T}@c,I˭ssI RS˄ Rь ^ݽxXu-8e$'ݩ٦, Dzz~ׯn=4Ɖ!+^xcZh; ;J_Y7XNjXiQ'B|[ / "!wYuq)[śE뼦ه}1L9>K13HFV6K ܷl9bw 8;QN?m*lo] gErRFk2\gM䌕9UKbH!TY%R etUOVX;Aa:4YPDJ(pJĹeLeŜ3✺de.xOb7,R \@rL!YfWܼ|h7R[7߭ϩ>Kۀ RiF!~X릧Bӯ{5%1ACΟnqW3\f%ij@*SX&·TZAmRMA=H{D}a:E*m.Õ9ceDͳGBmGߍZ=Zզ UUaxN#?걩vIP.S։h^iwW̟F(m%sg+%o KG67ísgNV;n[f Dzke q$H#"*1QFϸZ6Gb+ ķa}qj1' iǥ}T|e=D8*r:mio/X!>R@7]Bnq:H,rszW<,Zby7,*/]x{ݍ/v8eo):$6COY.FN$U|Ceu49!^d5C 5+G ne9эm6}aA v# $UmDc"v7 f#@0={kgϞYmlQzw05 oHd #Z+2_6X5EÛ@1j@k9YQ-H\UN"M9ԞĊ#RLe@ƞ0 1ޞӭ^*aNqBƖ )45?R͆'=,cu(rPJlo\A {AxhO=Y޽7`FTH MBؚuV+ *r6pU :8ul8kD_{p c|ǨaVgId , ֑J)P'] vHZT{=r?OtY4OLRbe I1$ )OȈzfc`V I!`FwYmHVY!84w/13v5i!nqې+;U!),HRa҂6&+h֝HFm`mCal^23qz3c%y=ڹR薯vVy@A=&-ᐲZة,z.FϒD(/=0-tkg$w< m呔 2!U*_VׅGfWVb 4O@TQyxdv2v ״LIvgXo,ilLL iS0*Me(xdbu2㈿G$F5֤tTt.S&R 匘$1S*(&2F4F#!Ҕ_Xt)1єfZ84ETJ]G\id= H:7Β4]5ϫYP8˸ ד{ub3;woa?/ي8㾏| tpy*_{~L=c#` V^)$ 02z+aG.-9ϳ;w޺vŋFkJzyi/u^~kgrw TffIDU|J9|G(USV}X̢Tg{zmPuopx-a+8gM^+noKW/ Ct!;Trb#zq#}Va³xfj^yUELRP$"1"ŕrc;0~KY8^;"y1DcVҕŹկz3/Z=imrz˃] 3~",G@ @Id˯A @ ĜKt8˪pKgA*!ʬĢ0I 'qҰ a!1T+ C-ݲ]n)QG՗SǘTSoڛ/>z݀lN+p =qdBO+-ɇa:ywo3${y?fZ9xnv;I ՇT$?>& V56H^I[IwubsrF~N_>.d? .I3HL##SQ=1_3gάmmllFwy[z<tN0ӼƑW(TaJO]oyk{y!P rX+D`YYDOcԐ.3t[e:$pMy%G*ϰڄQ%p/CRUX7hKd=f.>~5 GVb28ikk&"3D%e7[ pnm5SZ^oni͚]e%l9K*IJMTƁ,NhU%_E*V5UC".ךNZ~Nht$ѣl#4lנJ~knέI.xiNϝzf- .I10C낛 {5SYpum|itNl(˳nw`gSK>;Ib{tm:9J|&Rjqi5rj4Bp\cR)aMOx>% "^yC?Wr^T F´LȲ`k.̥*fD^ӉԶy鷿-\ز!ެ,s 2BTWPV- c!`UQU%'0&FPWA׵M E+rr[yu2 <̣sI!!1>5-lйE$laFK5ՋFFp2 x H&|cv;HyaܩMun&fd jC@.G/X@/!bP3,HO]ɸs[D3,- M$Cj0HᇓN"8QE JL,Q*hCն`5tX>`8P2]^o4PTgf^9L{ݍwv8 u)#YPjvׯw;TZrϱl <1ynݫGa1Ss>h 3:Ga2ep(uxaXrYAiĸk@ f&Ea@}UWޠ!!}>g[ݻAkO;ii G)PzBsy5syHT"݃0evIE|#q84 ,p]=^IEIY" a+s/hRX`GWF_-ƍx6.FGAN|vS#)&F0'RɞkO)6-#P7-߅#gP*7?^l<ߌS[7B!,.h/Rz~bzo>ɵW<8>{m ޜcEvYI W#'+R"/H@銅m RyQnE WC WZ0x[*Q ] +22BZT/u|o!~! NMabf5*o坍F=0M|0Zϴ$Mxc JdЭN{Ú%HԹب<eݔh` I%8\,Uic<OPUN+UGIvEθφMT&F߁dzAk'ξWϞ=_S͸ΡΜa*(L0~yG.m:XA;7מy̩s텅(zv۞~嗟}˅W_J`4H yEՑRAL6DHjjGIM4k+Hmۈ]' Nk1RY1C`iۮ3ӃW@'`.Ԭ"A~zۧ2F _<,̓nШk͡lFas#JمQ,|rl.C,Į8u/$ !4_*ӞB} c- tDҌ )Ϊ &Uu,/zԕ7ܺQ\}A\8B]Q6 bm+قPTGW;xcTcyU~oC|5I'i6eX)'1\mLhxۑ .5 ky0I1lyHc՚{c|a3,\•˗L3u-QQAn¨wZz!up 0/tV .jnH&3[t(<´޴]r h =|Б @Pj6Αbc, ‘;I:FLH'-Aj4^ݸre5b.=EچدSvʴ 2 6؄p[欬Ԡfz]ɼ(3߅\K_1 ]mp>C,.9eeq\D|Gf>?!Ru1-t'SXwpi.i )RO}r$?M@*[gztqa<kIvY4.¬!h=ՠ+, o! ɶ4O3f+omo1C^8 Zu (UL267V E_7j|: Q7Xn[S,$+8dLr]R"K!n4oAԞZ A[h Sq$qSd(<)R(\Gv#Q@ˊLKZ1e}Li ʤߌ!ƹ0[[8+d@Y&"DM/fM z+onpŠ/{ݒ-sT di[6Kz2 /IyA4iTuN@!$CB4D`CƈJrV-qxKgK*@w %IvC=>.Uf, 2,#YA!Dt=X&Y&!;5?NHuIXNY9ʍ(28'XbMuk8KRn Nk+JrYqЄO>x_ja'v\< -ȘdKp1͒񘞔%T=ըoFSe8#;CP L'лfEQ ,IIAD2n".~f 3!\!T:bILWpo΢Sj50Nʐ@Yo.y~"D*D:5OKjz& k4O8BRfk,* hDl>Y,حM2 Bj_| IYp\ U2GgaX:&W2w8<4ټ i:2ON[PC ўe0AQ5߉Hc 0~><MTE5N&_ \C)kab*smo3ɻ1[F<IҀRƍ'ʱ ֘Fdq#e.ң]lW.;L,jA@2HGk @l㊦;@"|.+xaLP_P'Z!c(@w;UCQ;}IH="Fb& `K#G`zuXr~F;$wL1254$du Рf7U9x9ٹ.8Q3H8Bݰs͊%<ھݪ僃%\}F>:+Fw, W)ȸa;g/dR Ӄ~xE'pvv,Ǯ5axp ;oƶ}giZJ2>x[Q=FXbܨوXȟ-\Tan;VqU*#&(I?5ո@][QB^0Ax,HCڅbhĬB{JwLEF341%)+0M͇8'NrcΠ4_K.n3 q13blkCd8A5F@ 8nԲW )}{Lg?..TvP (q2L-=<j{/Yvér5-ܹeV۵Z]UdV؎EDӼ~u}YiY Z!|0(/= aXR[2eؿiJ3IC.A$4>&¹B `O9DWPTpE<ӎ\Q2/Y7Jk1DE:BxU0~5"D]DQP|סLqCg{<tkdozJbʒ$΋W"#LHFܹtI!sIɱ(zdE0ui?ˡuƛ쯙eEOҙ4jus2ʋ$JU ϵjέX͚Spe͎ ;nm^)D/!Z֜ nJ͔{>d~%)8`m2vsq"mjH=Iô0Yk3APhSYi낣Z9G*9$moQPU/z̑@xr4t[eҾx35TNms'+ѫGoUhT;•KPtªb =aaB}xW-Cؤf5@ ‚Kľk  e?dBرDiT8.8U97=ŋa 13+RIYrT0 yB5)歚Y ة3P;B!⭩0 l̎R-(`՛ >8"~zpKL|>$@j,5!Bre̳vKגذ,Tob*oW FL j(d7ѹ:u/LV+n}RO >$&~".*As-FxbK`r '&+ C:K*xõ\E{}:`q` E,>b<iMh&CMz'afQ=-?lȮ\`Oײᠢ!^ 0RFphZ-Ggp! Hݲps( w#(lif ]e5)C:+GBo?eÃVE;B>0Is&R9:5q%pbb<蛫DU }j={BRDEa !FF%s 3@`2$ Lm_KLB nzɰO;/HkrOEAe |4,Pb ?3?2K,0,lhx2_9LM=O.ikT ^Ub@GFOԷvcG@0 SJB O/ W\e9;#z0;9s_y]bh L!ΖPbA+Ilr$菎TJԀp認JjrI$1v!ܾu1 s1~a8lG8/dwo#,,9@ii&i" ԰uOȓP̝MHk|=pp;C35nF4q͠ݒ[{QS/S$u <%AEؗU^g}}K[j I4Z# '=e*xxv)Tj*3TY*N$va` &FB B[km~9udJ6yx J8/?S"&Q$O>'TzW&k Y+,ڿS 8 =Y2 @kv $ ԞvIEU.|KpL ɦ=!T َ/ӑR4Fud[7Сc*:`[$jU0A XQf3.0%4su' )_bքL9\Ac2$j#m<PX 7|05#c5ilzq.uj1-Vw*ss]j ޠM-À~.>#mo#A HFn; bLjY NK1zZi ?ш8$Eo[f_Wk4.=T@޹ ZQE1 1cbH?%3n+S5Ni 5Qa-zx' N+<,4™`4bdZv};15 *!S%1M35NbBLU+Y~ȰwUQGZDTWH.X:&%VęLl3KbCPbZ21MݰL5 TJy'T.φ\ɖܲZPCcWyQDPKF6R0l6ހZ$i=n3pi&5ΒfBG@Zо8"7IBfZVdɿYe 9sjYKدzsp~py/D.ئ9T2*3.~[@ sEEX"lvtHcspj*i SI D{Ng@5 1CӅ4rSR??_G0wn6tvʳ_ib*Z}ؼ5R2O" E?b6'SAb̛$@gUaI`nć{9P4 ldvl %Fh6,K P.xP9WM̽BӌAhjL̀ZZ•?.z.%6-30`BHsձD4p{r]:mF2]q#*^Y/ʅ}:] N㺝RڋsAbԻ!zgX&ȍД$ +fzy" X)v .x,p?,+ R|/ .uQ45AL& .w0rPi2قej?誦`_@ٔU BKq"+Gx.게瘈U?QLݾqSTsZ%A z&;fCV>#QeJBXPA|{{D 5vӬ!aTHcT3taGSłS_.)Qd a ]{",9A20,= jDkzPqda5Eݽb0xHoE-dik6MQ`\<^s{.9p&)$j]ə㺑fXr)5Rv@ŁkڰM@3J=X4b RiuWD- 8 BGwO}_h(qg xtm_ L`TFÑ:b/d*lMXE),"-Q@טJ$"C%܁afI@@XIm=ʕL24dD4LlU]fjͪ8 >lɩ0u%j8X &Rk]}BEU.hcH0DD&:1=2~Z.p(,.e ~-Ec[9UŰfm1W-i]z/^\f}__0׌չ%[yա0I7njM5v5E[lQt˩n\,UE č5*J" B/=pCW)2^fuq҈7ǟ…g6îQȔ0ӆ@V|(Uk_%]ԁ:0;{V8ZDo$ r4_C%bh m%ӛE&TEu)it/kםf(: ׏cC8ŠaN,`kK}㲠.MЄV ;(8 aSٺ,>p(j]*8d0W$_ܗ?Go~GKM$1 %&kŇ  v*eֻTGĽ*&a%ۙ 剷?Èmc򮄴6Ԉ=mߘBKsU 0$[) A zeP)`T_P=fC8g5r:NRNM-bZa$<CęSM#a& y>{TAPhP(.8XH4bؚ3dM5sdzZ^\$eRw㦝gq{wzK 28^${bs0;-uT3 H37n2:X`&86T_8s\6KԬBrƒX L>'~XvAwh(:( gz᯿5u7P%HLlLO2Vqhr0&UC9$$qЙP2ZB6ȿ5ȱÉ̔ȓ,is~!hì0 FB요E@81!ΠPNat Bxq°7Të[`AbD`EKqU-UtY-}v-grZŧe{cÌۻvlbBe4'n 3N֘o,˂u|2]#69J @52sTKy(XfЏ3m4j sVAaw+ؾ ( ᓙ'K8* dA#jnڟ[KߑE_h+yq55B!{K;oAIjV*>r\Ұ2Y[CTYae„٦喗|?U1չʉ5WL?{0{_l!'oh!5!kLFSnbGDeRԹH0G#Ktͅ;@Ob)0D5N}Dzv5jCP͕7, $U18A`Z^gjyAl6RM@,e*%Ȃ@h So?yl]i"IE _|:jҔgCH=R[9&‰i -Mb%0^D}w9k̨A3;L 74ߵ13f{v|`Ҫ5-ȃK.VsՉv @4(Z/LU_z}L^IXUB:ŕ)܊JpXC*(GO7REp o$.>!X<$dDZ.ဂHg?z͵*R7J2$eqIDyV|E3󗥜nbvܛQKI NqIf$Mu3<ʏ2VUY.(.N* 8`_pxNb v\JuC|bs^B4M$fJ;5fQ$-余iP5CTL%~8%^[iEJ^A.>Ɣ4%9V5e5L3죯 ٲ9n!2|\WQ0#*-./k5cЖ1NCRl̦a=aDh(D*Ҿ>A#=X"b98Ekb"E" 6gKjPij?/LR]MdtVSM xO8CA+dI5LE2( aaPLRce"'Q"5ε'](Q PU t;@cy]&gBįOiNF_x$Kuce)<"aLrR >t >%„vm'yȸд+*.&9RH26sS٪|gވ!N92*g#\Z<4Qr$>18u@1Sm @2`x꿘XdǷi42ro<&\]؅#ȝ*2TDBGH LFo(x1}K0&UM,n,P٦p{SUT?`%^6#dR+ v*` q]\s9YSb,=O"-JLjP9qTcY;-W1 Dc'/c3%...J0Ifp4"Aq"Z"{ղA|@cL#~aT4a7>#lH&݇kQ5L X0N$&gԨ  #d}&X؄5`!8Tbڗ_~,c$ `/I"6/ @ 8n%l [CR4b_)EE~p1:%]@qVB\ =.X;JDoòLXKݕ̏*kYܪlmjmS)/jY@DWVTKq;rw%F pSt GO ,a o.nDo[se)<5"G RX<o M%#TK;EP Mdp1m7dPD1!gPmH^c<;d4׃?+#b[S@aV[ L'"dzJloPW50u|@ ҏOFYqQ&n`>sWmZT͐*@%7,N׉i<4ye$;*RWu/0y8@QFO؟iJ4h'!elˇ,em+FERi%ٺu[Tx}{pD8ߗoeTRp,̈́`Rb %˥jL38 FcpA PAX+?Xܔ(cFW' i6liii.a=JpR^:>|" q"0.jbbV󍯾b]== ýn{qH-v+(~=urrZ]?K f(5!{14,8!tpwOv"ryˈU);5Z3ťv_f|WT,$b>{ocg_*7ox nR@Nő1}(oV~3ݲk3gGGϟ?_׻K;nݺiǖ9C|^~e^u䕅n1*|4w~wOWQln4M9-J{~;FGGအ! Mn~߼tA2n8= |R{&P[7pkw5mE(Ik& A`R mUU |/~/Ν(w]''N}/l¾[n?/OaKϽv)rEF.,\aοbfYKBYXcBgi9Ӱd+ \^\ ϝtw ji81&_θІ׺cDT鉳n;x/X(ascccMO?ӏ^?m4\_ /}v`Y4y|NHY9\zr|s{woyވܯg?[w?:p[FlҗkMׇG]]jJ]pBjOkzymirc4>[|/r̪~APKϿWo=ڬ5{JF+KUM]j<Ɔ5&׃^?[;|lJ%ŝsΧkR- [H.Hiu&<@FXބXqٱm7Im4r/O(;C#^2  `{OKoS>.ru4tr+SO\l_"ʚe|OJ]'{xaX*LDNݗ/['7S.M\ř2X.cDxˏLOOs-.ОIp6/6~h\34V[qT˗ClveX  ,tȔY!`m*lA3sFYq݊CHԒmuÝ\t=:bOCЗoY:hUڴ kLQA`ɑɚs.͡C'bDcl"eJPL-XUǒi,?m٬Δqf$믡yIZuc.)a 3$|@TdqQ) I]҃lfL2m[xuM7lw8$g1/^`k7kUuSȏ!\^!‏!.88!4,5@}i5b'd% c^N?#RIb@X@+Fk& `IUCi)K9{d4CzN*x8go7%Rs)º׵?BkJykA| vLi;4[9y(Je䴦邨:gzDSS^ ?ԓ/΍OX腍Cޝ盞5+ Ei3ay;+NKXvVuEm$QTyRDa20gxR؞ "b?d>pJl>| ` OLL 91rb@ a?!f)۰ecqOc|.^oX6xJ0cFK$ȂӜw;N6H0ŔH:^[>lyA[J⛯UڞΣW{Z"pbB8 )w?WΟz"?6͋n U[C=#۷os *l0F$k{}Sg&Μde3{zuۚ^sfz?7^9rSbh_NJX?͕ߺ [U7Z ~ڄgi`A!,_ 3Nc+ebkjQkővIl'[RA%qp̳Os=7߲ _3g8x }رcm;;v5 p?>xmm'.jzkO b` 51KZ; ;i Jp"{?媱[8ItQ\y-3_,7eJ{7n?wخ18̅3{}8k788[?tzHyΏ\ڻ&//<5.QUU|W5ek>5Hm\k^8} LՏ{:ygC}ٰaÑ#G(mywݮ!Gz >;sGkG^ygsENIۖ ^a[#k-O~䁜zBZ[l[,|`' o`!PQ YV(Tu-,G9}m`f={K7ݫ{88%(xKVIʳ\[~W\՝Z%NQky!,m,ۭ[cccj5ps2??v}yO>Z}ɧ UkGՙ @珿}V;V@!,%[>×`F>{i~,Rj+?QF|5pzt6i?qՌ^&@IeEZ<~ڑcG~ ͑o_aiL,\o< ǎd95>?J ,zݵtG 5a\[QZ*>J' Ȣ,yObIH 0adDGB+pBpWqn]FaI?JA\-Vhc٫{-uڬa:!#0O)3qiWkq~^Ds$ձVM"C4NºA]R%[yy(6 (ȷ1 aahUpھQ֎ou\}뮳Y-=B_AI ΢e}MaT韰X3QV2&I ~ K"XM7mqWBʨj%@,sG_sNZܲjuxbڭjTlHy"6!|աi҂o 3! 1<ߡ考b)R9\sU@SMX獬_%t=*m/I}/TN2 JBY,_yM*Tfؐ0q}q1(4()prdex]#?uUxV)^XA˓}T ż^BVJij &Py^W0n쉽T?0[sX$Ne $Y'Y%l_35apWF+LtǖMILE; 8Tn,gq q ڥU71Ddm2+%xm(3Pj*2y 5\Es89mؚ"F׀Y[vxiEV"[!,891G*-"-jy^cZLp5τ>=pGH"q 7U!z%Y#Q|(|9mW_07ҝО^HIENDB`PKmPR2RPKx3C-Pictures/10000000000001F7000000E93CD1401F.png,|ct&vm;ضmƶm۶6sǶ_ԙsuwMUMU}&\^V AB\X| ߵb.!,u &6Nd~NFX;MJw3lhˉz+=<-6bfLJO \UY{,.)Q$bZYSd(<H I]7xv!B=,V'SGI.~1y'Es/kcoV2R8Cjf̲5,k4= P.d' "#m+2cxvF?P4=@icht-FCs8)i|'+nsM/bgzwn8(aMCo;A U12V/U(&~~͒W&\ CRKqwrQk%p&l9}!GP_gɱCL3\ =uBn̟i@"cоDЫuN(.`% 9P6MNi\|-HVM. z3G⸉axJ#6p]w3-Lz;PEi-l z%t՗\cGʖ-~:V[k sDX(a0v+!i,m/_dPMt#dZUT*M"h5sѩyfPe],g62ƙӖvg7#iF+t\ Sihy^Q1p &/)5p+,O>\n#վ1YL~Eo!/++10JJR6#&d[pMYhN_^6]-d䏌LU07W9gvzA5b;0M̶199 fo p5~nxo9]bB `Q+ c(iReNea^ZGrYXNkhF4k9x1%Ysš)?5tEuťxHg?UExyzU-ܔ(_uEO"I=_FNɾ5$d (u`wq4]Ld/.5"<)F< D(|}i@sm3%-̱Oⓗ *yAcT.M$nYxD}>nC)vY JmDZTw񹹄ɒ&LF/xJ{`ʆ=@g:&:i ӭ/NG81CjBqi~NA g> AesDLMMxQ<F匶.gΜm/[w:g]ӠE:Ž6ߜuNw>$֡QUiFsPn['~=$rח^@+}2P"%[{ o_-Ac.e h~m4ő|z#~WsZi铰eǽ1Q" E?q۵JqԧNvnl[Y*d5^) @"u(_УL*($h2 sk!=4W1e&A4쵥}DHLe|hh&H#SS/F)7HҨ9%]Yk|w[8Ħן3&^2 [Q!TJj[S9)8o} [LWu^ЏB]7eEQ[Tnog?۩@ 1%{S slIi04̔s*NТG~+; 7tx}x@]p-OGw0#+Ԁ֗H : -}~vB0!J);BjAFw~8m~|rQ̛1Y41gn`"Fb.۝9c , "WfV$Qaטj?aݦ*  ,&JSq`a轂N0w9#1M `o=1{ 9~ƟĖVqrj%dpb ֥L&WO`O99ZZܿE_NaH}'I'g',kQn)=ιA-س{@[jg&I ׃&:ܠԲ./ K#9 h wpvb/:X <Թ'h\^i|PafYN&:M$m_o]t[E3=h(CT%Ioc*JҔ`וiu:cx5=8΅ժߋr,$uB^FDނ{[t5vP/BaP/7Ӗ%5Ȼ0(ͳ D y遵1K?Eh{9wJ;:`Å { F7'|Q.e yxx/6caՓl(KVcJTИ`FJ.U$Ƣ@ 2tuˀ-`>E,@bD>"\ov4=,|'Or<2WcPWʐC rI4,?)F %rs Q vwH)n#cH^a6;ܜ3/2T;q˥f-`2p?tJ}-XDs29GjZVw(txr K]yElMbAC-1}.Y&1l?߈n(Mi'A+Px_r-%Fh50bp ->Y'S{UL'*ϯMmyar|(sw$#3u oP_G4)A%6WA4#đY@!t))]^c}X=[O$!qǀ٫Rsf?ed{'i͙g8qkl6'?S/v$OՐciO|o]V90rh\׃z Ef-rtsj4Iq7-ŌFo8dJlH q @LTLfdGhfq˂r?dOѩ(2vLwѿ$@6M{dE. HfR9泸Yhj5rcAZ꧁{pٝ{1 E|ėv@YMM%FAXvVC QnGV?)TC?/D`{"2,l 49/ԩp?m76,ʌ@UIh31%iD>yqX4EC@ ۥJ<`b7%~Hz|n_#P5gmkJk;dYrJp("J,Q\&#[ӫÒPf FWݩdZT3wΈCFE%ڵo pStYQ^1*cN  ^ <`=1e[;j]LBl)T_BT6%x.$kUC}ӭeX].oE0ڔsRB+(#+'Kkol(a3 vAy? y4{r" Nvsb#0R}ׂS+47g*nNJkUV,-[e8Ef@?0y6Yv(x-ޛ:bINXnajp~px<E3xo)\j )TOQ-qsHL^rȶYP$S" b_ms&$eAt|@գiDzP,W,]|>?j ,ZtKt"t]Nnm!xɋrrP o[+j gȻ@P<>CSss/S KBNۄ?]C˕Ê8zrA 9seK;k+JO0r|~tsېODZ2 = OXd1`)~{ 39HIN.׸ %V0<{vD{Ȅ$@bRy1]R,"RcÛK@ Y>{K̩&@ !r5DĞ-^xaMト Ua1uo-aoСbcR+LEFw0\ RF~M+HMQ1!;SB@Ay{|ɯ [,k%2¶x=( WDH?,G;e̴~a~u^#\6uXDbĸA;k)q~~ZMT5Fy{Voyd]1PINxmqm)p8Nb󇊢Η=z_Q0nzşDZ16Ydupa Tm,22ij}F0U[ ]nefS9q:-k.3`S0j,O|ZTtg'ħ]P}0ө5p#Eu>5?UWSpc0bm-4)?b&kM(I5t"%zM.xy|tB6N!+Pc0Af{y!X8@c#J:u|Wdvob <=`fkrӪ [>}^^Fp@4G <*;ֲYڭ0VηkG &WPw/~f\)ēw%(zphiTV|=Y iGm2<]m)K7 lkOfXPd3ٲ*G.8W0E3U w{ݘ|oFFH _z:IK۟ۿZ-Q)*wSmu-*Z+>RR jr$ u{YE]af L((L+X\P0xFPSg}y }XRqUO.ދĽ׏oY_.:1rbi1*{}*޵X|Fq1\ꅏ_0*91?PaLY^fEuyXDP,Νbcsvp7ֽ;Z*,`|Wx%\ hwpKQ OZ`0W&,V<cQxU$˘Z\0>!birl:Z ‰7~Jnc1>ۋt vIU3}-=@u.mg7A-߾.E=ej=ϖR)V|OFٍaEj,g UV풛!7d8m{n{I顝w'_iޖRj7xvyĥW:%P#\c*?ffJ7i~@az]I2F@Y%+vۜN3CIEYۘFeI CyjF'+-2i$CkRAW6Vz?e.\TiтUN>Q$Ʋ޲mvY=[">]]O "@选nik2g~4WI^V9Gz:aAoSFKC;v%*bƬP5nz?'pG  Kc @DqRc}d(}F,N99|\[\YAC#8{,{SQ$["oLaS|”c`$եݾw^=2.נ箺D־&{"%#/.J"y!"L gI¤qJO%#`{.2KAwY#[bPH6t ;p_ciy~F^%S Qv>vYjE"qbgVҟO;TňdcYxF?=)*iLNOLĩƇh>Oݞ,qY'WRR?Њܮ,шy֦'֏dRtNJsd%f2 ۛ.*Y-V/m=d/j^Ydy"kkF)d~tqy(Jd+huUyipzYNa,ef19 gj'Z|+sO,+](K4h–6&$ahI ekәjV3 x50i)tψ/0U$,%ڭ+iDU֒-D[K6^zge(_`2 Ql7Uh8J,C5uA@|]t]K<3vILl?L]-v缿u1>o'-:Z\0BS1VӚu"sv?Ze2xE x F{d&hGLH e89L]**©ܥ83-ЌkiU4rbw !kpb8%4^,Xl ۧ&qP6qxP= LF b8w=&$=*sJ0ua+~P,mk^L!kH)1Hfxu4]+zNw` L2ҿ9KBćEl ~li)79'aTE$$ % ]Fe[I-Jn}>|yԢb7 eHV7EsMn9E_hd8DbE6첢r#`oAl ||ۋ $OWS4x{Lĵ@66o~pΰH骿8A@2׭/.>OTzxRgC^vY'k?N%NQLȀ4| ~/|d7 pTrS*[nힺ z_n4ro`;ˋ zVY8 {o^F>A$^f:.S9Ñ)$+3`\g yO }` -o&>:Ppkcd*\&^Ҟ+C2_ )$ \T'YBlA&*N$Rko֝JKVm-l@$)_T@,>V~lܘ7ӽ#ݴ˧Lhk[PޮV](۔!M  f`)l6P\[[p;=` d(}o]^dA3"\?MvMP[ -BҮm{33M o%-' tpwtl<=)Z4uGJT1lpYڱ٢UmmmBL>ݟXĉԎQd(;MW򫖎.I\M ,.YYC%j|{|y <")o<9rQPW&iinolgN0 ent*ecd\a%ңH X)pJeZ8_>|Thd] \u$35$!Cvؐ vRYV/?<=e 1sC iqQ>;ex+kjd2Y4 |HHzazxC ^2𕐈U#!AKhr|BD˺ݰZ_*.`_kyyy~74MLM57-Lesw O:88$KG/񟹋cW(l>@a \U7z\oG)ұ^OJ?D9v($P^~3 ]?jx溾r<Pl8r }*,i8ZG. qz.D=P~X_(V00y05B/5b45I̝)%$FFfh2W*gvp˔~x0LԌtvIYPVBKsHc @I_zU4)9B=u;ULM#[hH=S7JV9.F$ Fza4x9oQ&lCN%yԪrsE9 iSftmqGRưYז(A/>X973ޏ6 pv}ik@2%!7rO#n)}5C!|^^Uzg獈5|5%ǼL#8"Z0_qB 57O!gȅ̚l>7Nlsљa4N%ޚ[4붛nR{59‚r2^s7ЈaݓMHѬ62+ݢ&`%}4@6Rl+Ź͙cSNYS<JwyF#r믗b 4D>c^&b;]kg3bnq_# ĨZNyչظ10+$=Hc\& ן[;Uz~؍V.[E f>l=I+S2Zig8]]pqELm{9k'Bm'n;0Ed( T1{jܭX8(}fB̆Fz.3t?ㅌMƯw[K|\צ*X'x['aڵTWZ_Z:~&PYXϖ]|ԍ,)mtzt4'8 ^Gkb>g-/^񞾉b";o9Wmyk|ϹsgvA2r;γۤ/dEՓJߪ2zɺu\SȁE 2㇤DFo5b `P\L{lch'6ּ~: y1ܰmW*EdtPe9YE 9 Rb- P/F6x`omVI@2@×_[ Q=}Te1yk+=2.Te i%S)YsG/Ԥ|b|ݡZ4uie:ݰp9e_HR7:smo"Q=4%>7 S)|Z|޲_V]vooli\KckcUiw6&6VG^rWWK_yH?]:z@_OMiјոV}E W^ gg~|s$||__UHe1yvzē~iP|&U>KeqĴOt03d~g-VvUqNJxn8Jnu[ İ*NPodzjtVDƥѓBȁjy>k"1ye m$Hqc9TͽY/$ L,@y{ظw?"j%pc49>n+͙ A!BP *bqQZ*L,O[I8An֧gWMXb Azo5$Մ3c"s>IDbgRP [ÒӸ2.h:8u_Ghd!^ ֔ƶ\}6ۓ_pE7Zf4g9ce'~8uޝ^N\ UQާиhkҲhBg"kUnu"M0G/˧ >xqrA*der94:Ql]gn?볣>]7Rq`]xKllS҂-+ g[擹\Mfsji%cdWE66A Qo99g6eA$BOG65&º omAG/?CI0e^=_ _Lr~0Hs tupTQ6.- 'SV~ŎY[A|z}(U15Xi1G9ֿLDP85ytcwe*'5k"}1HQ;Vr&Ŏ "Th7\vLghJ!5z5sҝ:k IJK!&S#_Fr3+<4FLY@`l>娬0rJxG%H'Q27MGf|aQԢ,݆D{{9zJGo%^^2m땎Vo,e1U[(^B Z\F]VF–;_S=2՟wTBjFg:=ū 9>#R~]ŕԶ ]+! w׌c#vˉ!*J>\j#hnjF?Z`Fm}9h=XbQGD|e*9)D>bR}p{Gф8@/e9U-@g|EbvgL^ww 3c87.>VWO45s@m>τd\#(e#`Xϐˮk&:Ng] &-^“cqLb 5[8!Lwڰ Pj}Z"k/s _̶/q6vu XYc bO9 GKā:ocj=Qwg*~ɓ6̡*~ k)}]0Mwߥީ5ŧZ q5ke]6WTCij_t{$jm<6 ǗW kFXE]׎J݌wZ"YM}@*픮Mθ+ڟV &%/fr&=w*8l(?ֿ,c >& PVu{4?%VGSZ K7c1f+j z*E?}ZK2!Jڟ,={->hW,MeG#o>>IY0_d0jDfd{_mؓM[԰.:mKtR<}-6$^ /ۻ5~Vٻ2[y7__j8'eP&wJ.0Ze9e"KN_~ wR_UMӱLw-cp֩;41^p_LB~8PٽJe e28W:z֕e7U=av8Uy.Z+AOT!MJj&Ӂft ӳWJ>S>> 0< H8DdfsK33\UMKQ +mApyN@;]z}|ۓ$k{TݏNi).>PӰrBzQ^z_Əp+i1F}QXYq◽yUy/uɔuDgO6G?9Ȗg1J* /] 0SLro'%?6 {+%Đ{&`<]>fig}bPgst")Y2oReYA@Cei v>S.4Me"p3VoW-g͌#+o#o=~s~϶To3t RF-t| /pcٽ4c|NF5=#L f_ؾ,VV~%з:ߩnTIDp8\g0wHwL9zQ̽ CHh ^*XSuB7&"%׶WF4k/!8][ Y4Cp'lp`]kpwK9WLTuWUTuӚ،<I֩} "9 ޿DmgEtu-!{jTY$Otodk eleWӦO=,Ys'w^ D7>A _if%z_눝B@"Vic9쭲 DA%xsMih /h UH|]b.O ޟN&%*!X'38 iCEGU ű%^?hO A/`v^*w=~-HNhQd),M;Ks?v}3"]4b܌y|@ F[\9XF/."dyK*t}O(/hCRGQ1Uƻzy0оRHd.!W9_dxU畼ɞg$J~_׈H y,,,YFAPyg|z 2o٩خ3^.MKpH zIP˫4]ծc9a⡵K:nm-Jh-$;: ^ڭ$SѮc{%]T%O^a ,yyt{AKԯʮ'5:;WB?x ; "R)JI}SP=LzPMAa咷D: 4}> V̘eCYf[nUgqg^qd>wdHˋH)|בFBf$bvci~Y߹<-C.- y{pM 9nLm8t7> c-_3twsI=^Xb?>J"F|aK\ky Kټ׷T0M‚@ ]'fjh]\bU$QCX_l:cPpgZ(b%cβ T.ɿz(~;A{yE9x }bd#&"[j'0JC2Ҳ?9(ArN);g j7B"f!/ϋ6UyH0Ş+}>5ՙ.E4{FxQ/t Yؽ:+!l^FMCD΋k v?<{"eVCqXبBpRGB]t))?θVEҐRGe`w+C)㝓~jxW: Ί[ 첨}- &ccl@Bň覆*};JXHE A9''\\\_Wb]ٟbY+X-\F6.}*/'יB[̈#jEA(*!cxs[PxEPdQd[4o+]<~QhdE+y2zD!ucZ_̳oY$>4S1)msSJ${}>_ l#%9-gk{J[z[-Bdio{HD8ڡdDK᷎e G7I $m_&YwK[SH=S#8 ~o{% DcZ=Wet_pQs$&^ {~?_DrB#CԔ'(s?~x{@?_pFI0Qb*R%9 ȇER\`yD盯rÝƏ(2Ұce:j+/D~MWU nj1m +mD L+NE=K@rh=[s.EOE] p| =X.`+mMqCi46=ԅ'qSx$; pe檧>ǬQO3Osz`E9"Ͽ)L 15e-['oQx/ʨP?hm6Յh2R8'Mvq1Bz"P':ߞ Dnym4`:b\p6I!vn@J'RVu#lrei' 2ƼO&Ib!TPD<[u$CtN^|^ifY;3}Z녈7Dꭾ,qjSgRxmX|MjmxrJجH>|FnѲ=}k0,\D4Zp5|6p3$A_;OJ^?kp\ѱp7 4Mt35L4 ٚ ,_q팽g 40f-}( ' Ǵig`mU#A#yPz%ZGĤQo,EKٙd٩ W-fG'8 5W$U0RAe0hY誐ԩ=!۲Ӆ[:hOjEf0z*6]0Rv ;]O^7QmfeO]uU?lH+ԣ|+X[T_(d8)Mw_ԅnM퀟$XMYmz ={쇶ҸösN·>fa`Vj796xMA,ͮ?tc\׎Fǿ_xh!2@LBD!_O7!u#LJYci3fsT>zOEvY y&(CX{ǠBHvv{7򱤤q66y#o VɸڙE޼ҖY]Jl$(,V#c$]]E3ރɽ57Lm{됎2.%mkb AvE|t޶/9 4|O/i=5Fy$X]Mf;*ؤ}p|zp: 9 tb6kDY:3]% "Ȇ\ʠXt^sE>pTQA4k5ʟ݃e**;A9^gQLoZ5[ZΔ?^Bxf#t\uy*$T:O6kA7p?B1=vv]nwXXJ&Rd!bN[$Go=B8 Gr&徣4Xm4.'5`RVp:MK5kЀ\D,8՗WZm ;ROEhŢkO'{Mo%0q>x:1E` =* &bv$+@0O>G UyT0nr7̙߳›P n;2UGT~jן?gmiByhz/9IĊOCg~V!'//CA&r p(@\[rקT7o` *,V>܉`y/8uzimc*ٗ..Ew $(a3z:q@!$xqDP+eߴdRI+;se0cQ 7[tEg_ʮMM]d`b҈/ #'_hh9?24I+EIc[33&e5򉮲z`H&#UdDDe 1#˗ǽ&Jyn/37F2H'}63:6چJ`b$S0ż)B*|X]:d&.{5$\#jtF`Mo'!ppȚy f\h܇.$$Ey#ұ~VSBlAa(#5}E dETU\:(#eqwng!{( VjKbbX lPN`yHHiV}VLw9s5\*`Z@bސX!xn0W:*0mm ࡓM+DSBҡ%P1X!Y]~sЅťaKYBSM1/#a:pqD28:*p^h[S"z(UN9WoiC֒\S.B$?+0/ b04gnxh;ze^3d؏۪6$[Eĕ HΣN!L(+4]Z,6&qI "熯}K בmgV_g@'ţZ8 |9%ɔcmo=QQQ y$<Ÿu,Aboh((1{zKm-2bL1?i  'Sbo72\6zN 8Ռ@s8 w|ϟ[&op0>N, 72ET.VD9 p.^%h%ΣQ0H[vH%nve\ZLGbjՄ[(!=!Hͽ$heSjBKF[uX<;6M |24\k6H1!X;DWzmf3Uڽ3vivh3]/g% Oo.oϔ= ̖ɤ-j5;3^}kJX2 8UAj6ck{Xl'#t?+I40TY3eR 7/AWg<[O)bJH#cGzPgdևj_%y,,Ѩ HUcW;xn +>S5s"oG3ҧ`6' =!ǧK:e 5'Atm"@ۭoN9c{;䈠*"H[DuxZ2eFW Ql|x]oN L2ߗ `;xdj:낯4QwIKZ[[21Q$z{WO`_$j1trQ'l~w,TBM~~1C0&˗WILՖ)q_AV;t2VoҡgQۍOf:EiG@B[(}r3d\S[jZ L+~2`p VXE%UĞ}NILQJ3˪AٰW2m0\49VBZ>50!mCِZ4ß/获N0ŦmQdBb]Xf!%‰;CFfZXPAI:p(1m^2}Q4Qf.P.xtwt:hQѐ+7|CܝJߌfXpɃXR!,h;MLhaZKFM Ƿ<6r>5YwzJPv>>GUrYlUlBQ ݯ:S-;o)<қyR[Fx!hc83^.}RKWEv]f%fYBo]<Pp& 2S#V\CeW)]!E)fbMJQԻgwʽAg )$c,DD,_4a 8&Ueə}Դ,/~WZ|l &9> A4؏alxH1a\ !\P$ɦڃ֬-B3!+A/ $ZՆPނo=AB<>ǐ_!G*}X;kþEF^5Ֆ "vUB}ue,7A>>",Ų(nZ_M"kު|TMͧvH5D15?f8'+!ÀW+4Z\)ҋ\'m^B9^B.2 Il }彐j  x eJ^@px@華EB J \ۄg}{_sᑽ!zWp 7!5Z/RZu ųܖU*COZ!Nŵ~7ebm=Q>90dtp{廈jeg.}!wnL G+'E-TSP5KŠd w=q/F4Նh+K 49r%BDŽK?'fH~UE:_  Tz [-x 7 0$[133bkB5QX +Q9۟9GMl`߶c^"IOLݲPmR o3 }鏐K6q^l+ )l$$YG8SV d\#P"9( rCd}v:ePNM͡o,kn}Eۜ7)8pSuu(v%Kdux 9w3Ø4;4V3+aGkW +-/i噖#:22#lQTىNdvmo1i5sV7/@Pjxxjm-QZTx^fg"o1#Dl4K[l6%# H1U$#70GCʷ~; |)Iw~IrsB_ r E|Arb0Qy8z{5`ml?RAPgRGIt9"PF@Z>zbX=MCe*}ƽ(EN5[)zN-W~8JFUKQ7CRvתw꺗]4#g< meGoL.;_dǘQϻR╢PKHCpbbbPKx3C-Pictures/10000000000001D2000000F64EB5542F.pngL[UXM%@B . Y] ]wwECp ~WLLWwU(@EGAS0޾ ~=QGV~5;`Q5XGK/Na ͭ髬ٵÅ~aS29 bkC! X=q;muݽ|#|>=Y^77E2zaA3V^I$zCTjTM^^8Kˋ˸,]'  )qlKV+~s gduⵘ@{&S"Nv߈{,` ,p:W:zA C.>/82?ڗyf /$XN"ޗґ+FSfwA8(ɝ{{ ty1tqkoi~Ew h}r@niMQfV;5pV~`8Dey !S~.7~,>PZÂ23(Ԍ'}5 5Y޵ uD }Me6 z>G7kLo?F<=~s&ɇ&JۙӨ# ׵p"0I1ИljJ.ׇ7ŸHtj 281|?}~AktuiEM;;_ȰBt~A&-l<o?ˏZ { zr1g\  ؞?:*{VAE6 XeА@F:HY9{TL > ]WsZ1q:ح^>LΧHDVK@Ӌ4N ^JnoLrm'򁒔׿ECRuDQۦe/ghƸў.z^^4e'Jd%ѯZ'sCּO͹wm#N|uH7vF/]{DgzS|,soP,7 Uu/,LG658TkZ?olBcLx,'  ]@Tozhyƫd,=Q˶%d19` @YS]lg/ӧo6An& ^%P+XTG` 3&x?FPWD:Ñ0Gƒih< ?p/\ni(3I1抋3m՞vʞyDW(ꡍZҳ ^4H93ᤈH7|d=md7C"2u 5@H>s.MBq@8mBz`% +[{,3^/6A zӍH*p00ڿ7݂$C J>bm4i}F/5_ kfc =x-KMju+j"iXhUjur=D0wS|焰os*c~OC`j"diͳ3Kh_t>i\q-2ۖe [wlw" Y+EQj s3&[,=d`Z[ˏrfE7 =[zVp47kfYaHK 0.ϦCp6T1نcF Dm_e-8{^Voq47@lx[+W@yFn~RX/ŜƹMU`ZHV%orKۋ| vr8o&ooԏAGl^oO5ЫUJ sҪOnF.fƊSO-%PN3s0 NI^GX<͵9J?J 2I+L_݇{j8<*W6)m;IiV%R~R_{,{ɧVedAZ kVOn"#ر*f֓;@nt}>$ ,FKLaV^)=3Ľ*IJ·?m`TfN+S;tc~}𤸽1mϩ$9Lj/k8P*AI4׮gVZM"*͚uFᇮ]a`6t- %\ %Yf>z\ᖘ&vy8dW_";%O-k6#돬 mqo Zipk-|x`PaS pյyw~:mwq ַ%׹LNh$KTvJqd; j0 ]wċ_w"{!΋z!IA1;[VdAq!fcSJWލҢ1KLU V A#/}'h`IiBCbY#! ӻ޸zķ8v=ƖUw&H7/bS<f?gAw?,~|+`0t$|@S`v n,+:y1𘷝\'s@Ý^3}UOJ]o?4u,/켣}Թ'xreqa%v#ۙEM5T!htdu[bz/}˄Eq+ebkTu\$zs̎Å]h?.bt̷?QӬ!y>\.[;:.rnit#|:5ra0k+8|iۺ9 d#agVk^FIY C47&mңy4=7@C: $>\G9f$3Ȩ|"e`|TK9eiu+Є]9PfOLRpݝqiV>XT]B!"$ (y2L7*:WIpK;Ar#w/G]~b{e[e3?"+`cz%FҜE*ͺ_\]~}{0T܈#bODdnu kiR~@L1_nރJ3x6L#@2"{uՖzX:2dΉ`s^r½wƷOs֨ Ӭ*~u36n`5W|2֌!I gftha j_ G1^rT@Rgٴ ]22ic:r&l/ ,MU""iD[ j=8q2l O]Q1=kbz69K2`),Ewܔ:v_t Z z tJ~ Y&w{ ޔat2ÿc7yB :^L_hM[s%+cԙK#߸ Z5u$ xoe;$Y˯CNMlFPέ$*7G6K~SeTOvDbsw&\nE/YTL ;z:'œm_X^:j΅G֘V\Xsp>yо敱!3ܻi'.1n-} f0{xc΃sĸb뿍ڟICSw3xNbmhBU,m̷+'z h Mޯ!ɽ&*u$% ugZ?6c=VRagװ}7[k$&q*cݞ}Q|K |lg#u4Z )sv=n2iݝzm󦺧vFe*6듂 ݷә;k2W#5 79Ӗn y{WJL롲$1W]A!R'%s@ ?0YJ<$i:U*Me Jb,e"6e'SY7p8v:ϙZ^F/fW)>;U-b$uNՄhBVw][9  Qv2H4'9MP i5.]/߁ ZҒi~֬ud49`uzݞaX,Ҙ-hߝ |Y|}Y/`REUf`$PC8kSz[#Xehm ,¬C>~Kmz5M$,3oaP6G&}+jwlR,M :8kz~r]nC?+(vAEY؇!E0g/ djл$} , )ssG#V#[Tm( ׭ *:ǟeizA+k<=г-muzI\ڂ>C3Z$^k=K4H*\$OmD3C{˚E'"yq6Qi-{X<FOZ0l4G6iú\zOxyՎ"$q~/gAv7d{df QÅnzZTT5eሜ;;z[h}:?x?ܱܤjta\z2 Ⱥ9Q*ak]VQV!}:v*ۇͻ0ȏOg}~/SI4^+ѻ,vmug2z@ǪYT9 ƾ{ͮX)w\7˞7?.i0Sz,N+N7KWY?A& |j&F43<ړF!zS˘߹A nݸ|hl#ͭy6ɨe[c3 E?P_)vho<&±(G /[_wJipt¹*HO/ℿ0v)ݙjv;볦t4Rx4KW7,O M"K'"48@ߑ|5:MR y*93~ 穸#2̻RX~6Ngp)2 A: l-Ǹ!@#`{,جdx yC'MHٞvx'bNƂv\ !? A5 KI`R7P#iXa폞xr4M%8F'\PKIn،irc).y U)`WVxUK6DbS:Ps!o^KpDG)t:eDv3|M@I("8@ŀ=gբwIǍ`_ߏLيAzm†<>ކ0DDpvX.Dd=Z8#AˀܠsPc &39燱x͈FgjSh.=]+.)Rܓ@1RD{Y#$c P^:m<- v~:L+|@8ѮcE)Y"zXsrˡSmZs7%6Ϥ6粒uE}Mm3⤑f+(wEP T:Ѧ߀ TiVlr3c+o<b:QsF26J়fzNCm荞r^t~^7@D'1r2\%L9~Eڪ:^{dXDUf RBtJ;l2׿T>߲ayn*'ij h͜ *n@(3G2{0=A~"bwP2]n0Y5JL,rxL%~pTSTSacu |KNV,cWQV I S:g&I@U28hkuJ"]"\˻/9(H҉yҼ)\CP̕ET 1Gc1y %~C+ĖvB +=` F4ΩOۿHh^o\Y>.I^gGL4+=vhk\t*ȝ4/XټF=6|;;LZ*!I?ˍىg!WïOЗexoP8oRg&fa  quU7VKKdR>w k_r$ V/ُ.>@i x(NeNf23e&EbKS6zbJ|^{"#g59Bø^m{ eySpu۝:\D\ư)6 Xh_v#Yv|UgF)_{UCaFۭ"$a(L/UCɯ˄3L`VhEhOk\)FL}.u2ڵAi-&cud/0~\C8=z,82&Ȥ6_opV޾ 6`RYڴULbѠBAt T{xEB!;_H3Ӳ/&C4<0yoX,3 DaL+Wh茴Z !x""O94ir0AoA z*%Es*كnw^ˑ?R>WJBÙr Oi:'eݟVQ3ǫRjmϡR3OYO w$o8zTzߵ_AW (&=V-끮s&z"cL0E'yLqsz!lYg*1ȱK!6 ;>gڻkG@D2p- LZPٹ@^/_<5D[yjWwf%>2}ģYKrT΋>=*tʢg8ggO`iV͡&!ɪ<%5]~z^[XP 3п_#ג9w]쟠 pKOIRR>◫$b^M&]{~u Gi7fФwӣBpa۸&aeǚ8>5"#A.#ۍ[[H||&Y<݇jX+1Si }f'˂e&' 0@ϰv0߉W`c%m? Cms4'Y]BTRǴ7T8*kȠ4;BR ]LRN[O[j M"خaQ [/bEXgcEAbf;9K˯"y߲2jK˰'Z(S֛;3^6{ F"=V'S NxӪO%ҫ{Xy+) EṶG[>Q}so 7:O~|3.:5Ad8zֶN:&[x>rMgVxZfz'LvsBF 5&`Yb!?Dmĺ>,B}kʕVoQuD\bo*Z9Js(b)>iW)?!]s7Bb [Wm(_(v 9K33vg'3lve ۭDKgY U +|* 7l\2gM] аKJSF.z&[@[Aurs,4=F{DžTp%P*%*a.j/QP:Q-ZY{,Hvڏ.}I&!W|q'n$7k%(yiDcjr|Gf+IɽxP](r27ۂ\[)Y=֙l1dWrEfqT&%`u,]ì˯.ܔdX)U=n>N@(N(OkDNΕ[u,X }2} AGʓQ^7E點_po|3v"-_ 23եMuGy@̿ny "}f7v&Ć<+\۫J "z D49GzT5̪Blю6d]fʸ KT,]B+|]PN6tV@ޜ^6ԋd 0 -N~'>6,Rŭbt:b Wbof4Euf65x?f0&k)>Z68٥=\3:L/\s:Ut2\}+J<[,knQ0+fPD7Lt4$!XF3IyҜbf>Ɯz;S9w Etj4V9J啊6mB> ɩ^}1鯭+֛}8^ۘA ?5+M=|5|g:x` ]nIvʛ P;2&X5ԇ]0K@ JuynNrs.[~{j]?jmWj*Ļ_l 4 BYHl,n8+@%%"cmQӴH熓Yn7t"K^8r9L*+3-KS eFenBElffdxL&mR7TOA_"_]:8 ^H~ AssiEqO'aBhiޟ!| W/fwimG׮FH1]c-tBLkSwuEM6.&a(k}엨?-^o yjr =(a| N f8dBE5YFX&{#}'+[D㜶aS;i:UƺdяVp=~*+}c} }j +3~8h|/ ub3!2uHΡs(M'2Fwt%&5,v<MmT 9ٗԞsAV_4fK-M*#yBI.! 2k~ۗksq+~ zVMfs^($閎&U9_t3mwA?. 3*ajx"'ylK.TSDUWqo|+U(ٵ%kC$سq/DANfJ룐$?EȮP3n&jeNLF>vQ*p9׎^ ͚碬Q{ e,k'Z{:y29MnI{oe'֡shV3:Y4d@FbQ) kIJkXmï6GqL!W*t60@S6:KOjh%;(Ⱦ3!Ш&ECIz]. |ѱ Cg+w9a*RN-C(UZQKb.t>^r_o 8gV/zzQ bX%= XlLawHV`e2y/K\Eʜ5+T?B 7¸%rs $XRib|*~]X5O_ "`{Xa`&u)=X32de t uy_V"Uz]' W>]4^M >VGg20?YH+,9- XhSpB47=ѱIyGoHZ:yMn?ϗ\գGg8b=Qxˢ ( q{vxdL='qp4~eߠ(p1ǻg-z j.6rG8|=, D%8mW;ˣ".;`dx翢Gf'U履9O.wz/ Gilp).i!Z8iDO!J ,o|Hʌ8{Y'5\GJ\4j7݈11|=-ԯpUqگI&t_R(J %~IG/%EfN`L4 bȠĆA^.H{$[DejN1ʝ&w>Hs6;H&D*gszFGm^5m''y3$k!Wnlu2nٔXm$\?;Ų \ӹ,+.-Mw1[bQ+L3β?]m +p㿫ρ۽%3 /O, CL;١{gjI;;|GOiE.7QN:Q7 ՠ*N'm߰w!S'y3T)?lvw:)uag1)IsB)ze8xzbD:pi7M$Hv[(0!Տ[CKUZ* lӥ||qۿІ Zg\K?sSK!C:*L/D hybP 2]$N7 >/?o8ܦKLZ$3;4bz% v3vmG@t}(vK2!g"GĦ- 8L"SV-7N-IWchYh-a qQN8.`XJ'6}pJlJݽ[ഔہ9㱺"=kO>VZO\4th 1mzL3ݠJX$'cG\}J+}&`$VJ˫.2LՃ>ÀO`*s8;dU}.9hvjȥCez4/vGY>乹A7󭮮PŅ> --ͫW.HiM 3r<x !3VVCo%;ճ>Fb xq[n~!lԚ7 u5]+V/eYr$|()5rZ31t˥7f PX۹C|i4J g(0U6?=VxWuua?llؙj dE.I0@]"bn=-spF8S5``0B-*((@`iX2=b}RJ6;X=flh5 CШN6^ x*?8d]û{~PP0篑2C F+,-wn- 0DzZ ]| KS\(% N|h&IxTXbc_%;+/+ Sm` C'u0n"&=w ,`eeEל*$q G@pտXp[ RC7XJ˄AfG"iQW6]> u B_nm]U&EQuIt0Պ#%%%bq_ϷTg'1Ђ򉚄 z=MpBж-<M|O[`faa_i[c/{AZ}np'0RGouyd"A@]1 jߓ,nVLG_T_PvY\ &ݵ#a?Ѝ&?PF8v< B.i |YGuv/Ec3 oW^լ 77h>B;^U2GU#OcҰˆ3DYڷbdK,M!<(i*o0hR;wQl_䔱_BCT(u(5);~:^KYSą;R8 z i`8HegfFib`Pe`A@LEk~zcUsj 棶[rNCX.k75<+k2pA='ME7cs4M+$ODY#BnsZq+;|V̅)3|C=΂6ÒOJ㧽XШ(YCs~hkxG&@EM A*&)LJw"1򕣣X>|9β9UfZ=֢|E]eVUEۆ2kedžr*Pf@.h 6yѡ_I'8IrP^2=S츌&d@a7HjBu4ڝÊ/VpZz(u2pJ*<謗;ȁuWi?:uh@")A6lGFI4DiLnLECbk-^,nBS/cC/&""oЛze.,i"VVKW>Ǧ<?lwLx?ggR lp`@{ZGD0NNO.QSxA^\ɏڽ#IB/#szyW׏utjcHRoӋP]߬*sz9W$u3 PiCCb>)'sRtQLŭ/1f1113ubacW^nzDwsL*\`  e_'g:o>/oW8^:`t'8+6^j_zzcGㄺj7@zQX]ہ(ч'&C/qbw(5Yϙdaea "T0X3QZӐY]iL@I K4C`h#KTq(?Oin*!v gó&kɪJ!ar_a\܀J1x(J_O:'o6mMQYzj }y{̹׮0HVv6ϵn;0FL~l1hp aLkCHmϬeduqb̆ k.)Wu`u6vZ$HWum *2}l{?L.5>s88F(;ljAnS%a!^@_#~p˯eQV33bז޷Q`d妀T1t_&`T#g*+@"jn4{p~יhxArdC;CZ/i-k`8wnʇjQsܴoءi֚핪g#%] 9߿#0#pW#beG}J+ؗ.RĿ3ej5CNͫmlphp(в H-xB+g y*EzDg$P;K]R6*CJޏ 'kbaF^֖ܬl{v>WxZJ?-+";|}gYw|p'X <3Ej@D6$Tjd_֮ xyx?g-_eI-)@8~)H~9oƨ_ϟ$r"AlrUϫoP]` =R<c|,<+l>𾉳Q$ !~n`&c [{F69`{ g]Σ 8bX;\(ÁGkCE%&lm/MqSW*A~5na:`B5uAAAdВ-.4l?O$oެƫ* 0bYyѕbMD]&l{2 nD^97dE᝛bL 8ES2?J{Qڢv曓jG禩hb?NA,f3R]]ϯdR6ZWZR2@`˕AJ}Qb}~g^j^K,6tcp-+L,q^F5FGEIŔ4mS%ϩ4d=\hl86 lJ)Vo*ЃFL3+H?s)k'h}+d д > 8 fr)62 )Gձ4 Jo &L##yd",(P%1i$|Wm~m_G]= 27`7Ί`4n0}gbJ`NX%wo:ԝGĀCSSt}+`^*=7hp8OB GL~*ܳ >f/g=xH4!j|HyS/sWg5VK!01nfr sWcjN[QǏ8|lZq~DfnwbȘ=}Ƈsb5L?lK\.h14+%+pUt̥;+Z}WQԈ00JghmX\ݓZj-"l\)cn)K[/.ܴW5tW(]dP[D%Nus86{Hnl0W!H$b˟c,p|!**T'C..h¸B~eNޮx],LKlĨs3ZI`q4{ET_w R:2Ïx ֛ӗ7(=(Fm4?W0YNp 䊣wVfI]v"Ng},i2*t)l\o)=fGr͇51T;[?Z 6?}Ө m ]ry%+8eng7YwH. (P1[G=|6>Q'LXH81%EגPTuHs]r|罋5tȩb*٧} > Btggpq&^\2c%6.WO8j-8]!LH(fkWiVCE F2q8c>lxW Ŀ5rԮk2#|+@\ö 9r>KFV6Ճ9vxr iM}Dt 㱏`UX{!qYkM9[3kh(Ey2 j](gv3hݥ,UٜUkC cqG<")K%lߩB4woޝ&u ;2|I1a ْ iPP~_PK?GUGVPKx3C-Pictures/10000201000001AF0000010D8B7A906F.pngm@PNG  IHDR bKGD pHYs  tIME " *N IDATx[eu%6Z{sϹ>Ţb%RD=LؒeiHb;Fb'HH$F$AO7@ NedYnǢh=H_Uǭ>לXs5׾%;A(vWȺu{|9$"x7|_sN$ʋ`L]aXmӀ;xѶ]c90/pmym77~+::t#s-@=<>9!{">@ "8,XēO9, Ѕ ;9vw=&. >3A290p=C8@D :[f^DkC@"$L]Za&=Q<"C0zp EǣN !?_8Ng]c%kAp p#B4! "GK@D.9݇ X$ݗBjgArp@p䐜]V5%@:G9t] w!pfw1ˆ V "A0C8F~#6]~7oa}0@Nl79p:.-| IO3h˥ =ڶMEAK`@(  ""& `!0 I5 s[wݝ$]}隦YkFmf1FQwfwv7mi7VӍQ7L΅{/~[]|_ݞ_9n_ >L{S¿Cm銗w`s}O8"|Cmmƍz*.^M|?Ot]Wb:묧6G1'ǧ ` NX.^wtsrB1g!0#qdi.,Vϝw";)I۪'=rf+ `rǴ:]$) e5>K9AgSY)$_׭;ZI\U+R8Z~8CbFZ4%_VnNbT S$)MN jx˳J(N(!w*Q-=1uqs^x^$c.#D'홼6]0T%)XL$߱-T{9=W%DiI~uZSJPE? Ib^*=cPZԍpk:fE(f|ŨyoC3j @݇N>wp#߼=n/SѨm^nIy?ܽ29/>9EkI1n͛~ƍoooxsϣV}koqwp:_`@ΡhG 7-6QpIGN #%kHG<1UU,R f9/8zgQK%2*E%" bC[46aC NfMo|ĥ\SHKR!W< ,.gb"xT) VJbVLi= Ϋ/ai>L̐bQOk-tGugg#4[yf?،2=*z|l4pRe'r9%3yV܏(GəN*^RL&~䌶Z%BAr/A(fɡ p5{udb18!ݏNP_8u,+ȶmX{w|_Gz~_=?iForwO.w^?n:m=s/]vYd[cGc;/o͛Ѵ 67=wM{v 1 q'csXBbTj0äd z+(^[&N53PjQ̆rMP=0FT; PK:c"1b{4&V%IN9sD}[M\sX%HAI+80C5ZJ|BD㣁3\J{$5*il{a8$ ꭓQe0$Gi1M _=65Ȅߤ!|TXSXQ-9(߳YS;3%xܫ)(GsD @|BFZ"!88SL&SlL&ZxoǻK^-Aw {kיWR8wx[o&G-?k_ ;;;xG;8E1!W)XT6X(ˉYRC1L$(VJv)+HI_gE5rBc50١Z&T3&:l*SX`i'#JD|>P>ź.1 Ǧ}li\w=%QϣmG`f@ew0__Ե'L]K@,@aMh!)8{\uSĈpXҟ!.q1L%a ?0*#&]I!q*bp`Ď' $(HI)Ӕk\D R$(l2*3\vS6^dhF#BD,r(6dPY %XS:%kL$嵗30;\GJ }WЭɞ*LX*S ®8HRiK>-!),AjNYpf31} qz/]!-RgƘR#a~B%m k\6.?f,K%}{Ǿ "ݕ8phJU/Y"¨m1?Y/ȹ_6gD/~;x?Ã܏x} !қrGǙS7u=b ߉N"հ~ T89T r|l]lZ(-0Zؽ ʡ D+_jdg$q<$OTbu(0T54Y$E!;dZQTl+%ͺd[DNd SJS%;x%3yDDف0ƭ$'1ώ )T+B86,) =v5$)&E 8>JJU$TJP` :8!%ims6YYnh\!X2QA! g}4 RZ|+w~>t?s Q&`Ш\Q)&$**O΍RzNl65=*|g2Q(d ɆDTܐ!$ʦ/kxHJ}d- 뱠gcp CD'>gqXH@7'XRF-8 s!\+ɠ賔XH!ɠUsc1DFkI%PY:C*nYUјً]L& ?kD X{bP s !KN3@g8sHb >ё9nȞ%< > !`P*pR se[r $h>8נm`ALn_/) ;:|9|"jjbj)^z嵿mZƺX6^{>bww]Б{H:$LVBiٹ>eKIP$P\1='aKe^ qb<%[fZ@vrKHQˠT },)ą`ݟ*i;" 3g,9l`>36$)0[\y.daȚR7d\'+hx@xى+y􌏛Zm8TC&X NPG DрJ1# B?SZVwSΫ[wgy!;U_O6!wno}O<$67XW5͔(ۥB<"֊Jq2cS4WF7dGZlDI(75|I.^k 0ʡ wgxcR2IݐiZ g%EƉqR[Ru-\Jd a,哃Crbјn kC>+)6Қ|Bu".Z`T#u*5<kȸnr6f7]m)P"Z{LJ`$[".Y#YX(]6ٶ$zF91SM]\20\TAȼNkbЙ\]LY3m]CWpm2 cW=y%K586,s1 {Y}@0?.o$XB <ܞe"YibZo^]ߴ} QpZ|bLlUvvc9X,Vh[idZhw 1ٮIŖF$zl%@ UoՀb)="ƸG'Qث|sv#))Un2W2J53.,[Y$" UT"acAq!ߛ=] ^i0RvIJ^p"uk, lN>:P`:c^=X,666Q6){ܾsseu}&yjL$ "td$tUj1 ;3Ԫ:FWXa& 9W n6`sdjD`]jSU?k'LnbkP&_N!dkǦ g!M#& =c<`g<޾^@d-'4R6Æ.=5o) N%&k3Ȭ"UL~`SLB2DITU L IDAT6g#ی+9|QRU㼍\$H=UD5JJM)S}w6 J9*h/akYTRxKvN`FF-:H{uWҴ-|κwac{kMf3loͰպR4֫%qꃙJPjU,\؍Z5is̠,dH0J7Ȱ\d1ъ\kUİc^̦VfY=h~!CJ&' 1<>) `kEZ j2DMᐪMi}9S vw:Y F+p{+ΌKMVmaOarVd2W,;ֿ(K R}#.vdv-d&$1\+ qFD :*Ih=p`͗2PloUUk4OԶ >˙,ZlArY-gǠ UY Kyrp>{8z03tz:72?=qxx}og^HG=QqONmƭd:u˗Zn *̔* z21DHᔤtYa7հ#5ӐL SfJ%c( B9<8{+"d,ǕM9AɺC*^_BY [F|b냪P!!qԚĐf Q47V2R] }f'&Vpkr-N:k6Rv g^ܝ=¥.?|EaôvΜErQu> GӒ"L>%C AUɊSgL)o T񔧦}*ώKsLT8k8,;@A%l['d#K)4:;`'3ZQڝ E>t JQ;7#C9ٽ G݋[W"(_hT4WRE>cb(v4FG,لc^b9I@ a^{'QY588 8 !=|zm4\܏aD#uuTj,PQ D%5Jy[1KwVwmJ-hP+8J(&f;1 :|1af*۲b1jՎH >sML$8 ٬szuZΐC?U)Z \WYI+òQS2Ŷ`89Vd.8;1Y@i`Ak&gdbKJaVID!%ΝtF̺pŒO'I8vjEdy2 V9Q ڞ@{:::Fﴣ&*Pd{O׼.!߸yϮV8M86$w}n]FM:.eyؒ*MҘF .i.c WU# EfU_ddc]3ЎHLCGV43Qv_VfL@r􁋵cǒ *kc "މnJЩ Y̲ LDY6sՀ[dYe(JvĪPHm`Auq#9hkIS.)36 uο0}llږ$vRj%4f%ڡ7jSqINb$rjOuX[di,K3N̒}]y E تֻQx')S)rXleBi@Ԕ`khb*}$3f\3%K'q&-JK.4ip1؉թO 5QXlXHH4dZ%|[eDCC=|1DtMi@2ĉLv"gQg7 ԃ5{flJMT]%֢] K&q,`YlmUS|J{*rThn*Q:\:x&X?<:wg [ :t:ȯ񝽃{o*xKfiq֙,"we. $`jAT·fQ"X`{ nQ*I~C4\Rl G1A)W5Ғ#LZ 4lBSHlGXR,B1 $B2'.VR"%Ycz}}sanWbtppx1ey1_0LP YJ,Lܐ9 ɮHȣKz:z i侮JQ0MJv Y,6+ Àe|Qg i0#:ߖ[E?o %Jdj_Air'$n붖\G !40ҟŒa$CҚo9VTK&8C^Iu`Z$nr URg kƒLk%z֩*! [*T`TqNq,!FBҘ=枸W3ȧy™jE,|Z9S[ hUZ\v36gİ 9Hk&򆜕ZJęNY96x`c[fruB @E3C-yA?GmE~dZreZ+L ]jHˋM[7o_#H2wd4"iםA$ڵx7h8T|dy$''']*JM$KIծY#,U>Ub&d R1r[&UJ ase:3*bF=@E*J73N/3nrN Wfx̙Zp}P<*&Gd&˅$Wd$8Jy猠UѱYx])52@YjJf kP3߀JN~6Z<|.E* `6qttuƥ1Lޝwz{^h05k?z\pW>l[q> 1@f# K0gZx 63@xS ]]UJWu=$xh<!yB ڶv|6}ʭvO/'0ϱw&6x,[_h|=y]|iw?[7޾W_}ᅩmܹ)̤lIf"4J%C|;w\s}&`6ҥsg/_>%8?~, [r 666o`Za<cZὟ~._(:7^1 7nܬ~y\ V5^},W%Z<#CvO&\z}pttcw?~xq3^]އ7ΆbVIhGOf< 㖬X,^-&V|lkzB6|i/lmݹsy80_b `3юꗥ#f@i4M%}.\r_*>cX`6g'#<xgж-]{ַy罟ŋkkxADxWq#:m|>~|6} GB׿uٟY밳W_}[8/} _җpu.oF4[ 0x W37XVmA;nstkNj5Q,J)vRRn @E45S9g_2/"XbfFDxGux/9~w~g/.]~lnn:Ν;y罟!4 GGG^ V+<P1/".]_~9gl9zM03~icKyΕ{^yuV&~gܼq /]1|02ԊFшCrzkiP ;}j$xs9|rߑDG\12 ?-cjDw7ݝ(7D#T#(@St/?bz9xo1=0lN71?=t rGGX|~vipt6MpxϊOysΡEY oV}]& ܾu|~R7s '88<ߜmawg}qzrj8nnnb\bym#W,LF6 mjdzH ]i2İb'8<:Ĩĵlt׿+Wz諏=vvvkyMɍhnա9nXx V5譹LS)坩3"BK@H07ojS0 $:wk}b;{,ku"ũ+-^u5w?ww͚+vN\˿G\p{G8C4I$O+p`dҡ^P^xR#_͏qV125"CtWOZ*~a]# ZTvkZ>y,mtG˰ hO˯p.`FDpZT,}.R ᩓjvpKy0ta\#S],{*ͦ]LQ=T2Q]{l)YU!OJz'Te8U4$n) ?Dk*+ydQL/RpJcӓ-t0MVigbWz %Iqŕq2|V8`/@п IDATX\ϒ%}܎݋*&`L[R䉊$ٹ[i) T}c9u{#ԬzlnC-fΙ/9Ts*0^Q.4]{*\ U F;g1l.reppNH5Dwh,M'"ѕ+Wq]缎7[.[M;@}ӂT)sҤV1B6O! c? 6lAGk,:lt:+S"mXE$ ʝ`8eBN_keF^eo޼-51"\t*D :x} f<|`:ʁ!NEV$Y* I=5"I90:5@䅋pцcJ!<m8bCYN&吝,БЗG>bYx$ Zu *sbeʮօӚg3qC|V KY  :3NCO",zc5*\9t`Y&իt ` bTTRuAb(K[Hazn* Y53T5mJ#KyrDEY :Ehvͪ¨~^S-D`M*n 㣣>.\po;v ii-gQzg0I҅]lZDp;iqU5`*k;霋8\`Le^ ~)rѡC%+FtzAi 7'fnGaGk:{07p 7Xr|>1eFPi;߀kuWq3CV:sѧtFtzv|}tI :)GEa% :AcF %@џUE9>l)I$y2_&3ߧʤ,-y‚K[S+EZ9JtpҫrD 2Pʸt#@z)%S D\v-J٥ȜCH|DatfFxc0:=h]8 bz۵VÑCc;md2 =$CW`4֌#]2[wTOGS%kkɈ"wpX!2Wfk5I=~٠q gR늘*1+#4e'q9/>F>P{|ls稈 4-TYAaiO_fh +@ 50dP!F:q` 1l(SsH`T4{21W$`NQjIߣ뵲~O !*E 5:25-bZLz=RDk`A/ ^Wb;(liS ű5j3:iKJN{e+ e1 2Y$r#ҰP9:nQ1Fa O &MU+LzS-vZs4Pw y.E ”Mr>:-qAָ9D`Γ#g'r4b s쐂h)H ::gd%N'bjf N *w,$A_b 8Ab7H>Iҽ/F i\O1Q|MR/$Ku)V'3aut"NNGΊ2,.0b%(C2|UBABR =Tjex(ՠbC M"Sߕ|"u:JrS J3XَTSf$% d $~q#(.xxnanB;_wm!tQ5DFXR&lNs5 tT z$h ""ppF&R+Z7sH63c+ j'Hz1KN-E)2[ŏ9S.F%YUIEs4c4F`2Q:DQ.$Y?C̺\/eZs \gRǨ>$D2+Zj͊?YQIۊJ׃|,/RYIς06y8O*"P"셼D n:Md -^d4V|ztxw4z9kn .W. 3m tCqNe5)*u1ߧ#"EbG<[SON@`\7;/Ǟy^x G70ă;c-uܥdx\8H|Ț.<͢ DԝY>U0$ռDXDDrF|$1aDž}XtLTsaUwydT 9.HD` 9{`)h`oF"Bu1Vi}Ұ@\e-4Gq. zp: sp871NQ_񮓇ڻ}{97p1j`%cǖZ-)x =de/K~BE^iN)&Ƙ/I BqNnj$Bgx:2A:.XG:Ң|%jdm:SД3΄cK P,R[τp̄(s|_ѹb.9!k!%"+AX_[pWҺoD5f1!Hµ;1к9]s`uI%pSm7@ %(Py 3C(Rb#e 0H6#D$)̰خ1}W?1?9'@KKu\"), 'f]M1L|2.hH{%lostq߉O3ϾBq׻¹W^GK-BO[*Md6֎ڹ)*@\95րPf얩fZUil:!!X*,ɧtR1{Wŧ.yWjo҂(<pD-:.T | D~.1MoMO|>?[R¹_"kgvTlHe+$vW.Z>/r@/ۈş{BJ=b,?~ }mvɊwmH ˪TA{( QJC"~ >V.*1%U '=`焱j=.#vUyp^|f2{RZ1k|n\Cү)dyKi\F}?%HVYץ KrkNtLc{\Q.H<ͷ䑠JB9FsL| =He+x#ߊΏ~O/0Y!41cc&" M‹|sb:[dֺx 4EvWFh:iMlTHkJ&r!rJQC!VŬUPtXI.Zutg1岨LFu6KKQ%E,1(&3b"a]qt\]%.AwZtF9v.~Y;y`RnuBb龉ZlգI^8.k&HE$T>*)c\xŴrG;lV:)m ellה"\:X;xgxw=f+xɯ`VL~Ԓbfd"/̲) YY_SorcaweU5>~p⥗p8s L&"qa uxrg\w'.!7p@ΜW=zj#Q\Z?w,x%XrX$: T0$i (;K%'I*<_E^ C? {9-qz$^~)塷bR _} :D0_.B"Iy!$E3W,Lȅ rT^"Q9Gj(V hg"RH+}5$1qY91䔣%n\>0X|yM\TUkpe{V{ JIc @ۤ]3[@Q'7}4^_U>@UVGPEݍvQ#EA\hE qEJm9'ޥiɺʓXtgsx_]<}cxR\R~85! а#NȂv sVZ M Ψ(((Ov/ ( T΍¼_FN'. f2pWAš1l҈ ifNloLכC}s O:!X"t]`gY1kJ+cGaT $nD / RD 2N|9D"0^/BKX;o1Va/v/}1Ͱ} ~2#71[Uc1rEZ;e"tr;%}fp:E .Hz51*&O7UJК  o 1lFoX*]hȞ)\6?i(OYFE'sHzMjF&9"B`vY\d,kDiK\IܵߏI#E̱p0cThAJDhaϼ{l_Ks$-YpacB0%T L lΛL *K]\-L2QBB\0w/cCt"'Rwgkrń vWPR jaڸoŤj.d+XO?ߔm,8>Kfeaٵ4^ay؞Ru]f߼C-ؼ՜fB`$4F P QykX.`៦/hLїeW3@ Ad! J.:ͤA+  ڶ)PRDy'Q <f^V+|L}b5B-G,k;,%7,&)VS) Lc# K0LdqQI|Ȓ ؞Xb ^,\T0Nj.~n>u`84#/YmBIKL>f\&XlޫJf*&w:ۓ̾gVcre0PޓzDٚ1i"viP3HX`A\W?Qΰe0K%wx2dt KuizàW6؛ )$ǰmmymV-2S;j%"|ܶmӴiCYiU>P\FBjfKfd0)MDdžLNsIf>=}WhcZPF,|*<8Tg?g7T5)I.hIR [RաA*9=I!JJ%ADpo};Lyk$;)JCK(dB%At/`{ʐINKԻU =yi<PL5˚@Ϊj1 Kz|]e=A*w"tR?db^QSj^⤂KSG_܊@!7|t1ߞ 6>&9uC"8Ļjy3e0i`GѮ|s ruh"IaN MC$U^m`B>Člh > Q!1)8%%A "S}j]B pBd8qYzQڑYΥ차P R؅̈*:Z0O*'ߣ#BBt鰤?Wоn2|.e&jI.t58zʞd;1!ޅT"O Bdݖ~=5 zL VWy|cUt*5?[vq$B $ёX;*fZ5f)F#fB;$[#)#| bgG-됥0PG!sbAXQ$)wӻk ]f U6ce'KS/dHڄ;֗aJ _n )u]zy÷ZvK IDAT0џ~j \)'UP[\1yWuAB{TFd<%$h =t'hamZk+Vix *nE>c@Z M՘Bx*8";S]! ~h⇩U PfxMxL)9PrNPJq\Yw 4*)4/hG\jPK4!ka6{J"E,#ь{G`#M}&užgc5}Zpċ#d+ K_ 깪v7DT$zQK]{ $Bpy< Wް*~.6DQ'4{ޔZsb@ 1K^ D1RFӴ+&{lDhtÀ}W Z3"WԹNC/A*M BV:"\^ns@M cDt Մf)24a 08&RL(aA¦] D.|TBs,_BV f(8؇fDj.X$%4QyBD2n#%v@B$[Gbă?-ꦜG~L0Q6!s<ܐ*2-Wkssn$(|@)Tǖ/<5ާ楖RI?Gog6anń\u<$D2Q ^~@KUI:&)wiRBVJG,\1<`/Ijd)IpaG:D1LgH*u;Յiұv, K̽sW7v:~Ck-6|ݏ=wEVLN'MCJ:*>;=.k#Co$)Tu!eI!TH"?'Xdx,AFFLS˷SB#wpTGWO:tP1)oz/HVd()`jLj_tMT{5Q1RVS(ܴ^>:JTc'\£N)l a7=%U^m+Nb,;P]oi.aK^>)/ιR<]Mu8h8A('5$5TR#+׌?(P .IݘlN;.QJ\1BPKK5m(IQ"MA]`D{Bh@>qūEZ$W W B)v^GQڗTS "q(ﯣF4uN5F׊[YiX[dQN2VqTa&T;-]l?A%rrWw!HI_.O? *57Dg:H ?JA9_Ԗ=Ã}Dh>԰ߠϺ4UxEZE +lB:Y,Ơ]QLėڢROTeD*S"ArR)ttw 4p}a7j`JGJ_o(_*` ɽe)Sދ,frn &0zh?IGJӨMB]Q#RKJ홪qB eZPwe{(y جz?_lQiFQndtI!}!2:IaCìvcTl1R!)v"w.Jm%# se74~;rH&ۛ xmwa. 0 M0l>*iԅՃX)][Otwtv+T@2x!8]*)z~}11KkC!/![  ŌV !(j,"%Ţ2 Npe[@zPbW̼I4(-~ą#Ï*t%~q lB)r(|V8fx(YY(nLAl0$R6Y̌ERPD.!QC-Hy<b!0TQN%$ddR&hH;o)9 αY/>;P D DP谉3X|fƺ7rޫxygɕ΋BG0! V $Od*n"M(Wݔ|u %e [lSZ+O*(ȏG]7/ +v#bB УRzua7Rq+W.ckgb6bH=Q3(+ڳw/|v@AC ͣ($jg)5gx_e b0ł :gWA/AavʢΑ,E`e%mχ *ô$7i2"*&HGvB zlVxm|tqtF8xh|UIA)YeeRҝMK`ޕPuצeh(\ iUWrʟ5KdC DW6v^!XS]!Pi=ƈmID{…mnlz_8r#W^9~Wu~9;;74`f41jW[QABK$egc`:+14>ªaL&І 6- k:]jۢ,hČ}ct;\i})Q$ )Vu1B ߕ2 bx9*tաگ`vU캫2L^M$(Vx[9k4Ld"0Sy}E83kgQ-ncC_%=ųVzJ&$R)l./WŌ=K\qG\uD)e9MSA δ£n ~E9ZeQӽPŐb\UܽQzCUζ5nѨSmZAUAiC.&kW<ڇUvKW\ ^@=RFbW*؃f6b:L۩HŝcǎvH(*^׋vv`h#iK/tK/}W^y_?vvwv]w;3@4NL&fLg-THi/1c{ J/5X.QW4KQ1HiZTUcDy1 =&4+5$\ھ>%tvv5 .l+&XusLgMĥW`KL':BMlgd(Dv{ WHYJ^  ̙_ Ȑ!4j{wBJNL`@L%B=heJ%sYn,yyW^)#EiEn&2:5g`;ڐCPhcTU0LQ5F'Lҕd=G(H g葏7tX 1 up{v/7ɻc+Jd>T6h - Gi!#XA  (q5~Y(I;5(pVdjqѢz/u*ȍNmfl.%3`^}[*JqeEK$gΜ?챟8{wBB\^YƾUq38 ;99w~eCuRB/ݍ!BPuGP˦rWhouQQ܉'pY76-bж4Ml KK3,//c:[2VW0-a6diJz\6[hzϻѝ#Gqw[g NO"WW`UMKzkdlwYyļ7d!/rA$7&XuPPCWD4 œ|BVB Vf߅Z C:z5^*\hL6؝Ha$ JPE1Z|gm\h9W;?o}s0T>TTFUIJ!T*TN'zcsyrƆp1ƀ˗/'x?~lGb}}W._^0xkEc0BZk#,|-\D^{ gϞQ$bQjnn"voukkq`ma O㶃Yāؿa %J) 5U@ !|-nMԳaٮ~N Aodh7\2YVQ4;H& xle9kX#H950}BIq⚌l!it|U et'/NŜm0PyKvcћ}8,TQh2nREh},#tIb06\~_ov*&.q5H:LʴT8TѣQ.Kvv76Ob88jbD*#XJ(%qetekLE 1_L%eNjȆ*I=H\)47Jq^gq ʹHz k]suǖ{S]3gpԩQ4\=wwϟOxp!&0<'F )c0) ש<(ExD%JAۥY if!s4;@4q1P>UqKf)9\HHP۰2x3\ ahj4= 1)h8"%AͶ"9MBU||}D1S)\=ոQUx!Ϩ7Rvux+Ͳ<ߍ S 1"iru9;)ٞ'TZB±U8xXc _XP,LV-e%EKlW|cVQb|גV+UX8oe!ob˯S/0jHh'-lnl6\5S`6_ңO??;G~7?+Wȟ#]8:Ā^z]6^ϟ?^{-+k5žbu|]>l3 /⻿c8s Ьdh!1'Y~3[Zb{ĐŬ(m!ژNonKϱM11+3H@TzNr R666KIXMA1D=Ri;`1[HA̗L9)x ݥ]i< @VVPaoL`A"T" P$,HUlБwW(wOujYvֽM"sc%9]t-5fVqqpY /!\׊aL*33YIA^cT]ɆO(XwE}H(qjgJMJD**Eᵻ"kd{!"JOhӉcN'Xnu9<'E}W.\Ǐ0 {>ތ d/OY=K)Kshk*9_ժRLT=֮m /@0f-|tl}U,v߆}k<{3i/OCKW."V#?5/PT֣ kMgݝTL6?*"TiATbJfmpa +\2O(Vs1J PCT aj58%1XH42gU)Ka2i5Se0dfA`΅K؊>ti^{2$>(%Q"YDW;t7蘸<puFۣ%,qYJۭ#U02n(13,|xUQge!;ru3ZltɎx"$ A`y-YSArSUd%6EĆ)|Tl ]C ~iL7-^[2{w'>[|2^t]*T7긲 6ߘ0_30ꔮXA\aE7bwNA>?(&;%픂fX4_DzJ&!*I쀅' wSfeB7 kO00:vRyjs]B1 1vmn,a)8'<}Aub)ZS.a!*#I, i(A)"ޅTawgv9J/6MW9`TV^zTy䇿<&Wr-rW+rJ}W}oy̙-!ٷ0$t.+˜Gt/ ~n_%D ED䁙_~:,r_N03-,$A ve1^ǴKb8`Ĩl `*1(Uj&l TݞS<|9fSNq,+&QO @6EW  ځ2[3VdVZK<N@.w+VKdN7r|\ 9ȕ*QwI8މ\=XYz641E}*Z4` *S!XxPyEnfymS1Sb(_3*@^3ҶIYRjrQԆՃ[xMfXٷ/<}iw=ugtߵņnؙ%`f`eeqx>W4t666pSPg2#5nUf-y2mtV̯oO.,9ř*ЏA* p((i!jh`qM帷Gז8hD hGCkIosDv@p5\2Zk!Y8w[`M$d8a À~!N:ϿrI C@ }GO>3W_m6g>籺rF?o ]L¹'O"Y*. f7eK<>!ݶ >sqW tɵƗh2hg zYvulu\1 =^4ٍ sB蔓m5RC o_Kx 1Ȩ ;\$*mqgSŐ AXCO 9q%, Dކg(  @ a*j"{'fTSD@HY /ghj%Fh Ru^u嫄|_F0rq`fa@{`?Vr ˇote tZerm8j7cl9+'^G3GLJwc3uBa7p!L(H5nV]c?`~oǑ{}Q5r]8W3ϡBM5z/gHW:K.sPZ:20b_ZPv4׃h [b^ͭ|/C$HqtH_z_''O뺑$)N7]|y#Uo#}tH%5P!hHU6PxqTioNg-8M̶bЫϟǓ'^10Urݙ;<|yLT f=>{.L˘M;\#7aq4)A7;^3{ ؾtG1{~uqmVW2ՎDž C4HSTQ.6Eؼ1V#l0.^oMk gh$ןy3u0{}^{ W^d2!fzB7-Xsg~N)giI e[ 1p%E\>^4<0YMLiY7>SSeRvʡ9۲Y;G0|{B&$ Td䂼"Zb$aGZ ;htqWr'|[xK8W3ñܾ#$K"B7AHt/Ú4!4ߎ9 &B:-ȐaRTyAhHXmf߂f7w]PT8IW@y'dT>s]3=f ;w / 8|GؼcwqmH}sĿiJwRT9[苚m(.`v;~go|0B n'B1|# />e|?CTaͣ|mN-~, ޶uJhWk%a!UɑyǎkWWWo&4F/իWv]oks^΍F{{*<7L(BspQ7oq`3oҥKy_1iA; hL[`/t3NrBs{$0+by6p#Y??0Cq^^&8cem C6T;.9>w@IͿLVr:0d#6H=t_ʗ^ɳoRo/?q}^~ az0fsl?XbÇʱ ݎE>*s5bұyXބv ^;n RD./x`w(&`/c_3!u0C \Mh4."+xDM5y'<6c5Lؑ fKK8xA0 6o>=zKKKho{0(ugq1VGlZX*Rct__f!mYzf4&4KS>nk''-$F}w4: ۶@ۂigl}_`07*R{('ɒ[?VJ*Pj\o*BQGzI$fz߿:N+TmpﺰhfOazvsU؍:M:u꺣==ܬ9{lD9|eH@9#ՅF()sF !b#0\CJ&VXYB[%̐X~Ow4Rjd#=d#w#yW.a8JSx~w܉p=R=֦15ǕzZ!,#,qOetVɓhINg/^Dwٓ4{&RNrWZf.)A1QD^ KW#햱G]mW|Xd ;0g}=_]ag'"w~I)5A+Np?\%I!rV!‬L#5X e"w 0bӿY l#ob;)x%i!(0W>ŤL(PF($A3m8ʡC ,n-sÃ>CѶm Mꥪ1f:#lO\w/UՕld'NMBk-ơw<Y@Dĥ?9\_G>_ZG3LF@@뫧jn~ǻڊ_ڴ򉍏ZBN7MֱV_ŋp% 㻑bfݛ)oJ^۫k+ԩSz7UhWvx1|PH.lVdɾ@%aU* 4Qov\\E)y^w?x'x;etݐM̒s)qq|moCҀܵh1̖k'U[e=KbD2w32[~0@+~I6d˧@[9,/{vg|RP%epe,Gӧo׋pǑ#8w6Zii`kg?8S_ xi ٷYgG?W[8q!g D&M||A0|&*IWleGez@nthUv]7oSJ*^MӠiZvw+Yg!E25~/Yŷ?/_µWQ ިwݨx ٽw^)!%AߨFV3mCsE,F`Csb yA>N{l\_y<~Ͽ| i,Bvxp6뚒9VDQ(m?.(EcgC#0,Mp9>sXnW sau 8bIفZtY\e':";ve\ rEZn+jIAL/<|81u0$GAJDh1ɽxV TW6QDJ W\w!O$<#F6&m;X 3)mUA-0. 1k[="U+qU oV/7.?~g{sGGm:U6؈[n *19R ].(+ze056L&L&,5f՛w)fj8wܛ_O)Hz(!og? X,-Ͱ)WA]3ԋa@D\(RIʵ@ M.l=/~9|wwt" I%R8"O, u{ d 6ECmpVdaeeϽx78ڬ+/ ڃ ?x>~w߇:\ eF.;L@3UF9}##Tʽ#_N:pk#moHNi@H%L9YwYpQ =] ɞx[G9ϱ'|ǐҀC^~5|㿂wlmnmk|e칪l"]&?{|ǪꫬG"ٯi@)w=sGq#߇1 Ǐ bZtMRBjZƺ SZRI8"1trYr\ Q<1`>&C-UF۹ke~= ›A?s z'{koU{ PRZ&a r mJ¯zX՜a}7T9m*MO;ηݡֻq^;!BjtJ!PSA{ "~oAMhcE,o͓=},&f~qpC CAؙ#p)ԇiVLJ20r 8 *%§Uz^C#prCI6R:tҌSbl!Z{^籱U_!7a0F:b _zPdͣ/KTJ.Ȑٲ!7+ IDATO~hyM">񾇏?3/6ѧ!KFHv)L  q6` DdWAP[9COWss(?;[^Zzo&9hDyR= %_!IG>8!Y苊ng +e66@sb(&[Iyu-SYgNm3.^ęֽi܃T0YimbשpT͗*@J-\Owfߎ_A8*68sa A7ǐ%lw6N\wϷE@ I>/E^]fKFi(IR{\qWi{ Iúz8s >? fd ]Dɨe!0a7w!L_3";Iꏂ`0sM kT-ж-X*ոZOW`Ey9(j{/daa H, av89r^}uzt̉}'!9ȠI \ &_SQFEw5'cf 3/fA^Ęv^ f\fAɤj/G㖺ZqٍrzvH,yis$1l7n'3la uD)"+9sdr$'wɬ.)uc[H,9Dw,m]:E0hp'2xطs4F RA"'O&<,>ٞ+u>6u]̢Q86dx(X"82uq]^ZURVO>$yloo_2ߏ`ԭ[Q%l6þ}|=[na<d1xO} MҥKw ɣi?8.^m:tG@D8s _zQU7/bmm G:|I<8s 666BcpUc=zx衇k.ƽĺ^A;2"BW<`xh8YK>FْdH^%KL+Ցq,RI{U-FK+ M#89D\xf3f},9\3W-DZwģƁ~OwqJ?.~?!M$\:kW z ^y'’kVr/>sGwawa1KW9@`sޣq7e!`(d 'PBFLR`(I}H ѻƞF\nz&;b`]enS<u)e{P^@F& `duNnYcXp877q Z${laI,67a a9ù&Q5E7hr$3kW*) R(hb^Τ;Zԇ<ox龺Lۑ-V9;~<?d'Oܹs8rN:;wĉٳgOxq{|Xx<ݻw9o|xW믿#GOOӧO4 9___E\x< %|>OGG8s >͛8t:/ⓟ$._SNa4?q7_,(]4rA9L T6^/>XtkoG]}xq=Wc{2Nj.v=/'oǢw:QAy YUdL8WγB|5[+v3g @8rw;W☱iS }hVz` %K$N{P{&i|NZܪp'EӧOcX d%jp8dFduyyI3XPIsej@DcFM4F%c,f +C{T{'N; )LUafky $r'Tp]yFދ\.1L03vUkkkA˗\,]Q "-{.62;;;׾1:n޼ON8wʲĝ;w ;w7n@4xwMxB: _~ fz^G3//`6&%n Lao)x]*Aާ]sA6"> srT 8L|s1hFfos_\$}Ta 4\㮦1nc_oGRO>x(Z(WRh} E:- wvK~ߏnh4 ۷~E <\, l޾7 ,sTݮHHF:3ΜG*>Qg.X4`߱# غv|1kb>D!K5i <4]>3Tgxjӥ~2R]t^Y4B49w]^DNȩ-J\qWU^Koųk co`m} ^9l,TjW|Zv1~4_{?!h4w?c#qB_h_VJ'T (?q9Xk?۵}=Է.^D48rV;h( ;8YU1X4-wp(`}geCΕl5͏-Z- Ag[-z౾Qܙ[ױQ̊Euv㱕ڻfg`?':]r^W/b p[װBdήu -FM&Cd”5gc0SY5$B쌚'kl1C#Rz}V ^!jTؿEeg(\M1i9M4&swIrW:{2'KqE%Nv.z;G;–?;ϝDYXwѫJly׶w00azn|[gsPYpsnRҳw*Tpީ!!fe2|Ѓ5zsW f~&>4y] -G)l"jpx<(iL AHEB~lW- ~46WV`(4ؘR{~{[ Y 8l?iw,#Lvcz٨$YN^x_>ObucE!FwVLBl77w^;ہ + H"vK%!gX m+Y{_55bjk ,B!w֢fϲ9qRFŔԱH)IFIH{IP&bLQ8*R؛,> I9IcQ(k M 6iI|#2lDo;2DLѪ"B攃oiE(OF?񁍪=ո7M\LXݯ~%~ϙ&2'&>(??5ʫ;+wsC%1ʗ_f SE.PNue]/,k,',FXu;Jo=!R|*(dB(бJCвn6Åpiu UB9xI"^,FwPOFvJBK&l=| KԷ(Ҙ`Qs 5|% `-Sssn:F@l0N1rDS ʋUF^dQ-cu~d&%R,)g yaOD1#=qpBio.ķUj̷oav܂kXct_~/*U31 _ZVEUx?S2ڊ (lbrk"E4݌)&m# :)fCBFP,YVInKdZ)RcJ%5ܸ)lg"@rbNwl/ȰVilR0٪C;{1>@e,XwkOm(KغiR2w9nʙd\7ɄYoVmH.!*,ugM)ӎ/= eD" 0AIaB?#XLN0^t9t K0Mb55)00;6C֢*K` Z=)Gd9(^dB }a6*/VgKVX eQX0D) BjB I!+FZ#Ċ8P)rh[Yb9G[*=,kT6TQ55 ca )8K⢎!hHz֜,@ygg(; X9)G9y{FmDK4HvA;T0tļ~X} Z.T1.\^|/kʇYg, KBL)W"ge] G0a 8?N9p⃏ӫP,A0M3ob/0r8:"VY"yXbߩcx?}'3GD-18XaJcʶ_90jJbo.u}:|=|ivgyԳ9%yI=pmQuxnWam WPvBCX)Qt:Qu: mxn,;ʽ1ZgX ė@3lʚwR{`RD/) j%= 6&W- Jl'WiZ ;aW7X?qӭmLG@4u%,v&izM(eM] 2VM bt&sI" YQst׆g3tr9+Ǐ`/X9z[!t lx%+Ƞ]<<"r=> CؽQc 1B7 JT:xx>sg޽ccnu":ݮ½dQϏѴ IDATgCmPA6dBl2-M&wǙk,c1Y)4 :w^p:cyOd/0<,hfK4%WW`c`-©|4nY`;AwΥMW<ӿQL@pp2ص[=zz6V; -2;eF 2nV 1ˆ"fR &#f^=fDN A>GeDщ8w ރ+Ny˸x]>bt{^b,иs0PK,K吢(L҂`0ܹy&!HzRzz9IP c)Lǘv|@W;µ^;v1d2Aom˭ݨᓸ-]{:Qy!.|v)>IgL#tp>u 9h]PY`ew^|T(~l;#<)b>V%zn_ߌ'VANќ'C;$ )2'%Ƙ$3iCo"]އNڅ ,s̖ t}=#t < kՕI9m`si?hM>2& ?2?4<`AtLonj{X#H:(_It|zCN 5+퍛6*\T7ב#GpAETUg~1ߝ{# o|WϾW}|*N}1 6psxWp]|?vN} ؝_4F7nc&N|L% f#% ɰ8C (0em5RxGvF{4H-eɶH^^,hl42- !!X(߂sGe,}LF&Gp-態TZl5 Yl8ʳpQT *Z\KW/2CF'dICGee|wh57`"vo[@>Di$`Z De d%Lyhd+2Tz|qx;?@g8D}͔+$D*֭ѷc ܢV) r(uxВ^*xVyRf:q/BT'}CẉPP-:'P6x% Yk}}+e_{;X?P: 󣳨l(]PaAƠ0#ehJkџ0sOgXLZ Mǭj^ZăÊ4_ k+eWQJ/5zyC"`C ACior/ɘE3 d^2F29\Lkؔx)y)ԐJE>!9ZwV?&,y$)XA^(v 2ʡ,nDDj;P tG4(:wp-4%=EADբ"uZeb!.[^2k@;]aR֗ ^.g XXa@(5Lف_8E cPd)!>(2MLQ{"t i(o $hB+X2}R 2&4Sl5'p֩*TUyTZ?>xU(SP1E.q!ɗEz(pem"H†ͯ- f$zN7f?1')+-IJl9(3bZӌI~=M%U+JJJk L>n!;J yJX-K o9Cyp{ɓ[y!.b,bf) Ϣ|whJYHFF Zf+lg$~M4 +$ȳ,L)1:k]ȇڨݳ{@y%ԸløZL{Є<&Y%(4[(Z5mȸ ӌAOOQ]IYC{5B+=FA_S(c]^1-ԓ1i s{+LTق[:\x >۟GO`:Koԇ|>1#@ؽ..y?k?)| {v8)V#(5Br=vxEt|KYRBQFeB\1)`Ff$^HF$XT,d⇣,K,H!^(3ZH)I=Ռ P,ZiKW ! K倫~+\&dBEuT B`yli"n$l+JPL(WufUjR\PC-FRRѺs& ~~mZqet`ŞK1yxʋ>Z@RguF]!y9&זҷ*:"UyƼ0Ch-?9- suA|k [ I =e=ÂM3xiB}+H*2>O|&2K8 B.{ {𞜨m;/Qvz?ߺ"J*Rc YgQ8-;^͢wڑ}] g.Y~ Sxϡ [7`]X2XLhtH">3 SKrr%oX220L6aA2#v$&$'NQ4z>XDNƂROPudTKX.Ihq ҎQqqtHvCwjExB"b?|TKJJm}).猰HEV.#"K>'BHQ KIHDx:a BlOeUDhSahZ$IXt_bV J:Z6 #ujf#4&hP[l ybekR|^B c8@Q ׄNl2Ϟ$pu,xUƨ*T w;2&}QV` s279EU+ЀZp-yW&gW4^,\I lj ~ʪ,7fEO޹[|,ҝNǐʛe/&[>mG: ,LAr ׅm 5gOh.ZlY|y6U`t$d !k$0çB1 (dmB B>c ߴq_PtG/ږj-&aSו||9d=!|IE3Qv!۲?%4g$IׯK!ArrH~!ReU|9*^B`lԐ7gXs[k5*vX]0P]= -xȨ,#N c  BӠ 'x&&$،+TwiDLK-~#0i 윷"G|, m YHԖQ8aOw[p?MaT||-s,iyo)%]9P E7{x i7%w06xQQVKsȜ^vCGGtIh@;, qDi\g{A@O1!~Bi)ӥ'; >xD}-[4"+GtE+ğYYH_UcҾTdoS ;C>eIjʓ}!dSZީx%bRK@ya^wXgԮ4 m&KpuW ?Ԓh֞KPpʬ 90]lڏUzC^?L&LL -a`/SfC[;$@K=MxYą,/I ےC(+n5b,bhLJ < H ;#&F4ryGJVʯeeX(aL  kS-.e5d0G11s)HH$j%N)CMcR!LH9ܐ2C IǣēݍQcv8xgpPu:شIu:̻đBC!cp<+163gGWapW. UIa@Aq%PEO وOwrAqE5-\ X|.4@ʒX(2H"h{U3~v1V/yݏ!6SpQtN7$|<-j^SW#k92Bnҫ<k|2x2!w&r0t4ikhͩƥ[|XhHQcͯ z@Z.(DR\F%|gK,I\g dk(.*ɲ(Cd6ΞQWj < ywƆ{5/3)(@ܗ%\ >(.dž̭I)Nюy}}{+J "Ӡ-W]ZcZ1rv$%a h|oMQlk)d]{>g|w˫ө+Gp¯H[=TP X򊒒O3C.݈z)8Գ9 A&: |ʵ8M مd(B}up,C.P>9IaEkMT)v:2OoBp *LV2^EX!fK1|z/J9|g\S󐼀mXaZl$)e$>'9w))"ӗ D xX`FV{(<% Z˪L2Q{;HJ2i w$bUx6zLM(kL^IT YqIcv##wu*D(9L."XŎĔ\~)iM8-BK)6[Ai. 䗲(Q/4b"s;ID8?3 Аu3n]$!L+Ċϋ33ܙU./"guqppp!e-A#JՁT.Ƈ$ DX~n㎋(h7r_+O0&qIG\2'ɵJrrH@a!=6DY %cWyqe|<<< vYF3[tzIIg#K5R)BYh!{YWR)ͣULYȶv]Qr/bNgXΌAY Gk@bsa>$%!hau񉑑2rdfP`kz-l??5%ut:5wh~@=[a@\KYbȻF\w0&0%,udOTT>\SUѐ~5%ZE 1޽668s|uIhFM~O c%vPcWcCS=efAqDVGm6I}i-fLRhpҰb|@|;xQ-w^셫%YハÁ\.a6Siq ՛|qzNiHʗ؋Z$OSG)™_GV`m [dxmu1;xh}=".*j]hVZ*t\=mavKBe9!ud^\kٛ8oHs9LO& dlTԒhxIEf)!x(.:4ep#˒xU$0n>J|ЪGdtdh$Dh6ėAd Ege |y82, N瞮|Xۢe3eh8: I8"HZNF*R9S,64Go >~67`hHh5ҡGYU(.:Ts]EW%>a vܥ'# [, 0CF3=u4V?peUU;,)rɏ {<`oK5#hQë>$L3gMgf8.*, S**en--\ ƲYq8hO% Ti2ܐl.SN5H&+TpeMm_|֬c|60cr `62xz>^}ᜁ ɣǏ? }9i0F3jK21+{pGNųi\E|.%޹|Ay<|~ߋ^x}^_Fr@e21^'D\3Б X4R"r >J@a|\2B3Wh-O&WȸF?a3M`QE1/tiYG?ɳ_A:=&@L$FbY;/፞1׋w"]7@p07zeU \>{x8 , 9N1}t*}tbޝPHxAFys!Q=q ~~ EL]GUs( IDATh{3<|{g"sBBA2{ߐ3KU/A`L6yts?0jC, X0PQhjwʩ5g߇Ƙ8t* 1!OiZC2=,/*1&BTF&a)o/'>.\ύߛb NmaXL v|*FtoSב *Θ%ysew \˕,vZ?"~x=6"SQ~0&l۟;U G.y%ђjƚ6:I=Z )":r̢c함//~Ys<xpҭ- &5M@D1x7Xd8=,> M8Z/L`礱*SNQ{1LR^7<,z[<Ǹ9ѱ {lVAّh^BS_tg'%"zQ2@HhQZ.zmgzgj᣿{ X?[Z-&?rjN;#418$ߏ|Q%en ):=5P?#E˛=`1o8 X`c}e{C昪5Hcknv?΄S(0lhW.݈;y2XYYG:{m 0^X|sÖ+Xjt{y~,5O-l&Te4)ihI\D gMHi!GWG7[gɏxwfk|N.XNpZvk`TXt]tֆU ` nPGO?b`~,v' 1t,(lz:ojMLn !ۓr1+x0T:Kg/1"˗q(/M!aӴAdz`đXB &SLe0Xُ{0m,B95n\:UJePj]á~nGaE ^ɯQ5C@)84#PVhP_x6Bax|Wޙ |wq?E3nɣE䥤L}q5TB#]LCIJEn6BÁ# ~e6q:r٠M$`kOA)QBɋY/s^Iht, P,4ā~obуq cw_}_\%Jrh[rڕ2䋟83zr+̼},>2K}lY?"x20DLa02{|3\:E/Ph70^|W**UO N Cn ث:LprI'2JϣLĉx/ä5C*۷b2ÁG7.cALv1*7؝-kbc'ay*Ns Ρ6x.:wϼ ?Mt6Ag茀/rk Dxl#! iȴg&EN xYދ54_g4ɮ # D-2Gw,}cc+_( ~Uow)C  `0\YE"UT׷7=ޑX gI|!^_3υt4Mt ;=@2b,8XBCQ{j]ҨptX' 7wӃE$4_&7xsدztb`cW.; ,Vk{(mW.nbZO 0_,P5t$2r>ڑA>g;QftTO (Kn=63Zoo} fw"q䑓OOKo?Xg"11bRqD`H4iQKc"d8w{i?P2lb ijx:^dQVi,"(l%uyU:<!Q= B3<-ϜS//vvv2ZB\ho\BYu 0LFQU(1[iM 6MPaE-onlb>ܾ|~DFhjew-Ƙ3NF S TJH{H#-^*r咲DJ#3#%,Lяc?W)| #i3n8u>4^ٛ`dэ9]7=3ō:~;cj|N4 ~' E#?;']̯ϙWTLˎYt:L.`e؇{(x`!¢% SP>C$ :sţyfW>i3i_s$ǖ5;oUZgp0SEASטmOQt-~}{ 9!PNH>(Mǻm2*H>aJ,AYpR/K <4) XȠ*Meb2)z*H YbsFE4@Zv&[n 2.32TT`v413RbxlR[ߨ7.h&3ڡڼz<!`5J G77^X;v[o_CXڋQvJE7NeDwch kW{lYb5݂a`RT>D($cꈣ-@ݜxJʪiSvɇt1hLς0]14) (x vJ3 ;bI|w no×5A_㇁AiʂPV,ah =#բ¨ 3t0F/RuOt_IB7}VC ^seLH,̆ j-q ͇zx+q}⣸6=T~ܔlI9"^A?G"]O5_0-0T:]tN<{𵇥Ef>$ 4i|i آNd!aĬ UnuKhp(x}CtwР̀PkFH@_NPDPV-oUe$07VL0X֪ \;ʇ+rldB Hw|aR&X0=AW^W!n.n"GZz]\Q?z Ou|=c1"JjR2T#v 㽒+^bG}q\m|z>}jN左(Lu}`%y%%Ox&id$^Y2(s91A#"6_-b_:] = +XKUYKX^nYvOP0c24\%]Le$}1@+qb}0mcXE|vCL|v`>`|=ʅjĝ:\PF _bJY}츅Z5VVVNkkk kkk R-G޵fb=ˮo}w[ƮfdSR$mSF+VlqA0@<AdKb8pR"1YD3{yka::$g5|3|δa\ T OG+ceѳ2}8k^Д%9q#v罶sassXYY>6771N1"5h>Gu.֎ KX/ണ#Hc b(ȱAb mαxv^ 137u:#3i&rGNj93.%+%A\.C@d+JD-;6?G>勤WCeBDp:>*O˾C,'{I$CD6g^5{~?sh|xo?yE[h NTb旱qZ -fi~R%J0XF`*xTLLEʾYgw&ksʭCp'T.ċh}TG4R(3ՆoU>-ݭ(MTb`ziò]tx'_z՝}8A@3fn 2GQݵB@ sveW òluC1h =P.H5a`5Hcn t!˗1Ndж"buCr<)).Q^R #kLlLD*PM#5mPN Pqȡ.ew&uC$46D@)W,UP䓠|!Rc:쁸pㅖ~&L']zmy]{ 9OcD=EZE1pCjE6(5q hb\LsfdxWsH'!d4µWqt/Rs1a0áC# =“#5{)FaciD422U: :A1c fTRVbFLC.px8Ņq vp* qn_9l2hX-ڶ#\Qg#GلKНfR@uE}oVGr8|&o K)Aja!uCs< j0kiIT%gR(.=G$Bסӂq t]!677QUU^t:Yt:,YLv`)8p0QNiO5;z2ZW{Yw]Pו**Ɍ hzЈ] UQkD-%"vittm.˅D wH#(٘z%eusX@^'lKgfo"=i;A༹-u[isҥgRm>|Ã\N d).JX1438S5hjLc &2.><TcX|Љ=bQrrGUBNaF'8# a\\y5ŧLx ]T wO_ ϜĤfjcppHwm*n9(?cvDJ{j,c::Bv~x(n>[򈌒!I5FB~|`Q9C 9~q4αW6S3ܾjjLf3,UџfLB8hGQob Q%([z]#$ v<'O/C8޷wx_pi u_\h>==b>9KEە f팀E P5|# 5n߾ =E g}I5TA/ji&s ׊oJX& F/]T,d7*6ohFuV)gKoUf^J-6_.8Fv0!q-CJ)eUM0E@/Ln)m(@bB7SújD ;`-P; \V#_a\k)qӥ. KЕϼpv1&\aPJ¤3Zx0x [û?Yobzp,pxx㓧2~p $`c zO ’˟;sӿ#2?c1\@/@1vG7 NՁ"pC] {|&A PlgPWVX0Kj!<0!t,-fm4_W_p8f|#Lh߯ hOMմv+geC=>I{`a  8O8~ _x/\]ęgCg_ Vcu;,soԧdBת@%'Isĉ`PyHK1u]֦q ̨}BbVJw"Ü m+cz1 ""G/I? [7ϟX_w2`'gijOc4B]7;>dݭ-|wǐ9"ĠH13Zz$B;?Bh[8?@396 ´cwm17T$~ IDAT/ aGZpV8;cr!Mеy'ؙLN_r@^h)DeF9Jr )95 /GNID5b:aT;l6:@ tٵyF3cXa>9Ăk;?<3HEY_lN.{ 9@ƿkHTq882~79Pסjc+OĿ8fO`: kW M~Xor #̯87vԝßՠdF!ij# ;&3d*πE\ ԥe<|jn9l n.ṵv|RݸCd"kXFS uOg ʒ8h 1z"'AkX]]̦|Y@UhЂy1k/`Skʢ%!+(d˨TYit.43Y5;;Z8򛐦{)X׮=oRX2k%j1ft$R ӃC,` k_zӥmt]7njZut"xEÃ9B Tek˸r24G. HaBeU:TiP_SA:@kp! >iA3U|J*A]9B n8Ƶk/-mD픴]mSI#]AA#1gX9} {GG8GLwYX\/. r;?"!+Bw%6z J]N 09iD Pv CF 5xOJ7Ǟ{5;,Wf6t:Ο?'x+%n 8~Rv.Xr]$v^i0q:xeL/^ 7~/*?^G=̂_m;X]b2+Cx!O>1 f2F7Ӓyk0LF HTux<Ph<'BG_xyYa us vᅥ?.rA!C>HJ2/x43%~ 1Fxcc*˦n|gws@i7HR@3 ~xRz2Wf l0">04xpwoO<(Μ8P0zX`6!:Eas[7ko%[Ão.(O>3[w¢EbICEOUhg'ãΣ;u.8Mbp7ZFlk7p_׺Q+¢/q<1PXH2R;1) X" 0mk\_l,t] #\}}֛8<y"xu:^xTU/~/__7]%-`O`"cynw7 .t>'3;w>c}%-Y v+BqjݽhG,=8w7^;{ލpntFs4axX}W1KSO ueef2/uai 9?}()a\=/"80/ gHy7#ujtv~A`_ W<#1"~Gi5+Q杻 Gh K%ll<ꦎ[G{)YpA0t ҄%,&s.h)e;ϿBy) Q I䌻oOth|fp}~~ k']?666be`4KlD# aʓ'0WMt C켆:Oa8/;߁Umg]:@ ó_t0<ɟ8OowԬ"t2vc\-#sN Y:"7V񐉇KvJC{GpjPgn(+m;%sJտ~" UwQ^ѓ֢AoIUFo/$Y$q!Ect2Ƃ90vGU䈨YoQr,sj7[#2OF7if#K7yvZ]SVsC/h QjFkz1h/DY;@r/xڼn~q?jxab~[@IF1s"*׌jFԩ0Hb BEh'42nL2꺉az8%~h_0\hz _ϑKwXMmM|NtNKWJRb%z;4"|T&Qųq\CSghO퐄A$y,#ۓ:)D26_؆C h m0?l PWj}Di'TC kTTH R5H؈%RŒA 8uYqSegNhCc U82xYt,0"(j^*FXY_|>޽4GGB:FcpsRzumeN&# / 6PI=IR`I)-\%z ;@4hP2;畲pmL2>.No,?Îͦx`^>XclO=0?Y-yw_&†Oȣ M+hfmQ ,Ҷ]X^Zyo`mM:5\r%v`k'F%d d!b{ܬ`8>s_zx~ꙧqCqУcN3onȏO<0[cdE]k`8>7޻ޡb1sv{ғ-[3B)IQ`șt>RmGfn_64N*H`l$u.av߅5/c 8-W*89`唂xz黑wMiRxD E/ErV=` qpp?)EFX`4{1{E؊`:"#ʛ, vd K }f~Q<'p˘]7P5 Bsj,s-zjw:^vNL ƙiI%jga&_*,E '),hC?iױ0/0^[AUX9_zg6Nq3+AdG3u?ÚMYM~*gZ-l%Iuxޡj; cܢƸ}\s$+$bD}tb|[bWRyrLKOW%fWN.%a8W啊qpyu͒:&-?t'W.iؘ]`1\EUU8<<3gu * 20Bv~0xǟ[Ƨ1pbtӿ?‰G/GV0sļŐ\[oK|b ԑqxkO<1&Gh3Tu!,/ bv9ʙff]9 S3u.B̲r(^ )W;.CV b'.{2O"Atvq%}uS IUxhbKv ߑ(lp[nӏcuc{\~M8{l޹?w j L)% Qgttaȯgn1ԭ>/.590_e]/٭GS'TC/$&.?CçP7i千"A{* 6 [Ԩ;lveY:nto`iNfO;TK%.{ĀɄd7uXaqXZ; <4JX&UOVRM33!dGuG`<.,ؽsab>x;/`ࡉzk^1 )&qxxepv0{ݝpMpSpXǣ>ͭPNaV0tl|c09IO`6y ;|al#29>ѫ[*ƢZ)sEx ػe41FK+ؼ{ @@3hpkۜY^PyZ(&}Od J`ge+^ VPeMbnhr[o#q{[]0tR$ j˒JcI ](e^ Zcr3/{⃘8қ ]p޿0?Ȕ G>Wb9!l$q13\& fDh ƂK'6֝-p"l 7H+n{syRv eNd:cdžb犬&H npkWnx 2qwk cͣ=98ɫz"u#qv|ClIv6Ǎ.CV:#׏=CX=۝wb79M} >/NާeeGi*mx&s=`WvD(I߁!1LXާm[tm8g{{{ho ts<3?AS0r[Ș9 \=s〟_>ډ %,O`r gCk8|o| Olj4x°9q%X[b o;X?|`P5hQ <^ƫx_ !֗'8 hfհc_.UVIJGV(& UD?@GRI&J/4Q%*_g#5WˑCIe\o.3tIذ(:NDM ^+ ) hSd9NE"w5`sV10J&ĄP3(|v*eoşԳGPH @3 qKBK$k60 _'s)(;FPM2z'%x,g P9󵜑| # ȻFKp)҅bdexsEԳ n1Lg,5%בY#I7m9uEZ}^ :܉[E$5 bb3o^C;_ǣ~ ㏵X5 mi.MRV2Exu+#l/oZg1@o~o~ K8}f'.a7}\MUS>e,>.hGSŏd՜$-9 ,GiypsEJBӎM@ /͔tI 2%p-HI=C.OPߧ`#;/c3iLb!&/{c\q R9Rd|I"k: <2"P;/`g;fKZ%qj3H"Tr84-x(r| ecfltl\GC2ZÙO/9G9:Ed1#_Nyp[3  }yY,H-œ>M\hՔ(j^ i&P"؀@eC-f mb>_`X`6aii=(y\t ] KG3>f{U8X'3 ,b1h >.81~κ.pZ`3$a/bP;be0Wķٷqۘ&&s. >O<9ʀvc&דJGcST9 GM5U,,ɫC& դ 3l4?;γ,`m k0*%DuV%om6 IDATs~ͬ©GE Yr /%J*2t' M)iw>nBfL.=B k BwH?Q2, ?3MeKoN4$NEH6K"̒GT0سtF94ό0zϹ_lEX0[\|1Aʂ3”A)]ʄ#I;K!vŌaCkHd>cGw9MTPp<"1F~Mlnayy1 6NF4X;q;;;ކg8 >s.ؼv] <TD^cgxŗܗ?t11OF3+{w7?O<޷G7yP Yj7::l1,pf|g\+oWX}~]>>̓N`gr#˂v^]P2Go&BO"ehʌ]ё8I#po!l/A8hǤ]ShS7@}=(;Ä^ [K3eY CHjS!GW: BjAJ!.$!}G، ]BH- T~N;k5-)aMbr_PI=*1u~:vqgh3cXxNX6Ug`3L?}SGQkG`:/LuI`LffStIICƄq[:gyF)jI*d1 1k@ K#f$'R \NTyAcg@ u3{!U]}/k*K8g΀C`8 w8ޭx 2(UzAX9=s'{:PCaq%9w Mǫ＀_yհV,e'Yvf8ƸTO+GCxa)vpBmǨ.nwHq49VCׅ,\It\&:q53Wd\O=+04FŠxr%oBXa'Lxa:BՋ9W BI ֋?2\@1`ścmSzLieٞ ">0G!Qo.jSYO& \(f'yv\v)# ෤w)6,lK<@FvI(!D`x8{3<^bWY}D.BGE$Nq" S4meW GP/Z]GaR I99*-g%""%~ț%bR䭒 ?v:#'lwZ0Ѭnəlc!6HrU: U36= be`ŒJG~%VTm*!/`׹SQ(_ ("4Ru:bP+2sҙTe"@^$CwL@vɰ*@bxӱ;"!('_D -]vHYw8vs着$OU?"TMa)xӎҌFBJEՄ9U8N״뤞H6ifhZؐpc+O$XLTfpt\I\̔9>t 9Jb”2ʸ^0Vb< $0qZ2 cQB٣Bfc}!jN0O=T~DX3C&<%*4~w/nP$ƣTCz{ʅ LhOaA?YQd$GSʩuszK,E Ǫ|uyc/վ;uc91Kd O \xw~3H˺8J v_0Hf/-n`&;-sxd?gJ>.0* 5u>3 ul: ”%ݏ2V1hJZh00:5Ab~2Z1K|96w 1Fڌ# &"C<<賖Xi )p[j'FlC׳dPOTQDIuw5H @X,WUٮJ ߖa*!65AppwnXazo1bfK]NwPi:<*v1ߍ>1_9TxTw\K?H<9C} 'l%>>(~mUQ:ڃ|_R=졢ac[ #R 1/a%^Z; 9G+ RG:8(tǕBhL%GJx T /8dH¢KqЪPʔ \i*@ټ0`UyDRFi"FKe꒰R$'.$d}#C9LzpidLU)A|Bˎ}Ȧu/a6k`Tz0ExD`)AsQ,&]ӑ#tO6,.Qi +k4}hL/F^@)-$'աu_#v,>;m B a.кKmEHbC0d,:.'ԟEªIHK2xsF*'H>M@[F,YU=[syLO dii <OҾgʇ6q%Hd"_ߊďr"IՈqApJ|Ai *DBqR^3"v0g@l~;FB RgUOYQs%oJ{+}`V?w*6vڛK>X88&)=8 L\^KtN9Y/ҿÑMwd3eWI\yˣ;6^JJFA r&l֘nY'I1 >JI"c_`< ?4;U@U>?#h!'YL_t& jRt I'[Frܑ?O׊L{$y$?(66GQJgZC%V]r=3RX+RyWMFo8 qEjTo:ǑW]FSh:Iu8߫2*HfD[BהgҬbz{Gre /m=+_CGqԩ,Qܻ1(Rar%ʋsVd28%yHJpχG1لM_>t=ҝ-Inq* "TR rq{=Zxv֨9A_ٗhKk>==픋$Br>i H ^*=doxr*|}ƌ1XR|r×я Iry:#)2$yD 2S /^0o~{M"*~t&d| )ɤB7)1;>d}?M^' {:Qv se XkfM/u "t1ZYыW| jc_ѨlʒyZp\ SP/f: FY* ;#: dv*x@ #AKP':FװQJ3=(jl3ҒSTgXj Bp.[h؝YH|*'١mDՑQc0VnJDY5d->M}AD<6EJ`(~K.bS6{;Lq1R1pE2HTtq}GUޠSQR{|zJ*#Ebb$),6{Y J(rQI aI)$ FA[>Y| N%byO{c&QX N6,J r q4f7LtM`>9|hip>ix:KFXlat5#TC*cC#ՅZOb/AN*,sB lc_Jʧ>:h34#ݿ8j3aOcѶ_xd$K?e9zErwaԇ0dtOSYaZJ)$N/Pa+]EouzJ11ǡJ7+u 3xb.,d/$; bft$z0F,@1W_ItΫ2rș2 832$ldsV;sJ])fY}wD?[8+Rdocd%C`5:Ɉ7 _~|滗;R=zX>慤 E<;6%߁fo$鼣d'2@:ǥF$e:c2Yor!!`S')7MV7LsQ>| M;V| a|{ }4ZK.BKM@W*`zӱ;0?0pUڐ"tax;+pڞ'gQUh:C[^Wx'NGHBds֐O-h$s.*%Ml' G̱SaH ̼"`ߏT$+$,\w=)X n{R-8p!$b'6ٔ<[-G$=Iztqzf|S24Yȝ]3P{m0b=vf :U6.X͑Csg'Oe!3a)|}>'=@ {yJl?F`Z_Nrf!xP*rn6:pƋ-,VW J]q!dC*x*ړAGp=Eɍ.~'o?̽i&}̪z{>1$A%$-KJV8bûІRa{vawc-ˤdQHqcsOwOo{UUfjP8`ޮ7<16='c2vX a I'>F[;*"˰y^?06\aMO;3IAkwJ, #[CC܌qF[),$a4LEV;\3Flnw!ꝎuDX`"Fu>\zzO(yŻt.#mW;%A0NZX]X˓ _:P 5GI>p/fЗ EQ DUד9^m‰s؏s7󛘽}Ŷ[0#fѯ_aۿ+j5 IDATMa?H d_U./W7 - Ht~m[F_yn*ؼ<%UZ(ߧx_<~z + Ԁ=Q; Dd|%7m)"cE!Vj$uC Ω^\imf{t κtG 4uW3Fv{:՛!~mo7Ɔ{b9&@ٿd.r7~!&9pT-H :0S'K,$ N=xjح8{[jȱ6(6~ڸݨ$h,^q\aaVOX\E3z/4u FcΈ!\*J++˲~ڑ%mU|UUޘ''*W8s>?贯?ASAhSŬߋxIBn%4*Ro QJ#dkg;\ yn_Ug%6g9jE=Fdkݗ(~7=cmwoy?%}%gw.Hٳ/ךY[KAÍqFj  $D[[!pSh]5n'!z|)?/*v߃?ȵCߙ (**Y0{ 8RR{)UIuAl$r h %iP88*V\\VNavC}qQtSC ňH!Rh.wu] xtL F!f!#)nI<:ڃ!^d7 jJ=[JFVQ^MRB:tϷGAᢊю^yUhQ%Dmj0iH:?AĄ߽ryn}Om'/;5*ʫWW75yQڙƀ~wg_ۤRט> m3ȝzh,xVΗ6J~BGf3цnG$<[i=ެ14DcI ; i:?o~ C?Dݷ?HoV<>[;%Lty+p Usx?S5񜐒+2|Tn-֪Z$јKdbV2:ST Dql:A]BH"ύd#dJ}V(Տ\CP]ޫve12 k1yݺLڣP%\.j.4BDm2 Sac\aA&$@4аsEa~5Poa0'^aOAƔh6xb+Z*[ż0 sV&ղo^?3|xb?RCG2E)JT{jD ͿAfcjoeaV"TG@TݽO@ bNzSSLU}]lԴ\cː\ JD0aRHR-ډ_By+iubDhb'fik.!N'|\Pkmg7Q.0]VDB/T5"PwBR4f^i9Λ3~d͔KGީF~,*2DXgKQ3K!޿2iQgUe2=kFc+."!f=ExV؆%tJPz@l$5^li >RJ~!IFf r/}?1 ;.wogo3DDT=&AJzn)9,[9~tm+LXn/?@E=>lt"{  f?f 6a87,wE8^T*X?UNX O=t֑G}]VWKnD/~9%w&mmX㍮Km\ݘ0*'evY6&T6B }_w^Rm} Dܹ-@77)UVQ("&EY["t5a#,Ai袈@񢑠:w&R(̫!DnBqlt N!UcE 7B5!ZU/Yd hx2-ECI(">gH8YpT"{{$Rz-,mUh2!>S7n#bjWM{&P9@BC0 EMr_K+}Cϫ2^oQުߏ F2<,X&[6ZwDR)^)Z~FagjDmcAs[Gp@ć LS{G)^t.h^msSF9eDyz %GipωR}6'{Sqe~XEl ߛt|ZGV(PZ71n%D3Ytm؉8AAoil˺žee3DED%枻*F+ 2Bkc15qsϿo;8<3o5.euYxZSjV g؉.k'ok㳊|[ CRԚ(V]ԀN!N]Bi-%GN̻UAhmкBkє#*IAw cjKUZhav8aa=`kmk3)H4>6Jg:;qZIi̚8Ĭă5!.*Br+m-(cp,9^/,>0DcNw1Swx15-"bũM+!̨蹁g+'γqf/VQ"e2^]iRU3H] ܾ, k@fD6:f\~T81E,*79<(ޯKƫwr!-t(µ=AgWJ0k|c Q3JM.f!vi&qm442{Pؖ"L(YXE R=i3 P]:V: ?Bj* e'V(P Dxm{n dkܨ&chyC_F( 4A4=.cHuo k48n$h6v`4G M*Wt3.UߒM[>%ʥGuNC^CǙWQJv06Drafv{U7tg)G$GS]^^r'S 0+UԁդM5D%RU9?{حR껖YwQI[LD 51N !Z5<K%?0\qG)Ύ^W)MN)/oX#1ְ~c}fY;dLn20إ#Fp.Z`Hb.o1\޸gnjs%L+p#. w _71ʲ3XAnZ%,bM$HҠ3h%ϰ;ay^I%V4|MgpBd8TVҍ@ 9֊V&H#ڻ'zW $>MmH}?9ڝEYk["ȫROO!2@@JE"YQ}[~nnC~11񅚸Q9ls4ҹı_Il~ąFc}i{> 0A~kķ&>t 62ޟ}}Bܠ *sA3ۙڈhQiQЗ3p[`qPXrc1"~C+i_qh-TfL虢# @/9e#H]0Bׄ6q1r5?6TEҍ5@!pj/.XDӸ,b%/8QXMqd쁱 RKnEFq)^w`Xts6-)tƞ慗5ɌSWxv%? r|~f/]-;k--?VRvyQm._˸ G>]L^M?>v>-c-w_Ab2C\8i 1F%[̷FфLHӑ%w66)^鮢Dܕ;uȥCBG3_f (,`,|Qs1\Lo=Cic]Pvm$LoY>XБ:##fL-\ˢFHL ix4BU_M95mFIuu^uʨxK/#C5#I󟤸uT*ų6bJyZ5޸sꆶn ~ ?8{XzRK]2=s EPRֿ RX0%ùb*RuP7E\f'VrKlS%gvc0+XWMGD0uc D$h*D<[%hpTx_c(&ԐMS}X 8!tωpL]L9'1SC[K\C<{㽋1r܉wwK-|4]ٸ5bumS'ONs Zg63p}9>|Evs{4aWD'ep|M17•^gYn8cWß| 37xitWPn}sp{kws2  ~0O]Bc'1spk̑Ƙti=sryI;=-(ߣґDncqD* K2ΔbρUh7kkLD%:[G~>ō]>LSsZ6Գ!.b.+@eQg#:$@ 6q&]5~؞F#ZU}v^6 JMܸRbJH '~>HX:WdWR>-~ $D@i +b+QZ)eQ$v#֊:Ix %eĕ93$ e)B R9܂2%B)&iLQޣ"T̯/?cQ"7)~V)|7^EQ.Yeg&PJ%"d9}$ÎWFUY&oŽ:uvT:}֋ڐ[MK 6j/i䥉 s\Db5ݸH vlKǧWdBs=>el>}_ US|܈3\IKNwg8 UGa~ux(Yn'_;<]?<}\< |!~3'Os| o0]͛}JNKOVHA*]޼dP^.2)5F&'>6]THFi6f2dkw !fXϦ0i& V9Oy_+"CTRUQ3DZz1a XM/#ch9d'OŽK6GotهE򗑵ma%cjmmeKے0Dڸ@[dkcԢq  E[T4^1|XJ`D $Bzm{rlx0\6l j߂W'vӔeI?*Uc T# rb8Ayu?\J# _\^@SKsDMȢt IDATDz$S!ޥ QiD)C5/@hMQ?-1yhe^nik o&Ƥ6ZSIӫ$Ħe,Kv7i[2ȱ&A+|L#U T׫vWR2Ec2!׆{ha,I‘NJG(rSʼ]'k[-KQJ Cmh)Sm>1)_-ڇ9~pg3G Pk^ys~ɰ`ܷ+_Ok<{y|ȍS{aEk|ۼ|}OCi^;{<:3ߺu\Zc,bߟuwϿ޷8. _lܷ]" X0y̎s˴R|*^һq๯ޢHa}cab{?xflr/;`K..f`”T:~QmZ)2zLw:Ӕ(IY&iX. ZTE)ǼKIR횤Ī"J* h:/L QFEAonB%Vʲ 1;[\.3s$i%:89ԥ Q@V2`-%'̆Djߪ?גƙXVcڏB[$;.\8 .&WIJ) cEL{mH !ES#I3[MK`; i'tQFJF.bCؤݔ5&i%$Tk *76]A⤡"@(jVRqXHT5YO364y:6­p+嘂uD!,um߈Lj+ɨm?:‘ݝMĵ!Z y R5aHE,w;vG)eǼr033ͷVxe2Gz]GV1ySL^vEN,i.*)\D)Y+ 77wtpi9;{ 鶠oY83é9qƘ Gx;lnorb0Ͻ.b^~5NO'kfi^{RXmxۖXR^_0;!i nb.HRIQLL{hO!;]'JK,2;;!,)I4k$z3$I sdPV!+cS+j18Ff*zTT]j8o>}D^$:\|N,1$\e),'64 [L&]v(!0rWR&{`ܡq<^g_ܙY3{aggxǏK\HS)i^1̲֭4r.D A#gx}TιcSΤyǠ{,c#~_$"TJ"3RQФhV$ T"I v4VfeQ6i+E$ ! K &MP`XJ3m])iw:fwsL7dwsE"&7GF9J r!xHCO`47b]2/oO\β ,3ݢd5/֒*\+c> E Хvr*Gdh UhUt 79qCb$Or05'#6W6eVKZno8ʀ)OTn',w^[,2Zsm.͕]@ΘvW1OV.h 'Ɛ]6T#hB+3!T,Y\WaIFF2iC҂|jPO7ҍ kѓlYñL%>5+7oI/ S< Ҽ'u̔ay|^wh~/fⵋO<^FPL nnG<`BdY:|i":VnT| 6l!?5FȐ#FN9$d1!(%F027y_B UZg)-$o0E"ZLoH:Q֒Y:I)DaVš`aqH NZS-zb[W7(oHz̝nwJ<ie fzMb[6gegID2ΘN%|qψ2{֐+tI[ǘj&e:* vkKlnS\,9<Xք+\c.mlx׃<|>^-n[f0$w8#mY:~޲5g̷wŹ6.p[9,T3f?ňQjba`8.1]+rON `Th66)'cVַL;K:uO~L%#{DsYHFG`e Թ)ҭL(홃$XBUv~g'enw$IKu|e5&̓MiL[vBVå&녢\ |g5&2MCcDI 5SȐjD~J. ) ׂJ/)ihCko\10 OEIPX)2Pǣcͱ;Y癧ޠM1[\1g}<$eDPb Q HT~!yio9eA*:r~fdB1<(gc2T;C%y~@X* t{,6($JCn X.t}6ռ_&((F 9s4$یcyq3k쮮sEZc.Y<nZc*?xC/dz$sX";e~˷s?s|a{Xu[}MeLw cpDII(s[#Ai m)K˝!'OuP9%wGlnXܧS)*e0 )}h-`=6SH)&Rf{{CK,S] kprb|q0byRcJ.=Wߐ_+).ĴzsKSօIK2,5!ǖu4}iZ/}<'y:vWw=p7v37%뭂Oң<~#wn_7Vyn篼}\bX0[ 0Ls䣒^hoL'KHEL`_T4"˒xjUEL%gӛ0R$02]sw_B')Lҙ15={:m)8<;;c϶ɏ!pum!Ue1eeHBZ"37n@:|dS-i*D6Nn!RH`.*M*B*U'dFFa: etj/JvvTbVI!Nbp9a$I #Rz(T,^L<#3\tO4Dxh:OC-yS+ms(B  T,v{%Z?ߩ!d5eeH22D"Ʉ".ogTyDۮBͭ-ʲKC)޸֧`4FhH@n{V4 K]ˁHJďk~&'N!)tId5SX)cRZk0)BdG&tFdg/nQ9XT#$ҤIIiad4IR.t8͔'&#ǥ/4jb*J6 iJZd"Jldscsy{2G8w_cus:S G?JIO7}Fwѷ-η/j)tYr!|2n) M& I ڒ i%sJ%Q ]UEYn_3ЦІم.tJr IáNtq%y ްd{cc Ksquׯox4~wܭo^H6FI|2ҬE9cКhD ܘL2Lj EAJTo "k!S.-a,$hM9;X4i/Si)F! ,׹FكKSKO>v.=V8 vF͠;=Wȿ5~-r()G2 YFj 7FceXɬ"$rZ;ո7a;Ke}< fS6loLؿ;fΐb-'i9(g&ݔVW1vs&׌%c}.Xډ+Áe$$02%tB{*cvl-Ȫ]J 绔05צN}gՕ=`5 J$qJf{mO;DNeW*i% 3A"ISrLH+%rO%Ykq_i-%e!mUFem$%e%>E)m޺xSSӤBL/$w^-wc>ᗺc?:˨?bu>co1 o hB ׿9;vĈ1*[ ZQ}P]Uxdx(4!^̦\mnh,hUghi6p4$nieTHyhe#C&v[Q) kmL'h'J)u,4Ffy2ST2ִZ-Cd,NQ s8U"Eo07хŽZc똘Ap2M؁I[.!BI>Gȉ;z>[ʌJk$bDkvK$˪(qBWiT:teû:ؤ.<'f9shMgt$I4/x'_~.1DcVRNFzbU2i~x#2kƀdgذS)໯/ivr*L3YNgqbdlpO7|g׷]m/slƹSsLxMvnػ5DlL ilid3 z1E(ʶe5fdig eі%BWR#+oRU$ asRՆnP}$K%i[1QMPS)IKt3)Z)/uA fs hӟ` J-ذ% IDAT/헤-M1̵=ޅڝM:SM $C%$-A93`ݢݞ0TBO`R=_KTBݧՙ"ǤS]>`xg{kwnrceΘuͰ9}8S ȭ4BM}Oi/CWW8]|7=Fڞe_;pnr f:f误w{X[z/@п=};;񒗳8ElKu-g(tt{%PvL1څ.3)#cFMDqgaz/A0}NjBIa#a0Lߥ;/hchR;/0 %g>l)7-q%wN z2ZkV.\)wf~xSJr.А c1Td3%z`ܭU@JP3 bMO9Ļ8)̀FdPV|u+,iK>0 wS {nG-'1:|ȥ<&ϽYG>ps[ vV.vyd7whh]IύdSk|?AőâB(2ɵ*A(}CsibhLMr+K fǭ71nF 'I1i%,P<ºKc&WjJ5/RtbL2̠~BlOhRn4aJXdbL^<7))F(N둧 nN0N]Q #fXX>EUk92-:%trM&q*o0׮ќ_W =Ez+;e%RVy \@6":GD<6.ǎt7:M_>9}\8F-C&{)&cؘTrǘp6OKOLͼe6'k.Rkゕ(%͍l0x!,8Oxh$/#WQ6N3t}E(ŔE`L%"io7vTSDnOyrx$a yJF82>HEM{cM>QXu+e!(J*#'g֩6>y; xD\|FW|s,,bu {\zr#X^r12z37uHb dy9a0BG2kd&K<;5yHlķƤ2~BeHVR*8~u^RLXkG=6GRG#ÑNb#}, dZy^SIb,#-+!L!j* &|q"9 SmL=Un !67RhJ#8g{L9o5P -aƌǫ8ЯR C3 Ì? < ݞffn|'Y:ReW3M=Y%8Vc7$ VI}2sp{y|ť*M4(%ImtI9YY[Lߗ$R0H4Üս7;<_P )%K'knyled2u*cU,`O^" W,,(^V@{[Cs@N I `@ex~ ~ڕ %-N?!7v-QZJ5r[X$*yl' k3TeiB&ƚnrNj5+o}F/"oj$q;rkADZce:ݛopϞͣ">xtoS$k夫#JCAN'O^dG}C:2Q8,Slnt[jlI?Iټ0&m9=_9fT [;QX~-2~lC.%tQyĴe!6~Lq8NN̈́GEO(Rm9f2 QQyxnjlB%!iòr [O4S`EǸ)J`tkҮ&*K`&p|Gq_79y>rFݥC+KP;J9r?_HN{C%*yfrWhgfG:_%~*4f]x*݄7}倕]L$8"rhHØ.Ku˅ wI(ζ4',G.ʓ8Juqq0Z)rcM@OYì%Z"%pu3ciF.3͈z-doS#llBJ8#rNp?F 23Q昕lb iF$5F)4%oow%`B.k[;Tz00Lr>F(,K3Ka<\z 7v986@\^{'/n493ۤhp1B)}MPocBtի^5~)߇>&:[Ǎ̑pwXl]᙭x5fwgguXlO`cu'RyNoͿkc7BDlY!i=q{:RUF3齦ӍEvpvm|tsF/#T2Uݩ6d~Hm8km8N^Ô ))%6h1rqAHrjQ[R&oBwe#ԺhKGe 2}IT&&uV(wþ=@q"-N-HrPK'NH)p*I>K;JuP2TҫWq1ęvyC =S#qf|{kWcǗk̶\WRz ;PNb\oVq! q"FEgWn}gkrΟy s!gNpLFS?Ir/3!\8qq#uʕk}ZMűErTXfq 4 ^ƍ-L#1TÈD[nb; ΐMq"?dѠFXsjanɍOxG3f|rugY_]k}< J.7W6eRmUX>3}=h/N|I&3nܡ3M4u>rn5UrD>gVמbceJ|,3 wb=#{lh?enX|l/|oۿ[}ǭ,.#LcL$!^—\YV119$qTc \(MW[!xu2J dyoLkڻ%}ظ0v6pF}Xm!K}\xЎ6mS K ,ˆ1֏cG$g#F${엚9b* 8I'"9ߌc #+,Xbl "H]‰@k IϨ;'e",T=`k=޸QE_?H|, RZsp\UB!7`KpȋLFSmPn-^'z>wKr t\4G|[Wsg9wg3-yzǾzd<'N6p#g{eg?h,qhT&SҾH*ce?sC(ì $@S E`҄ڙ3J%q9A:/?"[]$!R)COLcb7#%&+K5^Rs"i/4x ~o}Ӝ8~':Ùckr>@p*P~ٺG{B<ȱ9ufs= +1aNk)ϣlB+V<ؿw)ЅD"2Zy2Xjz%S>2w FqDPt)*b6pS~dx*Kb;1MݴyLLGcl-ţ|_#t|ƥM)'?:(]EdaH{GZ\UxtnFJ|*BYK7h򽰸u%4.{ L/_:跏pݝ,:TI U(jLE#2ȍ)ehXK*5 $gϜwOٗ62["c2ãq7^s%Kg*iVf8$pyc~*?rY5el;l.Q˥qƑsuu`o7fk{7tCA@[cJo Cd$, 6lDU vD+k,; j Wvymvcd"992PRrd!dmsH;b;1!vHU=_s W%=|c>̷mu1f|{h*佘EyhO M~)Mj%Syq)#"b$u7c!.Rk9"(h#ɤP%ז rl>q]YhکѼsj/xL$'=WL2q8ǔB,˪qnqfYNXmVDW֑\AoOw$ܼGpcZ.7x~/_/ptX9zI .'tAُT}tH(A(T#DQv[obG:+H?P - %[ # s2S;)"gG+R8ckkvy'k+}v !WVE$Qو>aRC28+723ߦY].\JPFbl-ڍ =0mNr#Kl<DlmnMp¬|bv_ ]?d*QF5gz,V}}6{{4ZLJ ︓Fm_E`{9sRA@;MzaAp.I^ ҽ-:̾P"K?O?{M>Poԩ1Vd+;IBƤ. OeG2IݲSk2O ?9ȠszљEIHӂ[=皜= M{m-}ZЉ}zܱeaW8S};yמzu>Pi6Y<[Mn ݌ [4Pyb0pUg,;O}׶sN|ßIq?Oxr\j-滐Y6Q5pihPS)2 ۉ`|w{Xwؤ<:I*{VGz#w]6)*I-EjOc-q0.̽A/*^_ui8gǎS)k`F?62Gʉxbe]t{|FL.;y JxM_tՆ@$Ɇpo,Fqo蔑KZb“em:ϋB/!53gTյ\JRScE\XdJKKő4ĸ {7:l-qlɭAZLjpITpx吟Q!IfqdL{غΠAwlYv>(Ë3 G<|$u*3>f%tcAN̕k=VRz.[9AZ?Q-,)IqVfEGg-`7xN!F4e@3YDAPc.tisY9vfɕW+di*O>vg,\fZ7rRn,n^% {3J2PRKn nc­հX ֠ $TrRDKTbhdFCc`gVpV{FNɐQ@#ҔgN.w>J{HbEt>]ϳ|wҘd rKw3/q>ķ8sI^Iϐ[ׯ 9s^~^x G~S2Եg_%-3,jt]ZqsuUl}zGe#$ ϧSQ$gSEI]ߩXMPAY* 󲩱84uߝIeV1&"k9y-lXgQH Y.y0ștwuq}\fOcҜ8#׺P r"cbQcjLQSF..7zѷӥgJ!2O}sF aKd6=R)rpO)rzH*QA(NAm*(.^Fi e-*f#xQN^aEX%69YnsX#=I^=OkKXVP4.!#1ZiE ܐ9XW!(ZQq$a^xc"pqny[^.3meZsg0Q |WΡo\vζ`e nlv-2Z o"?۠=bjN%lZhJFx8Ұͨժe|ߒ iNf Rĕ9֮rt/Rjv+fv(#ҫto趩VB.23Sxx7zOf! {>[Cf+wq'%APJJrNW)|יW+E7bg,ГԢ8%r6=0+\W,*suZOi ~?_qI$%mp}ΰZEɲif4SAH7^_ӠwE!/̰M#C e~-S w#ڻOeơ0󍊕-?G [x/nIĘ/eAk2RqF3RTJꭥZ) )> k he*,j' Y b,槲ĚUvXclUDo;;=ι;Z$ R%eRP[&Eb"Ɋ +4 -WDi5BQ$& ҤA*jVJI2MO1mi_V7:za'_,熍\{\4kbf8Ґ3'i2h08 *tsJѓ b*K?oܼ[4> 2\VAH8a$( \>AJnoQ7[r=i@סޜ?r$WֻtVfmH0gnJd#D!ѕi8EꀔYFcPt"!99E'Ιk:Tju  ϑ:6)TZmhg|N3X9:: p~9{Z-.X{aow,MY!q" bd3şzJCzS!"IwX½5\_aӭ;giʏ<5fO?Njf2nj{en8/ %$p䝜9u).pq$SG v|$HҨT"m$l15Ek搿8 bmF1bVoL)8Dq9[TVU.&/kz}J@ÏBv{8.I?&&=_l0il,;+Q[aD6#L򱊌4},JSEINb]AWSEL"GlUL!mXɔo[[8kQ:G-$!].p<EIAO3ъN.͊II,irקNp sCLBm 6DXK'IftC%t'I@KYx5dRmP3ӆ\C5dFBr\!hH@hC&A͆TGwn\Y?{syyK5`z"kx#AQZu:;T0 +Go͛$ڡ \ ٜ/rM%*R8P֩Uĉ(A?PI3 N$ z &KW`q\N9vp=GXtc"Gcn1 XW[T JE^'IB_EbѮT覂# 2cVQhZ7p0ţlՋWșE*Hz=Nx{n?oLNC2mc3O,__Y F5 YBeNKEGq =wsOК̩O%/1Xe[(yR0LP3G9~~*Ã=6xKqyo5H &G Bfu47c%ua:k*5NT~jE۩x+Xr]\de8ٝwIRyQ|uxi()ҜkdI㻸N!vU|bod[iV$)2: 38[+FTbDw/wl4”Ŗ2EQ ')0rSJc!?:kqh(̾:.R*"Y $Kq" DAD%Nպ,qTkW -qA𛴎 Bz0D3b0|ˍ\'r^F $Vy@ - `֢8dZKO J!8j2cJ̒%1)v~wsaiž)~ΐAL0JT*3 N+}\G|%ӌ$҈Az5bynasv_+#"Gdkɫhsm)^ f0DVbs7˕{/G }vI( 8v _C?e5މkA-KdM{ $f%F(lFɫCGk4}_ŚTZ z|Cn=ۤ"2"%]f~u_]쫯8E,ͱ|Ͻ\yIY~ g ַBCEfF٭vbI)CrJ,?Ώcvh<?2-9%FXRRhfq'u\''Żal1#y(nX{S%v|PhueQ2{YSLT0qÀv<γ}dPv)vbb, GN%$f[ƨhc'˸iÎU,__rS',<${ 'z~R`HR%DLІQT5U&pViY.1Ahm0^X\FpwU^OL_M6m-9vY#hY+H.GiH4kXP1BHQ)ǾLFIzTe7B [D R.GO옙8g06' xiTemq=BH9,iU"AA.t7t[]VFS:sS9xa.L̮K aJWaQ p} ? vw(*,7l첳gR;>~G`$r]1(R](I %YYt45TCo |˘K[WoA9,-s}p]Lj5v AT!1^&11f}wWWwW .Œ"˖að$H|$d 1 -%2-ɱb)jpgz}뻝%ުR%3yߤj& "0(;{#Z4Ɋ/ѺT\tWw+VQ執5p9 lʑ0E~4UG+Re ܥ1[Z$Q`„&~c_Osv^Oɵ$EK].]6VW,:CX#u+,CNy~!GզmI2 ۷n177K=c|uDuZ Ҍ,ٌHi,AjΆlܒ>/m RId&"#"Ұ%G!^VbuA1JEZe]R^@6hw HFЌzm~/h8"A AҚ%ipgcsAjȊڠ])QLXAmXSR%V)-sLA:&rlnZ)~ fCͭ8K|t>0 ]?|Sgc;o?'~ MZ+: Fc8b⻟ԃ̄]~?5&<>k٧?:VGʯ!ԢGA p Y+.W%/:(ɣ,??!ޡ8"*ϙlFF==u33e5?ve'MA" 9;eg+aerS#+0"S4D4JV.{$' J%JX&ׄq\dr5Y%T3Nr"M=V'_sbc;ҙ$-OqW[QH]|=- 5PG\Kٻ-!Z,Ui.eWzE˃80Α$3s}^nN)Q>bs{QJ_ZԥYvJ:S Tx&-KZ|OQś\u2% =5%+٦^G)Vwz?-$ 8mqrA4 ב~@= IDATR}W_>7^c6t>zr#b~!{[ nُ Rcn* 9y Y[R2 Ƣ%50֖qcHN.7Xm`l2H f!QX*T6!eD"b>//G6Yg"E=d@>RH;Ԍ}zL{%9Tv?nlo[cgψK?rڛl;G?O_ᡙ:;`ǟE5ćcs|}qNft-F8GEDT@e9.P^qs4LL 9qJ~DhN[&]; R㌞Z&+ϖ~~ӫғ)>x[sU9[ZGiȍA/o(eba=8/݅uvJ8޿7[.k<^k{=!ЊH'u4 rKC"Úl(X3Ű(#ߗ}$9Uo0sno KnDRQ8&Gd( P 0ƕ@I{;ij1$zR]:B~֚AL\kךx<̢|c$8+1AIg~#{;/ אa [hgn]_{L:dXo Ss'qW_"8I:&gl` /|s}(3 & KQ EG5RyjlPx;;W7_'ŋjBb*<iruUI 'j"l%Mҕeyb}bNLkkOVSx8[GcP%՜W#y^Š38ʣdyh\#tծgAv58@%IZQ}cc7kRK~_FIB. G;hBcuNsy*ir9PewEp"+T5# $ýO~ oy q?KjriܖQ(>(# A:W ;r7w磧XZiϿSVrn,kkd}F,Նrշ\~y7z})sK!UD7 zș>| rGj[ O1,};kP}Sk4;<l-([P_G,*1k/=Ʒma}ֹKM( .3s=ċ'iZ%cP5PIӒ)qThDGw8N-L}r"2#0ElɩU>cCk*Ywﲎ' ڨު W*c QFK2c,dZ4!s &ʕ{1}e1tE΁ա>:L+Ceޞ(ǸWB^-;^Pk c (@ xp-il"]^[ۡ<~4XӃ'Nys,>] |0#TGy2#:N8d!2u(h=,RTZ}$PAw>ϭO, Ɔ>2/09?>#\fa9dM)q֢QJ TL(RrAOa~%`L%,;t^)1uO網4{ )Xi.9ml$x% " x+GtOF)ov1nƘg8Ξ?˜Lv1Mx |g,H^(4R8m9zKαm!9 "P<ț:ݹ&AX=8B#~ӖN3$ r?5N BZ.b<Ԍ$IJmv9dWB)E\h#Wd3$͈RXP,Iڠ@'a4FDy2CdyA?MMfy7v[$hQ* z8s"MjhzlZhl)G2ycf#WBoenr5ë_Oc}.t=i"缟2~x9|9f!El9ūt[ey$--y [teLn2A~džPe;:{2I^p`}lG.ubXkrӳ '9E$=gȵ7F<5\d&'ΝWH#ɏsiε_i̬WYq$9._#z+RīV%8x]I'u1v\ -QV.p"]DB@L Bdrzz^[f˘jlqJ""JHհfLzFs Yg~e{ư}Y8[G5Hwv,fHܪc.  #Sf5FO(ʟYH1VmKUBJZBAIeX+dYF LfQx?SVHSg YRsV%޽IojC)pEqH<]:ǝ-lрcb)dS?Am\u{|z /.ͱv&o}5~S>x?$W_ j!?K勿{l^M\y2/=˫'∥s'QDQD?J⫹09̹$Z=Cfb tӉcu=HQXD!݄(0SΟ$ tHƋKL/?ֵ[N.#O#o]S#1 (@q4SD" vپzi)0ū)j݈~J#PD^IXB ľ#,D2Ąv mwXxA)(IcC/,.z;1W/RAF~IG}:Kl%PGń~FёJQnҌX*DGVF(,H<<8 8ybw@?Ͱ ԛ {\:d0胓DQQsx07MNBi2_!m]1]pMn=CʅӏW^_&>|шמ*zmH y3l_~'к }4GyL,'hޫJX;F$RSOYDy: WR2H%9?Hj5vcU}᧞ZLa^ Ct:Z{CYɶ톼 4arA0 Rdix rC! [,A#1@ ʒ+ 3"f|OTeT U !yp|S% Ȳ'x$5[ ,G'C,e=.C0(i#,Su&Wc' 8y'W9HҘ[x.qj,,sN|*A0*ƛWF"ɓ $~@:NB(BE@ej/>z'a+N<=M k8kQZf$[S`4G7B8Abc !Z잆Wdf1%"zȭ`4j  $ v+/LJ+a>.ḙ[xM>"N>k{Fѩmw4}CjJ(X/'au9E@'{)yɌJsx0ij*#22CWYBݑ:Lݔ# ]WX9gM2 M WʫѠ( íl}.=Y\c*5b.Z6NK*˝Baq01,)Y҄4M tZg-("juᴡwx@Q8zF %dt@XX?ge~C~6!W/c7nC wxFyN?rI.vsmeU?a9ax e39K9L0HrU]ms<[Yux^U,$iz$St])NɘWTp^7O/̧Խz'O^@.]FِN̳?䀫&Rm pzfWs612#/ Nc A$ (  aIUy2nKjG iU-SՀz5L3OđiLw ,S}P*Y;M$ձ]H~fh҄拈WQ[\ۑwZAֆd"0u2A=$ }`+qh8,I1$8b;;.1M'}3*\yBQ\(+/4ڔ]vճhcm7 (k&%cG)y6B%,+ s( |y(,AȧX /29</4G.\B:#6uiܸL;py]Q]v' |< kyN9[Uz7|Kw3Vwlu\禿GTs|(+Q9:MSy&U^k[v,ܤ|9^xy`|2;Wxy>=se$F!Am%աnO&8B4#Rk~7yzePz)2+0J1KB/|O%Hso=y*:W3)ay2™J (P` FX`l,dZIClOI*.Eg6(e1{tn5˷htڜXZٜa<8Fԛh:JZMA׹߽pٟKZ_[v9lrU/H6<3i~;^侑`Nn}ےg{5; չf4`rTt3Agz^ ,XSB`ݴS:pUS :gT;NdBO6kkk[տH!_"_!aC&qαz7^h@ky;;k@?d86h&Zr)|-8*; "KS6ٔs}lűHL۩?K UɄ76"%$s,@7Mh,X!S&oMD1}ן"'#ފ\m%(/ٝEHQ#*D`R'+#br!4=nu=58R) DXˠ5 _밶$A*#tNR’ZG1,qk 8_!5(Qh}R#-bI{/ |9# h>"-W ]G w_y-Cw٘ZFeEpTjQ7#]bo_zk9n<'u@|p%}:QN^}ֳz@GfFI+\IV`hC $":_):w|U6%,Rڅ(!=3ɚe8%K|CAx$4q0Lf/>B;S{㌕Yo1IÌ[D~$K8ZжMo.O" cmsxMR}#9nGz8xKE6(m)V`u!&ϐ^gWT8#+ϴrD8ZU;)H9>}qUxQGI~QhsTm+) rn^EQȧ~oT4^@ט~.>|]$=ڡ9qB'mQ5kH92I-RBjrWTR6 SrtEVƄځ8*NTu7M uG;[g˪wMC4F݄(K 8#(q^DJ| =eR+#"=A)Gq>uth!Jǿ-gs0Nk* !dž"GBlǓ^$1#$ 7sE!,B HVh :%^CXY=`s 8 a6#fqv~|~K]^Lj y_gcO/sQ7 "ImNg+":;"[a?@v[q@woVդIF>Qˌ N¤(qS>R1f'5ay%𪬷4G9ȍF^]\6Ea(r?ϻW,F/ea1,w˨y!xDgY[ i?to>㨫vBj(aIR<Ѕ!t>P+=~)ܭ7Ds,ax޷˜pC+c;N"wnslXBߟ̱EAC[')6ITZwDQh΋n23΢d)ߧ!I$nJ_f . ?nzkJa^ ^ܵs\Y>A\;񉧟%ձNW޲rZ ]11;҂BAL޴~ձYW+E"T8 YRE (laJdqƢG>PaPkq. À"YaV-/8dnK]ᦡB#ԕCIѝPZM>Ka{#+ܸo54o̽Yey[kOgsݚIVH%Q֬ m$ FyC:I1tq+%OdK@M$Pdsa[n @V{g}H1CX]# x6ǏKc~YEfm&2@EOZZ1(Q 2h!ê/۽) >X_ +t7Bd A:DGwMZ nmIl4dlSʥJIӈ8yqΨ ַӓ#hb>a3 ZN=:;l2 ƽ}Oc Z6HɉsBRlÝiD1[WlT%k*/S f`J¦.aZHGV(z=T69/qj9I1Iy L0|n3\X/@/xa2I:Ʉa4叾]zOcΡ8Z[%$/IL|RzHֆhcBnP0YX5^uPQIRGgcR)BQ|dxHyapfJi6(`a )\Tak'@0^:)sIYY,X0]vӏ0H2=>dL:($=}Ep\I(`.6Tq Oi5/|ɫn>ʨ*fvRd#!A"-l+Ʃ`$\#+g?(>2WOhﱑE\dHFH'@;DIq$ͰI"<:yT 8"Q)tMWv thH--0 I'Vh n8H1qdx:A@Fr#m& $!tfR#5VAB?FkndBMZ [YMwt8x{wз^fac߹DM88Ǟ[78n-҉OhRs•&a_ڥYR[I+q=x2Bt:(d MشU/TQiEm.mTK^m-~iҠqR;/&/],//&/ heܾzkf6Ic鞎|ײ+q*KDl\LRh)ES2NB!Y]0f(DfzRP$W-YS݌ZؽGl BUSBڝ$\)F)󋬭-_k/ǙNG[\R6EzHaH( C$2,}K q=tc <,ThHs0:fey4F&| T4ϱ#7b_@ffY&4:g?S!i\q5ZE>FjfM^㰖1wpIwoϭ{K7}\*⃔J1y${2!N,{- $%ƸǏg^`IhL+YvJ2{eVA@ex#Mc) K6L(67BH%_91>p Zx }RqKJ&gi4 >_矕v}vʉӏ@{>9x h #>RcB'-&! p2^d Y^qnhPb3WѼކRNSQcǒ h_Nʚߌ.MCy:a4؋릴3,mY]4Zدцb9k20cMLi3Nj]5(?" =V3]X1:{ln2ƾV Z@|~DQ(Qt9̒$VY n|Ǟf~18"sְ ^o-lul&|- ѐFYyxK<Mb*n'6úW;2d"6p3v=}@{a 10{lJRZbP]iB2GEHa&h E"7<+s3]R\:q.Ojβ**MI<)>}o+mo[Atu٣-O}18>Z%*MLN8Ac.݆>x?8l1&%U$Min]_%o&LjqsY;3?'aҊkxjKq}7߽Ch' Ji,./},ހ$2`v8nIhd JIp0d ЩC:h/=V& iڹ'iwC/(dj-~Ɲ.J$sp/ek|^ݤy|\"]NiaJ#5~!͹,PRT@)t9DFZ&>p]4Li/gUB0]mtmYU+?yWU[a?8ŎƵS\{9NZy{mJ\MЕɮ6hRxxAH5Aix,,$J'G6;Lcƣ'O~_/f0bg8a}W3@̿zju/{&{ !5:"uJMH c\Ecc$FJk, FhLdG+GR3ʼ_ ϳS8UtƍH>^JSU8)EՕ;]aN鹞# ?@&DQ:C]lTsp2+SmEf_Pf%ӱ~6,Tb,MU^lU]]f^Q22ѐ8RhAc ,,Md8[juaRM]cIIǺ}r}a}9vޢ۸^R 3+Kt>A,ۈ0N < DXL:B##P!HFI&V3֥2prucg>; be 6y_K'}$i7OJ./O!n$EyOiwEJ8a;;fc'@ q;m6y}5% -,"w$$hT_AT%aQ4e13?ɹH8,`d4` VDє"&ٹԳH&$&$epfm\|~nے6.Vvg?h44aƴVaȰG7tW8vѡ7wp2y< )W߹NKr@-1ӻgYE~nOydz 7q}E!c 4{ 5i73 D>Z]RԤI4spe<Qo4IKBI8,*w-]jV|w޾ ?ʇ<ϣ'fy;\\Řեք(qu/$jOwt= 4-'T&x# N:"\o!1l)Q6Wh+cHƑM֒MC6nX>l$oa ]Z$yH `f]~<.97 jΓ'fi4]vxWWMӘ]b,ӣq=ygݭ>ϟYc>s6;{gW|ۧ__A#Ra^zC"n3K~> 2cŲP^)F64MF>^9Nwv1f%)ф=鲴)M2S E{3-38ܧ uD$qLwnFRp|ht2"Cs.Xy1C] c2X=ɫg)_ep#.?,/|[oq=L =8btͫ8wK;u͘3sll2uH $G}z{;ܿGn4À~& ء@ Q+L"jeg8Aj^rǩKL{*nO=H{x)Z ]1Pw|y)}3I!s03?RAoBnaw~x$9v%41ǖHE<!MoDZLGc8.*:ⵘ,z2rF"W& M-+PTU@EҭEp'Rki$ }#tgÁfa)8 R;ɌIJIxv&=CV=:i1׸?󹏟fduCMOqQ,.gC6%1+NF=~xˠ,D#BLIq&Di78Zb|D3ؽI;05 &_|%%t HNE>q\oewx'_zWs~EMO޸pHi4:e!0{wL8x-ugW|%퓸8$#PoxR uIԋbYNgX<7>&Xit;<#ƜoBnsU<&G7.;hႼLo~gv'P}W))={L1 HTj51~qp.jvi؝RR!A@<ąGkjC U[ujWktlWo<`0|W~ /{kRJ:J'n޸ʼnӛ,,5}.^?4*>+hu[Kr$R77ZZDrwsdjvcXBVa.qmQQ{y;!QeK=zwtTg$)s3:͎UfRB!(L Se!hAJ& I⌸>'6}fW< D &17ǭWE2:fWos7"R@upˆIЌRCpLJy^lI8ͻ[HhdHtQ#V8T,fLQ6+۬4W'׸6W:O?Bxm3˜^|6no:?Kɵe~_+n޺ǏayEn_Ƣ0B|oӛDawؘ&FE\gif1I -G'XM$l btjM2P"xTFUU.]t!_eU{l&!!W#xA$U()Ha%׵ hdқ2S$\<ٿRGBsHHa*:Wұ6p`WB6(>Rx7*|.ltmsBWfٽLFp@9kR\#4 zN Nh)ˎ3h-T22hOJӦVE#fGfB{VfJ^cyϯ;|hvY2 u_ Fi͵H԰E,/skWyӜ<˗_{Ƞ"D8X(}:7lqQt0Q%x) {Y[g3{<R'N#񤾄Xg_{?'Nk?xVVWix.јo <4㱹?i?+ep}m.8}m^M> ?GS-ꑸ!6yCYbME:gx]+r.d~'7e/Bc)+;q)*v5Jnvwe*++x48~u ]XZh4~L㤰F*enu/ q8}P0ܣ)#CNJ kt.҈"oX.|/h}xs[H5-:-$u@x|1JE֝+;D)?${ t[M] 'Z(<3r}|h MD&Nb:EKQwce4vZAvMb<xme/,˝Oy'[7~7~˿ovx-&IQ|/e"08QДWLVDJ\uxCr _FKP[dU&?X+d i+pI܊?;a*+j'U4k-F)̧lpr*u{?hMB(dv>1!֦Hɦ[Rn4v$8q=FRL' ʮɬf4iЊݞv=!pu56!avg2Xdb NN`aE:ex_5$N`MbMNR3G6sjqQZYD)ƙnvۮ7Qyc:Ʊmh.Z\$SeĖR029] C骥Yu)>؆|PM?VXRX2V`Q8EELJP^gʼA?pm a \S¦BX0M!V D$Q(.Daq:i͆5/P6ڐ9;`њtsߓ]\]8Qd_dq'0)? SzyT=1\B)k7?7')\Ͻ/WyGK;/$fݗu)9TVbdAa/p|"咕 fLj,BZU P >Y~̝w32f3yaoUܕR{C,1C+e,<:mpY(s`1mL(Bt@d׶OB Hg'Sxѹ~q2@ʹqFZڃsiLg`u6pȮp9^H׷73@#)A4$FP.ʳ砵Mq}yfbCa,xz]z02r2U)sDz8Ut7Sβ75n Afnr( gEc=~ If{YS%y0²օiΓ.Y"rSa0gav6#r9nfc8= f*Tϙe[adTmpDBIsM ~MW?uJ IDAT`ðxb[Hlq0GKjQHTSd0&U;LY.XB4,b lE(q雦 V.\k|ij`r19tGTRL.&? rSZK8L0/'% N.7< muS/8֥!iʽ%Z 1G3al蠩3_Û^e"V((Cq΃g4k' ɍeVJ/gQsҫ#ɒ\k$`Xm(w ?3GRj* p]$FdJ}vvvU|=dI/qB.B,&"k +L5 ]YY^Ö* W8M*vPGT⹖JsNFpdiQf|AS  'irЈ),ińdQpBWO;X5塥M/}it}P'dNrdJSviAP14V<+ mE!7s-]*yS2 ܹT7`[.%]ӑtrE-\8 <*4˻qQ9B"=VFע˖IzDU&HI5,Y^~ҕM=w,5\>-jQկ+Y1ɉ#ZS?+Tf"-8QPr^ytWmn>D~ vKꗕMC0UN~:(9]Y&?!rk]RE`: {6EHK:K) [cGw' B ՄG#2JRB.eu1*BB/I$qS+sQT3}pH24wfq1Y.VM4$&4!^5CO [mU˩yW-Ku[65XeӬuxp5ϲkk 肪vZJO>HE´q2'ȌqicDBZB ~/)Føu_0#։qZSuDFfy4낁x"v+ Mc0DjlEK)ƣF)rh>'iu;xNS͖՗dpe4dU>yf#)~+W!I tOX<<Njz9 -\B=DF׎WO8 c):2L}*_ⷹu}V51N $*Q21E1Ԏ[$_=K(P<ꂲQ[ *x#A j^rb8 MU&idISE `^q4[55ofkGv(N}oB=vU -Emo/?-;vHT\zKݔڿS?~o~ vmlE}ge/Tyd|ixp[jN(@`FQC90L31 6Z2G{ca+/m g(E#{ˆ Gt)r4<׵j(c`02wz-5e;;>}{\zu]|"'XY]2X_ٳgh>櫼5qK?i~xH{ ƣ!]fqu$q;;JR^~G:M@HYhƔt[|?_,q;?˯}N wqcxә]a˴y&p}?FNd{3|W/w&}^To<*M:ɈTYGg*"l+LlUh-]P˳cL "E645DWZSp"5OŘvʦg)vu, ?'ՖL<#bܹG#eQC%gEַYY Ib^.4Wn]G.Vor- ؍Ә;i!N޽K]-s|<ݝ$iPC;ws tu&aNHb~yd8dVJ:aHHƓ hH4#|h0@z?˛kܼxTfx:浯>)42;$Ʉ} KFp4).h-#1f_$LyWDÔTX;y98nɬ;e-]G7NS3ruQ*B:E^Pb5rQ! rIQhxR;1״CgQ0UV5.v(%Ww%ֱdտ)cߏDF"te_$F9AySjŋ(Z;N݊C]f̋XMY'TwGGf6 x)*UT4UdA<_:A@R% W 9(ʣU%ԘubLNz` !j4B餂~\4 i;{䫍RF#(E͵#=q PO{+[vU7Zq>nnc8dHHJ$H"8JW>D2| 2?vwv}ow=^kc\km[%:Uuvk9Jay~޺0 =0D8:?A}2ks\;cox<>vvD=RUUa:`k bvk ߅?G>?`Od:şቧ߁^Wv[ۦ6ؚb.ׁ|SllljٽF>c'1c,k` ]ϡvZyNR8I_ 4lffdd/ _7`iȓ7-FVw~UB2)H[x 5'SK!u O,VTV<Ԥ /t &tq*Mp!L AU\D,ѱ6(ܔb)%:zV%Ǒ2;a 2t2!HDF6dŃ1Ȕ8Y޷(ĊR 9њyVN3k+Q\V4qvv5C>_/4dݝ=|k4:0wsZA[I*Jq 6Hc~ރ߃kO>EbN1Qarp#aRi[b-&k!p=Ty%Ӎ*L&onb~/1{;1_}qt|p=O`9J`Ѽ>PϡqQCO1ZTAHMmZ%"L ec "-ޙ$Yy**$t`MѴΉJtp/D]shNL!Q  ~-*/M(tN.IlcQB g:{R>MavcSHŞ\RZ S!AFط_Ϣz** 9qaFzLsb!3%(2OeLI7Z4Ʌ'2])m@[:[! :YWhPRDSבmQiW_h4LJG:f C1l6s^ d=̌]l_ٶy+EW&M\UNU Fc3|[`RUX.xmh2wq c8y.& NNfNĢŵNg=99R\j+sNR5VSbI>OrG>}ei<_d2iE60 ] gaŠ|,L.Z曔Vt1+cl clmoZ6L+cM XFS/[r< V b*P7DP[XfnǎsjcbskɹH@ZOV-ːmQz" n+-=Tj&NQژsk {s^&w'>l'g I IWi6A[NULn5=wU3{m->}mQL&08'2sWD}<;"ܵ)yi V(cS |kjLJ)@KBVMUDJX.TNl`$GjD_5}bqsb$S=01X٨^yl1P)RlAvVz QWn\o:_zd(_h4x4xqĉӂ_֪1Z&Pb>i69LE&<2LⴰéW]w8ሧ+(nP~Z N2Ս5|4`JtPhF$\߭rȏ! R})eA)1n$PNf잣P!5~,$Ӊg~I.s4Y_2;gVܿsO.߇O^mI*U\+I;gLYcnPbhI)NXoCsNuCfe vcu20n gEDZs0 ɀ8q[.&yCMчX:{xZ7-?{UVZac:͇C_+uy֓Y\i[j%SڞN2R}F#(R1.fdR5cb,čM-AYLAΣs+ܐp"~Z^t >)dA`$<81.8Ֆ39kũn.^A9Eُl\!u,p9CFRڕ4M&ړQv IDAT]_謬 ̭r =ixpG76U!zT(Cw5Sw^9,,mZ9؆Bwt4TH c ^=}\\KiB+*!J2tRd:!x% jBCܺuc3{[hLCܸq#\~P&GpVƆ(W9 X\v [[677\!![~Buis+(Ĝ`-,,Sنn|& eebDep$4^[M*",IRi"=F*b:eMqpj*R5R ,ZXJo"Ĥ[HdR SæSAt sg>Ǭ!,ܨ#0B4Ӹ  (;'dwNR€T;껸Ȉ3 rQHJ I(PՉu`Ɯpq7I0%6(r5q0pQr8[PxܡۿUbYgD}9dFa| GJZNͲd%=T0dgH,lM8g'˱"r?0d% z+Kl V#~&fg'xX,.amhtc''gc hj¹O@*1 ,ddԝ/D=4>O`{{z۰`kkП_?փ÷/s|Oc4BӱQ:$|1uuiU_I!AJ5˪,-UZc2O=vvvq+ܻz Z-: ANbAy޿,zL"( L(yJn@Ģy)PPH1ќ4Xj8&Rg IׄJ)Yz D'e.&j|8*RSFQb^/Ϡ~6րQ)x=\=qK1&㪪 c򟟟>TU蚟 S-CXUL&xߍ&1ݻ_?c?J{z׻PU#ggg/hMӴTRȠV#U}Vjj(e=m\bDZkcloocwwW]=~Ý;` s6 ͎2"KL1!!VΧ֘%yIkg gH_J`ԫljD-k&Ti oEUXͲb!Ԁ̰Xs+f kIeGk۠Và|56zbJp24z~LE£ā`Gp2 NG˳i@lcgb$ DôkgYQZv_a:u}#-vF}D30e>q4IY,$l|`RtUasdHa1c>޽;hZ)L&z>cc,tKId–Lp_2|+WOzgQU~g?O__x[ߊGGGff2 |v1^Ő2bbu^?D̳./Zc4a2`cc7 ?5nݺǨZ R1hBf#SlR! xq 1q nY|`')B, aĵS/Θ 7ӝlE/EUAbT TRa QB?3L05uH Z.&',FJRWxIlD`',-@ScvcĂ3 @Ahj S:;?3X[>/vHT% Qd<#qkl8 *Z 4& f^}RdֶՉ/?].( /`73ZOuqZ Ĥa Og3y 솬k|U߾w >vt99uLO?КI@yguv%h[X y_29՞D^U-PGQxg]z}|3C1߸uݠiZ?=5Hw~AQH~"Rsc2`6zTvپu訵Nf0yuX Z9UntA0E =n}_W,itڵ(ƹ 6RDhl{))$39~12֯J\*%ʱ/RpFF:WͲE-Z]aVaFJdjB2ʑ_tԖv#F",V!{u J9Q.Z06(ִI8ʮł<`c4-▒Sn9k>V~y{56r[ >V(R!=#M ].1 7ac 7a"2Ji#EE˸ rUn "մr":fÄV|춵MɆ;>>};7oxݻxo{^a>gZm/ kM/deӾoȲ ^RL]&kJ;cJ%@J)nPUd֞[6\ͭMe|s3<[bow۷_*jo@Dn-px*'$p8^ᱬ PRDNP9QS:SSQ6h#hUd<(1L-؜,-|}#\+~:\tS 8ۃ8Z;e4nJaUfsP̑ax\" VNl%=!_P;PBHݞJ]J:D\7*6&Χ3LX8>0~CO?0eӞyVvb`9>-}-j(hŨC"7<;VoC,"e(D s" )}Jc|Zs^8dJ,k@M}V%3ȏJ_BwN\wMSը=G^\\O_G>_'l>~O>|<ӸvU!.@! e}y2Ƅl+ЋØH9,򌭷 YJX) Rɰ'0rZ8Є /gE]xCsqU|gمm "fūp)2+!#7nP*Z"w!G(Yf(A=rE~S=ȶăڮ,bK-X%Z𩤗9l3k;Dؠ%H 4k门gWL8{쐢eV(cP#N|IAPzP;[}[fc.tI%ey3>QbN:ʴ888aBad_|%huN$ωɉBL!O,E"oeYJ# b* rʿԙ ,@JiehC]8 `>Ec9% @9L[@i,Hq^PJbɔ'X-v0BڦJ(.AHh'Q?Y"Kk>7)Yȑ%h@8 u$,\#2Ph%L+3j"4%h5>#)CɼLkd^kW8ª 5Zkwnom7 ?bcØL'ؘbggj CXFXk1hN濶e{"1zwsH8C6E+ * x^bXg8:: ..fKlll`k{ O32A3aV^\*{`gw5Aw ;Yz& dj"אI{] &*&Jc"nh&[e$]]sN<2j*.y]G.8 2{j) 38,lK*VvZCAted,eNda5"js5NPƽP _`|jӁ7\7e SnT]vgZjύ5;߽wm&|u]\U1MllL1h iGcƣ^DʙE:J/` k"$~8_9 0r_rbb1|>o/ N}дRǘL& +~kL}·^k6A sY )BlJ /.n )R2Ŕs|;8:#ZJiCI(ʊh^G!Pqcd)=NK8-$ꗘ" ^(Pu֋'# CblXRiɺ،$E] I!&7]F!Mf5:_&Zkvww߲#UU *Zon'h3Ji誥-e=UUL*wiR)p:0i`I10u6ҴA)!fh~P'3|ŗ/..^g/6yONl[d񜸲VuU(P)#Hځc(dȦq$K Ç@9&O ' 6ʢa}b/x9(ljzT(ڕd*8oSrLŏ|\fU21,R{-=z:j89g9Kf\}ZgqZC/EyY|W3wGeҰw9pO``~&-RjCkSUh4Gp2Nx3OTUj7necPMK'^cCMⷘw7OUhTٙCk|XWr9f^iN>^GW/u][kN93/d85JD @{MdM2Dd&LDV%R,fj$_<)Oo#Dm"̈г)ϕvJ$Dy!|A!ݗQ"EZ~}~tBkJܳ :/*І.φRf)}&%;jD-gL92 6AX,+l/IڴUc;PZGxe,RS{ `S<6@!i"hDDcXWzu9d5O6't4mFi֊&JX}|YN#i304MÍ15i۰5a7u=z4f4iY4sf^Zk\3sӓ#?alTВK-@ "5'’D(JYY5Z|([ryTus(Cd^%@aQ0] .Ӻ: eڡ?;6yvGYr=l|Ú! "˞mv<x#EN^CJjW5lu!AhþDj`S Кp`?cU4t%C)V>v+*EJoM+{Pr4Mc 5.3s+eBjS-/mݿpM3"B)ZZ)(EV)DʂK 4KKi NF BN0$s0eK=  =5uB/pr_؞S|vڲ3 %J**[.ą #*a9Ό,!Uy6e3}a3뢾%AKy !2Gfym' qV+ +V-{_G+/]783.SΆۚY0*VZq& tssu is;V)Q+7 +KKm @]@Ix${ 30aXE ~Bͬ^Gf{T#zWfY<֛.1(5s:+wS%V,:Z 3QQISV0x.D.~+*;l~4fΐ5PֺTFP=x8{=YWp1(9w,"`7E3EÌQgf҇m653e"B&YreS72zBA4;_ܷ"/| zLiDeXj Կi&N$ ƘdI<`še7Bqɛa:_(rƊ7,yl0C.Y6b!0lB;c8Ğ@ZX*i=QgJukL{k]*^vB=u\ ds'G%"Sb ~w(X WMkV::<#xBZK2m^%uk!KiT!G}avhh/@\Fl'q~PU%). , : /c$IDATУ8y~Auu'mUn;شC.oʿj1J \X y Ai Ka=W'xBѳ#gQ+`]zQt v2hTٺn\a<QtQRW/눌[yz_tֵ(@֙rE7t:ƚ<< -Vs n'm2^أi:cPQ[U1ĎW2q;t:1e]EW#kV_f{g J AHR zV㬹 4 P}c73!ꀼMӰRlvA0N!ǠNW4D<ɖ q "ɤm?y 캒Sa߆hmڏT'kEF61~'ތ5κ|=y*RAV >M0Ύm26pKsT+|@g/-nբB+EP$1 D+<'И{925y6X.k M#՚ ǾL kfFe گqh#^"%3yE|M럨zxpFo?m~F8XRvW߽J"uH LdNk8) l?e4:@,$% ?SJ{K? jֻv rzл`2Y.`VPwǰ[FCGT ]ZuԙSDIK=ԪsEqtCkbDaqc} 襫̮AEb8A+דǚ):T`suw[Uk?C ~jlpDK 5|pOVɃgw-\T%hȡ,N.eD0ii}\Ӕ1\Xo&OLiRkSyM:R(32L+6}.ާ5|ªa K Dk=xe?son~7~+N7`^afY̘nl`4Ǻ* YD•'I38SЅ򢤵NC.?pTWj~:']DtfLR͗<-gݍd7 ̃{(|hB6?sN.oQn_Х^ Ӌ/Z۷QM @ߠ2֏-ڽէ˿ ߠM@GT Ⱥ2zU:2reV}@;$(*llJp}o޾3ND,_oM/q[)j~~y&=XsR 1}Au+T;]ֆFyn7/o޾ѷmloosG|)n VWsD-5liik@#3#*^^dT m}#.n}&ryBHIENDB`PKsgAgPKx3C-Pictures/100000000000050000000320F9B9266E.pngTzePM-wk w\w`n.n:ߝ?=U3#ow,'dUNJ(P Bܮt"UqeKeJ*t0qX;Y9[XػX9ÀE W)qM_NCzy.J 4Op{(xB$w55 ܯ* E&5!UUa$$RŽ鼍XOAV؋Qt|pt0v=L<;HK[jlcfF po1yJ\^ /+(=[]Ͽm6Ma$混Yy]NnQ8LD.|V=Ǭ&=ִpdQ/7Fғ1X)GҏzԡW`GCp~.q.YP'?m43gQivF਻l$(Qjo4 k}THW~ ,ډxGξ_kPS0 cis{>GƑmyjN^./v?? gn1$.vo<9pqpM餥MA1d%MtoS]`W3!NsAc߶yCoPRScF̟7v-ʒ͋--`xQW|zu?6|:0| E֟O*'4=^8-J2d YlK/' .,ċ^Q#IK[&)qcryWYd.xpvgW!4wrH~<ށ },r_i~-@u^~_><>^8/ՍT[&8?`6sɑ%ůJt7H>[RdžajL 7t=.* x>=_mM_4W^kw%0N= 0n Q&,aT 5]"{\ ͔ަ$3c_ף&ڇ|1A/Q)n~Jw_ ؟vl*۵f$t6ý։ȝR,uF%U X(E"Vs;cee" +66Чm%"wXc ݾ'-]gK{Yfq"O9m5 =qdOcF@YoٽANʚIªSC0xAa<>{a5үKn|9 cPŘL{RatJ,Ⳅ,qw96⵸*89QSZ-\aŁ3imTJ?YM_?Fb l3!'c dݥ2Oo2,|JYOOa~o7, x~hދλ0mO?U־~D8{Gfc;筳-:{v&M49 }aD4FZx5nnNH%Iـ_#p&ӫHAfE_'缺XAŭ/cid Ĕaޝ ̼D\A߾WVj6pvtes"VVXPjvpbXt$c;V~ӟ(/||4Z8`OI(fuSilI{BJ-]iVT]qH +* N/U3R%Dņ7L޻^| KzԴˊeO-~ qmRΚ ۧ4 CϷ6&{! bB&:K ѫpZ)My/Ґ":߶6&3'El.ư Xz!_so !gV ѦD;ֻXZZ.|`L4>>dVi|8'*GJ]WF&o*}+3g=<}V3a(}gDWE=u]6{}|2D7:.3G~>D@B4}l|^^SZ/L.Z^!؈-$Kw aNO_{X|9И[:>c[9}˷A:6iiiñ٬ ]Z-!F]^/&C1g)YV-vkoF^Jç x,Ip-yRc6MwxuniYJ͇oV`sz] ;{`x̚bQp@f!{.A;NyeF'oM =vvn 4zpy+fIl-P:繊y.#T7r;qF/Y?zP(ep1ot 5ui<:./;\ϲuUWȌaN8)<0 ёr@e+U2&TN2|V78Ă?n89,ڏϯϩ(疵L>cFi\*n.M7#G @SOZ6`zmՅLɺ8ࣃs? >t}tvټCr@{j$lM6#\V1{j>!+>!t1tz_f=,OAfyei%3.<$~VbPnwj0P }7nj#:3w[&6~~PGތ-fSC??"3c;71D Ъ2.CUw'0td '뤆!v<^^s^T. f $^dɭWt^?y$no܄};O6㿿PQ_{e?~~Í/ ۳rDwpQB[5;ݗe:Bv+͔dGUD/6EM)=k=^jJLM5$ÒurX j]~ *qz2Y܅<x=Bَ"C_es&boh8ZMV"}KC/:<5s?tsJ#)6 *ߜ˳\x 2byMq޹{)=n}̨NmGS}ҎN]la=\hAa*Nὄ6esšn\B:"~*yaK>@׬ݏ5YW<)%`<ʜL@7宷~י*kސ3_g~iC Kφ?z-I&S -{Bk u߭A3InsV-IH2C|x:H%cgcnWvȎ(_jRr}@/7pH%77uZdI`Ώ Fv+Hʊ*gropq.O0=F+9wSy9U6n G}ܵI_xCLӧg/m,UznGYu~~vKddۑ*a篛ғ{ID8nn(CޟUT7ykpT>w^_ $GWYv &|8{y%?ow _+a7]x?w2}s~t{RPan5>ˌӢٓƧe®M^kӋx*fXНq=.7ˎul#$;8 gmo\_Փm㏿Őr0WQ򻠈]>Qpū`,+L=TXAn/?㨰1G+~dnqe|hO,~iX>>:/ȇNsZVU}}}Nn}px}fo-g~>== 7~7w5n7`0^{Bj|}&;89aIO%DU:0d9jkχE,>\YGqP$"&&.N :v7Zc.x۬_lg H>Rp /ho?,FZjDd@ 1S4u~H g\X9(Kf,"ł/8[{+[3v`J4U幸qry}sI ./ʯMP6:=4|0Td.X:iD=M!>7.LwV/zT P`珠[߱ {VKOF#Ԕ HAAadqv㔆*<6`.!QmynIPhafo wQUm-:; XZL&.ca+" Ռsm_l yUb՗qpQTZbfR0ektGQ`߁Z#l-G!-ďhOjרT )]$f?r_fAg!EޢʒtF&a0rj!(3E%Qɴ4$7>/^u' zϻLk{;`)L*Gc !hZ}&[F.V55lCRjͿ_O.nq[5Lj.RJ"JDp3o6=`~\3&mOt^4>uN<"!ON>˄& b)oS'{/ ٫q,N{8/o,B6ȉ(ۤRɰ0B[.;쒮uC~d^ ٵh] ]h:l8;6>f)d huָa$ TYҟ0V#ǚ KC% PLo2 w'ؗh R(CϕB%#&orsS%( +q+ FL?8T y,* O=aqR&4ϐE)ܙ; oaa˞ސr}<7Y2v SJtX=^pQ0~JMKբm0 ʐQro2ׂ`,V&u~BπcqXT߅*'1ºvq:Pn:!Wbz#11t> ~27"-2-f3ơYK U $BJI{-nڞR7s B){2c:4qgECfHgP65 V/1[Ru=D`,\c@&1hu 7`D]tLKEvI/-*HT&г:60b@R)f Hz\@ݔi/[*(zU1X(? L5f@qìr)T. yRؘPbЪ4b : wպf7Hu?b߭p ps>gs.r]V&bK1܀'!Ǩ@N"3Uf|mbm[=fC˶bm-L|h<@OKO$LcqG./N:Bk=wu -gv* T+ >28Xx x@gޤCTᖚ*1跻QHXw2fM!R?2gS$223mAiϷrz)sjɽc9P1:Z"%7@l0J_*Rv@IM{>8ڸEʃCK˟긒#\/MeNjG0B{ʰH8J$ tQ3xKxɽn{Sj!E&5%7{zɩST)QsQ K ЖE΅@8)AYm{Kceą@Ag7n=]wSfXgiD=clCܜq٪ 6\] l$[唪2|`Z bi ƦEiyt3 :1T5!*pV@'|DCKƈmԿ%7CG$)!0B7d?!/i~S1$_44< 958Yٚ됑>E@9y>M}CmLX[u}@HԤzMpƯ~*y6^}y/̍C[eP>\zß_y90*HŐ&aMY,'0RF^@ff@w =a*r EŧX7VOJ%vV jHꜦD6 c^?Bra+<&Yvxo>P}yA!( }98`9LHa4Rh:sH Na7 ʔbarʲ 8[]L \XQpbER*cx$ 8QԶ~'O @a$T XYɴ|Bl2:X]P?0mTģv-#8 *{CLG^cn[']d =BvK,HR cTSat:"Tux aߒfvjӢQ:5Jʤjš,65Xy.)iiTRpjbAxuLW3/wm8<,(ؿ(Hc2c2Eu!M)b:tp3q٘ӅSF-vGujK[OET= ͖jTҸLUΩ'M-b׵VքIzd*se9~|BֈT[މ6 LB zK *##YJrTɲ/0w2ܿDchgO6fE(Ux/K 1ʷGGP\YB2P3kE$ i]6"$ 'QhӖz4Qi@C45 upJYr">r.lT4mL\ ee3xMve|l;\=|'\ <Εpp2 Ï?'.n}չ^&#$Cwg,+C[8iPc7,|s0#5rN:ġ~PHS?hbTLZ70N=-XGkA=j/] f*1JM#+P %+%AE uk1ؕ YNy}fR\KduG(<Ӕ*#\;]$v# _1R L+J;o،ofvdXpb7p'I!n#oMC4Ў৫9q)aJI9%NuHf/k%$1w2} VQ $IȺ{j}1Ƅ>}ƺrאPOYE&r) .ƚzqClj\G 8LŗsP5>=%= ښBM펢d'9WVS)1WJO$Gո(ht/$ 9Hj8%R+#eXXGnyl2U2eA >a+F\rR,)e;Ϣ҃Ӕj] }=%;̊;e&մWåhЍ#n/C/LjKK,*P=)9O5 =|JXt/O#s+P0"{5';˛q5-% + *6Ď@<@/myc2UC* X2G2)0Bg<~{;'QAM0,ɳqx#k؎ xRD; }~.x㇂ܷz+Q^ ^"<Ss"|U<#eh㘭 Z81MtoԷ Sy4VF3)(yPqzAmD-t VjꪬmbqJjٜ#3b.n OŶh@t5"*l^J4kJ%%f͡o#OF T1dB&S©wXVBaV<*|MHbyhSX 0B Zx˹3({$=*8"cȵsQ-G_9 ^oŨ,sZa'#:İX݀uvBr2BcQ[ee= qmkQW{G/R;ҳ&./wZN,6PNe =|،f8ȈIYS_ӘeG* 4n7M?܉&qX+DN=IV(1#U54 1%a$rZ%$HP)-,YFEC,vu !}?{Xw"Mf\NUYHݰH8 Lrh։Q93 ݆q2b n[e-V(fw,NU aԴV.2eꨤs|۲9k{cF8s&VQ/"sswo!eǒ5eb /p))](hpIbHMmcU-o^隞(om82p=]-."YZ+ϋRMÌ]=_z ]nwČeTxѱ5p$ Jxqa,F#7Iy`U,1~W7 a8Y vRV֤׈_yQH,Hgx Ej p|aJEDQTDZ}\P#i_f&4>a"SI0ÐQgXre(8ʺK7±ְU#F+BMS51)c%NUQ\A̡N1j!klȭ`]NC)>]5Z`NYT/]ǡm`o($'otZ ̌ dy6_ʆWEjz Sc,|ǐ^Ji:@'3&\XRQloXBecl`%LI&2 +kdy 's) 0 "Q]oL@Z QvuyO7y0 M zt2u5!O Tؙbi]zWFT6+ؐj!fj|X/6""m:0X(hU@>rf:[Zx$/ϤƘ!4TdhpL:X S÷JEu3ivcXie)zE<01)չsE2L< & ,U 4s&Tr^>cYR'/m2 ^g"T,B/@YyhUShF=r0_[>Satj3Lj xjqؐv5Tm4lHO1ʏrd} n^zh淤jt MT0\-w+|CFFqs,;m9A$Q꧌!2*u)D"ŋ+~Qnj:ް˵ Ɍ-͑m_PR$)꾊9w/vsÜ! "gLMsO|,NZ&8E2%I o )q3?1^Sptn XǀIx4͐ao&^Ws _k.e deī/Jg@0ׁ[ 4`lF 0B_mJZF<?zp~E(2VbJ9`VeBJZivq2Jć Q _4)So𺻠bѸ50+ׁ jorDqjVyU8m@~3G?1S_;J=q\w`m*׺ИvI%f$Gi '@!^[5:Int":B6xhU%[@[FS9n"GV}l@^Gb`%aKg,m]yISTDGan+tjMZ_걄9QXMbm9zщ1!kQ>)%DD' a[5 `27sev/=qw/b&{eD:v7ve[X@XSeu2b a-X6th2~u59FC^0QVaO3˰PT e"HD]ס0RUDV"-`u3*J̨Rpo"H>Ul3oK1waF\wAc>a+0:4MSY1) HW¢:}Gd)<ƃq`*dL=WQS"/3l#?u/Zn6W(>y{&yMm)*SwOТ.hK hkmNoZYi"(+=3GƇ$a~/Tc_#l%ϓN" 8Б(*H*ix2<>[lDg;{=%fg LQHOC:FmE?HN]ל45ŕhI&Fk'$]^~R\P>rݷ>VAI xjcfټ6HI~sc@-W۹R/g J{aNEH{!"C%uYFE~c9^cO͒x@ۺmJ9Dk{cRD=z&y@)O|I)R`] y4̑*ƔvP,J:`777O0u9s.XP^݀@LP40ȱ ՍBU;0B;4M]v%}x}y^ j{ #=o8յtҥ[ZZlRvfvN\6BNz)ceRJ*m\ж [eKH)¶rY[R\(} $V\և`Yr + hY.^DB+lX0)%"$Ubf }H8hK Y @RyJ)`*pm\1ibxX( Ȩӈv%ױP7fBk39tx3n_=K"RH)b&"OyGp\5 qMMM'L8n-۶3%0a㠝ɣ'E/?puTrʼ*#yN?6Oio @Ӌ#Z4y彆ϛ-<%i&ZUa*{_ 4 QAFʾJtdzBSmSbwO$Yدc}>]cZtQQ*\ i07H@o-!|߯De`>8u@_gnfip۩zmfT/NtBȳliv炐spy5<:͋[B0(D-.k/"9t; v\p9"̞݉̚"H)OySyLJy\\幖XÎ򺷹;s-^s}E\8:9uQBv{2ALƱYa&>U7Dk)zo*PiM̌^9o|f'h4I!m;<793746 Y20DP &`G,J'!,wl;b P ؅ao.H)@BjH@fdB I粧X" Pd !ʠK@gEڎu\ńBK"Zx2,!WR.J!m7t%!@`l/V#RDJJKk 3u (ER `bbb`. C[T'aS&}k_$)_뺮"AġgV*THrn "CQ_W7v8u9;hÌW} Z _YaʌE+x $eg6qA ޠ6ȬӼ"zٿQYs =o mS(IwWM*g: &ڞL3謤TQk+52r 9dT^{[y뚗^;nz{I;"0h_h{ Dx = ܰ.B!\iIKJXRƗ7R_LV(5!Dx3eP|ۛзi33K)B(*;>JӔDHW{2oBlw 6Xy o50/9g2Qψ"Vo_߭#\܊d~m`!ҒzCJ!`X+* ' (вmGD)Ѳ,uA7 V,-)gPI!=C@b%VJ!UJ)Q@T B"FDZgbf۶ȟ`VROe@>W@m36"سёkOBhV)K=\ORo:Z9] u*\\G%QNY۳m9e庞(SW[Jyz :!eV]bT;o>:~qg( ;L#9 snEBDDF:{#[sMhg*>`Z`!K22]z3Lp/+^WH̥d5 SاWy׮pLʻS%pó9ļ0O2 Za*5$Tј$Ϝg)]P0Vzzfϝ]+i[!;H>|s]G;u(r]b)oD㔙AD`ME zݲi4K\-_!>aKS-)tj-ޓ5R.0$" '{8ڃIoL&f}=P XH!;Bc<{+ GO&W'k6}hpё됧V9L`H↨e 쭜;8K̖I/Ǝoo,bNcDU[=4F3d \*?zyuB0^Mضmyr?3@"bׁuT_Vp*mTFѺk·kpb>}SL#/7R+X_#GMIs^͊LVDkYG9uz,N+$LTId瓙fG!;_r9{íP](q);S':W( 4WWqtE3L\͛Ī+FnE mj`;纬}Zh\5UZR1\ hMYYRUi%h2[AFY3AXYox8  U,8lN'ƟֆvL3N[k AMYBxU% Ya`qzmaցic M(x%±n3P2 Ma{z+DRJ946 Ӆ<}e2,s˘e 9;c JyXmf>'.R#Mv=,jR.,RJ"J9/)s#lqcj. )wYa4&]k)9)d9#R\{>'$8=EzW)S58J |)}Y{RZ{̚J'" 9kJkCv,B"gF*_˲"͟K< Pk4E~#[S}.Ó|'"FtzTІlT١WDVB-R47`+aryx-mژk$@gse&ࠢ}Z3}_kApp(J]$8¡6Q_˲_KZ)%p\^aZ~(̈NTll#6SdEQS&G.fJ3S),A6+  '6Du EUK< ;S%UNE !>PΕpJ,SEe.| 72N,Gy ^YQxSaI+4\EK5O J=!50$pM܊Cd+k  0KF-+mCY~}ӬȲl&ຮ9 H)BKLZ-ɜF"]9̬=P_Vivjf)&*vfm 4ks8V̧Mi)9HJ2VSu^)`O.-9cFkq F$|P\dm~F߽ݞ:wK5zK a,9R;P7"nQ_ql6,r,~<ᥝEsʸĎȇx[DgdΠp~I ib)Z>&WKM&tZfL_[> )}DBV&x,*#pFP}V|∏;Q6liSs'qLG''&+9]ƻ c~!µzH3!/Xz*t*+M|gzV&42&!,K+dIUʴ8f,)x-a8R`ZlHRc;Ziٸ\@EW~%\<dK! M&RyBV%xVJ1lJ)"q,3'FіBkBFV}%"MVӬށ|2Vށ#/Tjب!vpѶxZPC+̞¸ gpf~ cױ'mۆGWzy^KV{eteS[3>]w$%'3+#1iKZQ&BD3_ogŏϠֈ_z*sɄîGL Q$ԨH*mmfwfEj1zcƔ`U'WE“\{QKDt[ѾSbRIå@"A{^X W^Nʜ+}#2<m[61D QJa\4G ^C UhQN/@Qyjsun5xoj@6:_40nUV*%|ljr?H{"rV 1Jd34+ hRC\_hBJ˶|7&9X 8JR۶ܲ៖)"`TRJ֬@YĂY=DÖp(#5givZ?˾B)?pLB(I)fl HG!V&s?*'3z 1k!Sjo[= @?*z:jZ4{?Vm&0<~Jn6:8mhh4o޼/xɒ% <::׷w{n͚5K(9:Xk}rsƵo~}?]v5WЫVn͸B=vP'+fJlN"9BF̝/s3МNPyCsDc2$Gc#XS%4u"BS2xν\ÜP~sDKXj:͖}Hua(;NyDLJb1։ێ)8yqL5LJ!r]Fy.&lZk-\{$X[#") mqy5[<]RYز|3a/c4$(OԼDa\F[F Oa$nY,ˊvmXqMB+ن\0uyƔ稞䗃0fcYpMU A=0|ݳk3}M1>#5Mg˲sCCCK.=r~n)eGG… :Yfutt<裝CtBNv\D }v#&@dMs+diS&\ԪGiM?AwH \ܳ[^ypƩ5XN3o mT l^qk=wlU2":%Ư}hp*45ebޮ9WE2:rY 阙!C %*?EJ qҐ-aQ5,p妩sE;/o|c HEN}TlsN !3 r8W6zRu]866 k IDAT8uݲOj;l=`@g& I !IAk6NHˊ0FEHb><$,Y(E2 ^CSÒҐaCT,Z ( mWeb @Bk-Rf+qldMex`ʴi睫zvjo Fo -\>$~&[Q1־l;{{.:j^x|u_D7ScvѷJ*8&=+wPsc+ΤBڕ?M w83YYv^Ҧ(Zo~~gEMo~#sēO׿|1QzW\׷aHfu"lP1 Ϟ Ag6VǞay]m5^"b֞m0h2Y+VDQ170Qn4RU }˒>k}"~M>f1u&3KKo1suh˲Fe"CJ*BheYVDdM4 45<߯1&"V:هb]s`ϴ%簷[',t$$b0ǬeYmϞ=e/{ܹs׭[_1oݷo_ŋƌ'`]CF!Sy嫖4nw~n< ۆ皎~/~pf,kKwVϚ׿|rudY۬VkX)-wg;5V.tή)"$3'apdT* v8۵seu .~.UDŽ0묦U-?ml3ae@EuxIwQddTjbRL@ XqE^"ÇҔl|k+XСڦNݺmSO=u7BkEԶLqBѫlܸq4 g?W8޸qݻk) *[ 1H2r~.*YHav<m39R\fخraĎS*0FrY B":Е-(ke[J)JH!Z3+fl,R`ATk!--LR|S$ͽ1&kEƭ4C7]$AfDz5`]f*: )E Ѥ&-DbrMjd@wQzcq( e{:\kߡhhh<'%kG 80XJԴp… ۷{熇 E__?`8m<ϳ$%#Ѩ2EJ!,gi&ym`DY{naօW\zޢg7<_=9#oh_v+-3m 5o?՚^^y˖̛ Y|}pP싮l鼹sgMv-]ODۂbMXC==5e7_9;~Ejp eesOn>ە\%τgbP X3*eJ]b٢9&M%Um?s?~~7^{c͠˖^w%L)顮oW5|ٺW._8w T_\w`nhl}g?_ Ը[Qjeu7XmaV9']ZQ, N<1qբBE<97i1O _~P5\sŗ?VE s,{kxeT:P9CMpX۷m0<1,d 0\$=c6%{Xwe PY22Je1wvnJ-fhB'9'4L Ųf"4v>ݫzo̷V/::RqHk20Գw37i7vJ _wmӿX{dk֔drׯXн'W^n[w;JrgƢel9sv# Dɷ߷ș}ŻJ8e[J h&a`+W\?4d뙳laMz Ԛ^yMrW_y`O~fsәWΔ@ _?zc{z=Pg'"4W~B:O0ht$23gd!oE+JbBBȔsp'^RYt$""G;qW"A\7pl#s?$W}^0v/-]k_{>/(5Ϯ~)*?yf`hp/R;v oڼT<Ͽs}?yT2Fя}y/(?nޞޞ%k_{~]{vZ' H^Ԏ$_<*|ĎAa;R *9%"!ԬJ!%kMDNRR{Ȳ 0kU$XX?3JZ+I"i @ &bl$@='KI_ZDlYX/KIZ[P Z-l, Wa6Sk 6G.Qx~tRJ+ D91+S%B]&X(]a6G`RZ ގp-99.|uԩfشiSjw ߲e˹$LRDc[]30+{rH+Kf>eH_>Il0WnRkXԻuRP[ŽG—sxtᔃooλ7({n𜅓r:Ѳ7m?w1AFG{D@E3_laPik?ދ/p#]{g]op?6%Ő ɪA%g^3oŢmyg4R3\?Ҥ [ko{'1u{^~Fic@iӡ_lM{y,h]vfXodh~q\<~ ꫷E|r xqo"xƑAd RMH[eUqd8TEnŰYd+P(6udsBƏַ;v;:ڻjo%p 6m/O>ql߱v^A'7_?Omݺ ^{5k{RY__?|>r+WZ~E7mpumذ>eO~S׋>;{|q;wv-m.r">BR } &\.u/nآ9Wis[%-hS)PU) ˍ6rQ粎JHk-Eٱ2Ӿ˲<#?=<7r.m^'-M@ f÷Ll! ! $Gxa?bC)LI{@!266v8N\UJ[~>a`׼}-t8 gWGyxː g7== g]w9|xːĤ9Ry϶C>m :,;u׼|*?xwvY6uwvÀe Qko:`(&&5},tr`gcR$e僝Qh=#{v5m -5/6V g]w-L=[)0k~}ۓJ|&zWAqG@LjrmAE[.G&$,:=|z (R3K± BMʠJZ,W4QS YmVjDG)EjݜYP""7kDAGG5U{bٷ}u۷{y#俍G)7p߼ˉ蓟dU6_ )}snwy?< x+$]]ׯ[~dVΧ>}{_w͵ß7lχ|;w[힟?R3cI/roGap*2bgpcc"mB@ )ɶ-M3HKZE (9%APg(ShhQ 9ڏ,mSڲE"Di*2ZۖJW21Oicz|$E$8$YϤf(!dfckaY@sOAiH@&ͤŤME:OG6SNxl#Dxs; T"BZbgS4sw6-{-oX"μf:ѦkooXn4K&] fBwm2âyY-ڱ{(2ƹ%U窵=)h3; pheտ֌taS3OSL:-ӛ%3j蹲qZv tR<{:-ݡ3&Ǿan<3<ܖ|$JS {'E3 6pc/ lq|{ ˦tT"2:8w<n?v˫.(0{ױVP#ͯ#A}1񠂿Z_ 1 cͲZn*v(o; YbnZ4UoҤKDWrlK/;|_,[,7ỳ0<v-@^WΜac`g` XSvгPՎ=ݧl5MޞgEo1Ϙ@{{阢uW eތf>5t,l߹5<_g͛ PBɞ:B@gMm6o?)I`kն`ƆZL;(e8DvGF<կA КrfG8Pfz{_} gw c7b#Zv-:EI3Z.NvzϬ r̞Su$A/4"N(ٔH6k<:2RFaPא=cCM %Ag:xGD/nz1~뮿kK~[Q)z{Λ7/N*2Ƙ*җy{~';bgQض뮿3~? $*Dz]C@s{L_ﴲBH*Y hfq<5J%sE5 a7ؾ26 ǶnY0S Zb6vZ Rif 95*r^\v(bLRJ ľHkRڸ2\^ AʇמHphMBTZB5LJy@`lzZZҢD`feM{fNϣQƼ[0[$H;<<4k֬M6 &O zU:WDW4Q]rIT"yuc i>i.zzCis[1.cl-차{ˁ%iK`dMVlO]abm\'/nKlD sa~Eiy-Ssvg]vBKo{s(t0δY4:j[0S`7Y(BNs  =bOjۂA%X-л$̽arZ[0|h4 ]B{OVۂ ݝ:Y P^_1fGyڽ+jΠF@dWHP_Å : A(id?D˽_PH}ِwM}p`L~%?,%;Gm~׻/~{n~ߺ[ oh_ǒ(y^qUW]6E &A:;]{uOR`XvE~d!I*  "w =A=U#{F^% Al,^)plNrZks67AWUfsQnLRkH)}70>Au429 !#͡-ە5OE襃訙64 Љ|d $3l=mQE~ɫW4=ucƙ5c+7I`xʼ9! P엿WLoҀU:%i;g4Q{G4)-OgX(0en XS.U-6)se(:{>-}36]~ؙpp`A79e!#^ZPqA\꟞kJp.j8 IMҜ bf5FKr:Uf%dd#\$ɗG8cScV|fDDLFd$eJ=z ൯yM[[wwG7]_/M2P_ի[:į wEŋ%?oy-[w޹dɒn-zP&Vs]~AFGGӟ4=--۶m[no~_788?duz{{;;;/^lٲ[ >}ٗ]v߿m۶(N)\^w{pvƂ{0-׽wxs$H㶊J3M4J?]z޿}qFÆa9f=F=YWEm9fbܓ$ƺ?l$ie@mP׿gΛVB3w.@~O~~xluoz/}d`-ƥ֏cxMcNÆX%tJ`3s~߼yn=w>/dۿ믿Ozv}ɲ]\7rN5C9%n74ܷ~{6\v^;Zםҗq|'Ɨ8C}㨷Q#r "v$r-_'W/?i/3\nk^kDo|n={m~3]wuۭ~3y~WWז-?~_o_җWա|o׬Yʫ*>s:xʕ+?eFbxD`&`-ߗtjwhLj|Υ7tpW?zˡZ4#//&둟=8uiUPT_0F|kt5K]ֳsۦhнZٵvЇն]{Vu뮆x#;V{ݚCjJu}s8\Rc}{g ,kD:8ePeɒ R(;F8Fkev' 6,e#Üstliy7|^~E1_7|wyaaΝx&7o|#xȧT\Yz#FN¦++h(9ﱯmۮW1YzFJmomۮ[6qJ8VcV 2DBHHu)IKj2{5Ze SB!RIҾ`YҾfe}͊iK "eYZ>k&g8Q*|B?["zT jǗ{&Y2?+7ĵ&o~}9N۷ac³RC۹Ӝ뿶2h*U{=E,gh}CRsi97ٲmq)9ƆIMM ./_O?[lBttt}+VXbmܰa?kΑѱ[.ˮz{Nga|#9cfOdY7}\VqmBW}[+Mh:PU:NdűH]xbڮH0Esun.j=%;Z 9%]s  r9sP.Y[1N)IX SeJ_HGܘ*ՆS^eXvs\65AGlȊ0wēO|w<>Н/#tI|?>  0 6"(MMre`&䬋{{[JͶ%Iv@"tFtZkiڞ23I}ED$c@i}FhlpY'opkK2)QXr V;T6+e(gHyq-ؾ}?<::bŊW/<844$hmm9sfkkkwwO> `gp.|\\ p~'-OtZid%UZEsf}Qw2,]T9BdnM'7+֟ui$S2*"#N^d2EMdp2aiK-돟T&Sg/e~"'V|bRfD rJ ny LDqu}\L )-)JkӠ+f{8UmKk_0ZC@1+@)e;Ʒ*5+-B@cfK)E̊R+xi(4`""!@$p:жmO+HqiRB %6| 'ң (X)%i9ቈܱ}eK͛wy絴0s޽{{ 6l߾ݲ,uʫPoР&\4 IldԞ_= */mSiA9Z*Dvq}*8Jdѕ8:&|/%87{,l܆849U@jBNDT斻e s岾,R=e2RDe>iP8)dR\|#۹M/"yGܶb;-YHAɾwÕ?eYbf-V<ZYHV3dR.H)/r$К-)ȨR}i֚(lB!4*bÛ"ne#>fDf1Q~wQzLqW'! 2I.8"!CC Q N}z)ϱ.SmĴq_sH2K}-Fⳬ|#Tg7"v'! Sv,iVϙm[2kAd;%+,RuY+\c¶l-+Z$df!%Ik-ٲmV iGN CfF`K ")I>JyƪYJ@+bB X&5C[ŬRBHF9Hk$R`e̖ %"o(,LG!4F0I5iRuݑ=w )# qe }yG1L(%_!gOI7w EEuU7 :NFDhV8;Rָ6=GΔ!uq:cj>IfЧԚ!ube_ =r#qgՕ&s)\iꕝZ%U+I p2̑n C ,<ffA}H!z03P]mcSFhAT'=? chOI/2cF~nU:g(t4Nmdh6hw B2TefdkS*7N9e%%n-skɰr'%VH +vXo[vRAE?ÍƄyJwEqWLS7A${'* "-8<:sZk|]XD'P !X+,RJ fM]WJ""b`Ce"-Cc*&`b!!?;@;eI!HDdI1Ae "&J6~C'0rF]ghg<~VRDS0gƉPISң@xݙ1n-!"bJ+fЂ\!Hܨ;KD DexYk<s] ;!0{tM!̆Pu 2A fe(Rܟs$ؐ %&+V2Ȳ,cm†aU ":ײR n,A M$MSdo`^Tל,I0I3f*A G% U$3pfP0ksJW;kID͞_;x3E #*OM1ڽ0vVi ~ "F{'}V[vY*dEQAݭ"L(gza.]dW?*f [Hdn"K;<U,'7r{sVRGJabhi [:`"XRq<BPr3~SrVZRCC\6'ږJ%fmW  /BYZF+!e |ffAYd.bfŚ PXkn&PÜ@p}Y{g͎_Kg-T̺rp1ضxǔ> E2p' E/ɶ?='A{g@?#].B-|ls6G:8G 3}f4kNWU3&0)coTCN銨~yl9h}9z&u$UsV D" IFRBljZ @0Ϙ9' K%K $̾Qx<3 Ě\PExnr )ne Bl6RxQ@B-!WUXv3Nk0'`J)=Cˌa3An) )6a)&2G_h2n1/v ~m\1ŘϾ/gCqg 5Sh H-aXUuQ$%L:dH,Yc;  B12(@pjC/%A;,EV  HYggUЫ{s#_ܵ꡺nհ;>C`sþ3Ӗams7}S/Dgt2;U=nFxsG `}T8Q!RL3H5 Mp-nwf;9IMg;GՊw 7Mu_o19q)@z9 Ѣ[Xk1"-}߳c$]gqp\3HYc#BDF"Aa;E48KB%$!hgE>'͊7n#)*[F/:Bag2 xD`@d$\(=]}Α_" 0xeDD/|BD Is?z|>0@?iM2,nM/Ͽr+'7o Zo/g_^:-6}+8y^z{Ҩjy㕣4gv,]}å}^+P; CӉnL۵¬ZfL5v>2'5e(AW[M+=.on_ojhm[8RsHџ韽  oiDH@y0 *se?Y\<KD|e"bT@zt5#V ݴ#ÜtW㢽-D.I2t]2]NX# IDATR"'ͽ6GdJ?1fXl\G<Zku6z=u?WW'#1b<Sڿ)Esjj[gjګ}S_?7X {J׶LR Z/{3%ޜtki_# ^sC(Œ+}on3^M-]>¸6c%J$VÅV` `[yR}~')o$'~WW{7疉 (ʘAn6(( ZwB`D\tгs-a_;>88ZaEK|,qG0pבըH"¨Ak8㍝sB0]] Db%EZkq켫[Ȩ8;^\8B D , ;!fhuL,Xxa#+?Y1˷a8W_y W՟ /,{/VjꅳY11moTl}/=)7.k?}qY֓w־gjLfz pk1+pqV45X \#{J?*%V9c=whG"dd‹/g>S+/ʫxyq9az)笈0;vl@B!~@ifpΉfsD?;ڙD*ZO[p'w.ŝ*\n8NYV"1NۆØMeHٲF\T}c#;=O7HWWWnq~~=w?9>^,{9ɻ\~ç fΨ&xvd54x/A=M#i;\+>&a8??{[զ%nKDqT@d\aaX,}U-08f" i;-k%3~Jo_}ҳNol--_w ):K9n|Y_OMvE@S\֍[ⱭZM%o[&&S*XfC6CIx*oiRS  4x j܇i:n&&13 WA8?t*|*s],Zv[d5\8l6x{ڑrcw"[,ѿC4 obܾe3#AǞgby˩O>IiDx] a11dHy#PJR[dӤ⋙ccGX{yqm= =_VfxXz UoDtz9A#b:# Z,5H܋-" sG#c!ї+̊HDSs.{-abI~ ^䜃NDTD&۽އ*:ngÆHS iZSz t1::~yZ@jM?889 NؚjpqqP[U1eNPcwp|gO79]i.3uG*A}ptx.۝j@WWUk`u6W1NNgWo˝X9T;$]E -Z{2yrLvf5.1f:QlfY%N˻eRE*g˵);1bwGo/TB93:ͳ&+q4y[H]mMS]W= ̧ґ30aLbHB_PeNc9ZfQdef::TlkMbhh&Cs\Ûmq cˠeU#S+:S"< J>$]ULӹRWa5c)RiIr L]Q*!%Ma& ^d)"9?Ԡu(R&k 0 =xZ DȎ3;aQ@X+"l%M$0oQ)$ë}:߮cjF[xi<*cQM{c{< Y~)7љYu:p1R@LĿh#-1Z8ܖbtǺ:|tzݟ/jAgҋãC= PPsã:sx?ԕeQݽ+͋+VLJ9| Pwtرh)O/]Z?0OW:>Zɓ3] _Zt5.Mnj-fSVخrdvȰ AJvg's-ae\Ncg"CJ CӅoRJd+ˢdR40{?;TcQOT^ $NJ]z!y%vJ3<4l}|I"9@Oݬv0e7&3ʂJ9FO Y&Ymwa٢oH63EH\Wf7E/l4r\;i 2M֢(x>SJFn_v2`LÀPc)!=X#t*4=`Ě_^^M  딏ln1pOr  3ưsBZBBԋNd4w4*TJ18c *`v(#03s@bipb0b=+Td10 N;)k |IRvs( ]13  B< 08Xʇgw|>tBR4N#u4TGZƿ4!i@BGL3 [afgX8#lVg/6_? ?y'Gw4_=~l`lD;|uwtӷ.}0:w{s .4!ݜ^GGG+M;EhW Mtzlup|<>Ŏ^h<~|1 e\^ H_-G rn˷YvD']',S;9=㓻 _~&CKi w& MhFw?w}LWiJyk(ok?2/%ZwͭJMdVim6 s +dxY-WpZmNAˁB9?v(׻oEf3|S/WUTV0Vp݈y{2}PFf0{@]a)S-JcfyZ2ӸqMXƺ/ULhDJdnKM"KʠeL'$BSmHЄTc/*)GY0ri:n$k9@cZHaո);J(*N:Z-YM O$F\!B 7DdD`g9kba@D3[kPI7f "jig[~ ?&ڟ);r!BCc}fp2:!D,CrtO=|%^`+ [8Ytp@ +-̰ ͛$fgc]0f"]/RRJ)k R*#)ҚR6#96Zi Q{3"{"?ډxg"RJY7 F_"";g1E@"bf&A+3"2 ͊XDp,z׳8 ?|yxmJ (avʟ?"cAtzͨ#^pX,fVYvv͙}XK{w~>oIAb$g1 ui2uG׏_BD"M,88@RMQ}ddY@+@{w4TجG*~3Ї'+:G:Y(깶_[T39 x?c q7Jc,) R P[ n_0uM٦ءRA"C@ [SQSHiMvY|BCũ((r>%ZD*UIJmc9*\R\%(ZE&%nʎŨ~N[ۇE؂? |ԥؚij@.=WWȥ(͓:1)c,YVFչyY[pFAvʵ69PVl%$d(+"\ޘ֕n520HCJmMc CT4m 5&&;vGJ^F9n<5픜%W X@|䈓Xu%UV} *mpb\[dD@$g-@5=(>@DB~GwA7RikFр{qEFm19mW"TFDH'E_M]}H"~ wƭxw8g^,1` ]yLYQ<18$$];oWރ;>99X.4. ƇWCwN_~;?&DX;77+}ٿ'-)aN@4Hz H*\ /tfC|m'M{4^[1B<:܌$ ;wU‚BDZbSCxxjM@Dlm*/BƕI kK<7Cu1傴9<$o EE* SITD,daܚ$)KYFfdK!ϓS"5O҅N4"*D#aOjGgm'Zf Yn0Y*Bn$- jl%öR!̔1dzd>uRYs%{jl1**RWd^JvI&SX|Z0DzfG-yB *ޯ&0.Q5YS{hk1 _% !L Ԡ`<;Κ֩D$j:;m85Hh {.B FB,dk\m闫-lDLH$0s60ıɮt*T0l!$e BM|;i 8Ljg*x4;VDwK3 n1? k30~;t-aBҺ XaR*ڟWR":탫ѳ Z"ȂԴIHDV37[K=>HQ>FX23`7@/OkFP M/Ȏ!k\ϟb= |ˋw+ :O?Op|Xea?~1O֛Op G&$tl pС''+5Uɣ~X?O._t&c x"B`c]{.C}$e:>[ӍCTF_X掟{qqvzqzF:&T{xmI*VIDZ3 q|CmukZƬ杢:iD.!ȠM\Y3@"e  % }*\PzX zJRua&W$e{aMRZ@h>e5$gE&*M030RӉ/PZ k,ss!IndQY/!u 6z\ɊL5G$b ΢(SHc3OQ09wLT԰PC:goQw,cU I5<-+_S`f[Qi=g (lTD!F`fے/n t*k %&fzi#O6Q*6 mʸFJ'&(ҙ֤dOր("΂, f`"b]9 @J1O30QfݸBDLD@kmSׇ1sO2J{b5)uu]} 49>"kWE(D~qQ]bZutD@@gdftzn޷[?؇_8Z +|чNV(׎~w?s2;av23 o{puofs;MifAlS_8ːѲЯ?yppskD}SʗY-5H/5;,DX`j֟%{w'׃uV:S*=޾isYBR<.J)ݝL!ֶEJHd%BW(-K/kjU²:4"`L+KNيW5hd Bgj g-'?PNC2&hDDpsiԑY*Y1 #A8LY@u(q?uzl0 ZSnր27]?MR?:gv(dVV@WelEÏyqʗqp"DWXAH]̆y| P|ޡ5qٸFvBoaMDV <X)^XR>(0\};9DD"nj "T< !3A=ڷBHD HED8u0QG9B(fE-m`lŔK; 9/^{Kw>oԇ^z}A;(``p ,?G?B3 ]0Y s׳~\\;Gw덱]]nr,l IX|]\9@ƿ m?bgZt8,Dm#J ~=YwiD/`Vh9RX9Z?QمQls's]LTjZ Ox?a3U:bּsOE[$B׌}o|V:vP >(4l A[ֽb4loj[*tĨemVY`nnD㡡8dN w @9d&205re+vΎOCb,J[fL(1cXdM׌{|BWPXEi*cQmt} h T-R$шL_sW!e| CmZ6c dg%Y<Ϟ:u=7#b2d8ʬ%x `9Uf1^EA-nP90dD>!?cIdF~+# 7acN 푩TZFa!7.\8c7"kBDDlqK~/1L wO~#9 '@#?WpP+r3|)p2JXߜܛSl1fK;ŮD"c ,tO~/WGw_xnu_wxZ_ 'ZY'gJ4 `00s=3~7~}?X̎9~'$ so=GLW!jrS)"с&K+t4(\s OCa @,mt:"C\]P+VxtQ7-d%+q$kv7U)5)Q! pgqee"X!;-4FfL/ 1Jwn5YT=cDmt= T@aPEm)YY%,*dI!DEdlRAQ_jUL2Q/N([NViafEOE/7R,4>C1lJ->d^]~B[T>W Q|vE*di Jh ̽q'~?3/Yٱ{#\+.Nsc7.0 !3ӝt=Ť^>=1?jbF1}{."vE ] lCES'֨VK"2 GSfo1X ࢀ'=_f#ײkIV\ю'9̑h驤H['ު25v=>ݗ9ɚ×0U46.0ܱ+gN$%{_y`֟VS2֪SmH)^7]҂9gUn_H2sJ+s؀1{̝y)@۾9|ho|ˤKAjJHgDNۘ1%G$X241KSV%^(A^"0 Z u kuZA$Z+ b0fpA DDs@2W(mX)5XC8PE &"㬀" "x8xw@8 @"33hJ)9.7䜃`|x ֟='cw[~ >,=K;Y[>zpgi}_ʓяW=6U+uݩvSk]ʅ.אb>YV"CSH,4jX d&o aKf:@{{-JǯG4anTZRiϤuUڊ Q'1߻ZI\fnlVjd;F2=, 8Fv&eި*3";LẠ䱂_43kiGA5i1ich[km c/ωV2hSxfp[QC4`)8eRƅpOCON`h qnŷ`+(:N.B\*gS-Jq Y߶Mk{.UνҬ~6÷'G1ڄ 鼝[6"Nv5߼&u#+H9`zg#;7 =!2cv5xVxta$RD S;y/i@)s/>]9qGwzy|_=olTDQ{w#כ}D 1n}9fEb'u{o?{ /}['x¿VO{/8'+-La#raܛE,^W_d7?_z_~[ڣ;?~_]GV~]RmkL>9}?M;lU*3hj9xIg0<0q+)N@Yvv H)K;nJ SFUL^k:pʜFpS:T᦮z `|ݒ>'d[rDD1jwzrrR(hj!9EC%'fidC,ZL"&!3oTf-mTtBQReOY,^BK[E[e% ?ȏN+,Z-li--.Kcv (DR431z5;ri z|3tZ[kdozCԲR`cupƪ".j 2'ل~M[jױ͐,ҙ%a;)!RܘF39|3j5JGLl6ک BL]́fU3,9MH^.>``+ٰؓN#&7ljx}˼ dXξtKsC'ipAUJ0~3m~Jd)"ev >9Ԍ"c\A^iρ6=I]Kd+pdi)#q/"*"`g?@5=fşdL^m"ݣ@ֈ{itzmtư4HDƩmX*ˍ{9&ً7W$"a[yb<6m<%nBbc}rc~s[7 Fu։YFdu/Dν1uG' x:Tw>zWދ^|{/v|n9~χ/{Gv, BcړBo%ՊuyLR*lٽ(ųTv(^n!pSZJ`*ROJVA *&*#N7'ֲ<x#,6o>Ʊ#%b-J$ 6Ӽ3UG=t cti.Xj683M_rDM y3uϼ 2t1,hm5MLKpBRhWaR C7--F4i¤\`{u$!N LNIR$!KDzR,iE¹| 66'zoyNFH-%1f"hmTuR2xw1٩&Q&ѶkOM'Dҋs¸G+ fjگ(3aT/Mjj-< cb]4i'{)(1(s\\;42MԲ* 2>=/ n%p%% -t6kk ^mT`^UG2ߙy[)c "8vQa֌~`_yob==)RJ18iV>):~wW+돕BEѥJD]ZWl-[i"9폰JO.xETؙ΅舝J5`@B~=swׯ x:TJaN_=ίq Ɖa,\m%_<3?ؗO?w48:']ZhJݨNWgWu&o)[~w'0/Rwp|^Șc1"M5ןxoO:@DRS5d;KVxWze7Yۧm%iOH# T$ Phn DL=D }?6Y-PC˾VR#')K&J]#c "<w[kQcH;?i6JuRۡN@U?I\X!%-̉KN}. E=ƈ7n ²sxџԼAL :GɕD>_a04Tzw Dg֯z@[`jY"SPK0~7j$^#'҂pɋz&A%OɚȘE绲OLYpRR쀅-~E'D i8{?JdG..fOPD8<:<zj :%2'5٧7oSjv l f ٠ )QYsqH.k.D3I{( !-zK:Ig4f }u|n9b0WZ9j-%ƅ IDATpmW)7<Ѳ&0Nbm-stih>gPޗ-)fXՄ{q9wQ3CL9"xV+~*EO>~*txI+[pr6 +UӶ)z̖켮%I*rNKsiJIR;SMym Pm<>g,^ _U ζ)`IcoD,ka8db%!G"$cz kn3OfO)s~/k30RHy0j~X)onUUk-1Cuð7épZg}%"n ݹD#"{n QBW"B"N_hy~y6vs> Xf`աcaF P>>^}Z"okgr6ա' Y.fË'@}=ytahqy źg"_'w9+٣^pW^:@up7:.\=_c($(3ʟĝdN.vFyKG hH{mdגggc]*ȍP˥ôg,g9^r#,ʏIk-Am [x,n! `ddAe0nX1DvKA֊;Ų@:!#M MH4XzڄțR FUGzׄhNd43a[be_ǷjhMtr$4s|*()+R׳+ $B+1&jb ֋8fL^fD8Q9ΣN LJI;.{Rֲ,+6bO{Z X9A*'=xbR.PUS yBlg1yzNؔNf[JM A2b RD$ ,] f"vʘA봱D3@cBHޅX,sf#Ҋ@0wsy: BZ7[bp gwcuj9,qV鎍@&FM[f"=B3{ Q5 8<C\^^9u"쀔sJshuQK8=9;S?A3M)c0#[{ҶbSi6DL!ۉT 075"=C"0@Zccz"0frGX7  VXH86րPPi81-w]-̘{} l`^\7{Kj'X|\z6h * ~)XDRn ϶ QgWhփR|3c'q߽RW?ջbS4Wˢ?A \]ɰ'+@6;(!߮Tx`fj2K_\-"siڴKV˅vֱDzauHS^ NF ^9uۮn{[֪Bh4+Ж4s!#{:$aS1IMU헚B9{T3 ̾i@<UkiхNN'KuF oW,*G7MD}[Ƣậ.ryE۠|+LxɍI\ [DޫG`BI/aR8⌈wWF QزtD(,Q-hN\9O'+|"?>uJWp2lڹձt r͔1ڦ&E6˝}h"()V=5v li_B5cNw͝~NXrODTЋ3W+m P_j`qh/CKkPlj(BAU|m-ʋO_y J8MH GPEEMM8{q1g0k,Bb}̡ iٵ9EL5Ƅ"v,% g-ΪyݢTQJQ s67"YKDSpUfpr(KHj#ZO}|w1H?1)Y)m$""x^IH!"RRJdu( E=^^uqw"H D渮" #p]/ $`Ȕ+T*&!Їrs8 U9:D.Ed@XB0眤D @T9)+A@\<sdQ}y-$I)猅aLmBB$P*WEK\0 _sZE놝mG~/S&r@m+d("B$)"R !\ݳrƆq+A9*]iD(vڦHQ$-9@ #s9BM@x.B HT?UD̜ bChSZn~]NWϩ_5!^M+'0QvO#μQ+癲3e,|y42zIOM}]<{k7gYUax:5#%jo0 lnMF/̓4gIAo` 7 G.K2y'P,a*5BH𦭷R z|ʷn#(t>DyPٱݸ̩1R(KDzo8U$+,IjIiSdfls򩒣"DQ3uDd0 2eU E!u`X]9a"d;cu"RAzoVo̪`W(*_0 նz8IvՎ>cNwU~UKˇ |DgJ.(Vg,R k9#@q1BU90aŲ($r4cèSBTߪAJ"kDQl@RF z\{#aPX{miWClJu딊6.rmC!ʠ.*~Ds q Rb`5 @$rH%eA SADy A_UYjRd?QGN38Ttt$3Ϫ0)/6WMEȆGް'[L ,yX4,y-3 F ,1g=jJl~fFf8zYh(L] 5 L=l^CV̉&H<1E Iפ;)1ݚHA:Ȱuldnt0UE{kΊD3T6Βn43t `}`56Rd6 McPGqcbNEc ÀC~bT6ϼMouL1xIю±\IDLjOL͝pnkO;KCHcR@}&gGB I+1yƂ5DKZ@H_k*%)@J8$H3$  @Q'Duu2Al $)AJ AHH_%?jkطkUsvmW)gͼ~lkiiog2=}C$ddDkB_]30G 1_JH$ÁzGkkG z UZTe)GKc5?j.S>7 {S<'{zuloKU୷Am72%9 [#%ZcVh”@MA5-lյHZUoRd 3 4a4H~TQI,Saqb Z&ʛZl8Q{cj4٪Р,b`H|L`ωKJtIbgͩޯkŇsY¢f]$C%{7l1(65 i&ӈ 2@L۴MRq[K&'%AKhkb6i\܄f-4Dfݹyg`NrājM v^%4ϡ`ڳE1mc36ۘ VH_d ]"G`(Id1Dƭyʆv(vҦi[Ɲ T) %Ѫ"!G5 DR*:(2$%1 I$cȹR"sFQ0IJ\l[[D 00)h`{ vxg#|? 2e(d ~(P" EH ^d +S ֛?KԪ'/f4sԧy dy^erco0佋I ]ぺb ˸:3h9D}STIl|w//wmqX rʖ>uk}"dT[m+g=P(2 <ڵoqx㇛V cf>ՇHƍ7z!2ˈ*h]96x^dN NŖhXҨɓ:Კ.S+z纮0 رO4(F M(yLIF^|ɜqqI]fX'HmEar/3j[aڶvayTRh3#ˆbJɝLt͌8Ftr^WT!aʜ@Sy:pEC(9P*ˡ\GǷ?~mJ i<\m;iYL©vw=d$"k;M\~*&d=Pl9897@~/ ={'s]:&M(! w, OQe5,MzUO.hl)8cuG0 0*fk(z72>;R" nc s2 PP7F FBv來O\aJ;7S13B A_`κ$klB0;-PGM _쾷Wݓ4pzAl(dzCfa>`e@n&}SC`\"Z ΄4i0r:7 +jtZ &y9ϴi._U4VJTГPܩ&fs(: k1sY3SUaoۘCR|m,TE497kIk6Mm>ٴiz4=g3OY-Wo}BIaURyوS, MG:8r}Í6qRy¤ PLsϟ7u玍e$z!*rQ$cq( y^}@X.ߗRr;A DpcL`)8Rp%\ٓRJ`ȹI!!JeTvh!w\il. 2}""g$%g\Y*8^x|8 ;wID _Y&1 9':'ddPWT ut+{ S]ck?i^pU2eH)Ec: QF|Ovxkf4,bs]\.eu^_@DQ<*MRqcSRZ  W|ԟj6 FrK aP\Ԝ2?b;.)bU1 "p@f 0`9STEp] ,YN? XlYHNM\; /AHľMGzo.mmX7md̟rCr @@2-+,٧'OW⌷2@S/oh3rgP5a. # d)K0KxR^N:C"T*S/v~߳KLhުQ) ?jM_BDB5[)qRE>@E?2iT#LɆ;LRҞ#.E1Ɛ9sǩT*mmmSN9`+W?*uQ%K b^G@mH)b 3Ǡ 2$(QCAaHR*1}@dI!" b( 97{8jT" C?'0 c%*Lbg*/"$1 9aDTu)=voVf9 a`9W#"UZCQH2R9kZ+ކ-X)9 \WϔW00zc# R!( ? CDGlhч0NJeƌK/ggX.Fe&k(ElxO2F/{k|1Me?x /{OyN?` dԳy)! X0GMĞ'^:8|kj"|N'ѷWꃋ_yY[&nK;ˆD8Կ9Oq ={꽷}N;*vkoP}Q F6&`<=gފ5U|$~ȣ}a]u>~$f٫?LIr#cDgW~|s(\[y).|߿7kCųÒ&(u&;xkUmI^əwgN_u_zۋ0'8[έ=3<㌳~ Ye);`\̴zzR_~ioNkV`SB 2]׭T*_cQ:;;*;H7%Z9׬=rO?Sܚ LMm5"߬*C9hM`[ݒۈ#'[_8\jK'oso+"H؞q΅M?ďY}4x 6LMO rֆ%i&ioj?aB<7> ;d'\En R/ UJ8f_rcĬmv_EiP9xktpS8ffT9;| Y&I,Aa(\MQX#prqK Y=r\`o)W-cO-lRրbue \lSb؀׮O;! 8n\t͐#<_Gu\wOmV)Ua״ C$J.rr@I Y+q!qJ^9f:ܤ!!|+岎4#&eea.0Dθ#j v%")Նzj];xOCUK k7BBJƹnQR|fr ֪"gA^bLQ]+K;C]J-s"Qڠ\Vk{pN92Q$$6?EaR F FG},:!Qf@5miqeV~:蠭z-:mj>fmsz9~9w#j5Yh5'>~޿;?w7:g\?o6]I8+wEo?|ύ+:v:ۿ~k|}_!J/ت;u~0f-ZL$޻g'pR_h[ӾwۍqNguc5a\@m}9WrW=9yMqVv87^({g9%iW)ni3?_}oO%CoO&^жۂxaU'"oxzFoy{.=v_Ǭ ˟ks ze+oWn^ z]yNe9` aaSiwu`Io=tmw" ov^/V è`㏘1mtˁE{rg&w<pn7IXwUYum\hBQL5t#!ڸ,67pϴKҐLLnJ kd>,peʤ6NIq0AdA%C\8ٿNw}ZhS7ĥValf|2J rHd\@f x[얘ؤѲoK (nf1+dE3dWxL8Ad3ŋ֛8T.;~,8ZBa\A uHE W-:@|opq0"sDRI+ %J TY\lcP6viOd?}ȸ6I*bw]!Mj$'eעNCrWq HV1 :Z$I.'}PY!*sFR‹{ DOaHD _ڴ*2B5 ⟽[Ҝ7-_g'g ӦN6k{}m޼-\ }]['t1^~9p+5lqOvf4@9`g\i,鵿} nωϏmV|5Cl%x+T5#8M5p?e%!ꧯ˿9Z]/zքy _7^ gsڶwr Z;'NgΜ+_8)KG?Y_.[vㅯpOOΓ_Nn`q}G-o>u?m/NoEr0_`$HD8H~X@PLy puKw%?ǐ9k?bX-wvu;zTWV_CS?7nʾ?=fKn>&L0v]utXx etEV`^}j|R eT5%kN>唓O9SN><1&Dj~oo򎉓&M4癧WI3ʛ͚5].O~7]eR%P r\- v$TUG~~<gŒ/Mdw΍82bQQ{^Ϙ#Z>i͛Z9a䉚8;m3#UuW>TՉ:ܜ~,rO/>_ݬwT>[˻?|VzV{ޠ ,VtCo n:w>SoɒՋ^znmw:޽{^5m-]j@8ߦE!s?6 cY>Cis#AVZsj֡[Ef;ěgqp{ (p͝6@ cZV?aSg?jqkL_U|(i P25~ [Kœ<&c2Ӌ pR(V13hA Ѻs=z28)è0Z]h'L~Oyq- 'sdʔzki %Y]֬|.eLZE6 \zH3rF:,&&&827X8z4m* O%@rRrBUan-zUpM$a:lu-#C$<2f'[Y#c*_Z> Q5_0SDQ*b Q$B @z=h˷ˮN#˦DNSMor\4#شԖen҉g_dMzmm&Ϋ 7WjMO!8ߘwQi]45_G~W-cwiXG> 6ƿZ4^؏N?;f+.oJ O+h’֩˂/-mT_7W&_8쨣ǫz}|gc/2ɶy ~mG}̻O<x>4%a^hh(rJ." "[\ҫ.`8Ѳ1\0W/;:;Z{7Ǟŕ/?t׽/`, 2:{ q{CwU yرcw-eԔ*e=&3nu1dlD:% J0?*i;utva4 . Y .2zc2~c Z'g!b` IDAT]V)50%m4'oڪkCh +*R B'9ՙ"E`V3 MehcFI9 U!S4{ ۔<<}g, #XMVDQR9,fE'kPm bWdP3 'W Qi/J#d%|^兀Ul*sclHA.dJܤJi4 q%[##^<5DX*.kܲm7'TXP*A0ֆK K5a!4ŗ1LFu(l)a]R%߯}=+E""r9B) 8a.K2"R $8LQ VaJJ=7 Cpu%REQ {զ#BahCv(qof3\*`Q)M2u&;:T:? tF'@p8oe?i* _[8""# c ޸(҉};P#>X6 %Hxit"NbYH`ފ9+g~:?GC2X%ZJCkȔ1k=qj͗趏C=}K?=JÏ-~y;JFZݸETv)Q:*!_yʫ9>}_]ZHcF @cwMѧqq9Oo_~U㈹-m--g #߯փԲEe؊ÐDW5_h0sVkot0/|qu% 48~ bJě}P4`' j!whsŬ|oIw7;SSA}&w9!$5u4@reD޿MwtOY?~ǟߨ uL??"!s0Sp)@D}]OrG^ξY}X~ʣLdb`Uu|KC:~3.M9;X}֋S,v=bZ}&T疫yhO9Ԫb7Ba$kuXͿ6WA7 5\نPКXj͚=WM3q9UhHuL-:kf2ab+üGn =6nQmZdHmXMF6FL 3k\&[d3߅6~e.-4 wt߆t:k (\#'R>pBf3dhXXe S0EekK@fX U M mYĵ+D$X\a%2`\E[PT2\t.uNu1= *I@"QH8BI!ԝuE"B"""8IOވ踮Sܞ_U,AJVFV*4Mn՟ʔu/w"?6j!cQ:67"C )SP2asM,{j5A:܎L$C`Gvc?oA |>Vܦxn5+P 6UgJͷw_R/VZAb H=kE2‪WN9]?ԩS .^sjd^I]3(#F I hG$zœ@>{"2Fб^;G\8gɌQt\&yހߜe.=,`ilo`uuҚ޺I==5DH rw]C;O[SM/`pѻ厛l?{?u aiNUBՅﮖ;wלYjAO" V*ß^;旧5VZt \ یu^^$◬c]6xeaFO|Kɿ+=rvͩ'4ъזʝ?j7JSwybς+Dƻ~>e]K=<{?쥖.A߹_tPB7n: ʓkkw+|._t~ <|=-z^ u>Zx{{SQKgђAJ 2ۈ^Ojl/bS*++?Zo뗶~ tjz>vL؆^3ޛ+ZXϊ`ɕ-*IIQa|ᇫWja(W|ǫ] ?}5RvNωJ.n)U9 *4L%yE9ca+Tfl&SkH8$Wd@OvϘ1٠F7d0 #fC 6d&\Ft>ZYFD;$@Bԑ>ѝgT3_ G,^R) %M$dӊr̼ɛqZClTn@0F!*xHC&1Tޭ'%06fu85$s>hf"n#YCW02fyI̔Vm4g0e]EPDq:xNJ:2u|D)v 7Y3:CӅx#k;#;%` @BF+0B"H)JA૒  *Ct CFHʓUY!2NjH" sɬ=0 չ_ bݫl]M;B@Έ"+vfk)%! uI!H{a@e両R!p$8SsRƊTIn<ѭp eKd@Q2 IFD$It7_rN/sz8o?8pս֯n{%&{jQ D 7KmWhh9UdZ_Sr RsW/AWf\D+VΡޯXvewWzɕ/:X u"zܤ)^xV"KsˀW ,gVZւjTP=z_MJSH]x햻O8k߉%y)cks 65)>CZǴ$b y`q&3@]?çTvVGV-\f;_22`hۦeM8O?OW~79T^m_ˀα6W5gM ;籩'-=lRz:8#=xݝ&3;4c G!~y?hx/AXy)genkn ?|s}g|gWo~yxkޤ==sA;4@vA9oҞǞy޹gxЎ\`-}ο~g<-ٹ'}[/"/x5[|M`v>?:؎ ?7.SVzDzQ~}Z]j՜^_ly@АIhTL͸dfK32|*|VP{ ϲ v|0QK򖃥q oZi@|V %L1kx%eYnVs)SA-FX!I߀aD=rХjH6DgV/J)vlM c*,h5UrծɑB?[Ӿ2A.HknY[qc3zZYd s[ nt?O[W6vյdm:V,nв06&C"5g͙uCK,s56ӣd3ϟy2[B뒲$οj)S7O@AIY'2mR y͝ju4s%  @0 0\@ۂ )Am 2$( !QJW\U!Vj1 R^}BXQƳdX$k@^#PxsV9:"ș:`j19l!Lgjk6XykQ@X bK.] IROk#DɘT1@^*h8k;v >ҡo4~7F~g 6b'tFfHǸPo. HF!`Íƍ[ ƍ7q,t42C* Z;Gm1 CBM_);r]G{z{#H@1ƥSoG#}aʧ*+9};iO=;wyرyd/L?}m; fvE$u oeT b4?=>=CO̞Wm֭9`?/@}O]G{n{aņXt/~agܮUv=>>^[M\CN@{IL= }?Vwُ᪰m]dϯ~?"m[PUg_zҍ>tU~Z˔]fd`D7@S{yW敻߮sW-q,7= ;^r~ •A"OJ%۰{=}wמl6K^PO1Z-=mCߜwpe/n[pj1 ZƐK, Zkښ6*&ċ ~1JC0͟*:~h>43o#'ftLx7`XRg+VC4Yeb?K0G9 '%k7gL/TF#̄9,h-3/Ea7g!k,Y 9uO΀KT[TS3\v]Sy?ԿrP""P[.[Wǵ`/>a=ѹlj5]58X: =sH/<N?T [$N6Qso؟wzޢ/ݓ̌>eg֜aGWwL<7>Y¡Au9O>x÷؟ȯv 34L-}|}NlY3g}oڊwq~I;pA{l3箾ۗ~U7u4Mw~stɏuj>~RSU?\3'A/y es@b'/:ȯv E^gM5`ퟹ“['ϢE6'g'S30$pZ$dڡ :aSCq&"RFaTեsߞEUR֯W9FiG#oIn8ڠɆ p[j .eofQ [XYL nӲktitL%,XXsCD20& 3yX3g 6"P9-U hZ( Lk2i{1ߪL3"apm @$Knyum.qMwpG4s:U/:|t4:2=xl2U`8kIDǘ&8QF猌qdA+{!uF!IbR'(we8Rz=I0e87T*>)w}U-!DTE4'm0+{Ռ& 1,u'2ta"c q8yn9ȴHZD;)E';k  fϯJ(yXp$ːK՚+!r$cr\%Y2qeA"g`{׽WL[F*\]]i3Y/8"6v-GUDވ tisw9s\yJ-'xJ~{NenNghAJkׁ ؀N}t+ 2u5uȹst9Y2|':4r-CuLY5 d1UFmg{ :1]weOșےADe"H\t*3:ƶހ9X98Gܹ{Nn . (_+[`[7ކ19 Z"=;Bir,0,U(1 F{k)֜mQWQ{ }AĴʀ\s$[Ͱ&L!heʤT!풗E|}̡™nQN]n'o5fT2r30f$/2жp>0pSoS>LvH@g'UZPgi~D溮뺕JsqΜY/${Ygsoz= H JNfQR?>2I  ;EcL 3+AIu7 DV $I䮳˧>j`MWk]q]{'1ƀ3})QDRJTVB@xѺWUY$Fny3BYthҬ MӐX@b+&fC0 $ 66] MDFCpԍ1mjBkeי(uгXjŋon?75{zř35[ "BҚ-"{08-h7Jiza_Ah g._6u<֑l:@ʹM5b7wOn_Y^W_|C?o~5;׽}|x䉍`fشl?'E!ԕ}-!=b F5e|N6Ď30ݾf ֆ{Y d7L2yTSBb# Yrswg .\Zh@ "`TҪΥ8ڷK6۾rwc pWrZ'^cTWEZ?lHVVN5m[\TR鑤12o] IDAT/ 1,ʔq,gAΣs 'n>\=͵ayTzbZuI\el1y|'I5!ke#WMXҵ&z&|r[lU0YkSU'(1@Su*-eBK90r+oQA+f0|> ,@\ԚX<iBOg xbp;~PkC(0.Jܸ Cb3]+V Ƙi:Bm. ڨp6ֺ:{hXkE /!R:LXsR%lf !RJ5 {9}ișv$\*]kt#/}-{rҕ"B0~z8bfnf&V pNB @%RD `l{t;3 :5'}WpѽS@zX=0@ϿK{DF?tg  |nw/RmB"=36'Ntd԰ػp`I4M+p@=QL%Z'+ÊF@Kf{kLQ+/]V  ]qP!oD6 Ȱ.xS{kۿ?~# ߙyu,~Vt dYug֚ aՎj{[XjJb/"u+ՈO8[uD3K*Voog[bbm=eĥ#SrMw Hz=gIܑ bs D]ɢmen =Xk*\9m?>2i!dtaҁUV(^L`JZ=$GJts!O8cacC΄,|s>'J2> Hʪg6,|M=[{҆:!  d2:ˈdUuR8јv%[!y( F, !<_Mv']e .#(GMuY/ w(EuiBH){U4v?0  d#FIaB#Қ I!l}=7ʔR @u#$hml 1ƺFZB"C6pZk4RȦuM5ZA+W. !]{\6BӵJ)3,KѦiʓ"x3ݧ[_2@?ĎLu\@Ѡ$i HHk-r7v&w6r{mL$M%,.]S< %;tr<(B_tnOy&0+ZipY E$/?$PJ"ܳ{Z6Z؃lKF,.==WN}wq|(.)6 ug_z ޅV W@zWkI+st &M諯MDCb`YmyJ\q o# !Ԋkr#$MHs;ז0Ӯɵ~dUS&,0wYm՘K|Tޫ5<֎pH.!{-)bJ$"e{p9q~Epf,mj"f́V D_?,L< W8$m!VznnҾ&٧E{V"y@iy"j/pM&ua:LDv쳸_:X&iȔœZlW+3^槂+Sm^$TfEgr!:J9LB``_ulr!""C|p[Q{4(w' 4Mc׍iU!.ݿvN`)\)%Janl-0Mcl1iKBȰs|T]R)Wj0َbi_*Jk=K߈ R4R6-]~Fw M >!vlֈ$@h jZ?=pγ6;®$V٠& (W!9+Q(~$&!I\@1g#^ m! D(Q XgƓe)ҟҧ,QrN)L%fDpJ~˾LiV~ ܆ʷn#owG]%M nMf5(ں\1(Y"3(T,KݬgPE2cyj{&&;%ʲ\]fD/40#R@Az-d9ebPkT*,~ˈ%:Av)W0 _"sSo(/(ls$b0d@+X5ޠ[#=)74R\jFFlX!FpJh(q9O(V ~f5O-(XSƋcY-HQZ69إuϸ18}F>FL"LGL(IɥuՄ6_1ص]?maܜSJPifnsE;Zki+ M ТA~ f8g8.Y9ߙ4 668Y *eRA)\Dv1l օCԫ!\۶66ʐ!ChFـnw@I9tf0f{QܲwZtmQ/0MAH Mb%B?7~{wn#6:9k\JyNWMd0_f+sˍ ?g yw\:8r_dq+T}Wʝd+u&xf"eZEtZ$nY!(*2gcixX96asid`2wp𪂻VdrxĬ1YыU3-;+(&PV`^~"pO')3YaU4(:s ś^R ,LiL<Q%C,gšMjƉ E?O@&hA2-i"S(XLp0 ' lڃuX*'s/, {c2tw6-1'[VZT14DP 2fNv]WJ"3 ԮBaK)C/$ ճdl}v .ٓtW fݾ_nKB(Jrm^R֖~(,cW Fǫm]J&JS} I(>#gg!$ $0E#q(ig(7jp,}w5{Vwbٞ5[]3A$|Q}s\~F1+:AnH½!$(-*%Tܗk: =[ F\;#n=[< xD}T E!P3s)c1nC:4=VW1\aLh9cв^W!"!G(͖ Zf67dN&] bı0w¼ RUVBĦ:BՑt'.)hі=VZ Ш jiR*kMZwU iɱ@ $a *mOja#ɘb! ^1 C(|r.]FHJi/7 F~;.N?ۗuSj`!$?)-(dj+ !~0 i,LcLr Rz V mmNtjwCz~{r0OjB ⢹5cY'l%J)f6nщVnvf+g\eFAe+_ ixK)~|bש镨pYj.F-eUWAfO|jƪ: (c*UVef\Q!Jd" AuV(JuH=M{MPVqss;ER.fAԇrb;*_g9#K7W:#=yc#af_a4~f{=,f19cYUrUv$bdGMn UwӽpȜ TVyQR$- `8y'&M%Hs8s{'N2WEw!XoLZF\- ak(Z')l#~%@$oa:CEK}J X.mWpu@UsɲSu(Eh9>&/ޤsxN9 #1Kcc=M,CњqR>63q|qɘʂ˺G ~j E6*k,ZG2)~$2ka*Ŗ2I9`X&, ipXGƥL,3́/16!ɧ h\1Ějʛ|c|Rtk_,q^ LVvK+hZ KSˑËc}wp;$<$m6Um묬iRKbd QMwyʊUkc4D<a@@@}B"=@@!ȏaQ$2 hFӲ[C2P%X m[{3(̞e/EDet4`4RJ2&7FQ iq70 M؂pp#B]s!&fS.FC!6b L ! d ic {(P Bt{ʗx$vh3r֕"Gaa0t "ڐqQ-Vg9Dze)PN `9A %s=_!6?,En6q(ԥ>q$Hp'TB.QGuT(x*ȫ!G .)E ̺$8o ]fJ d f~t] Ѐv9 Sj mQ뺾lV-dcKocy6ZK!1(F$ ĶmPA(@+{W!F*c4Qڻ(^@Dn ⢅IzWR.wp!MȀ A fm^s抔( _U{gxx3觵{yB&-CeOॅ6Amf̐/-L d`df.Nn|>YjasՏ/z`.HˇXǿ_ P|eD Thm?tl6$/T%O匑9u%A $^ |hȪZ+BlyUw HWˈ`,zOѡjƺ$,FiDj m܍G-&~vljaA@Cd4WMRJ`Li=Dv ;#@ɇغwtu}k\mynfݶaOx0vZKm]B2;::TS|'Oo•+>u>?<.W)]=1<&5 ~;g 2(k_[:>RV(2T4#pTZJǓ2&3뫊N*/.3QMXU~ڐɄ.7ʖo#|j3*$X8m;^cBq g Rr+ϮT+ԔX7UedL)|RBPe?&u?S C068NUup29e(51E]u.0,ˌ{rZ!8 R̍F2|c`}v#;5 󊯐Ȟf% -锺AAO_!9#yX P(=7k!U0$ds-5ҏ.Mc#>QM:A75{ne&oi xfK,g? adAk-D(!dt 3:670Bf 2m}lcPuzn(!VH  i[D4Jـ^"Ac4)뱄!wv4"Z!bkZfpf$ 6F))r1эyH˄Vh.{1vBcgMk1J) ! gG#}4w,vfTt 4K IDAT˂Lr?icr<{P~"< ҾJ|'@W[`cZw $UKٷQ3"o)ជ:mg r+cWccAfx y>_CDw]!I`4رXjYW;W*A r[\ќ.U¸Р_M`krϘ/WR籰 2ܠ9`sjņU1?9b^hr)p4u̲~sT3m*chXQJ!霓f^+D<_pWC؊D܄-jMCZàh|(b?)[UkBg#i,^~)6 p;1z+Q!h_XҐ!,hH@_&pao6b"VVqm6^?]E;h1:vQ7ȯ2z]^FBXNd=jKWuB  PwM_8`cAi뿭h$\T;m(a)]G|QzQ:lW%&84&h)}t<3Aߚ~6kұy+pM-`ys0}ej%th-#ǒ/T<Bsc <7`}%`TM-k9LJ(E$89>u(҆x^,*(fUg[LNrOQW{ R- 쁀Ŏu[҉{6k:KYd@$T0mpEVrPn1s &0Ū" 0OBwE 7lbR%e.ʄ !_X+@ڧz%@/2r&O~x.AATԴ;.Jo("QRךv&u8, ťf r=09X@k%taݬ#4vAD 嬛9`Rld۴6g RxF6JӠ vׅCKhK.imn?mdX7Q!iMZHVJ=e.,[::xZ5-Jkw#ClqmW}=tDYX~*@-/vi[cile__yO5P<ǓhxdM'J-CgðD eL[5c@q2>a?)P\uDNc^߄-P z%#JEXdZ8D'USэz~w3M!|rH6JdQyj \i&Vd#R: {F{k IylL8yU/l_ro?-0amM|iV2OuҊ3gD4XN]B< .r1uܚ쒃r ֏G~|Z5tz,[guJ3o"i,\k-Z{&0@/7c SHr[&/ Fc,02ؒFY2<,j@!8_yHRJgZ֕*8=܁n0[;m31x-[]|UXrd=ڙϸ:ɻaX%.#7 Rg-XGxlj1A(4Lb܅_lQJSPU u+$$wTKGJazGboլSd]MdH~F aVR&as^ 2`^A4I Kӆ!Y?O$C78 S~9/bb 8D4B୧a-|BV\qpޢmPTk}dA,^[Roz)5H˅?2s~du^zJ;W_R/|2,{;v(Jƈ\LVe) *Z=N2Ԛ_zsp9)I\"_xBb) ut[<YDpQX b p,c4-ש?.sy=%MSN^)mOض6Oh"!DlV-4dVԶb۴B dh"d6ADd]_{PX/jm.%# Iy~K6,% gHae?h @0QȖ`QWJOôdۀ nzQjմ2.U4yZ7۲wiDyb^Q]T1.i~䜶V1{ĥ̦™w8L b܇7Ie3*d挞6ÙOcJ /e} T cR0_Kek?"3ZoZ)so$S8PoK}kmXOWŝ4#so#w#EEpDeb=NyMG?^OENak$SaRVb!DJg}=o rMWuDV Vc@1'(3B#CC"$9ȹR:2H) PC0D$~X5Uz"@ԀD$P JCB z.uYقw\l؅ߐ9Z>bK%\e׮ {rao|ߊk<cLD=Y:}|ͷ\wR`([Vb+D#e+Jl6RF@X"C "24hRCJA24Ph ehq3|s}ϹְB pϧ(S*R"@UUeՖ(ZWJZ` LXpaЁAs\b99X ZN9&?#12Y/ʧ>[TUnNy"'Q2(yܳ5]47M9lVضdiڲݿjN䔦PF,qC渆q*#ƫ bļX@l[6%*W 4Tq)f>,`A攡u]ޠ"%.Z&_a)I{3XYe{9>F\>M+&}G&C'A_21սe۶DHm btٶmaYۦAiA BJc! Bu3`^k76(a+a(hM `{6Z7Mn!Hu]!.ZkMB4J))ck}ߋF"Ơ1Ɛ@{Wh=KFj-f"8d%#!މ0g_9h$<<3- DQ#X|+ DP{,ۭ@2DʘsWAIQ(6chӗ OwC _?oYQ,.V5+C- FޠtKn_ߞkz̭gΜ99;R-s?O8 G7J8VEUif@:^&џ*À=_k;uW";0),e.XA82Y_֍ cQʯ4j Ikڟ<]R$FQ%GnvלQg̅S!WbqHJ,Y5I ,>'U3 HTJqj_<3}ߕS$AA,HOnnx;_x+^^uݭN}CO='g{a~auLI1fxw83w 5HU^;-o[?}>Qk),"7xe!# _h dJUٖ:eGh%F bNى|H@ya4Bߋy6٩"W__2ouYH\"&oy} ̚:]W$[% `sMs7o~O?t}'}]oxMw=5z٧ҳ/>{E?>!F)iQW}d<%) 41-Q(صaf"U|>ꂵO b/[2D˚ċcpXO ߬w&h"Utĺ(.KƯM63zP]!}IlxUY,+= 8WI||r oⶈ^#][U3WƆ2PQB8ώ$l9 6]ש ls DfŢzccs[l>a0 ]7 0D uKH͍1w516!=Jk-ZM+"jTFkjk0!!0R!#-_- Y !ܕH7m@@IVZ!y(s{mW.1v[HP!!%CE0lD:|㟺x{~D+lK <^9z6Ɛ~{~ CJ7 ?lv";7 .*g'ZijGqX;㼷g{:ޙrZʮ+X:TKL^vےᘘq>0~%R#K+'N).2Sݸ$X'\8z /ۙ@8H" kvB]f?} !dBXCoAcVMۄa\a셺 %"{W)eo$ɹM `続Y,-L !gZ؟Zk!&lS9,GZc9\;lSw mB4F0 hHF"L@d #P q~*V+Ï>qa{NݰIk7b8P2JkRn%y!1㌅>N7 A F>Pd+yw]n^ҿ{^tc+%}c#Zހ@nLD`8Պ[Om5o}K>̯]!TU)9ђI!lUSP*#:nxӃWO^k7Ϸc0@tjM̔R \Ha,G~!Cz<"9œ(J "sϼOOnf?7V.=”) L(.ekGJ FMMy2֢ah j ͊ܰ~.Bܾ&zpL \/0hZ+[6`38asxUƬ E?t`fg^p-/ΛΜ=~ cLOΪ8 &P@ p7}%Q?1.mgJoƵ|2˾OεW~w[?ۇ> p&,Mtj4M8\<DU&3G'oF+yMsF^Ӯ|˵şwNuiO ܸ5r| aZ@28++ c"ty"؀XPu:y`@nl|g9ޕSꎯknz];7%ģ}[?ᄐ66w͝'gO_^|'yw{|[;?zI|$E01k3O#FJeX.#a9#:P`+Wla8c#n(3h>O.hٳw_Od9=.шu#P=i{#=_% [Q;j:ەa MJ ^z7MEvʦiB-`lDO 람6F1.D9*e꺮w\,bx䙽AinO0aPR$.c/T9جg:Rf@5,E6ZxۗܳDsf׼`kOn^8rң~o5hsEchcwWo;}MO~#ݩ{ZMA~wB" #HF/(BCSۿe^`+L[P;U$CuePyF8R}{N@qKBT+!nFo׽/~3d%3p$ { CD9`4Ҫ-JBtVxǭopiU&SAK17[_³7槿nxqkz6Kâ/n2n5@r'_tK-䛺;^җW wz1h LXՉT&,J#&i+!'A|yb!kS1R*ƿ@z8\ >fJ썢Xm "Ʈ;cJd66fdx۶u3۶W!u,@ٴ(!(Ȑi3.B[!LD(%fib$<ۮ1m@`w*{I; $"AFJBA)BBm#A @9u`v:RC#dmccmC|XK<͌1Z7'+my-pÉ`.9XE`mR@Ń >agOC&%?v޼;>_`;f['N A>{?{} Ɛ( &:g\</z>}+^S?Loֿ[}dGfU׳mfǀ{},CY+8q_|˾O+}[q|{L5$GkLZ N?\C0(7] k~PVm|ë{^A?ܥ/ /7#FÊ DZ0ϋ j20[eL |+A 88 lAs}ם/x/?sv㵷_g[蛯 /m k_!=9x{ғ5jԍ7c5Șb `)'&UT\BzުSn|d8- ) uGW0w"10 p̙??؏ayI~v0zS )|L[q \ޔ_.dh7[ (ْ"+?֡3<3?1 += (bwR /)νXs`xfs%9&R܃:񚺘HJ'u Z7Rj{#!#P CF\n/"=@$޺(ڶBR &ͭO6ZںswZ{R*/ > Wa *L&R COk- 'BMATj2RJb,mw냢V\pPm?K uzK~?سGxɭFM|eeϾRSAsGsH͗1TzɰwC.n ;}oó6>o_h IH \ӗۛAKxKޟy}'v÷޶SBmfZ)ʨB:lK ESVz\@/5Ƙ_}' '^o/t}{Dw$uӞד@ Ϭ 9c<[=^z'gK}5Y:88?am`R ÉnDz[#8o}{P"uuvm%Vdj3dXLjv3mc!vD2J aR$C" ˘=wU9Rʽ{llo/_?)rY˃ } ׁOe4/ny*Lm*[7P^q*M}XQm6 cQlY: )* c[._.pqi]9Б`iweֱ-^FG!΢Ʌrp ֛n4B辶ɮ4^=迪w; s+ՠL{jܹsǹ{}ĝt%{ϙToN`?c^Ά߿ lmkw*I"eӟ񞩍wt'7D?|>#▝*yeWz-9k1N{')X]4;(O2r0\>TzejlΑk{DrVOh-D\SJ1F4LMMylZy#m۶[NnۮPkZMP9Ku (5k)t"*xQg6ju:9WI>0o޼I_./ѯ( i7ǀaVmlmP3DF$(=!` G} "ʲA0 3}MVjΘ$i5XϪ̬fYEQjl NiƆ|3J&3wm룿sA9˽8JRi#p-%q}XZhXtt<1*zPg4gxD!ʚR(н4f4 cKA qS |~m^Pgw{"_yB,HS`mhϤ3vd -@(2B-"b@Xw?-go9o9_lW;{ûy̟XC@n$ZT*dSh)g[tXu_{k▇Oſ`19 }6,N!jרW4F C^} s!E>y+?[n'_;v5F~p2k%h {iPlr8/^E(0/U-@@y:|Kv/n93՞|~S茎qɢW4٩puG re g* gkghP"eKo ƙ;vip9oZsHfRg s TGRsh!0DC|9-pNGLH-#ڍ(xU_y7i)_Gӕ쎉cXʂ"vVRSerfCЩbOhxw%"TMHsmuBqDC-l:=E0ZLTQDD8ݢ(\ IDAT2mQ7!:,cȴxoT"$J)ি![ZYE+_Yhm1nDZ׀iVDy$ )q)DRZfg$%\[gK3 !Y$(tA5Wn٤ J\*|[X/(&w^zo} C'lݐp8svrL~J?E_%͟<矒"UkCld)5If9#>IB^8x.z%3tҝ59%l9sZ[SS{?㟹]޵#?1?Wm瞵_7{s2qSs%kC ^Gx<=.6MOOk_\gb1m_WʁW|xT 6b46y|#PNy! $Glnt/8G!VO 5 ؠĆ^YnL`ʳmV=]aŰBgلB%"4t]cϮdVA#tn[TySzbZ>|dS3Kǎ|3_7]r+7KK2kӷ~S7sd[mZf;Gɂ?H$VQyk B2|V@N'sFIjgDLv)E1B:3/\_h- Yy焌!rKSDBk% XIjGh7ݢ0 %O5|JDJR&tt( ``gK+̌iV%Dl@Udf7ZADs")$chZk7;XC:t:EQLҝrnȹV@Sۣ==ZId>un^'uEm} Dz]9esiffFO_?:׃M~͏zk3ʛ<T(Ϊ TX_ W%֙\"nyW[zc" g($q 9M~9į$lzkY c"kuypW {NCW˓CiJNpDɁ}yܱMϚ}PC_ Dq$z@ށǞ/yྗVt 9şėyv??V=xӌYpV׺xTJ#WDr{ [[ Jvmh\2ryOsB'Qy^hu|Xg25}eDEM (.4_\`m(]VUKOd0a3 \ыVҿbspcw"@Dq?/--EU_4G"Ϻv-S>Ãkutt{K"0`m;bA#L]NJY:.&\y"rƄ(2dDy3E(C@Jw89R"p#(bR Blk.q,N#Bt灅,cn9jW_l61ө1*lbob5}B*H10!*gD[n{|ψcBP$ yK~uZ{rqU2@*>ޯ`)ǣ5Fe0?o!HݏDpٌW*zy`?+~?6׃onDZTǐk>N'm22:$J2Bhњu 5 ^0ꋅ6 6h'I 8ßkjfwطO5ܡUaKb=&F IUHZ#Yμ#&Y &di蛹l-%Y[`&Zoٽe޹{s7ɠK={p6m鍫G.* c0߃M >Pu(+憆FmhQ+TZW"l9HcfVi8nP{+pTZ_30M߳mV=< ᇏ97dNTPMbo"V;f۠-4~"ZfynU±7 ;5` L>9߱{΍g=ā<ر}v:͛}`1X?"۸ct≙%1/ʈh+u*l;[Zh@so-)!1nHWi$/ />m VUԉO] !+IXAȸ@gLFꏏ(PѵA1ҲpVp2kYAP)\4KܠP0_a԰kכnUV?Tsuv_{XJ 0|GN;( G7]%w;OFBlra81ȝ[1%(o.`]%+ ޫBhRH @(DH Q Q2RBBrPJ% ׺=p l-/FUbWcrEv,>k*D$ Ua/T{1LWj)"J_2 1$ct=z~]kG9nZK"PDBQ~w|QԆPNLv';z {k8>lgMHT|c'QzzlߏTO%5JTyԞd\7b(@=9`yOoԭO:Z_(k1n[@n}arbiA񡭘Z IųS*f-cc#bIǭ['\+9ۺu+1 R\p%\/—\Oun~V 3ZHxmEƒ|9p Y X5TQJ ޛ7ZJ2ϭbmR,]|2X`qfjyuqyhv|߹3;ZG/vۏ]qaS>+EeMKyg?L΀s7ouM2GIJ 7v$sUYw7MOz7jPpYaa`,HZІRڞODQK~xRw|onFeUE5X=KbLza QQ @3SM/} >[Kkv] pXnಆ֓';ͭ ޠayq$׆*@9ʚkXTMw\OaEQ [i+SVMӴU9(}/(BD$y8М&L( ) 8OQpj`>jk#9+PPlBJa$0L$i5پ2aC.rD ňDB4i !cm[~}U"r}]?RD,oJR fjU}u[Yg*Ы}=?p^՗?ΧS[/%udp208" C` ﴒnߙ_hcm.zdf`֖{k)Jf wM=Av0j_̤SO}ǀd%E_ iayVCܫT}Ns mذAW_ B&f˒neJ^%:Zq]B! +WGװ&!KlX /8F[W 0G /rjUDt:\sͳ͛7_~yU2![0^e'ٟT7VeeRG:TN5GX1D:nD/꒗?Û<;9*Ԡg3َ")a>{l(c=E֠kSd&\+cUN7|aX0 W:&uX3 H.,JI |e''_ݻ?OXU$Շ۪Q@G%k":D @00&dUYSK}U fC^T"!,\lmg (r0Yϋ(/}ΕW?&77:Q|@B;Z!H'3,qΥ(/avfIHΦkXL k@/9ֳ׌?x֤iJ)ߌloQ{o[LO 4B ѐsC,D; eJ"!*B$I"20IVm'S<_ʇ.cOھ9i@Qvb^۪ʎy?x6wB,G{? )blB.Ju{"SU,MӹnEbxj"*#odl bO * Pj ì} o}!Zz῿bh~kxK@yvM1$H<ݺ :`8P Kt1"Ч>_s ihΝ|y_mZ GnJ^7==s?s۶m۲e}#ޥ'9::^C <u=>qb 2Q6-]G.W_%]'S9$>9\߻>N"Euv~X l)Pwri\k>ձqs={ݳe:fM*QO#ޭbS> S&bVnsY bTc[/.1pzH7Z&q"*VDslZ0fnn.$I9w'skz];;r^65X羌v_*7Ζ4oed*ɦR!Cұڭn^d@g8©+.@ʄ7 p΢h+ vm ;< ۅa^\֤GcT {HV08^Z]8aP> %p(j0k3OrQZDb{&-ӲurɵIq:@ͥ0m,{-W(ys2ɹ #ZC>$u~^SJA0o~w}m۶mܸog?ٛoͦss/Gh7?h1nD:M.u0.[8D0KwÊ@(9"z|kXM\EC]_kpyvJN*rTҘ/*ݻW|@e$ɁRHS bK~SɒM=u kX'r JKg7燏,E!EHI%v|/w !M][9l%_c5NkT`f©bpfXE](lbp ݗ!xpV#Q~W|Kݼyc`,=u NR?ˏ>݊` <5"zZ5X0,l ^/Z\rb4a5`?˕Z\)jD_-+ 6LY>~ͩ~/zCm>7QRWo:ZNs _} ѾkO`9U p21*99Zb5zƉl/B-evYd¶W0U:3FZU<3.Y=)[ܻ7W^y%c5yݻ?ϸUe9\~_!gyf @ 0˱)Zޓ\}kF :_1FG]xmSR(iA9{8J9"J"@-I0.D!BV@+mEFD:F6J8cIUY!˜s.Di}Uv[JW IDATCI@$RR$aL#* %BVu6 WmY/HDE$ZRR_ PHѨ3"s}1ҍ\ODis,>vε9n6oJVnd><)8.].\U*i%Ss JlR*"$B"Kqv`_чEN }ݳ>sϥ>_ cv+)KE }T@Z<_T,WmB}*n޷套e3:-BLcvń䚬Ҕg\~8tz=%&%gs[7{8{m{SpT͹&"yHF ~|z>3Xu8yJEWX9oG8-4/|޽{tng;/6NrKf !/eB4VbD  ^{Ç"KϜU chjb.*UK<#į<R+$,h?:ڗƤkY;6‘Uiɧ8PA;jMLv "ՌPMT_ƥ#A_N£rD5.(C9@ƿ)!m:ʥ n9@{#8{PAP #8sZ+"てЇ>iӦ{W_}ku`݄{]Q!%+(3`x~ɡזs8|'GA)2~R94&MՆ8cDKۯWJ HDI% 3J!Aqʳ (9XE*EfQkLGm &Y܆.cJ4D"h J)pXEQr1[d[V}NR*k|E {uJmh#=eG+~ݙٲEKӻ{YqcLt'#=) #Xr"LŽLWU:3CL&lnN>n'w]/Q*gɂ< 7BT7 GtBo,끽"0`O0]yiWG۽eC`NZj0N׾qbjmW}Y1tjPݫ5oG* fÏq@ *SYbff捿c=j2'tq!Ğ={7{bߟ韭,˕#PR#Ȏd䥯;uө1-`+ZWCgy-qN&Ё'MMMmo;u믿_j݆ ڬ+jȬk+\iMha"!*-8S 6Kza "cTj)ȒU\),BX)8OZ<ωZV^$USL㜕%AYMUkVSsJE#RJ$ҧvc@jD (&!pJtP:xy1z2EJ"IBE@ O]r{Ye LHT|@Q,yMƓ{ Wp"qBGv.$V0l;$)" 0A0w,DJ݂8N#" Y/7 TQϥ(ER"g4P&ՃDVT?֋^L+G$l er3X 8CB-|?σ@3X\YV"'%Aq2̶Ve?cS$\_ wË֪mTGFGڥTQf\Q,w#EY8ܗʆM1%\-w@"1X~Ґߊaڕj( Bnʻd^s+X[[3@#nu4$ Ye9" dku\/=zC+W;9"qw8zxP;yAR_ D|'DPA@ӟ]mEAGxr'"#Y 2)"'u;4w<<~9;72肛aai?x`vz )/=LY֞L]t\up5>cؽMdF$e[pV <`?UA),/[Η:gڵk0⻔E/xB_`</+u7lf .]Aq :Uz69rѣG9竫cz~qg!DwSMMcu^G;FՂtd*)UZs !W !vE56hOPJ\)jw5.~ 2Af7umȲlń=J_<*iJ/T"$)y~}׮:CGj>QM8אJ>Ey0D"݇2Q.8܂ܼ>#׳ p), ((% DIY9Tgqݿ (r"DIJ%LxHi1&d" Py!= AyHQ([QJ%IK)E RR*Ƙ$Rr^':S#J r)%)(i\AZ&l2qfKLld98CDJ=[O}:~iXit㴜u+4'1(Y_7E[nlIjo(4ls괱!˻Swfgͩы}1e4ݝ\W!f_{h\!,?0J|$?s&<).{'=,sMENFZq}c_bbod{soVcQ~T]AT_-e-ZI;\* ȮTWUܿ7WmFOiNLLdRr\~>.d`%}Vћ^ˎ.Lӄ!HliykfffϹ;f2EѠ2CܘB`* U-x^pS`8BKT^갦F/ Y9%&Zg;tgfƉ{;l-lNwm% 4:Ҝ~aT2qьǼg$ NFCU9 $qZz%gZ֏gnCxmT=J %N&d % h>]:G!^}-޳+Tr3%Z{_]d %N\ VD477nWWWfgg?MLLثbCM:֭*vk v>2E鹡~2!ae%fꝛwخAGN4DCQyݰ @9'dTLb̽nQ_{%QΌ_ vG $IYQC]p9`:T#D &7ZGVFW|Q@Cec:b?5nn:c? JT @uȕ$GjNMOZ) Xi/<**Rj$nHe& \4===33ox(áI9^g8&&U e4MCAǷf$2 L6uѕ- }rgy_\瞝6N-O;{m̊c 'R!Wy`!rZSp2֙>՟q#msB8"^m`y3V::sHtLj#F-Qxx"Pm \}?&mByU/:J3-܇ B]5\4 C٫rt-ub͛7K_&&&z"<1v\sgH .; pK5˅hگÁ^ y֦{<̩$"|˥0N "_ӡ $ byVۇ+3 6jsJt/&"{TXi((Ȥcx*VQyH /GRp֡hM] `MS^Ͷ Pr$E"LrRn]@$4rF`_UhǪUZ#cPR:PDS)Jyl9ݝp^ 9RJ**/}rD_Ɂ(I/bI:4h.TL t@' /J1m3Z0diu1!])=B*1}nw-9Xcx`T`פeFd^Tͩs &+E( +ٱA`͏v#t qKd]Y՟n׃_]'ocۣ&]1-8@jMT[)Ci,:QQ9O嵍ST0m2r=7ܿss,@wu_> rCwv9ܛBMs Y_RVk(u[@/xl}4T%5mΨ1X...Zv+%[2uZ C LJFH͹bdy 0r)p [㇁~!ŒK+{O kf*-@夣yXq0x2??_nꪯwPu:OU'0uY! N7t#SFY]gf߾G>a'KAR*Dl;E|IJ)4mIQF~j:Y6 E UJ[RJ[@)UiQ)R@'R$J)$F2D34iU_KDuTVJi˰mLpnb %ci<1LU 3J_X,H{Jߴ9p(8$$bl,oavvM6sHt+p0xUMR_KuY^KĈ'ѭprdyA2.XO 90h3P%&ܲIkju?.kң!ft[%MZ6k n*3jvlj,F.HF yu65Ru?qtZh#Z/IjIS~iɸ(buUIduV[O- |!5f9^!M.-hɯ/ &З yjhV(M^e Ci+'pUWE+߲eKيӫ&ؽmس{MH){oBq\"F#'[v:Tcam2?я~uKW#O8e cPy( (NT)!`*Yal9c B$zD IDATD+nIha) L`-cHD<@)$ !sQKrѹLjLgVٶ/IDU|,"ҁc!Dg)e(v2xm8>}i@kR@%KR^.i0 ~Go䆴w9r3 Xν0T6X1HVlVS3Z%К}tMig*sD* 9(_Vri@kSO1=jf]o@8]1=gc4;2[vJ/ HCla I"e;PNiUbD.>TQzaZ>1R3$Lt,cܢ7w<|n;՗=-ndaL{_KKKD$I?rpՆon$JgshQ',(GS+y!_*+E窷$8$dm6!(dxZ<}2OU: .@( Iv)*m2{)AheY UAD_qx8==} ZXSɀ+XZ7j _X Ҕj8zNj>46UKv^?W ANƨ |ر^ieXAWײэiKkyy7]|}'ٷt3mFջsSw߾Dv;P`qZMh15xM%97ޢXDPW`v<~h7tAB(i8-Ւ'g Jqd.vF;B|_b̒̽V',k3)SdZ,TQ۽0ĖSVtrgk"$iɺ['kgG=ڍcc">r+oygg;XGW]o~`כh6yvtiti"2 I%ϥ"n4*m+ェ• y/6@D v,g}SFC45ju:c~X2gh@g96dcªMpQ΁7;E! &, Kue Oh"d~_0l<:焕_uTni*NrE!rgah4˗ [96 xUВ~X,}?ӆ bw<9)xѰZON~,9G^"rLVNWM ɘq >gd:QZ3TuODqUJiǍѺm;L6rPE<+M7}E.b7ی*+4r<9%%֌^NjmQT  [5Nt!_|C@)z_yh?_ue8}^y3'tcϽoƇNnE]jӘ7$) R8̾`Jv߰:ŢǙ߻W=~6xюYBnh;ю'?}۾ۼrAwi͜ЬVQjyV^u@4*""7}l Br4 6upE%%X"G19xn̰Ơ bzr6^w䃖=F"8MS?=>Z:m!c=]4 --B>O^Ï4[pގ[j';R)*~yY)ptu[39F҈P7+?{妵ξG/te/ߚ#s ^$pkGm>Kp6͝(0t#iV:ܬJcZ,Kz&%9ž0M"fhDrY!*hxs:]WIBlfG?ozӛ._ D׏6>|?8VRxN[j}oԷ=f~ёTZ/~_լ)@d)JřNj \_3HZTPO69-f&I&a9Pک-!pD7c4Y/h=Sxsh|G;l4},/Y;GPy67"HDž(wd5ɄrLA|6 P!6Y 2{9l LBʓ,J *UqYn^gf(S|/~?}< ̓K`% E(poʇbb2( B7ӆ(Hf &ʷiBpάXW6R=֖OWv!j~uo&29Rx Yc]3#"CN7k:b4fc!1b"i yrڣGnSFBݔ4>.̰T1C'ɪn(Dsp=G?䁳DF EgH4*]Α#ɂB Ddu4mGϭ<}svs?MN]]&a2 }=J􍐪7M{*r0R~Ʋ~fk(>LyF FHpAqc< v]n&D7D; 9q~&rՁ1sZigP"xl5Z ]U4Gwvìraq> &4` z*އ)Ж>$oNDD)kȂ_-JQ 7sKp %ְwZ~S0Nf- i!J?O80::q_7>>=~0(r* Hiښ9Tx?Ի ^~0L1--,͡+n,YT!_ i2{0vADɇPE44'9UoBR`L[_ Pc#xZCo|ٳ}{artX`d;::tXwToϿ}iw+Jha 'oP|k"UU&-%GQi`ɹ;{j@aD<їv} ?t׊z ӎQoon_MiYUkWuYdΌ{>Ze`3Dgb8ʞ(KF>e)B".`"}Gx-soHR'zVV}tn` nxQh0o) i =BƁ>rE2Z{-aGk!IZ"-ak2t? mR&ey!+jv%fZ/V>c@ke"o zLsNk?#9B9k 75Revz;{rdjT-OFⅺ/8>\uK|oT0DYc=>ڿD8JuY)l^%ࣰ9 0ׂ!V-_V;S!vI4󸏢;,sBLDpR) Rh`|Ow Y|#KɆ8<`D?PzP@D%`9bra8T;A:qn<2vtwʣ)UЮԓ2pA&KGlW)QX`DLR,=C.+'.׭R(SGdVƝ`gz;IEb*/vnl"֪gZ(^߿k]~rQP/}CKr #UیiRC> ًv5 p/eU7˘@ҥxiD$ 7Q73=B?p?`Z}+:-Β\' ]?;_:_S?pM6c1Haߨ/;A|͟Ύ?B9ugi/-btl^jY+o!=p||翝5Q{21FJz\ *P2QWhoqr{'eh`~GJFyIv=R5gFq(rsX$1 mY_6iFՅj8}X.P~ YҺsn$Pi{Wðl׺[߅=Z~!b`Y+"ryW|&N1θ~LE/xSBVilEGJ+3(.68rXDhdq>dT uM9G!(PJx&n 69|-35uL(gGxX c饗6G7CݣY0;P BRP&s|Uγk~hw,Vdg r/e-ɗQO;%B.EͲ/T_>(]uYvZ.֜F ^Y"#J*|K_>::z׻ַyw(I]… ǿ{}gO_}lZY5l& IDATs BI<9|sƿTvq]ylw˓35sfVŤg/Q3Cq3 ZwgcsDیk+Xk`8vZ7r:㏛z/w j㬦y_qv9RjH⮑B\;8GJ)Pߨŗ0wy5@DƘCۧBر0‹v5rg3i'[oIX6uvMl޿/H"<:p~K=t=&Ȁ3^3dU׏'׻/_6G1' LWiPe!G+.@ѿq~X/rzεmQ25۲w8uYS*}XZOWQwJyZaQD0f9ɏ}+_?gw< ju0W^y駟}lҀf',yW#7RǪ@fT\4T)2,# ,2/fpz$p3Pi}`e=N;$ ;xVpXآѦv%]lٱ|)f\qȬݿYINej+{yFc!oHDLk`9BLQZG4yч?ax;jm~W}=Eyf5&z4'P/bL̲QdUN <6yjtҞ;}:s̙3gdt4=Cv(9%yx>śBF[-߾Xl9N)x&`&3YgQa{/ YkC7Ġ3㸙AT*S7 cHip8@cXRVJ)~9Z164jYϨUIqy^QJ9rwt)N-~$CFSLS狥bXð>o܏^Tw8Z"PDp놝63gu}pHւr :@Q`pݺy[㷜}}kA7j4nu]j[[Nx¦( Yµ!P=F~*ޕ|u-[Iet$&hRfXpz26@hԭf <EW MIY';(eׯ^?~~뻿;7p({KH<{6-aDRh8jUwҿX:3M0kw4;4P Y?~⏧KG>Zicjs% mcJv e|:x-~ݦNԴvM$0+q4R)?ע6oKRsz)%U\}o7?xދoxΟp֭[ׯ^{%c&L6WY5SWZX|h$)奙 #Ky\O\x(nJvG5;RM9{R?񋨒 KHfVR o(+/%cTr/?wGWgP)`3ggN'Ai k/E!Ϟ ^iYM3 XXr&(.5Vb ѯ)$/N @@ړzokJyݳ V>i15]C5"`ُ(P9.ɗTAdˢ2La*.p[*%Ķk 0M#8@ľ_ӆUnY+ 8"Vf>8XMf_j0fS&A}ob\Us體m9Bf]K n&-G!  P)k,9t&&"g9nGDX .thX>x)4'1vgRqnT=Vjmj!:XH׏/Ÿ}G8R*$pF!N+$5Ձguw O?qLWbvga9zM^A]drJ\n_*By}/;/QA@[PcCzů("47eԚ_U{r0kׯ^tm?R=Dtaduhސs9԰ TFjI\"40T5pvci3Ezk5w'>漠.`+ )BS2d/Q;[yB5k);[,Qhu(G1#GeiUȾzEo!'կ?Nq ~ _`V#aKXs!.J5,$r4b D3X[QE&OsK1:Œ1.p⽻TqҥKaEޔejRYgCTqv]9N.GAW111ToV Qy~BQ)IZ(9g}dVƘɌi Nڌp09Bq8GFkcawèZסqV3:ERJk=,eqO]<5=lF-QYnh`#+XBnmvK;S5%hiSL$!&2 \l}WWm>z\{W֝Xv~5ɯ}tYCS2el Z>DT2rbqPNKIVW$xxվ`*0 J |2gp2vLu8d=rkP-s !,U,:r<؟C6ؑLZvAEEE6x^E7:}4pRw`NyT&",6Q,M\Η10c߿4[+^@?ɜAJ\J2 ebN"I4՞ʅe@(_oڏWq[9wtZpPFS'5N+2~>g(K5(YCDM_)8>xazxr}c "T>aJ@hd6+O#X(ȼ|B$?;btY)*P K%i&nJ+5A&T+<9G@]3akq" YGð3)~0fa8cZˍ|';u?*.z9DWyb{39RJ!(%Lh}"vh4#KFb"-!*LFDcG`; rq ʜ$zxѭ[W2w.;''z;;-Ҝlf+sfe U K T٫IRx/<ӓ(G~ӀO=~V=ǹ&]eݱw>%b"l_!HZzV^]xO>V5]ʞdˉ{_(DToQ#%^svtm6OUȫN52KY ;)UO!(B Y5yu+Vc_8UGB/X0$f}O;{ʕ_K_=v+p. Q\Yy-VrDTDX",R݅;ގ,5@p|'Vߋ{E$ 䃘x6k.!Ԧ,TGMmJB;; 7!GdO\NTeN37|JٲiXmkbS4YҬM}!@$ٯsp>8n@kęToƍbv]3f 4Du5z0XkҝֈuȯXl),~}o\L":r!LSěB#'"im=88l6Vxuv ::ჯad xgG+zا_zco~Ǿϴ.yL>p?)A&)HU +,4WU(;%v=e`>; ?O'D6Tԁ܂q.tGI NHg[߰;w$u/ [om F x|tz bVUgq׹B |% 7aE[ƵX7unY3<-3cJt2YCT.TQ`*it;[ztnbZ Fc:D9Aa)=D4+S {,@>XelwWoWoiP?=&go?r"Km R=@͗]3xRnf“sk#D fNe"T&$X[Euf%fkpG9Da>AT~s֡ kh:XO[ڍg>'ZA 8::/~tJ)kB AouZR ) ?xj0qГCD0L%R94M~ [ _ XrC;LH랈ځ}tp=]7孇͌Nν-ox:sV:n۩`3slܯ,̜?<̓ }V&Xk4ԍ|VR(Po0c{ͯ<{ _i ҽ p8ׂ66/(YG?E{0?k.19pR,A̖b#~tOӈq@kޘt7NZ;g9Z&"#GDƚNwuۥ8s""o|!h-XGD1`s@D*^w2uNkmAw rTg䜳gMXliR8Mؕ$ҭ\xH_>?S, 7vdiw,E1E1NTv[땚~F2<ޅIN9ז)ӍeLVZ7.P]t B|*@rɌguJ@ m6Y|7GEU,ӡϨqM+`e5VI|4V(8( qҠ 1]zw~lԺg"dԐkE&yw3+˒zG5I܄Yk*G w$yLk`y iyVdn^sE,6~~i9)͸J?9NRy0Rʘ9r@`qGR_/ֿIّŋT7,5kc6P&I];G@k kq-IZDDUk)]?1ȹ | s㼜Ծǎ4r+}"9!S*Je$cTmWj_8ᔰ|]/e]ΙTVI{+R`F'&*\D"&pEI;%l>G<[u]xAm!A sM J͚K,M$*DȠv14|Wh pJD^CP5e\ ~BQ3%( G * .+&펚n[p ZT!>Dy)'iBT ,Jɰ)$ %o< @ϔ= k U$VL"Bq2Fm$ģy3zdaF$ÌvHIĀa8XK,bC5"S*, IDATc6(ERDzVR%w2baB21Y  Ap3VQC~h;)ҳL\ e>?vK^J53Zo!0} !Vgq5ðBB]i7 jY~2#CT3Z(=Ī1iUJy7(áЄRʇ^{kSh݋ Їn#hMӤ:BL)Ju΅z3.rG"+Bɹ਷֢Pm!TS Q)Գ j_NWf~;N?qd@uH׭ܿww̸ɭwME=xf<۩9ܘQ4P唃x%,:} S/kc߂.0PsY/ LӋ7μ_IN'ke\%Ȝ4X'=&v+T4i¶Z@._gVB9`<TI qҖj )23'G0NkpR .ɀJ8;tSc|RLHF1O{%L Jc~qؽLBp >94IKʏʁ)`2kh7D aF✛9JLJq܄Mv>1g6?ˍ|^F;l96Rc╿3LDF_xalQEa1塘 #=5uM(T.RGĘGr^% O$k%4W);#KjhƏdl e/XȡR!J&]ȵS3hВ&3](EchpmUMAhc#k)$A?mN:U6ln*Z]P4F:]UDuow-%U۴ƁQ=͛mr:H(v&dAD]U!j#˭}[ Lg1/* &cMz4.E*+6%r81($4UA$d!Vc9(80{kd6fC,{~C7URey6I'wI,~;Povr5ˇK(v1cA-<(U*3;"?c*q$:e)VSj"JG ÌM[K lٜ#ȱDȲM LGpJ#X@Pa1z@';_Nk""i}yZV^o!RJir"RiR&0MVŕj=@΁R!Wֺ{ةY u޺]-D X3w=NԺTJ:<) OQmSzpk<[2Ķgj|aQdzS8dFֵҩT(xl{/Z^&˦jbI郉{'B[tDd*۶&@QLʄt sE)ɖ=2qLTPd 1vfe3F ]&&fe\n{^Fs)&DYj䙧xϺ#orar2aP.J)>gTl5HJ8fcA a+MyqOӦSt$w)|Oݥ߹Ìj %g($ih~ Kⱕ]XO]\HVcsw(fw9?"ݼOҝQιUk4_ FjPj6{ZB+,B XUnΖL 1_[Y8+-X[ f( tm%S 4ϖ*SFZgA)!E SrLNJ?PP"5Cw- И.Bxoc*'͕&{ cCX %[foO^*FgYODVgdoQ&Pbn&T9wK% m#)ʶȢv2HŅ̄Q q ZPͬuSOql. Deӊ̡3y Qcej 53F9S +l2o PhLm>i֙zVHFdk8ˣkiĔ,{!&jCQt1ʡ ?暉op2|̊>TD'(,"0(A%I9M-JI%" HPgb8h`p ۩qfp&"o)|K/RFJOblP"`? 챚S]-Om91&?3ʪI* 2H_̑y%U Kyfs Kjuc/`;@o?"Єd٬yqWzɰ8qDZhnhQ0  2OkZm}~5xq!aPJ}O|F;8=4 o΁1(Ǎ*Yc} _"ϿexSy|m}rVi\STrRcC[y$$]T|UHT loaQiISEPoVeJusTF=Tܲ ]ۣaYGJ"4<2/&,!c_<.v /Y=\NS.I7"D1HInkrCzH1Ñkle)i5~+߯Te}}' g y9 6jVSaTj.ebP)G`"-Ou|lsdI  +%"D~e{o?Xh'vn;3S [zq[N.okFu]}^WV~5 {zթ~W{/* !=ThqZrWk/O_ю&7Nfݸ6d}p`Gkn~=Տ\~LVK*K35))N+[]e~$˩@T_tTAT1|-Ed=7 3yk2oi(\3:Z+* /ԧwI%YwZeW,s. ?*'=84.3-i%HԣDY,_C>e,<7k֬Ŏ*=,Jmd!":k4͸Vqi s4u\Vdi!69@tGovyKx1& kx s Zw9Hi}u|(uCs}O(ۺx^RSGɕw%73Ɵu8c1vdg&;f3Xcӫϯ_k%g2:ɒ g覗ѫ'ȾGdڐKFzw(ˠ 1lruLRF۩fFV@~ZeOb[{!lX(} KUl{k;rI#r;o(*+H&"oMBMۑuMZT-bB&!yAm3 ꁅ0H!oQ2rӜ*Ji',U6 A+uBjRh77PGD c'9^uYD4R8Naľgϑ"I)nEjws}$Z%wez9ۃu֛Lv 6&;v4Όv3qƮGٸ :c9ɎHZ58`}mS2=9ێi^SiY0Qt Hw3)قf՚–MhTRZF֝); ɾ³))M|(,U [Q)t+wTJai4!^Lu۳C1).57*'ј%jyt zVX%ȔFJzPmlQMVڄvt9]G2Q̦l7oeOWց7[155NY_υ+f0rI"P`sT 'kbȐ[yA$N#kJl l}*뤬H F #*MIqNji0MB2ZDž^*z *&$fFZ@-`<+M4VBZ_<(JWKjMUΣ(#,H[y)Q8M5+ I(5/DN&E$OJ !'Sdvs|3sC"[$isFZq7qu"Ƌ# W`?j<,=1ePu]GDiL^wι~w; iWJ@ <&75u;;67Gpsx:ֺC_x/+J)XU!xr{K'鹇4 ~lO&g5M~Xro,"uOޒ?Â3?^߄ 햻P"D|OllNΧ녑DU"@x&D.wF;Asy5+j WTIB#,kvNc6{t=%[a;KTcPYG~]?k/N/K9sw7gx.>H']>~;Ί"k=w IDAT( ""kN 1cDuu]ŌÊ  HY y½]Q]Uw߾o]ԩS'9U%k}J]ZVדTo G`.sG ]yRmkdkpf4[XO Bt5=u08!INQ1ӞA6)iT/H':$(A."!ن>AhX띆(BòҀIa'{TZ k8"Hh122hb% !qRrE^0X65u$jĽRE:c(LArKLQa4۱/9uyL$U{w]cƞD#|q-No$I"BՁ[0iH E\4~0NKZ5X)!̆ڗ1@ĉJJrzL&8fKp'K$_NX9y!GFLUxmֱ+}+K-Ici[*>xwXӽ8.M,U-`Jg- cΘ:+~GX=T 9h'}܌6T&I u) BkzY,KDM [Z @ 5D}zyaLZxՋ5&c7ejN؅9m>0Nejk"am(Gvy YqPK!H7iX뮬&~#>k,L 5sd8&|$1i:9uS1Fc'kKdS D:KՒCjPC!"{:sQT,]xXLhZ8&V\R x@Am>X`V:RiZ:qH$l X(߳4maEϖjt2K56(o~a k ESjw|uV+ `׺ ňUfLx2k&/W~oK&2kIo_cO}nnji'x̙O/߸b•")ιj?c+K6;-9΍I@^nɂekwPv}tw6dR igGf&A MRFta~]2$1kvmٵf5筧vDUM5qL_H Q!bղjLsZǎF"!9ba^ -dA{J饍Bق 3ƹv i+_$B~CfW,gYE"C?t̔|˘F A͙r8NіIr(7)uS%:lPΑ PQ&ZeQАĒ1_T!QHuGe# @笯TQr͢9GmU  vmXx]Ah sQysDB$"@9} #i81kӂ1ZIE4UM †D]Eˢ"Z GLXU?iE㨨{cГU:صavr, q,؂/yZc0l t)d1eڵ3\WY>a`8K"(ġMmu']ʹb,uc,-đ 0$ c-)cȄ4f{^%%eNdU(O7 RإL(-))u]an?qx>#"0¼GpDBqeN1W%qqj4[E/@Lܥ~02R9Gij?"Zfs^}sP6`UneN X[MΩ|/`AdsD ..%k)iv ~yv_ۗmަ}{n,`NO=ތ\jH4T CW&k cc|MNկ^UɸP8̦@f l>ӯE}^8~>m7/{u9=zΤ/ m*܆)sj3<@Q^v:[MVBȪ/=u9Ӱq"Mf WkVIlh`TB6,:ECj%vľwx:`V4 vt6aD`/.0izA BY*DѴU3r$&0`zc&)떒]bFDqf IgԔem+`֐00*!և,4 M޴j C-/'20V9(T#f G-`E 7H@=p{᭓t%?9);bFky7BByk&Vhz߽wcTEO"|*u05-7E2 B\+RښUE_[4 z5fqhb]JIg7g%]ÇOp +%RL&Mޚw=]n 86Ǎ:eONZ05RUuΨM =f]QMYK#Ny"rà ω.1|.'{y$p\rz,@B/"9}q`g2" r 6$A96Un%"pÜLDv]WQ(ިz 8KB`\<£p(aA T0' 9Q{UODՃ2OJJJl ]3m<߫jW\Yqs6r'u)EUd[VCJi{1SVTl\yl.dw۫[|ݛ2p6NNx1spÿ[U .<[9N3Go VwT3.7O۹ay{DS6Pt##)9hȒEb۱bO#+Xc[x߽@ʔkU ~OrX;^y}坱o<9=MkO m:\}r?ܶiyX[vy]#.kuƝ XCyws*B3폾nەZ0>ʙF˾Y ?Bn}.? .ބXG/mw҈Y2_3_N=/]xzd+T7IZD@v M^8{ƌ!5vul oݸz׮5_p\[g<2nu?Ys/[96ٯϣ,Tkgզ/]AMN7jGՎu3|Bsȴ;Kmܾ~^pP&˴C&,T}/=r8lp0HgQѴ$aZH%> T>U/4Vl L]p)cCdX*;A ]"9Xn7o;qGWU~}u ^WO^D 9{?rci<ڧkrxuU+6ʯ X7jgz4%7_WG(iXE;BV5BM#ߌ&fFujod>*BdĴ̞1kI8>oVw%=Jmw?xu妊%oۚe8c"f5U6V.6 R,{UǞVFٮgl=K z??.jse>yOװ^ةWn^©_ fWިE(j2C4jSfKC~]I|;`%6SN2uʴ9kyӾ6u6v=6VɃ8_l5UUun]ꧪU󂋇8qڪ?Nx1F]tʹO`N9cͦUk|MGqhS>XBF]uK |W-O'Y"SZkJ)Q6tBƘHOR'YƢW"g9+BX&H.'A x@A0$D'y$ęU me|7)<-}~2-|9X>| &Nf.zﺮȉ99.˸0$ːxˈus$tQ_"Bp?Չ(,oOgPqB> DúX:leA%۵S[gYG=sus?KĥI,j_RBk38# E8<ΫF|Ae[v=t͙ׯky9zݔ@kzܦ<(6ojW\vS'6~csU^1;/Ň\Ke\ex)nKV&8LӚ w]y&;|gߖi}?je&oɴuw>zY7kyvԝa^=oz^.8:}^A3A~rW>vpZgv^='oͶ aЯ|̋^]YW6}ӮG?uNg?UozLޒi GRB]Yf``4z&LiI?,O3%JZsd+o)-S^w{1wo4?c;,vUsZvftVw_ ۊ-j}ȣ+K:_ySwuѸ?ov#ۻO/.R 0)6fXЬ_d)yD-Jo sf*!(_JemymAO=>6XۧZ3ӷd%[|vpW͵;G=k#_qwM3|779GO(dx~kzdl~eASkF;'|w;ILF#\m/*∋Dx0!.U b=A,H:%V҆6t {^N B"UX7pB=aq\}8X^>F7 e3Y@P;{V2&\&[,D(¤p:ÑA @"` Lm!V`(2s8qbCEh%cu](q X ø@2+{II_ZZALP_|ɢEuawqȢ^,i:slw/|gZKq}{|s<X8Y bhKlQz} ?=j^f<}}Ťyٲ]N߈77r8sو { p7kLWL^=2 W|4oKk:aԝ_za޻0^}Fo]s_t>?i^(m9 j+-Y:Z?zDW%/1hL?DJwB?2|GO VLoNFZiLIT ̜W?\tž%mф_/o&w;ˠYSkSf믞/7S7{ڷyvr;U:i at|]{߭\Ooi$)fyX)1*+d *Iɪ7n VIfGL~쵥^'|O\V0?z'}:{:3; ve~fW:58.'KXw0 "TtuS#"5yQ=P(dxėf m<ݗw}m[pX gyЩK İ.#Q&O^ϩ_yfV{m=669 }>cD>1 9q$BqyCWy8 CrgsX 9DcYUCd N`O]5e[o}ii,槀Ksm;N<߫# B8S%OrpӾDI᪡:+szYӡ.tjfhҾu B anJ:mS}VG2X.{]?jE'gS{}ZYQ6fÔnʬioD3 C@tsA9Xt–9 `S*&4F@cb7σ%\\>2g^<":Ay9ȃ G }GhWBRmED rs+jc18f<9PbreCg2뺞4c^>F]1瀄ˌasEiJ$0=::ZZCfKJJʼ\.s.ٯپt*nKLΉ"q4l)`ˀ)ʤ ,Bx/(hkTCۖϟ={k5\xYP[U% %NV<ߗQϋNWId$"*aiU$J˫)YIUN֥+e5]|pcqɲ:^?tpF,|qs7r 85᳣*+?\ڵ}%6U~g+iœUcX[^85=q:Vlԭ_&vƧV zcE 5˗, ΘQ.7Ϙtݥ7vgUD:e}\s>m=&)U>n,O9^Eu]j>s6nx~̼_,-3± G^\[4QA0Іƣvbրŷ@6H^N-fK#r3@ӟxqY1 㠱Kj[ε8fe`~S-Bɯ+~aLش_3??W.X́6}xڻ͒~1(FyM.jֶ BES6aDx5fL^I1jaa6$ӘD(@P"l6PSU{|7u}]A,f Mݟ >.ʻuNJwWDcyuNJw&iWj\~no$Kc657[JP,J֣@\&f.^8g/on>ty` Mj!Aժ*{}quj5Z c jjYr55و֥uDc!; RDۖ8wVnc 0L%"頦)w|HɊ(@lnydu<CCBi~Wà z ; {P7ҔEQhHB g zo79j,Ś@4M {M)"SBI6v21 Z!*'i>'d 83p"#@3͔Y* 7p0lr0!L&F2>ǁxh}s(- ąvc@xVqJKK=󼦷8l]W./ݷp9'D50e6@Q  6d"篃:4U}9S}2{⪺o3 aw7oȏ|}%w5i;4?r U{3y{>s_Ol&Uq!e [Dulp$͡ǜCoqܘcU^'آ뺎sN_ {lǽA^~ye6sKs}詏g rZuz/|YEPzj# 5gk߯kޡ>ZǖL4I뚊gnZ~kSA'>7Ԧansvx~ӟYtm͜ ~G~uvKI8L1PNbF\jr#H5)t e?=z&۴>?4[kKva% zpSz(g3.> .dwtF=[> ^fMmyZ-|{ʷkj;rѻo%V+v XZaERҷ:_i6/ѳRT;j0z k v|#^{y+;}$cO8uM'zM9>}sO>3+}z.q&D~ܺ/Y^thns~Q#|7fm>XրBb8jxx˹ rB84pp=6ubm.~dQeiAfo~skߢFPpLZmέ;/ú *jLee^4n׌|wO>:DƃTبQmhc˦ GLJ2e5m!T)-qs+.:0(`Ŭ͖w?jMDwOkoXRbJx6&"pu3VW |3Vٹl]S N97|?Gl|Š(Ng8km0tb\!]>:J4بIe;$2nFXY@II!qr9D9q!PI|/@"#a17:0zszl6/c-rcLa'ĉsDK<,q ,j溮n r]7 n>P{ x@#ήfL0y^dY/}?B&q6 C_ȭ#q1 b<9/--˰{9㹥Uo.FADm(NNR@*bL\+ކS'6ήѫ3,/w)ݹzi\>'o~#FU1ƽ>+!VVpTO"!%CY֒&Wb_̟ARܲz]|uK'?sc'̪ 䰑/_VNYkp8m9stjPn껷^Ҽ[$E]twޛuŰݼ[q؁9w;IxVdݒZ8EЇo] H_Ih0B/,@bEG`ųQW_?;U+f}:a/'L/^\>U>"׽paqͳovuS0ѨZ!_Wk|k_Ȱ.(߶r|b<1҉Mb~[18]lDK%ь֊ǧ9RL4PjJ|I))c^қ@ S.WcL\>GD!E3ƀ('Q%W 2O5:oY}l.)O=q20 DFL&#̢gEd㠺XWjef⺮+py"*qs*c,wJcpԓ~RT؜`0:t [W K-aΝ;wر-ywltoo;&Pń?} %5s)QIQ?OoCLs@ 9ŕO6KғM j9R˄RWc,Fmt5ط@hfh*4GnA4WM&+;1N `B-Cuq^yI'qE(~BĎ*HaR(BDp8L۳e:"ED66KٍYl`o((cALuJՠ^!6I 5Ʃ1$|6lQڠ-daH"hW`MF͔HA9XwGyb?#Sm @nQbUHZ&H%ndJz+h.&)Y+&`*V#c2>vAH+0C X& UJKduƖ&q> ^"cd2eeM7oަm۳>*SwJ,Y \_7A@H:TΆt4.s*=q=9a,Ym6i!, 4j-DQRdcz;Q= F=!ɐ'EC+Bh2ɥ"*e7HzRIRPa_ϩ.4ڜ"4D "5^ X`P~nd5#i#UC-PVPePPQ}DSU{0{n0 Dg@]?` MJ)l$f%# cE]-Mk/ӓlņ $MN%{`ʊ]{? n[㣼FVK}REi-KIƨnaB}Y@7#P#,"0U/Ĉ$1Cс x%TVgk+Nj*j*2^,X)AWDQ b"f#LRN}V_vѧ+M}*U8KM1lxqs \ѫP/ՆX0.mƑBfubU\H$02AEmݧq`0+Ś|ˤؠr+='#Fgk,984t؟o<=|hij ^Ts֌䀨y35bjp`!FAsJ$l2p+riH"LxGRv-(1'je4^ֺAөKh#Vy꬗&YhS3hcY_`ZZl11%3ꂫ> Cb8QI6yyDƐ!s2YˋlI #ayNdj_GX)?2q]W# * <`fq\<=Hd\D==ƠG3>qt[y' wr pa. x:s #N9G<0'.[(ca;:QtZR-';Evۦ_`e# ǀ]R2}Uqi5ZIf(gJLE8T @E`4F/. [ό֯H7bj$N] c62b0`֗3DoM6 IDATM8uY ՟4aox>3^팲i0c>G3̉~XkMiBAlhPs1BCY/*t5mLQܖ _=D 0!biT$ ٦}T X>2ś]Fk ~SURDj7q^HM:X*:8>ngMQBrW wë C![*<"tE"KZ,|J5Kd"AxDƧ5N-Q&r"0%3KԤf Ex9u fbFGc-@@9({@'Ȑ8s|.GDbKp.B P$\p$(*Z8Eʑ+< ,"ZQb(L+/&qFŋPja>W )$+6򾏈".}JtL}YYٖ-[;-8)OyѴm-k,S L(6+LQ0k5CQ 0㿞xoZ%dJUH+$0*gF1KPE4m@T KՔ5UF(VmC fp藍g7cJ@uY@*k/MGo#ILvk@` #56mjx:-jslJBƩEysT+hfG{1(X'n0]qtB16"m/VU┈iok$LYQѡA?R$rQǛy}7D|@LLMÌ,Q>))֘*' 3ӛg%rk嶨 g@VVM#jE!zL#{b1I1a-Ex+O|׆GM@ı2Z2'V(1$sEiWAOUQ|P䧱@#j)7!_e@A(f$PxźV)DX>Yէhu_eI9U>)"O" xyH92u^>lI. @dsDlI>s}n6r901pCK3y 0.@by&}f_1b5J8l]1"pe{⺣ cD](x9c,y'dbqVԛf.A@Ȑ! DNN]1ҜH<q dw~$NIH$(E>gLL*ub\#*_*VC65R2{JK:?]-2m6@ۊ~m 5*ΛQIhמӮm_Jz!>)"]щTߤ0*ik:fM~LTv2 1bcQ)ya+? }(3Ր!cp >Ɛn•9zԷ|pĘ?}o0cnNLԬ_hڝA""Fi{K^?gOnڥ>|'2(gF;N l6Vی ShcdʵhdvQtmi JWղ>yln˚?;rЃmV3JK?sV?tYVPvC\a%C"'prL 9fL#ֿwAgaOM☳ W}-c:CN^^#XS'SZgF5'>$%@^ˎ%AIV<:<|+u'cBl^0*tY BMk"ޯ(XsC8@ 6 ?8s"X)2HĢnInh8((Xc1I#Sfx'r'b:|6G S5∍Ωq@?-CJh El^ﭪPEo+8.Ш8TUk؍tg&=CV__G]xJ('dEq7/03!uXu:0 g`&jP`8 'I9xcbˮLeM9 } rhE 8Uo+l6~RqcUbR5b"Mf i>yCϸfbMiˎNrب{u83k(_/laŔLagJl'Q[KF:a9QGO6c!\ JJ0(lM &cJI`[0$5g(Fz ( n}ڻкϣSak5sT0zUS3q[Ց-e?ew>]r Ny"d2#sЮ ol(k C w)m\wbh0hArzS%H5?:鑯VV炝f?=aoƏ}v}<pڞ2r̔/lۧ n߭[O}Bc_P%׷忾RX?ϻwk^ݎe]+mm~Ge^ؽ))~EŶ6waFu9]lɸB?U5)T@)ĔN%܉('%I1bkxdT\7~"I*Ʌ*"i*n}vu[4, C{M/vGiҊ?ɰlveUΟguRf6o{db슭U;wj+UDq3&O-_ze&N5kCumVL}֣ۺ@Vo=kVt8A36/gQ7%CԄ;_b[l޺He4w/m-VY_ňXFwwb7Ͼן:Ʌ5"!W=;n[L+k`ma+^]][W]UQ]96fكPZS96"kT[e:\57VU䛗n:Cnc٬6oZ湻iXa-)z{%)(DQAP$S,"AJPQ]%gXKX633ѩ:9EwtWUWWWy?~SyN}?;⭏l>~ξϗ_r)jO}~oPͷ.}9>xOu1a'g}+oS9H=fg̽W[i [(Ӊ=HY2)҈LjkD7#j')냣!!j3Q/zLN~]tΕipdvv=q⛥!O#B:Gّ}Z .m-:58ZĤ% i=gbAά穢'XH%xW&PL1T-:^)]jHMSTiꦩ"E({UO)EDJ,UeN AiQDԋz. dv:~R4^*Bk~uhiWnN]}SȁՕϑQ,(E73b`B҉ -{ThcQ~LGf.Lei8뗵b{dfϕδE=sN{]>跏V#+}NU7<@һ?:pfoJg|?‡O7\}z#u[lo nqֳ.~6oO}?k|ğIF/\[)Am J\aBw YN9o-rWPw;0}ߕ8l/Myղ:7g썇OG~͑_<pio=~O}?hj^~Ƒ[~ >ٱ睰:(\2)c" _̣vж[_<1>~eu5n@۶z~)?ʵ|b|^\|ܻ+ۿav2h ZsgdEDs/F: Kg~3[9]]ro( }=y&=;~޳fy׽p{oY |{7ojiDS~w/s>O{GsfJo;'u/J[砋pȫfv;x^/<]ːM??.w[砋pȫf>|'NwN#˾qMV/.{ߵ~Ω&u|M6lM@8]Ċoyugx>'S(BR$莌 !FFFlLR6n^!صijDRͺvs/ұqS+ۢ(x[eͦicZʢPhtJ>tH!)TT9@$ei(D@|EBJj@$Zz%%|ef\sܕ1 5bM <)TٙO@Hh.YDe7JӁ\  w<ov8s_9z|ټ۝.S֖ݴ m5ouz{io_b-͒z•߳Ԉw~x+foܝߝ2˟gsM ;w~eU>|g<_s@?sL ½ H\^?3L3xC$ }2;}2q]@v%tgILAR#+Ցc6ɏT0 vfLԢ{=n'o}5Fg<"FtcF[y->{VKB?ùo.7.Λt5SMꖟܹ`r֯uc~ Kw]y{m}QOX]7egW+J9c'=qܟ}]~8=:hֻ͎z\\}}֞׽zhTSy;%OMS;X{YzߏnT}_.;{'fj#h0﮿\5%6;ꀵ~߼E p끫}w]0z/d]Mǹe7;ꀵn8`o^նuOV^u?6G9<^cVnCx鮿\uX2e8~JAlYe8,I/f.y6d). #_88kn~<{EA;(!SG@Ci3-z蚤teg9n IDATCA)Nyq6;N{fۢ."h?`\Jˢu RaEH}5x=5R4=0-'|!~RU==j:aX*):C#D$RT JJU) B}6hFDc'- !$jZ`ﲏ )%W !trnYuӸu(H)(,˦i=Šlmj$!Iv:BڦiʲHC:s{!Xv:c)_Gsgz^M7/>.aƳ@(yO;Ҫ$T'F4Gbୀ|o_f>w&Xsټ*x-ja1b㎻)[c:c/\~&=tW^s7֟rſ6=9s[ٽ>vO-|o\g~y~xꥷtW}Ǻ5~|5Y^SCRz "vP 4dj[Q(I^jrML<5;ow,ħ &6ege:㢩K;¼FȞ^`bFrIi+/ ]P=q}ɑ=';Md>.euG;kWYu:sz@?++moiKc8 &g3>`鬼a ?몳>^w|)ſ·_ژ00X yaHꦆ#TƢd2=y[餷m1}ǽxU߱_xOCC߳*/Zgҥ .{wڻN@[?ݶ~zhz+!f *o[w\<ţYBEFtHt҉m6!{zfn01P(D(|¤>} O\kM7R4gweѴyCȔPW/Q ydtA"CCu1$Y#"~-qX4C*CzGK럵mֽ}pTWvY rĊ&t1nAm.zMb]n;1YLo1 )/*;T ,; ްUJNwijP@nj%eQ(%4JJ ;2JDOB>(JRBao  DaR.jH)R`BY ?(Lh  YeR)IR)UR ѯ* B-֐6(y#&19D; u0ȆFY,J =|;zm*:%&8U Gp7^W=oӷ;^4O7w]vy볗pKVO]+pŖ9#λkd]ܼR缨cўCᑷx*'s(ܧꃰqaz'Cg^<%Q;PFlo٨~-%vK6!n\<ȠX E0rLoԕE-VM'YG7|(Y<]e.:,lw븅<=OH(eˉ=cL眹M9;>> 5_)3vҧq9loaW= e.oY gь .F?n XcBՄ[.<開O]s쩟l[uyj%]y2OGqoέ?rĵ>GL=}⍻y9]ތgDCFzֳwҶ!K*(;YZQ/lj3fx/;$m1eR7G&DQ~$DIabdt2 ZAIqAkȓ^yhN0Znj/.1[?DO'U1 =@.vۃ|6dlк%5BT!gQFIw=d6 "ؓ}}M;ͯ9H9b9޳g[3wO⤌ H zKȕ+PњUOg Dʾɡ~P5d{#m^a !i&"u1^BHʲWKnkv tli#yebzD¶we!tczHOjup̿.Gw/]p.dЁNvpfDG4=4CvCa^ۢe&6_~oznW ǔ:_q5`d G.=8t=W}׼?NE᳿xbj#=n_zO>S>B7C~U ]tw-ml"njWx߼J~˽߽#7?iN\BZfi%@mVOWwzG|Go{AwM7y3(loFZ>}7/zhq8 О']|#PfLZi&o2Z5%0碇+ .)9lzλŒֶmGG7d&RjR,d-B\M;W^wQ#l~DHm`i璅^1O .}1}NV=zߙ%m.+f@ `F=ɣUnZ)%]q`$%"*MgYRb`+ї˟v]黦բ>Y_}8{Bss/SZKv'gM i!iʏ~i'1Y{9|<Nx?fO Yab%˯Oj| 6l ;^|x#v^ϼ{ woߎ<[_zCϹ7׾?2;?:aNxۃGv}vkq]O,Ys^xbzG޿3~xɴ_=嬗8G0^:4g ZU_I{JwEap[) ,1`l +\0H;1' Y`naJRϛd0{[-_hi[8+R\yѧ?e̮ +VEXU_hY^pޯ͸_5ο.cλX3{ ' ` /G5{UWzOߺ@=n~Qqd#=@}륋g=hus 9_}ONoscw_tޝK<_ >Za7s:᧏\sE+v/*Op~7~wJO[-93_4eյW7_֕0\c+ J06^u:b ))Qu |,s-q5:{!:)9h:G m/Jn}FbK$v&~$yjL/~'WBj¯%94g5q^%i`,G2Y;s[7_[do5` "E/LJa#ϪH)D@UϜLJUUP Iѯ$B_c]UBz(DQؓ@";+K)U^+Y!DY4xB e[檪~Cج ց]R1U= //#::D|FQIO21!rC4ܽN_87cӷ9j6^1NEDf/cɢjDSaWAa߸|]={umGA䦛4?hf;|_s!uS}⶛w:oA9LK:\ 㗝qIʕcw5,z `-l3~_?W 6 A4r_]NI8d<6ՄCDkLn>'zs[Xp39/ 2(:"s5mN+[ z <>׭J-~{o.K=]z/-WVs͢6Ϟp>ǞW C.mhwxsǜ8hNg*$n_XGwgu)|iG~~cSy;;o>'%.ٯOS|E;.>oЗd)G*ȠԌVhBaO?RŇXg^}U? yi7O_u%{@g7[ҫc,yk a}sQR;/9 LlR^롛oWO:/i)`}'ngmw-縪gyL߳.]8n;q>;:+wf}[ZAkQM@mm2r(|qgwng ~ `"l%}*Lz-z<L&kvؒj͉ҘG6}D- ="q@'; Y4Ͱ/eqLA?,bx 硸V2 11}Ax#/n:{#鋟 ;-xnXjB#)ZK`  CS?yt뽹V~YAi#ZDeD6oxYz6qOZ0QPef9 j9}neG" t[ńKTPYrf!pPA;'[Cɀ|ꮿc:9c{F;K316cZu#n4-[mњE%Zv:eY.3mW;)'}U! xK/~ܹ ,zMS7FJڍsfܬO%"}ւ R#UJp]Ч R Q IB( RJmw`#ё(B(;Non!P(7@ -R9Jspz< Mگ>/y(sPdYUmR"*(R"DH@QH!D(Ds4U]K#BQMBBž ZC֛]1wЅd9Z C(⮧̮@7iB83u[)r6K0Rlo⤐;gP?7ĭlwXfaћ73}:7#ʹ]%20)lvv$fֳlЄ 4rBObʪmnٓ~rŸw{CDz R^u!KjKƢȻ#av(cKY,F2N\6ָ{0 bUv&hKN%"G[0Ц'huc#a([cw2&'Pë7@d8 7ќ^m7T̢٫fvR :9YQ*:kmJ) y$",Ra+n},H{M?/,;}?v=KtӯMט6nLG!CCS&<9'\8G:T1 _ʋtd, SH<<{ EO*mV? oomG`yÓ攟,){we}:?"laQ~w =4%3^IJsn QG&:}q[?}2ZwI^$p2.*@/2W)x㔪ͤw;$O F0"`: %nbJ!;Gڬݺ ""0f leGA(*O b ; v݋!3D7xØC'U1f,,zX+\>y9E03`jr# ~r"6-1*CO,5in;-V])w\J;G% 7ӌ Or2H_V،>NV-!0v}s~w?ݗ;66Yhp)omµ*F{^RJu4ŋ1@ο5xͮ4uO_2>UU4RJޡe{ge]BtGQ )Ut4:: aώ.L,d^+ t:@4JRT-XNQ=-{P阹BAPD$uDY0˹ eC s~@\ Q`)ە퇂O"*]0g>6?%c`+üo&"/\s"{I؝o]bs(/s_B 8V!b#/zDybD.f[ Zr禥<0Gw-J.Q;gp,sgk=O@(BcnHf jcI7%[f7Y=AH Ľv:E֊̝}r cDکelB'CߏWQ_"ZԻ 'E ^5p`"\gbby@69RᘹN?d% oo# 1ƔS7~VNhVËsI1AkexRier0Dk_ mCN"qd1Fƥ &mj\3J!,Az7rLtݵ ?9 3?R 2\r>@3~Ff1znx*hшjȬ=(K/F@u]DzϰjJRzmzieY0FG:Q, %ӡɞ :umNHB_ȦBhJI P`vw:!%zlyK`.Q꟥;+'e{^ /p\> sV4ܦf yj3c4$ 7Tʹa}-wd#'8!? ޓ-ql\*܉fC'xZr]}>_ nS.Lph Jlܾ%}ycd+$9pQmo֔H)4?sΐҬrC#ylL{@'[LE'{B‰`DkG> LKр1>L!߳A&&eqe;p') *.NNMT11f^҅lEG7 l ą2ª3sBb`|H6v;[d QmIGyUǼGLC }fLQi~㵒!#v,î'J!u]66QHM%&=+|:4QN V,(TtQM#Em@;RWn, @"bVuE@J¢(#quߑQ~ +:BG#Bteg.!)T)%"S֫ǮפċX(ze@$ ۉPDYNYR)j DT1ԭ \.($"8Tk6K>6,("C$$ iQ$n`&H 9pb%2h^yS~ [{ ӠZ+a%A*Eö;ݓ{i #̷drv/_uA7p Lc9H(ドco:q/72+J = .q7'x>[ >omwb^0>:@k+!>=3,K0ab#brEϙ6ڂoyΜUFwçd"{mdž/wGӖ z;TLvantŽ7 gzTr^Zf;vhi@ӍW1pdCE*ƛw3P\ecx҈s |Ad!B^a5ݰl2D07Y DɛDݓidIF77zςU@jJO= J$z.R'"%UQz)( "flzHD"YaVJo^EQHiw}u6rvʲn!"t: Bt:55! "(},e4f~,O=#Oq"2\ϱ JBJNy,q?)z;)2}:Mb4߳mZf󰌌d'kQ D_nDAtprL1&Cb[1w^G+NL3 z  P~1X#ƅCKaC媛rOG`[bjsR /RR#hI4TqK΀,N=?t?5;IwYwSH 9ڷ{I͊ ߐ{n:7^KYTWpы,页O9Ji Y2r}qtA@'_+kPYwme?4yHvbLػ6 znp9G O1/L>~aVS ts2Y;ُ`pc 3b7[)6i+탟/ҩӿ  I#'Jp+@7/libf)0L)C[fDĪ9MS# i̅:U=N u`YWb$R(9ziD`W>iq`db)JR :kW{uY:F :񰎜 $! ~+Jc$gh˲lHG(`4%cw8Tz %44X)~f)DvWQ@;+4w&CMʕ{0d*-xMځ@8 a&N-9:0fAeLD)3/T= tB;fqf"22GR!``6Z' 2x>z3q·F| bVZ\ ,WZEYqq=- 8xKb̌ 42J׵u'HS~Ai-y݉>=̧h|_D]iE*%) A,;MS4NU!@C %RQt:ݪ,z〢id;bEia[kC`?/BH"E $@Yd/0AjLR蔲i( RJi#UHU! "Q!z7/ 2D:x!RE]ׄ:t^DkHC4?٢S濠~t; MlN*f3i1^ TMhǘH}L(e3*Ԑk",c?T x:@gŜ6Ôc0„fːrV軖I4):ݻD!?'v[E9G/Ǩ ?X8#*C'e^=_!zAvYT`yT /L}]=61In:7uH&KE0IƻfJPcNÊ_ـ"bB<:‘ϔ62wS~fYmE>U!x9`>In䐭Ahģ)C*`"aaZS3A<=&-:奈v(8<|E:rQfRv 4o Mt-~pZtF:Bq3z*Pُ bWkDTU==+ ͺ+V0B TUkאusQ:r桬>2l{Dye^"jk #6pfhM#ht`aVUeR(ȧ@cPBTyOh"8oMx@'[N5)=ta{S# dzf A_&DDeMYTMـ PD\g|!ݨ d$NV q 5ͣ2Kmwl5ΩVa}9L*Ozϩ5K>gK;0)Cs}P@KBa9KĖ r[nd-_SN9rN&p5AN3ױP,\xڔ=uI-pO(|:b-/$-06NҐ rby6!v-f\>Ka}PYsj_5dcnj/Öf7wtP@RnY'1ZJ S%dXWn r*EmfяAm\ Q}(6Oɒ(dt4: *WN^h]1Bepbh~ϥx2D5/ݑ 4'EUUJQӭ BHU葑ѺddO)B >NV_t;:ĕRHS"C4=)9̈X"!(JD4 B D$뚊vG͇%( Bj RnT*JkD֝P)ba?,RRXիl?*icm󇑛'R (Gf3<[#)u]6 IDATMVo|\eI U% @%Un1(t>*]u=r5[ t:ǫcfLCiYGd}vhkB 양eDk2 eYj c#ݧ="%ܭkSZNu^;.ti#:)2_&=iY횓$@/׽^6]Ezf^;ip= Z4W쉒o];m e=oAǬTLyt3EgraE~HyB9$4{Sٵ!3>|B.cᵔ1a—4 _Tc/  рrbgjm(tKͥ>櫫*#L+s1eE,vB .yL$ jA]D1am fwwȩvcǘ$x+Y}kkMX ZR9& N>QT~mvqGSHr0 S?ȗ߾gJF)J*bC7p2BY_h쏠ڂTpgBDq: ~B/ _H "ƪ Gr<% 6L|l-*˲nI^YvB)DC!""tGH _@%%EGJcOD@4fU(tyִ/ o u> w}gF5RGX]Jm*BR-yԺjH(ˢ(@,¯ghE!hݣ]p鏬뚗z +VZ+0`X_Pb9[Vr:pK2UIg&/9lBwRm1a""cAD;x~Kؒw0$9Y햡3k *O%ȵHܯ0/(I ܏@K-W>01xN 0a []O-5kbiO]aGʠ's A7)51y<A@*̅cdp<\ 1~ɲ%i`pm%Ki,7Hr9Bh-䌓h5{6c 6aZ5c!q`3KPJ g3zU@DUo}* PRu%D! $EzU9#,ROp6N]BAQRؓb>DJ)ݙDu(["j}t4^,RimV B-[Wy `(DlHE{|ރ iՕ6\ndN[ܸ̥B/=9sg+ڄ՛d3'_=^"~4hOE I%=?d05$Go}=ݠ|c [w=j":ORv$<KqĮ.] =&=_jvQZyB%?AD76w7_.~y[` m#d>cc{}ج3vZ_o7>ud;ʌ=u/!p|\ib"d_F:=a+p6ڊuZ ֐aA ږe0$p#D}HrJUҌ O1`W`g}ԇD.n [zڋ~kRfDwSTp'[ӣtS!8`ժE1Ыfz+u@‚\s'L6M=M{=?4dpGQn@8ZF#U5O_tJ3AQ̪i N[zčBtHQQ:?(:B4EӘ/!U( Rdwp9/"kL8Rv2o)S@A!69z-"D'(RJ B4TJAQ"ΒY\ⳳ^L ɒw%|-Vd|H*MnƼ%Q'asDXȂVi¦ĚOپ SژIl0hC/~p* ? -ٯdRp{XFn Bxp2G!{BxM L--^}9Eh0 B!V!x1 RżRz3fx{߻7>6: g$Yc/>[G{=ςYcOˤ[O&IlvR:nR (/Jӟ/"02%ߜNM$S1$ qռ+{0iNfjVˡ)űƼIU<6c"9v(b,whfLiE`ym_ׁ]$SD{V6 ! ad0V$-BN5y.r^BN*0zD`W܃:~A& QlSvyN9`*ˎaRcc @DŽne,KT!Tޣ*a!r4/il`7MEY!]=˲\,E!5B,DB)P(iB eSJ"SIRB "]Q>%YmWZ2f[yξb4aYٕZ >{QΘ(&Ե8qmy3f&Wl;8 I^DJŝvDDhM[Yڦ;X`ҽ|}k)kE^m(VOGwtг UC4 )9[Rˬ3 WHAF0쟠>w^q yJ0;{p="BSpbH劫q1p?#\ _險 pw$4qG~|HtR'ɓR= <)q7dm*YͩXζԩ?얋Zzf)B܄f[G\t4)\s$݉6/˱?1c!$K!'I&υuS&! ̽ymGQ(Zս99G L A " D&x>*xA@%WeEpf"OQd|2C Z]]=w vkuWWWW!,nODaؐg8~="Y$p5R+ÜS> _ŷ+p&19zu}4gs&?Ks@/'\Z'"6ijlv}t8- lEx2NeW4'Nʋ ~Z Ɠ19ں-lRiNo9W㹤ؗ{R뱌1U]v$6Ew]$fQa0y1ʴ!JB<!YF5E ]J. rAK;\O7ļBb)DZM d^BX)vS=}8g:2A.LVVaZԢb,8,HH|MfmcJ&MXEXn JijΘE*,hv3[+B܎lWM)(_C#WZ2\ϵ LY#b$6CV63W"Մ]@kjs$V[iyoZj1c- H"r=s'#UwԩR!%dMvگWh W]1M8wAc;0njFc+[c!>!v;hp(wRL٢q00g녭}ߋ.ǚ EcMq8`85aW5nCs!3XcO^G]}"4Qh'TQ{PI:91O veB(w=ͼ3w).~XL-\\[R{  V Ri,2Pjf.ufnwV4*#jmVB̘(X (W[aPmɤ ۄfsY'Οb_yʼfݟZP!_!'R!ng=*SN1|~ѻ0@RemR2biD \X e<C()mݤ<`bචΎiAu diQFf7bAG#PrQaZИۀM -’Dff';& uNT[HeiiYvTl6ODApUDQט5:z I1bj`m'kw@<~<CD0[caPTf8p'0"1Dee8sי S~` r9=;/V3k=q DKp] ürn^91=9WM@D7{Kvw;o}|I{{h,5}GۡMכîGaף4@ҼŃ'޻DHni䧑Hn"7q"M˿5o?umqN ,nda"%d2Aplsǔl@zXiTjֹaږ4rU ]7?I1V\%wpΘA:F֯J)1ZDFB|EJJY4gU<:Kk ӍG3T/U# ELPWDIX8w0jz@R$_̭RYe݀IAVV,JԝJƘ>'f̀ȪҶS)DVeTL#p'ihR/f(3Ӕ* B)Z)ͶָL %m`K(s jJB 8h:~O> ilD!K 2QWѯ!$^༈G:1A`0"R.F~8o R՟TI㏢,HX#o@\\-/*D}POVV{P6%`ȹ5aBqz*U # "O7l;Z6 Xy"$j=  4Mkto7 $j5yٮ wykvg~Ec :E{n1?[c]rE끼UosZg8v&1j]wD`DFiw8fy7ۭ]{ܸoLo;kv+vﻮh{cGu|2` {wM&riݸιi8MCNثQak'6%WKd(T-Rɛ[RӶѫOvusJs|QOMe;<_ކ|q®/-E^ڈP98]moUuEL|(0Lu b/kl 622ﵥU(;5ulQEw%dS"b^hZ2Z`XDMԓAX,9ΙHU&u_ArNdwnM@d03 |5Ǚ !} IDAT^ڛ778w tu֘~6Wc۾tzI 3$nޑM׏7\ƍ7n4LalxxoNݳOy>| 9ocةH#LP 7X|ऌLsbTqUyL5qSNx:Z0_Mݭ֜䵆[ -+nTtS3g[W|mpդҐI@_..O<(!l?ezľ?ڗ.f.P!psD%92Íٜw;>\r%_җ.g?/x;ܳ';iđ[&7ӰqM/^w?7^Ǒ.5T@iKk"#ﯽ-{_X2Ω廎 Q2eŽ"r)eԡ$x$m:򸭕R& [WxCCT0hR,-+HYf|*II*7ao f8֘_\m7E#UᔭҎo{jJ@q53$ʮЫke' N"Vl,mҩf1̿U_}_|8/}z.7:v"-$լz{Z(1~ \*)?UofP.J2 s+7sT{ZX%7jɂNql/"'Rϱ/ͺ#~ I\1܀K`/;L2,FqriGrɺ٪ vMw[=pWe$)aDW-2U277#Agb79?{'qef~wMc8 80lke=vCt5y-{F[%\,'?l6"l:jý8+].1a^P[y1Fe\֢ar^ =kn'Í",/vhth- _ZOw`ۛ/iw9& +˿x}?] B)Ҳɟ>-9S|d9eKkD3@i6C|؋8/$d؆[pJǂ^YPSjCQ?GPguVǰV"eZ#sX6M&e'9͍/ERP88!@qRP]*q_ػzSPJv7Y>7W+ zK^re_xկXu !:$x)IRwr]iEF:^˦bk9+R$ðR/쐙J+bJ̦bq$JLhUeb LR$H-nYtiT$ %e6P m Z4 Xi<˂v+wbp̓jR] #k0Z _Yc1iilZz7y|v~F>t4zvλɹcw{:ƍc#$"Z2qZcx|̕1svLô^qBDBDG>H^7<ԭ@k1n" |5/g)@s̍G 4OGo]?M|D(nvW+~j?xQ?Ix4ttmncJ{/rIyׅ2j"w"hWSN-'aYbgf\9R?͈xU~k[6v@Ts_D@Y8S_ğy4<j͞uZ`xERBJ[HRtVVʈųz X Vu^N|jasWN> Wit{7) ;_ lP iWG?"!؊,A\v_t ,V(*}RcSTBJg r-DLHڷ D֚R[j l,*oLveUDbw(83r'\=78f9]@km{*y|1_Ew*eCyٌ saO|ܿ;>vxM#{1Z4("{]p8#t4cBڕBZI\+mQK2Ela,R~Yin pk艑vp"@4~w Cib XGZZvP>НE.GQBao, ?2}ʬ̌3u+?E,Z(\Qe0\NW$c33l)= AfW]\K/Zq A m7-֟KV?ivBjQ >*q+[AĹɘBodHo_Q}92EPi:&** nh]QGaU2ܡZR_yE=1IƵ32Ipl(-HژZ z+"[DɨТ-=Tt\ȱ7Tp;hZ4#g&E&"",S~1vLJ!6L͇XC<_ 9)Bd>!KUkZaZ \L pГ/u=Ľ3]vj+!#n{ fZyL~GOc"?V6-$١Ji$ _uCv87Z\5]߭ڐ^.4 51 ϝ3-0X,r0Yl. ins.ujw{⏭N?wͭ7 [sϾ;pD%d>qF <=ҩX3KhfM8?dN}+5^LdDoK뷉 -!ߚM#:|9rhȲe<*ۧTnYQϓ?< m !˂ lYbm$RU}R:QeiL[8Lsa޹U{Ҟd>b$쫹][ JgC#WۓF­o T[QrTтh1NVM~ڲ iIds 6d1U)csB3⻞!ʸMjsspddl]R)GL ȺbIt,cLѸR0F9 UZtdpQl^K?4(NS"̺%̩[G!v3& @1}Zjak>%NYZ cR5NRV^ɚ hdHдG |3ș7QЀ;@ Æ<ڤͰ|?ثS!IxT |V#0hFP/"DŽ1^P0;f9XXК Z՞8,˳E/G9^ 0huWe?:d']X~4[oSߝtrlh^yemkVas)I ZȚFJ*ωFbfݼ9xj(d뙱k3@e#XP4ՁA`Rob-OM,6ۼKc5:*t5SU@`r֝?zDj0n۰C4z S"\R6\A+u#hS 2QPKT J5|!B ԎƴBO\1[.I ҦVpP5-lWLL9RK m>K~5L_o~='I+TM|`Y:qg^Y',r_((OuxީS8Vz.zsum^ѡ9N;˦u4{Li( L [ ®)=mW 6Ơ ix{9x-,nnoK,#&JpS=ӳz-J,D-B*<_.@5F2/$䥎-z#vn IMa{OA; E|&<+$Wasd㪀u IDATE,jlK0)l5j%R)3)*ߣlH] T˼;jJ4U`Diދ2Wq^ Ѷb4d6Ċe ,XQ `.je%9*CQiyj#ꩄ?KUyS$4 s ̉R4[3XQ(z"K'iĜio6R+0 Qg}aܭآge&Q#Y"0d@Z@{28JD3t ce6Oϰ-zuo֫%̵H؅U!Xœ^`F K|D[ '=8f^ꟅL}xOSio%#:t_"/(a5w_$2[6WRBbr I[X_Rys`1o܇% f 8j:*[B1uFRQ"ˁA+`+@0Drd@ESeK@<iLn $n@Jڴ͔X8_mSP 2k+dq)mv|QX%0vOMSa<q[CMzTDK1.ҡf\Tp&3UJQ@Z3 X5mei2)%N u~TyF[ <)ZM7uQu#6[[ؿ'}aE-fE7Y)gjaCjX8J4GDq# ȔqL:Y#(ޜE6l *nBePȧ zDD kL"zlbX,FH<1w!/H_MXʆri8a+?:^el&5ˤ5G&}*S˕I)vVHykoP u^0nh/lE#0(З:A.!eTeWSQֵ"9 ƵfBh7QSΪD2iAXʔ78FWfbELF8٬H3xj-L%bK5?Ug͓lE#Kq27'r!w3g,c+0Gŗ9S\D"R(.+?FvU@@\d[37T.Sy'C8W>?u >ϾC]>cwp:]>zy \?O}S1NiHu#!4b(}XAփ'we IW@J#U惃FiNpOh$d^}.eLd>Y!E;4#]Y$ Ltnš,azɬVaHngm vF%vviR'zQkVu"@=&os(Ņ7ti!h\JU6Grcdma iZ7 d1xFu'o9EI>FIu&M̓wFOݛ S[G,c!SWW(^G 5 bެ[[C:Λ,@`r_[5`P&KI$;Tڲ5\0e U3ꯈ yz7\xQ#j sDe?aeҪ;˃'TnТ!"掱ze5Uz9kq%^QҺN,Ƃ", ZCޣW4'@Wqih̪_o6 zphf?kx_4MWqBxp1Xk""ھ7o|yx%"?w]ױ8M  c&yﻮ +":L8%"r R+Q$y>_t80`p1?l?|#G#^>j30Q)+&TcN}{/]'<{C?/}? XsU[CIro>k_>H;6}ؓ~'i|S/_Lsi;5jm.C?wE wtTyd~cΙKjU>﷪DX -j(44(SL~v0ߐ5OP0p9BצY,%;dJ9Q3 -S01-{V4dW4řѮ%{˻ywO9+&E Boੀը%JRY~j1jW]Sǁ=XO4W?^18 $~,i$$q0x 5e-(.^G վJ8`%څqqٯd$ ̷ K>㊱7>xŝ˭esMH.nwђ-op`9Zft V")[D W_휻/>LU t.=汏}k^uax{7%}̽}KG^}olWI+җ#w~ 3 Oz ~} M<"U=l4ks(5 6zmnCN^EhMd7c9`ak(mªPkF(2@WD'G@ŪRu &]T❍uE˲)Ðqk0iSN6kRN8W[|VA Hɢ9} M#ҎAaBYw@u$Uc1W2jBMxA[G]5p徬`aݻARs%l\.V/+h-@VwIRDS:B@ThAy|%dTNfVXb0' BR\?!W}9IcuQPO w UeU.ަpZɟL6VI$ T_r-ڨ!AIKpv}fior΁SZOľqHw߯NdvA4v7$έ|i-P@v ~ oO( ('М[ tD̘&㎋qTу̭O-*P/y#+dg}aQ;B=b\)XKn]NT=i-_QsxmnC>ڱ:e,!(3:%X7DEс9sJ 9Y婭+kLc'w"0hhFdޏ@DaHO~ k,X%}uX1$ F_áZ@&av; OdFD5ݹr9g{OON4mA?kmj{&k3ƅ`&\&Vv$S8],;y%ed]9?M8d~㦑i+{*{ @)dP<Yw~!.byLՋՇ< 'dzݭƪev#R5=P'QZa@jR"E7.(̱l l G|˲uw%SCY:$Se8DMjoUay0NX3v镼RŖ{d#w-ulQgLqTB&Y",~/!q@d?GvJHty~ϓ:[gUG>p6b: OǫFx?~sg?p uug?䉏8C}ub9xBsbƙd7/ڕh:u}WF_{&7N8lIЖ˻jw_,b >H߈BlY-%ceMUZ(kR. jx}D+V"3CldKE6eI &ERAu>TrB4ԝm w]Gi3QD[XmHxL}9NR?w5<(bJ' Vt^DERLԭ)m9K?5;#S2UJB/qpK`aY0;#3Q1tMMly+IlKDRՒ0KB?1-sI|,\^ %ٚ@9䭫Ej=3@&ëj #J\XZ!'U=Mq] yBch40nbiD% q5{]y)P7._P;uX41![op"{sˡ5 {DhR)!ru&0r<퐚*H\xxxe?ߺ~w{/]JiR_'?w-nܸ[>W>җ+o6nZ@|_8nܸq۞q{ 3__s˰q\okm MAez_ Gr?K|4_?zy/?nbs3pAEuë3wkůx7M3ݒ3z {ǯ/~qS~qxP]thuޡ#Gr)~ƙgygqv'rju"ff4 ;*ŢnfI/=TUFvA C`8Eh@Czj iRD"Q[yNEE `3׷hQz`M9(I\27PAU16:6]PQ1 :ᢞ؎'Ml*a\S IDATFʸD,Z]$חVǥZP~P+<Ft,\UNZYVi9XI`8;ǹߴE 9Set2XI@$Z.SM g{BM%g1_x Eg Q.V.P#2-X)de TgJ9U]6Ig(^6`[(^ i]K$F"]@ȼY_wƉvhh5|/!'캞:8D=">L!^aHHgr d0]i烠{V{WVrB"z ka 6RZÊ[p@fӼ9wX yύL 56 "^%EW'Az(Y5lf"wM2/~O ٻ{_-Dtݵמuُ|#/ӓl$S.S?{gxS^E_+'X}z^pμ۽ܺ(Gg^ WF`ܸ̻twݟϺC2g=>gO>t.z;ooyU;>x#N|o5/K/9Э"R?ϼɷ|ύ^sU?I~O>z{?~\{G=x)Xvp?^U~⣾.!{>}?j~]v|o:|?+ίG=Q_{kn?֗45]9r򑓏>|XF8 r K0MsႸ\)$nFFQh$\(PcSi QbfV\O#v|" g|&BDd юQ[cNشس-:)z P( )")_ 5e 8>K46T-I^#җ]˞sg>^ 5 c/^}/~3_3;Og?v{}U/{#=G+~nNoO.yU@'q~?r8lS+|'_uwL?;?yQ_y?C_U=^ws_Ig_t{Vwg]z"{C};v/|qU{#yzͯ0nOy7]oW||s):&ܯf 7K~7?9tbIEDkmu+I^{h5N)F'm$4S48ky#-lT] %,s~Tz53A9.;Вu D*ӀVP|6skeԈBjXQ/AQTOw7% _PIi7ƐIS0+U KtAi{TR+i/ْޤU朔@%#@)Mi'P@r6RJ:rQ64!D43س,V{zvo-BUMևVjZ,wP&^(m4`t];D d]yRh4UVUiS;ŒvV>H ,ϷͰɦRJoRQ$M]cJ%sIi4O-1auKg)hQN?"ayb "2II5{uy50M'j^ǑGľib]a@hVð G+x, hEȆ{ }OyYkiyY8缵眷_~u3LxO˯{t8}ܤ7-r{k֧_r)~ϴz}'_r]τElغy]o{}sIݻmvU}yޙ$Ds1 @Ae~$rd~.?PÊIMp !, K!\'Iɼ9sLෝ9Otu_?];qo]y>}y>v'w}[>str8 NWI˯9{vz#q= =q߿w}[c#-wߵk.c|jyۯ~ׇx>}>e?>~m㕷5UeD r8ownGW~TSH]r~'뮻[vh̓J5zTL~XV94 " B Tl9&vآK(FTr#fԉ J!0NSjWB9CV?kAJCT^P'fi,O{y.Dc's -6Xd֠Nd@i& nV?)?cgOE I>d0:"r!ORt4{s̩g4WSZ5MU,}̊DYiLtj6ՖbHB {2]\Ny34=TP*mH1aDciꠠx\|!WX,TW[Mzrt΋ t"D8Zlȸ,G&QVԍnsJ)Nj!q ~v{3-71lK0G4 =8 g@< :&Tlb?kLS>>۶>*Zk 64J>]^.Pp1iy!4`Ggc!q]e "8"oEH$"Q*!ؑ|"r;?xywh nD [m^QG8y}Oo}rss/׿/_}IJED.2a |ΏoN{y7rYy_TՃ;Nyo?{wwnwz#??ܾv_~-ԭ)Ϙ.v~ד_U|_Ct؎Gq6öL;7;C|GU o7yQ}QGO.s{>s q_~k#6#PjWO<^?re?ݟXɞJ_py+8uC?=s/ۿ{ǎ;w_[8aI錤7¶!W D`(6h KVIm)Κ$deOJܜ ?9۬U+D7[q_'ܯUѻu ,\JO%5u%2Tf6[@X |5V~X 1bYt:{̼9 SH@PAl _. 1E_!ZtԱəw,1Ȥa[przcQ!ry Qj2+id0oJ3$~-0YGnkfb*:Ŷ뛇 +`N\5k @LAݻoEDV$0Vi"Wٙ<:)WhT+Hд+2%5O535 U€ ㅕBb48M/0~;6~fm,KOEK04]34ka:믿r-6 mvs:Bk3ƴι8i"֢A6?*Dntr{7/#4ւiL6!-ǒKfrr:dN5\nP1޳Gh$LYUQ}ny ˧޻Wj;Lt_ܙ<ڥ$~~ti,a 첹#[h&co{'.x/ys~쓯:?9;rܻw1͍`G>+\{wݳw߾}~iHn(ZIRqpL:IsmzuR+m<Nl "9:BqѴ4-> Yu^Bo6e;iW@dhj\?,|Jd5,jF?qdSZܞRc蕢. km|aUa|Izexgmŭ l$RL|-S[/"AI?i 嶶ЙΒ܄YdauʰB] ~|y~iO TE! Q@<Yݓ/Lͅ<2yGLK/NzSছnghws{~E^Sp/UvIQ`͎us_·| կPwo>l}N~yur{n7| >p?p?v. +nqx÷q0U#GxԱG?cw7iy˧{SvG= @˧>7~1"3Kp?}BpvcO~b{wy-v@˧SO8恇vDŽ*LROnXb2"0$O}h˙ľ:#y<F-ycS-U-\X=ZCS4tg ZB|%idEQqF~,;2GȚh$45'!YM3'6""@ぇ^ 2\IGkMK¤R3T}EWNVw"?*y5R~)Ռ.410ɡ{kQxGO(K [vtb2unAC#LJ@z9],Gq 1a/uvcQ4M9"0E|-660h7?CΊzWim;vVu䐲w4׶#4dIڶxYw1]"11׌=vCDk"i @sY!<ƕ`G-vDc9sDvD;ƻpOw?Y#"J~MҔDcX3@u=q'<?q{^pߞ}ʩSW'=~" ݛe3c0>텿vđ6p}! &;ZAZe Kxt}Wyמ/SO?^qxW_/z3Ž-P-PY 渴jޢ&vA*Mvd K|kᛖ:$ A(v#J]̙iyP@ԉ:G?[,A9dA<_oInTN}i 0V#M&9$Ige*U.٣A_WK?tQoU>Q ˛>%xΉ~ۮQ?ϸf<̟|{.|q?[n@{.{կ~_~nVى̑lUtDgϜǧsr=_xɉS⿆5_x眦7̃U/J4vsEg3S%qo{{{>}k>}F}ƫ8#X;8M"u֎vGk-/nH~l*H)r %eTJλNY>\I3PVI*_Qf!p/`"Xu*Aқ]".gsi/!N\I yHɅKPlQ"jOF V sR( (!h"_3{Q2mMu4BDEv$y7 o\1;fOȧ:21EUa 2 JoRϑQ Wϴ[KrqUE?@IVɠͅlJJ3sLZ)4fL3*ϘNr8%P$HQUhfDK5lmڦm]mX.7۶ݢ@:[qcc>oX]vn6mUdEl/5MC%6&$88?=_ú?)&׹m@̧yO9)yc|O}){v?/7MϵrșxݻwP@D۔T6ʝGK^vx;?se&OOdiswۿ_xil~'=c?yS]7_ٻ,7җ?3~R</~?~/|sK?gҧq7}^as{?POkBZ6;рFFJ6%5ђs5V+P V,*7'.i Ytbku{(T9=JdT!,Y Bёd ~RVi_4|I2ĸdzn%EӬFM#Jb߲\"o f|&r(gru <O{G2T)V#Oy=&L.h ˖r|GE0IJG פRG%-I'SWK#Cv&@SsV9੫1|FRAP ~hM=Ӑ#kGEZzED791&vжJ]0 M089^,a!}aǦmq4L8biɅҞ>A(6mCc#AP*v~ rq#"/҅2n-s&}k9'}__>~E[ݟ'7bG߿"Ӗ͘l b:T[( ;iOG?}.{} :ꨟ}{c~/=}XO Q^.o%˟I$V,Kv T5Rb1R+xhEjQ6fWgPPeiBLPJwNLcY7׋5R8P=-z._S] ]Y79``ioK T23UΦ9D`bο1iJ'Э&֝1wBW"Ȍ0lL P+:A+~ Pu&Ůes@]"Z% PȖ!n@.юDRT\QJ/|T&ፚq.us9n("lQdW5J0x!rq䈤 j|.eAdKv*F ^TI9EGӜP]G)җTZ^ I9,SplM@:`LjHYlNNEg+/DF*k qD$rh%;#Bmpׅ]#4Oپm;mێX {u3P4_- 2ZljП=;9pU*"z1d&9P_orln= g!wp!}d9SJV`נpf-m-BgS~w ;A%:moΛwم2#GI8 ή3_RsgOٹ-mm xP A*Ne0QbFT|b _fhM$Ҭw!{M(JNlSr_XgVFrrӢw+Hto:fU[Mxx%-hm&d֏Crxf'ds_[݈Y "=|/(R25R F.rŶ{*vFh}@5;wb}6l1o4-CO^#Gӛj|ӭM-JE2 w'ӌARTfF$kNegJOXa$5Mq^E_ï/w dny[ˏ@At 4Y׆#8n%\P#&Bh]^ b KIv5vͫm@=SsD,嵜 $Lm7f$2"_I$Cyblu-1EmLmll}ub۲_""9-66ņ,nî㍍ahk},`60[mCꘈ냴>Xn i}mۡ7 2r4Cԏ?˗`%hL ⷠБv ooC@lqZn.">j)7$1/!V"SVn}ޟ<БG>nO}^4$ 멫8&& aUS!o82aT/wRh Z7J]}ؤƮ&CYeN5tUձdF_wd ]:' xQ4eQxP}U Wk\97 NndݳSQ:Ɋ"MDLޫ06DS" ȢyDF 9fG*@#AJ^HKSeʩCFBxRUhYrV70&T¯mcf$ӗQjQŌ3$J9kW;fdlUb0 T(S3.|mlvxMt632 bGk 1H8Cg@"Ë%د\d<@L 7Afs֙]ղ$Z!R!e )(^%&8˜8q#4H71fzrG' "-DG;½iYifh*} Ckf?;k!x׈{8;mkk?>˗Ndqck6a7t dH8_)_n袋瓞?^ilq[K3 `^7pu3 Z\OD~ﳨotu@ *uO>T9(КR"IUed%jsCk&d0緥LHX~@>`IcMYDEk|4Ͷ% mlQvb88"[!nll40.֠Gs1>nO 4[GADDƤ+}ٻ8Pmi3AY mk 9tຮ}p c5U)]}>N:餓N:?_}++z; i o}Ɖ2>[Z _(l) Fj0N4;ee.oA,e*`K: VčXUZu#nK*u=JC` O0ħ{iRw$m ?))-T=)ԹgQV24ZYNi҈(3]'.&:5eg)~> 1!9zIHLˎrA =,L[njD`) 'Q )TghIrRN(HIPk5pj VpIJL++iFlɀΊ<\m8J9 N?M`Ճ \]7$3'^bM8[S㣿ۗ$*a'DVX d=Bex^=B̟Oᜪd~t!m|TKRB|iRxi4 ?*¬'> .É9=!wJ Ujk^Jm,Ofr_%O s~'_Yb^A/!RtV HVX%'tWF۫E2Š5ļ1P"(儦+ fLmWFOd9i ɂ9_ќJqRMƓ'8+s63U'AEs ga7by3cT5D S'^BK34F0R:f?#|rL*BLɤyP,,4g i~DrΌgagve>O>y OONz+XgB)FZL\ H]S =E k! \eƙ BmD2Chh$X堮Ve[B,$Z2ZiҌRV^{ίVSS).#`q:t0,%9PDsknF, 'Aų4u8EiOfp0,BGr+00y5{)5T*ܡyn,"*3Ҁ䵘8nLe_iG{CuMU%r+[ $%#7ο}3d0JaV C  QI7O,]Mj'%yie,ɎGA IDATT*DmQ/VrD?S鶂xԄg>C͢C\;!ûmJ6q ,xK+hQVHell(iD)Э% d^e1&m ڔvk(jZ[`T`|(o<Ӿ%6B3CVS熨2H ZPRC޴ L4mks]v$`-G@Xt}CGz/'o  ZG>hژq4884El,ik@۶0 9B;otAtcRq`r.n[DD1 g:# {1ZkXD0#_0 MF<|Seuxڄ*tzr#Ҥ&ͅⷀ-ӄh<wD V2uJRN@^k*3b)gڦjꤌB2,SC)],H1BƬB2{,dlOc@1XVd 2,Il@lb!fL({U,J4+s0(X ѦPҙ}֭lr>;p|1*5Jtw5#"tlzE'#3l)g[h4-.'<iC,[K*z.(Am!ՈJ3se^y|ڒɉޙOQhA+f!N=Vֽ,+?nlm2L`R`:mJY)@H/rP9jN.7Rq$s'?,ɥB4WV9\C;ͼ}zRSA: EI60j jcZ ,}á١u^q{}`\Z@K֎&ܩ~Og8xww}LUi诛z1~%g.Ew(Aq*@տȟUWq',NjJyIs*Jd+ɕbPxešϱfll%K) dY+Eg417 'Ir><у%[6;$p{c?APbȥKkr+dV+\ĢSR%xe{b WdVrav, n{7d{UV  y9JpiP;bFȻJJ-GQB+*uTVCb7wG`U͸YW Q|)Ȅ7hK, %)dP2ZӶo?iƶm۶9@ܶ]]1iZkf˜sζm^2BCv݆c~08~5Ygۦ[fȴA$l sY.Gd>i AgА/JTs\K_R}P78]}!Ek-| _R@Q!gK5Ik7 嘹V;9.\!W0nhZflt"Ҧld6< W@hDw  = {TqjD&=$:)7=6s@ݿIr.{EgWB(Zn]b'Z9=d6r_ϺJ!pyrױ,\!oF'L`S@k;$jaxVAwّDg#,L2u4\#&ybwAL)+8p|^W=ԧadiBa^l+ĵDxY5[^ѲBtP3̰k:2&rTP^ZkBN)Kx94ԥTZR[,+ "QLzY+Ai9P9.c7 K{4Yk[˺7C ԺrI2рs>[f.+;!:ID^\:}@p8[|ó۲_h<|{D "{r80"tqI4.r)DiOҚ)mN+}% ))"TEd-2ߔIX)Q~Dx.I[^o1ʤqzYOVu P]mhˎI[>jm1KOk}#+rN7!\KiA? lzbNʼ4%j6,@PN牚lOlW)eҕ|IT>7eyZ'VlHT 4WDP N0-sq+LNU=qg?QH<.g.1ݙƂRV,Rn\82'%OiwZE,y\ TD 52x#M Dz*X~c Fg8{& Ө\(0)vf M .z*c@Ҙ+/D5nQ{7&wF&`…Trz$ͳ4ՖOas&Sd 5JGR(hX%]N+}bs &% ,bh#6#G4:X,A"g,66\vјƟ  [l3ppGc`@asrQroܴ !":,vegzͮ/; ha{C9Ѐo!_-ƘXucct?Q5ƈO5Kmg^_x=dvۘ}sO+fJ^k•yEeDZɿk-RkwyxMbVJI*D t.)b,]KArDp4u6qIzXL1N5iʤCԙYK`$s@o%&}-ʪNH*wi1wprxj:Gr7쑙ޙUZrq㳼&WU+ƒ%b+RDlJ $WTI˭ZI`0=0GoC7Aከ@ ?fItb-_ud$i |KZ {Eby.dU4\?:$w h{H|]w"|KTTY#)|/2ڈ%RM@?Bi^˭byVjW¯XiPQTO9;Kټ[S)K)6?6 I$^"$ Gučx?'fhzju3ܔFV!熡'˸ǘB?x"оo/oWQZlh[[GNȵZ:D\.'xVg"jC6|Ub"4 56i;,#`-;[~7SQgN-vt*8#Q_=A-~oNr K/ ̧o7,-T  T1[ !0 {|VIY u)IۢЌx 2jR`#q7BHX`W`1 ̌Z%5aKKX#{VSR"V\‹HQ:yN, 嫛L b<t#!Hd[Vx2=[#!t_'HhŭUڭ V'o Ϥ? lgm f,F4Nw/DewڗX)TlEd:-g$n7K;LrTKYD1"%$T<QC3"T[1E WCEhD#7mP;C^z%1IG$Pp+HUygl>*ʦPZPtk(JZJSV0Gl<Ֆ@tAr"ELT"[1Ċd*$gvmǧjۦu-%j9&.66jvmcfc/q3mǎ(OcHd,c玼9 0 EkLikkmDmmy׎x587z$ 8$, A W۶aO5"!R^h$atGdt!}#}g_5E 6I=ߎ4%';W{tDDvlH^36UmP{OZHe#ڣe\k/Dm[D M^_%=KY@kF(1a_e17h: [@E_MTv9ktQ J<7on82~*Y_mkldQUDA@|&>N4剥"a<-|Wx_$)h_FҘ,K9C) dy^ҩUj#$2#R'h&BePU(-&k'T|jpB&XJ*8f(yW!(5twi&@uc*M&` y3N5[+kAFhtﬦϑ"tJ”7\y! ,",ZEaR|Tk@P8#aV6e101~~&xDz7s[.7 @4MppVևm;|cþWR|=iM/1H\G;9D1=> .T/ݺ-_'́F<;~5"4:9X)S2Da}I{  1F!=PDJ)`iw-1,;H w΍gRi)2$ֺY]N6%[HfEtG3X0:s)-߽0CssJa߾|,yT6%5A"^q^gKtޙl6NQE\nwv}u.M:G_}}97$"H!bjY׶-HM /!Wmp.;} vitnl!7"2m˞0is9 Ra:>?-aETRcZ7 iG dz?e@C Ko=O;{Һɵ:{+n↻^Ӵ9SKۗ~f7 ed_VྡྷePygEO;AY' ʀ[VW8HF` ʗnK 87ӣla7`eW#,iy J S5b 5NZ#ۊ:̾N6b~ߒ[:'[s/`:{ b0M CoS(*J^F2Lhٟ82[BXݨm0L:KR޴ 04Nl2OblM H oCnUˬ@5e8LGJgtskaT1aI FfQXx*Γ)v2&t&T}o!Eaȭ$o3vr:r< L3nWIeg\ԩ#_:$_5*f[*+Βll :X4[g+K쒺RmO Fu%n1PV %"c ޯ_g0,s€Hw Aml&uv]w2u)~wG&i:JlH4+M4v勣)|! `x6Fic ֢ElЀ~=죎^Rpw}憻2M4k:Q4|)du׀?״^@{` S $-5GywyKܸ80*fSd$@4[aw`PdCBj +Es Q#IV.t['J@\`[)@#)3Y9 7f'ޅU)N'g^MPvxHʒ`AΦ|fTZelP^j/J5JefAE(FNƬʤo%( JKDHaOkEAlg,~cA|ct/^K%v|6gUrGgQdFݗLY2I[YI.$r[^2Ci  GO?b/9 YUJ 3?$2OF AP앐%}Pr EB9@J||>DUn[(r { Rl$O(֕.%`1x<kГsص.i$ИE1u֟CŶ_sX.78m"Zڦѷ>#40 lG躅maƱw,m\.1mӑ!"B$t:7"Xn!"t%`˷"À`ڶֺZ;6MCؠ1} [-5?._! n,!b pT3VeijMNMAJ4LE /BpU4rZU~HV [K !I5i3\:bn ŀjf|oYsGiT3:ap:IܚVaCks|iɇ̕ܙLG'jԒ`F"r9+JdnQe䍲PYEg(&(=Q@lZf~R1HE YY( ̃m'L($h$($~h*:S'Ys/L5)Q,+*_5$yb!`僱uQ*WaR+|L|+߸ Rsjo(ȡ:IU Hѩ^*_捸qH)9y N*%pK <({}|&k;V 4G4qDq^rq2CO3Z [~I #0ޜ?dum`MW^+|zq,/YAH?Z_Z"sY14V-GChٷ{ADsr$E6AQ"@W޺J[)m!O'^'iI:Ls?=E9H*4ELVU"I.[ l9%uQFIg%dmT(lhM9% |J[@qӝmz39kQ46QxW+kjpY˔qnRWnA3H\UWoIm|fZRYjVE|E_Mԣęaήi4( ֨؇YA9"RoMt+ SPQx M S,sʽREJۑ(0e9)@c]Ӣq*mUzɪy(PMRK$9ds %}xSJz9M1\_E^!caZ+[3vR JbJ6Hf9ȴH͉+Zy=x`H`TA"=E"$$]&OdatpZI/ 5x IDAT8HO)@ $9UODLR2H 43kB2nRV,d^mZv-qD[A@b#B\ll'4Y"u 0hm;0H"@uq vSZ@U[pQPƠ%oiwuw\88@r`7]ov]׶-kZ[֎`GG74CJ `AL$G1nm"%X*81öpE|Z fuꕲ))_%U)z[I X4du\ +Oog sadzz;kR:bZ3#KzBk#Ǯ4EVFAWCyou 5]30g(VEEu\/M\->j*y#JlRsRk&[d[S8 V6^rrHU3'J 7~QU ALq/4 >|ƮbqGitYgS5ۜAQzﻀ-fv)W !˦ xue{j'Q4F$TD.*":jwb?!=Lѕ:ZhTjcXg4 SS7&%(Gf鼲<-32 O*SJqHh)~bFh7է&nexˢDˌ( Dk9(C&)Nl#̈d"(H_ ~Cr~ D^~ 9gq"rE"qa^.nT9猈ރbbz Mi>` %'@qEd^.2yseF3l2 ļyu0,d*Z8,a@] vY R-[TEJN[9)tZObLwA2o{ xyՍ4:lQ ި5 Q5U,7eOXKt@o 5U3l2E`S%InU&b1] JSp {~CJna(&ar_1k(7b1];*M暊z# g$NXx,dѐFot}(,S.|*&1”вH$xW_ZX(LuV邪3:CL3(O#Bq&$#`ʘHLP6Cy+4МBټ4QiJx'f7A-$!^I3(mX,C@,-K E]Z"\`@)?N6)]tXCDJ$j@-#nh bж n ,6YrM6mhLӐáY{pUQ ?V sK[`]ƻǎFLc48Kp\auCZ;87:{5 A9:cȦA!p6z6^KOW{΄C9-)L)*Qo%|ei6sk]7+xk᠊lyc`+F"A:WX'*gbs(:(&uPє nHr6dskUaIL2P_F0SWvKNݶ`QbbWl)+`<0'p ?PfGe0h Զ!ZyjH_ICYG&Y=.)+ $Ӕ~G0Fʬ+DC-N*\H^ U[hZ':zWdM}~S-1&R?)M\*s.D* ]9(} >o-%EbFL L7Xמi}Ja^XurHH^,bT%j芔 yC< saa零ge_m嫛00GzNJ@8L1;+ѭH¨okY)j_{]Pei~ydbX~|lGyOH5 ֚ƨVp)y䑇~xuw޹sM7n] Aqf"T0O5ߨ^X9w_$Hb댖확HՍ^ U{k\[ҷO>c(T{s(V9h/H#tg!Y)b˱*XuKGLb zELGoG܎c0ܾ}U}mXX V.VDV+րT4>5rVP_kdHOԪ}*38"рS]J_r} tyPRSQE\RhJ T0fRH,E_O tUX:1H.b)RDQTNлH:y(pC!qUmGIR=UCSR^'UeUƤV|h`&ZPզ0;4ZMU3c:I]N$Kָ!7gPs bfG+mw:]Yti/6V+|%Vl7“B3  hGh6/kS[+ k, 4WWDukIZ4$\\<@\Rn;[U=+Sr~P;:X@dM(lk"3dUIws 7D N[VA]m1~]j,8[9;D2PHmG ] !+ j N"bP %%vJӉJtPh9חJ ME(R {s.su+eY*:VK5$2Ky1}I7<\ȹe!Q]vaW_xp0zYђeU@ءu]k]u㥐TZm:@an$٩$I3gΞ=6X戕uƃׯsu)e]vgT4Q FiЗ;t?Rɫ`@[ݩU C2e>m`YWYk;] R@j.kB.S-l3v3=3U*R<΢G6 @W>u7*W!=@a6Ί0s鵀zƩ9\*L`V6??ʭt&׭@Br2 bN:?{ʤxQsyIrri^uY/~AbuHɫu6/O>`Aӭ[%a )=0tgz+}lXIBeQT#9ρ@$IjBDVKj $ɲL K8M3Օe:YEc̤q1d P[vQL(Ȱ6([jB!$R)䩵 ;SXQ|ؤ}~3R )ZmuN%+xx ԫ7Q@GFו+W>,WZU)'m(*0bXQ'*t- `'vt ^#>L$P4*OEFMqh`xkXH߈DְROEyk~ZXSK1EBm: )hƛSDcYhGK(c;77 4+ P2_9}#)c4{qh-6:XP`DrmDu ~v+,yenb Elq Z7)z4M>  oo] d͟?T@2ܸ"ΕM و3"tO;Ի ?c#k)fY=kn? Q.&TVj~VwЎnqw__WUa::}j&ZŗnC?DOCüR~xZ5%vԦ:@hJBB j&DRQj%d,(9 HA1LmT&sGs'$\ @St&j*:|`K)D@9͵BJD)DWNM5{WnׅĞ;7M?[qmqeM,{챗>׮$ĥ /8hI%ԧiҎJ\߫^?~mn۶sy ===wum奥n܋L:;JTvr6+Y4`WSñX)-ܫ= vn}m+nLVSyv'J tUfBNM>B{`ܶ5굝jnR!}{脖ȝ:(}eRt}7YYB .:(fv#>mt-}*3ZM&vc\Ŀ[YX|ϼvA€! h ]6RF_NyCՕTh#ތ 2D]K+`BZ'ut;)friEHc|xC>H\n \3Iyx3˟ߵ[s.>iV41!XRP yRW 9*3йc#;wu4;l>:?JټGFw޺n0+ BbRbT0q,!*B;tSįNS?8,-t>#5Tl Z.]TB{* 4*"~YĤyiAFe)IJ!4B [&baj28Kc&@VÌ;U|u%X{7kKUvrε ؆r[TQEabDd RJɹDD)# yǪg[ 7R&% 2$C&`RJ((9ŵѡ\UvLzj~߼ylEG)qcbyl l`!eNRH.^Z̶oVsU[К9b͢qdUfy",l"Vj&''(__eOC~6k싿oUHa`*r2gD7֯ ,t0fM2mU{ y>3hkQa$~#ա'˕`X[Eq@ƦY ;Wé\pê]%hdzTk t t.P!Dǵ֬ElA%ېz[ꎽJWl_n-V( Gw>H"\9Nt'KJk w=y䋍H6bH&Boφ{n_xɌfk+ Jx!}q{{Kh8[kP(z*79+|V IAO̰q=;tpm6-KY3m>yśLhvl9c;y׹4}$u2U7T宙M~DCƉ&ïE2+K~^łf Wb>۱ ݇ndxfjWW?ۼ25t֪ ~L>-(UF1 P϶-JgRͶL /AU@4ԣS+N+J}la*,E "%4K!3qT+ӴVyA'TMZ#M[ )}!Ւ$3 $aGu$dGB$^k IDAT8B)eb9*'P$Xٷ_F]H2}3=BŇF6ӈ` bב o-W׭,[k,~w8:'\ N^*ulm 7AcI*g7 v7799o\߅S!M|-};PBYd{9iH!1P*i#ۖqR6 'ǧd?6ʫvH"$HJ5HM()AJR\HJ> ɥ$)xI[x1r:vU{׏m*3Xk3y( Qaʻf;'r)5EFq&tN8.|,xZz\TܦrqHKhIMa}ٖٗ8CrkY amBr'K #-.Z-_ S^Asz7 \^as&ʲU&}Kq{@jvN*HD&_.rm!Se7@q+DctblO|ۻn.V2Ne> p-b~5Ÿw?7.>$@md/:xlV{Jɾ2 U9t1K袊N1F,8,|tώ8kG~`l][n;zةXI}x{qNY]bpd7u2+g%]9yXS{[v/QқKCz-B(_.SA, RGM\( P!Ir4}u2$U*̮M]@gsey-A1F8bL; "=(҄=  /2:REH@D+Iy+ǯ*2$% D$ c)tYh*F @L2ɈI))cXH珥.vN@L,-i- > zwO҅aó^^ݝuqElc%!Ҵ5-^?5[#jKkg.}v$Z~,5o^"@cwD.{s:n .i\дZ;w6 E:ĞȞ@i Ugap3UĜ(wJU]h:j2gl&,v5z{%+iFyW_|(۱e'j Z)-ֲh1YM}"]-ߎSFg,,\%(m]"*tJQB@Uo;/\i]Yjg"! q h$D)tvi/~]{U/|I]e* c7;m)M#9Kp/ݯP.W6Pz4v&*L  m~@wOȑKuƶTV3TaBv?F> 6C)WuJ`wHK3 CPN'O_|mWHW_CNo]9Xc 79$P\ڮ߳pg4geVSŋ LuO-1?,޽gwۊByNu($7ɋ+aeTz}pHz쎥W{MT쓊Jcǻ?oZ7~<7 |[;}?u{'yYZ7 fZMr !vbPWTj4nr#Uբ:+Xw4|un+7<Ξ  tb}UH3hQہSJUx]QVH]cth!n7 w(/A@OT-.֚xJM_>~‰˯Ni-.rEji6nLr2Ѐ--JnFVk "G̪JF* 7"]h߸]o;vnC_ܿ&;xff ~n!) R;6&9N6ˀڠ툁Wg(tk&+OLT2e t^w.DT3t[/O.˳JЭ1dt- CԸʝ( 5A j q'T*NA7k7^mV=*-4WrUud!MPZPh_[0pln&B@eU {ul6$qj#cRYfsSTKbu#mNykRlDR3cV7AuR*E,Vocjc:*yl <*IuG!Iy+ɎH"cWXm||bw7S189 C_{ko>0-ٱAX1p3-{VڨSޖDb=4+kжB0odF$ˬ `Џ[l}Reaݿͥ+ͥDTܨsN`k /moI<]xcjw!5UJkW1t'Sυ*/!Bt+ePLE~p8xʠS-"fN^t~;;nԻ^p~9u H.K?F19Q.CLk]yoɳ6>𷏱ç <}ӫEUKcfh>`uήp@ۉf,Mu%1{89 yua蚮JʿTkژ- U02eʉ쁄uaBsPRjo#A-ؑE+:zGמ͊^ݾ_%r_}6Ή.^gp iRj\qHvI1w>ܥ$zxYy!yqyƙx腮b$@B*?^jSpPH)lTB󼕶:)o+W5tLv]a-[Ҷ-01*r~V=<"_B!`aI졅?cS9G'~s?Dj*qPrX CeL.XFZ_!ڔFR@p_+obF]R4Bw Wv{~{@oX.$ą䂄$.d+˿Wf3gԡɓוM_ÚT cH3J'6TOVwqB㉵:Wri2hm}vkQ*iT4}z09fy8pݻ<裏Ciq| ϥ&GFb=rj9}4;G߻{)ss"˹5+';',NlcP&znjJHX؁6'fa]]#Y^%l˜@fϛbjl`j(㌒~cR3ȭv#X|sp!^-^)%dqYD,ke$"q織FT'IMDB=éj$k(:0bc,bE#E8s(B)j1B%B.(J.rs  PERJ51{L1 RHmF*:bL@D3uZ Tv541c,i26E*zeUz奙1#{[ٳϝ~dݾ$4#NW槯7D4$z\x1͸|eseqVc_M?497gk/5'numKQ ej7V$8]tiuuXj=&M˗.U/㨾}@OZ9"\p@j *WC0kBKWc]+Ѧo~rc% B''Ą^ӟO er2GiQG~VR ^gw8P^1k:cD\E>X.Qܹ˯]xu1\y ue6o4ˏѵ{oWTυ=VPg|fThDkխt}Ʃ)6xoոxt 3VUZNNNJ)y䑕Jy /qψ H…-;~bw|dBn r^@2ruߗi:t VT0Kcf=G;HnprgވhUuu'Ou/( { -&Zꂾ{n?adj7̞Bgm?CZlF?MLlK :gv[+ =1/,XvaU &"LslxVqLDYZgϞ8qbzz+_{ߵe/Ӥ-Y2f IQQ}jC,4c6q۞'gV%i Fk'JvCH"dkzu['Nk9>u/(rE]4:1c=5;i̤5GdW= #}DGz9[,/$JdŮZ ; CJȜElttoZʽIp o6{}]?>S!SS`bhڀ0-*g۰pУA0hN0cOT g2 fLJϵdtt;DG1PBf9 IDAT[y5Fy.DzZ܊83TGQe@1a$2MwS$b)I"e .8Je0gu #11Ƥ$c"P1` ڊ qbd3Gq.qOsKE'sD7f{gI(%c,'!KRc0uז/|Ã_Ǘ-[eMNvu>evlmr[?,bKzL絡#Ѹkѿ/ƺ?LU(HkٮT +WZom* PtQ{ 9^ggO?ѱJ8-oP<:*"J#ؖϡ@VNWXٟnιB*NΎq mRd9/3G?ͽ6gŕRy+!w,ԥ@AV4Zʨ.& QW[ IRn5ŒG] Ml޴qr|W+OZ " ~\Xh}$\Z⯾ƶo[5jAuзn;CZ:JЛK*@0IR  w={DQկ~lJ)4M l$I;77xjٳO}SCn-߾WOzx+;ṟ hx+=yZ^vak~ 0^ RozܻmbxɛY4TPcş8v%F ofbo8|e['q ҕ]8;ǰoJ`81Z6]eV Lq`o)wˣrY ~rWyi8Bd (3OReDl4o}[oկ~/RR@YW Ug߮T\p.\v)U3DdZ]q_8x/R ۳bzXTXR=SBl(SQ  M[%_evrQm L"$.A+Ԉ57 EcF_F$ PrR^FW,t84)wu135Gj%66 "@ a &z(*`M{%Z\($""x'0On98w*^|fdrn7;;e9LD^ZvO{!Iݽ]ns#_oߎ߰|/Ν& =sjfF﫣$BI. *N̔59{]cZμZu-V]0FtSjju[[W;k PW;w\}MĎ`×kIJ^K`d8ZY?qo|Bڋ;'o&HUa:uGՓ7M8vx \`mimBmkPTڃ *m{"G @L7ܰqbR\-LD W`嗡//X۶%5b$^քcuVͳv3[if#V*v|ar`"&nuHMT^5B,8Ioi*+I$I:t9缫 &&&6nxss]Ī}\6 27hv@Ŕt3gx5x"] 6l Wub,ίNp|ػmW2H ZW\Gq-J:6ۯ\ZɹxbJycb-Iٕ- p.+uĐEV`aOyڊ/l/7f&jӳ]_︭ Kf'pgZNfBx q{ Y/9O.TdeY.\V\RNƄ0^c;\U rwPoPIBӅ鼯/W~?]D__2@[,YJViqD\BQ_W9\ȥ)fI <\fxUOH1mYfwU))+&[026ubWZ_Z뢃(:*?q" Gso+/c꾰hSr8t_T^GLWUʻ1Xhm <zC AU삐Q4-૯gϞ,6nCMOOK"={!mY7X$Qp;V_hZUQ &+~΋Aox?e k@>'=]u`*pk..^eI-kQR򑅯~ <<y&xFy6k?KVCxFOW#0]yLp#N q}ES؁k$H--إ1mpA5M^pGCYn^|rTg~׻=:W:v{dxj&}ʡw_g󧹘]]83;vvlټaֱ cZXXe4-ݩO.`#0-rb3Y5sLxqX''ϝjyQ(C1,`9|P1.Xj{Gf ݍ\VdzTj6BmNO; y}!?gHwjDeHfeiG $R Έv  | hV1X6FWFHUY ylaHcalzW &Ffe#/nRdߩ(W9ϲ_8LЕ+;W2\f/Y]XL|א֞7RMur?s޼ {o۶m[$Ifgg;7*@*qk;SוМkub?Z0vaK\`ܞ< ۓ큮X]{߱=|ۖ{RW{wZ sKC UI}_nYIm)#fmNKXkUB% 2\pPv.Uڡbfa#ۧ.-\fK漡d6}L_"̓ձ9>=ku|h`ѺRP\C(rm.*Qn\}&ZPz>a7V4M_y啗_~yrr~E|gϞFxY=N`w8B$Xe.34+Tl^m^P :!8{J pj9W=!#T):{WFHcl@oJ+';gs?_ tݱ{u#m'ѮC6uG^K3_}}]{V?̊5,@^s:^S2'Y_j˕˔ߚ"_ P|,"*7@?qvܹ=z7D\\\H9I5Ĉj>|…>ϲJܴS:UV"BT`x-aSI1+"{%t ~WGxbE[Ƙ\Ji" .y%H!I$HRc ]qt8=lߍG_~IcaD6PEJ =F@:hf)Ppb sZbc87nhZZ;s浣^^Z^ZZZ{<š!w][.ES(<T{3VC$ ˺br8f = =͒m aδNo;Q?\'K\{@6Nϯ~߲c]7oTvQg^Ƌ3O.z ͌XψeQG@@ 9PW8K4 V*Կu%q ct(y'CN|˳h-!kFЇ>B:/O˭L 6Q+˭w#JOOϝww6ٕ vU_t{Vpv ʤ` ̴ B*͎%c$-H~5V :-/$%LOǣ_Ť$0Kn\uBZgz $k+9Jc:5: zɵfKVRt6_૩lrݻwZ/}K/7/Ϝ?wjs=###O޲e C;]\:TXApqjbߐjܱw1eP^1DD;JyQ.[Y:XO6Wcr<ɢt^\l==}>rgP/.( 2Σ3@bwƜhg/hf-ͽta_bR*ey!$6iTgزMjVu1`47#WhvynavafWV`z /33+7*ᕅ ò?!8)LCs1m7Bͩљ1s ҕEQ"bExvXW:_{/Ii=;w<|Yđ^H||CR{^Jc|zJ!~{Q =y55'ÊXD&A|fff,LeQ?o>u$O$x#4ylҐV)^x'/{/%Ji/fsu?ə]zb,UQ|OhݾD U/Z[[SO `9V5 1z2a~}n(K\c&Zg%f`f)֭[O8?eYK.xLMM}}߳>[͛z^` F@SQ* esq(Tźq{>=}I4@`Ѷɐʭ" Ws&`LwFFFD؉>9hQ ֜k-pv `VR51SB#cl&- ,~e']v`O@_É2D]3AhGU$8o߼yҒg%؛ꞿ|3g&;sS=)@~&:/}އVWWw_;'.8qY8  }xއތ>Q`hu s2֪TTҗJ+ϑZ:9[gu~;8K,K[_Ow7ݏ{ﹿ^xRʭ[޳#}䕗Kǫ{Uj\7ua~MMus6[l0Fs9]3V럖Vr뭷~'~;n^L4;;_G{}ޏ޴s9;3T{ƹœ'֘bSڦ i QMC-7%ϒ46 .sܲRy; _Y^s0綏|ys[/:0v}Gx]oiqF$#}-`7o?qVQ*cK97mʷިj]}>w*L<`Di胴F ,xkTs VsZ;kus 6~,k"Sqգ7^jQc]ˇ7NNNoٹi4䷟|`bbyE)+\@yFRi Qȍ] +Qh>[eFqgu< 6#`Ĝ.QB *uDiHeȣb?3g *V9mQqnXtﱱ⟷Yܳpk2ń)zL8m\$ttKә"6I-fzjp\0Hgٿ}[g:&̕\:&sT'EEJD>333338B ҉-mr\l⦆l,Y#g,wER'ׄ7AA['q:H*@ [鵣kJfbBfs1Db&B(( fB0@1d&JBJMʳFY+RJ)*+/+#$X61):?#PZW"՜*, ZBqv/`a@H$m(g(ՎF\ZXshu;7/T]z-ܽu'{&:pZb>o9m$zpͮNuwLNN>߾}ۭݶm۶˲4q noߝxSi7\7 f$N{n[~?j BhkAfb $jX[%`ۍ)dveV0(]mn}0\gϷ5pu7BcfzS·9 vݞ=w}ӡ[Fנ?C5p,Hx}J9!1| +PyOKmm 8?ppJlZ59c> 5?E蓟b2@ Rr%> hߢwmt+HmHe6?"1̭N&co\ngU ˧O嶛əS}ۦ4YfQ*ubkWAƢmiE{<xD%R2|Va ͪ[< re Z&C 7E]U-@dBI^ydlMh׀0_\8m0S8tQ:_sh7]!ӷ]WH'PlY?I58 )<01. a̓-痋 ^7dWsĩWtJk>vZfZ @b Xܘq1J]w ~AC"4iXD(L7] SU&tFeeY "37X!T\keGSk IeΜzxG^8$MjI.OчZIQ0B&V0pmf]֦q |6<F@%bd⨻C`"e~GxDa!ZT61ϡوvaw\\xsqM 봛~Fa>`ؿ97^YDؕo%2p[6ٵ$(p`+H(Nk ޡr|ũQѺckkß}*xc0Q[g.,ļsSܾ[ h;NYeY^wQSNY4u F#> 2:Q@yF<"+f"6(}6x V˲t/ҊR&̉Gd2}XJ>la1 Z6e( PǴۘ~EF3qM(Cf㮮HJFYu8-Dt U6QeZk"3+&0hBHmMhE ikR+H󶞂paepes{[gzD/ϤEσu̥vo鞁*ڑC3m`_7\?Uә`ɲ?Ї>tǝwܹPjUU&߿3==EgwJ)G" `F@P~n2Aau?ʶ} pqhQ|SAliB ɑ{^Տ'/m߳ _&Df`~ν|&0˅X.Lmpg#v}ܲfq oZީv wT* liY\m][YY 6:2f& ƜX޸N?8m3oo>_wqqX&s* C:8SrHFwɻ#wz7k!uN"on>&t`'/|RiY`D,QH$NS31B&ⷛʲ`MB\!q+ve[", i&n>nL 6nau.t i2TB@+Sք(IJD.!"2RlIp攦t΃\P2!0u0k"FͷdV@B+@ dwnc^ӽ|./msW7OONuUʲֱo#_ݘW]6,x["2UVef?s=\U[Y#O-3G,i(Pxw0ͯ۵ٷ1=th ox߇/<ܰ'gU5tm%`e:Q? 0˨9YCr~:,A"s_@.$D"k$r0]M& KVWW͡5[Wg/s)o޿C{>/\mlW}Ļvo8y"3OLL+/<|P0BG]B2u,Yx@uyb+O8~%y= 4;~R^i,q_JN-51R QdmO6B)3Z1TYD,%*HH)9"2 FB0#)c#|WwѸ]-fΤ$E,2ו D/4 eI+7ЖJehIY:jM݃ b.О/uPp+K3[|aP n1eyP>ɇa" 3""9w_p^f[߸?r饿r6cɱf#}ˠkW_D{Hj,SllOnm?hLS0&!0s6zpT\JNu3 ٭guU)U|rcLSX]Yz~᷾*k#k9y)i!b!y6`UţP%I'ؽVGfJ5c7ϲyz (r',R 0(xk~;%+_Mh^vfhº}uV[ΘbE/bc)Ū }*##iQE͜Fl5oa ze(:BNfIiCG@ׄθnͤ V[yr3`4İ1)B'ms0oxyǮ]* nӯˀeQFFn~X?rW{O ЭU峧VO=oٻ.6ߚ]4{UkfwK ZVd<WE7t.qM%R}?x:G.*g2d_袢/vwu{o_q۩S4 IDATy^zW_y| Ɯ7lEM\^6ՍƼε|hBviGkV0ïC.Cd0}!zp`^ f&!D&*]2)m?WU B`eVL(4\,wRJU+&rR*ۯxAJdk&ܘoHMypڼe 0kÓBT1$d,+=wotdUcb^-֙ə^|cJ%ʵ7EkjMQ!LC筅fU)&$-5!5gf{/8L~p?X|U~&Qf~ۅG[TjnJpgn^ɩu46E5~[VZUL nY52aT&F ^nǸذ0&02F`~D`kf?KQV\TJ)*.+])*]5U~)6}Cw;w"WF[*),?!F#<ӣZꢷ\`ױGٽ"Z@4I1[ߧ㯼výKEg[od]jUi i_0:>7;yr*p842p1vB ӹ?daÓ^>pxn.dm$ ZߠkS-?|^'41||覭3ݱ] b@÷pO~ѠVZmN 7'2"gpvFAFB.eLZ"b7 EK=Ç1{-2̢#$DGvп o]Y7~duɪh Ξ-2v9F;iG,Ǹ J:}&p?pԞzNYpuR¨)!lUTt7~#Фi0K! ˲430flY 2sQ (&.y 6d)hUJX$J); NzGBi{.@33h-]`(ֈB6 ۠F,C++UuXV5]1w<Mv&Oxi2G7T ^:+b+up~'JC}|ZS?-*۶m,KrcvCˆpm!r" 6:eN9-sP{ ,7~Fr'^d%Y$l1#g!BDMo,TUiT<˲d8C`"u{E10ZL|KNQ,B D)*+!B% Td`i#uH#JJke,LR(҈]O&^#IfX D6 43f@@@f(4,(ȔR&pZ1ͣOЖSOsفRg,٭ ,ڿŜ>7ˌ <(l\gZSfYbbiZ+@7pldpqt4ظXپ}dY\e)6[o?bZ&p7#}ӿc\̋}9mӊ= 6Okt8b9?p_4F;O%c11ן?3Ew_e⩩8zw~đC%^;ӻs*v\\ȎV j43i`Aezqx>y$Z-;FۮisuLC)+,8D;4T*=/huP(Mee|TjҺٕ爙ȬѶ~=3O<IqM{UFo溗/ `~P5,mW+caM6)B1>~l-!BHC:6{>ocI))o XnEë_O8&o|y\ !ZmkrvD D`Ö:)᥎6wIg\pp;LmEV* . 733ٝ_oH o>pT=Sϟ-/榛j_t~@.bR }|F y F%!hg\2hJ?#w}KKK޳gj~az"ȧ{9+p8|&9ςw7|^|Qa ]`aop G,rG%-cf(b4#/Dچs}>\s*>7LY hm`4!P)UC&& P 3.MʫvȬF)fs!gk\@3K L>Z\Y X{نHDɱZ܁], MЦZRm{M ͫāVLh69<4%4VcItkҙ ]{ kUD/Rv $cƲc陙(L`0!m-ծB`ΥF4wf;/7ά @vk"֮=\EAMjv-T^Rtj2`2ׂZ }!J /3Y?m3+޶g޾g`W牧u  WbZVEQeU&6o߹eέnhGkA;5Rq4'pؽ Dwl\2; R!N|LOv@6;!'F7Cen1yS?FL>GA(jfmx֫6"Aj w(tk֐ґsUJ'!6RrCbǧuzfֿ9 ELlL7ۢi>/$|+]1ʹܣ|KsZs6նmXU; k҈,r/;k0Wy&p{k{ 0~7~o=ZlK p#+/| ooȥh\Kz>0\fqEJ;dlɅ)X >30&x8ٟɦM~GTk| u@qa ,$ؽ{c=O.(0(jM1[7ϊ)7A֛Ӫ6jqc:X蘾cغI5 ׋<&ē"*h)!_wH&|yWb"`k@f. BDMBv-NG)kc6'TU̙9Wk)V <P6"TU)E$Vu B*%fΓLBRHlu&"ㄶ $iRZSM#h)f1:MOD5;2Ԛqr`871.f~F]oTZ)% #e# :!-4jZw߾Ne9111Fy2tU*H#A\_^x" ]HoJV *IXyG-X` w=畫QS{Y|l\U@O&=DfC&] -͓Mui| Кl.[LS) ; &5zԠ3~'e=%fw8:(bUY)4UJ֚DUbVa嶛49%ku'pbk)߮?ނNEP}+JW)vm"d҅5<(=bc0ڊD,1N8uϕ/j%4Ж[ [MC@j` ^;d3R p;!K*w+,h=[2Eu魕JmwSDn  xEo%GCډ#OtzSҟKfɃCfMD7xS{WЃXVE}f0P-Ȉp=7AΩ^\K6ZSjP[o"%V+4n"% qSe8Lൻ=[_E;Xj(;-`EjpcZ:CIF-f("Z[+[g`7I(6~=9ʋK?Ƒը p~~|s?bo*Iyc˧_}AؠWk,!2Kl"m-FN/F$!=q{S e~cؾ}._ ^@-[lݺرc_W?S=D[o3FyJr`4E[b>NA, plr.a d >x~2˯b7-E%koo11"v2AiD51,R(,KfBUbMgJ53v: 8,<ٞTyUD |<3d R&;DD̘:eUU<&f>,ٶXQ33N`D`I@!2+6 !i!i̜LkedƢf!PkFi3kl. eJ2^Y17٪vhM'.,^`5T2N܀SF>zMitܰJY][X\IMn ߹ufx-^uriiypԚhCFF\hmXeH&JEbɴ-Ҫ(`@WKkRiX/.O"`R+ϼ*g7Rb#plZ~MV9P뽌t : k35G/W">-KKns}_Pi*VҕJ"VJ+UAbMd23}UO?eY=|Nj%`;\Wh\Y~iVjM(~1hsd[31F-|HJ!,:Ht}o P R=o_]]}n-[,//}se(9555555;;/g V45Ɣ&sD[jCS4#MOJvb@ۼEG"n}ÇsA KTp5͋lOeFYP3V`r(Ps&װ⋈D,U.CD'B"3ID ZDk[CQњLibį x1=nZk-$"f5.3uYYG0??̇*(Ȥt/T nfJ+̕b 5v`5iMeȹHhx5S!%y[~QG*ֺe7޼O>=kRK]o޺\ V1@Ny{ͧGAKSB*:F?̎ZS#nǠ-F{͊G7N[P̛LovaL̼KaKS#-fy\ 2p=[FSK+јTdg;Ir47 Elt pq~nA! ؜B =Q{YcL`|/>Z >q׾7r$m͖!d#vܴV+m0ь-CS=3qe^!Vt1%A &Q ƼiHQuaMaLߵx5T!G@ ˯ {kkx?iݬ6 0 GAb4݉q*L(y[@fOL(զrn3ê9.Juo]II7BL0bNUƚG6",q%vөo#nU pګɌ7}MqK3Y(~o#c>t>;Cu.nB[+Bc/7g/N~g)9ZSƢ`+ Jj|tMM;6hҹ? r:iO" !ɧ.qx\]&O=ΌI-C;7x՝o^K^{ ?? >1-,,~+>{ݻo֭[{Ԕzuu_x_}5dz47qh'Ҟlw텓&FIL<5{ >`^v7}w>{vu: .޽;Mjл~ŷw/rQpצS?qK!mSa̱#+=rTbsCq@`n׌AI50-^b9<EV5M4￉[~Q:m٤и& ?F~[QجU9%%xUmw5i>@k2N;0fD@̬́HA칓޵cR*@ddjoNc^\M;gϜ;HH&)` %WbSK=7hRj6kԦ2=9 ??D2p099)p1(MwZ[aau#3j&y`b8֯A׎М mMCmܞdX_{a͍5,~"ЕOq39l.|#1 XD UbξQ|].2_=ޫ]`WiW|dr5D3zkc%v7 :%WZD7WW__|innnǎ333JU++ gN^'p1O*\KEQbv۟$>#O^YY)j_{Z]0\JpYCsL #.69Q AʼJFs*ȈyQԚFyYf;d#f]&& Rإhk|*XkNxQIk{RY\0 Y@yror_I)ٜ$ RJIDBkC Zw\UZk ÈƏ ơ-4+4WLTþfl~~DKyfE,==O a \޺g%LM,@ih3fE \7Z^[ c>sڶm[{… ֭[7o<11qŻ ̖ݽZx;uX)3ix3K/Y٥_z`hW b(4_ԉȒoNGmF-&GR3wpu SmdgR >kuiڳ`ZQ)qGrxɸ}tmaMt4o|ݻy{v&&s"$na+3:7Җeu˴ҐG>{'`G-MK pl\+j7YW22j˲t:͛=qǕ˗lzk6mEQhdd,weT<ǼBAZZ99<m=5j Iڦh?fETVyYg\5j'ޒT|c:3*|ѵ%m㥡UMqDʨN٣)k䮩*4kMPԯ`F*t9@mcj|Hl!?V?9xF86BsmS'0q05F ?|;vx'N=w7z(,W\ #=ϝrew(6VCqAi1tFK`ͦJ byE "HtO c,k_} R>>z[o';=__LD}1t~ EĠ'd1ud6:׻mc ;|l9"ώ&B~إsNcڎ5/Z1f1W`r..fwT ?OK.B|l #ac `N}bbDMF5QoṆҐQ.5rfЯmk~ЇV9}mCp3 @ykb`X#yFsd*Zi->y+bv0w年^0{w*%zSR)@P%F) P֧e@R\\8aYdQB!Mz72.th_(,ʢܽ-KR B.6 z,K6#jWޢͦoV"BHI.͞ M\.9w)*D#Cæ=*"LxYO7#hS'>\_*Gb _U}n^C;5Fn0VM:^ʲZRC/>@v?g]&1tH8U,1Ƥ[*9b@00*w1~Mlz_ٓ / V[.W > S6lJ׷x-Qɳ9XHO8jX%L{+V~7M7KP%0UN``@Dw(gFzj@"aP/6ܝ]PNɻ$~4Uk`5wK/`ˢkusvm E}lJ$s۫~vI::[œ*lqQTUP`ʛ[?ׄ4O:<26`HF+sLu+k-Xa6ѦAQ*r fu69Zr;ޒxXwtYf2:y<6ޯrg_ 7INXu@H6|sneeҺ$Ѡ*VU(zw7'dq,YhT~ 0[-{^ X5%Óf.z|4͢h!hE[ϋ@RJI@ȲT<˲$QW I/,Rjʚm:vA)eT) HJ=h"!deYb̅ I!"JY@7eYvF DTmY27 `7D+lz8vIR$Q Myq# <Ns;;-5$M7'cmfhvYk4\ctQ[o0=EzmvkɫT.zWN EHڑwb-"X!V["]MX떦|/Ή%x[>чxS  e%'%p@TrթG ORDPkBZu)YezOTI2k] ʔY eRF%2-ѡY $WCyb"6*X5#pdp0tI0*JA{9 #qc8^6a/ TrD@Ʉ. 7P!;kGci`{N,uLё{V並 )5J<'8G/j$+|x#͵ (rQ>SZ "UJ\G Y5$NqS"vJuj/@EY2{}Nj@JRKLBQ*6"l^T*ƈX#D{"*KoGI@jBvY64Q)d!<+}614,K $"ˍj _ XTRReAY*)r*%[JIՋYVo׼1h| z%9hP 7StLTh -|ihK:ܢúcD"{zSClM*D E%Ic0#X(^1ehl%o`T&VS \hZu<*dW)Lúz:#T:)4D*H8dԲǐkq1;KE tp3 SAD HXc`aU*n}!B)+%.k?$&)awh1C#NUiH9cx E|z~H5E%h"<Dh Rs> 0h 櫢 Y) D̅$ȴ`gܬ,n& KYdPJufBD Uf!LEJ)4F'!RYjh:7qr)dm"M{ `vjX C0uS? !#T.UqD c05h_TSZ"|*)w) [u\e%|d'0U,jyJU5;P(;܋nH aoot[2'<,)(i%vjdGp#b;BҦ wM4y$'+wtCPƿKn+v9^ȳC17͸it ʱV]_( a.GCke7jB`»<( P,*_6M%7z *XL:O=#ɍ8$\GPs:q |$8?Ώ4B5_@C$ M-ڸ~oP2F rZ>q Rz2J8)%[- D"?,A\m!P*L():3,St#ZZ$"V)n']jsBΓRB(ͶMŽcE$'HeKY+k_%cU v#ke:E6C)j G/zzk_yS6u2=-r?_c>_MouOLƅV^p { ̒M`q9U*)]t*N*uϕڌ0O*0Nc7Q(ّSw$4.uxH2zGmsݑ;Tg`hE`j>Uk)[^ ;&"VpQ MO@`b`pqHxiDo,HV^T:uu6=4.m[`!1ڎzl)(@jQK[A ?9lAxw" Pp:`Ms=-EkSPPuKJ`a@sMH0OAAuuyS:t]Iyv1@h&q;yx?1,QگL$)S}c9'٦3hy$zI*D(03/Q42QulW6! g\ v6LJtG('+@(,'%KRς֒.ݖe)hk'6ϳv[ʲ"k6{"RDH!Rnf->Y2),l+DsT,H04m,J)2QN`elB  "˖`,}"nBFH!e;s%|‹ka#,C TT|*@UH`p?ΝR-׼ 7eGT`"$ k cDRs ڴhd8-bpRDPBB';:as# "Lb;.QC>À 7ՎȨ ! o-(,z&32Pv$j;KT $luͭ"Fƹ<ŴhZG$FjtmiVВhf +MI[כJhb4$Y , +c*kÚcyA390.j*&te "li<3Dɾ XY 1y\`vF;#&~4*B=&S) Tm~M.tj8nhL!nk:f<^ pWjwTQOi6ÕUdUT W~VLn_qB> A1Z'Cj(LU%=*FT!zx>T!>n=?(dϋj[HE,#hd6R'0kvLj=JY_SJToϯuqw/ A 5a4,ح;S6=H^ܡ&RO5y߄=&hL_LoLARq̧X[_{>_aKE|Q=jmM]U{͚l ;䃅`X}a!b"_T^H[uѫ'_ dT F0a)FW1!"pX)q<% NVGcez- `Y/`8iRoÎXGuWtW!wU҈g06`F@~%RWcR_ ~@T۞@!܏A˿#R-o%VWLL &DFfvOb(+i-<gW}mq-U*[d2›ā"c`ZZ`]hwƁϬ]dL8Խ7`p~[uM5:?*J'"["A\?91(Yha3Mcà4L*۶l`}3b3;.t!J6 EQl$2:Yҟ ^Ȃ}=fpx؆k4[r:ZD39vJ*FQ)y`t@:rbɽd&y2U* TF{\yj Dj4֐ּJ@ lZC̳hu tpբ@%e- K H \ADD P$-X 2D y'xx&'&^0\(zեHrUۡL䰌sQkKWe|CF= Xt.Ji61lUa_PZܹ:%t&zkq'=h=j>8I $&q{J>0:6?%0m\g@#j[W6 TYڊyZbc{㞱+f #[OLM3_UbZse L#xp 菥!$:'FA3y;=0O*%Dfgئ `)zpyFW#hTclN8:<u/nѰW~zـz(yṭ5t,iF&EܾSCᤲ-Z.MdJաMQn?I.=GfoNB8enfTRʒ$E ш,"j {H xRЧ@˲DJu`ɆIWePR|2/`I/B/gY~O+f:˲]fYМ `@DD@vs;͂{<#I|lR,l/:Әlt#`ATI!|&S [F-2Xw{\9~7{Na€gkn\0CUWEFhxĺ2J!c05S0ïV9=g5LFul GU/]rƈyX!{hAÙiDzt $ οȓyGWe$ktUN~+O9`9TU 0a#^^qC57љ+ΥR-rE*9Gȕ ( )eK|Hl4DfRAhYf,'"Y T&nY* PJY&\s z#`S *HRJD _$E$B{^%j',VJ@J.#( ׁuB9vZ)UjTTR("ƝNu=~c>ë6: wWeJvY r(Ta4Fnc6`]TYbLJ}vFҩrL?'{4'U\=&lB_Xg(1!k|Dn1eb+7Ȩ]sxMx{u%Kk4 (CkF  {&;uάUJ*!C&5]` ,t)~^_$tY"{YϲLէn!$^F81TJH[ƶ^Qԥ!9\㾤zQ+{zZ+'&;8% lxO)o9L\!rL+VJBCƼCw>" [Ù&ACMXOk,w8%i)"Nذ)E7q3"G&c\6жK9u2Ǝe yRQŦTCeȖuXX)Ĩoq(s族CtTeдv|Z1P1gZC].rߏxYM^PȾ~R}\ / 9_[Hz։%̮:!j~K7Up+R{300@U1B %X7Z@"b$*3R:S:b#F @*1Eɾkfy[{YnZC`gAZ-DTH*v~Ooi&H\+]y"e]h!@WӤ:0"mD5( (mݛl{Д TzS ǩjT$eƬ7P)OoVP){3!*,JCw|3u[1fwvb3nBKY,ZҐ>EdNǰPE[!L[*dclAni Z@ .rx^cVOk6FYx<"A|CTToG>G d_CNIg_̡ǐڥEl~M׫DE؆!dCP;^KaHxy+~\a2%~z*gcS)~ rA ػq"~5"Kp q25p˷.1'x̚@$-E.x7a7L)F/tHq7ׅTWo#Ӥ!ԚcCoP()P/M8XVr-zsC ce.?Bh:IyQ E03Af QC4c5M;H)2{K4lU;kP!ڧUJ D\e"3T% !^HX$Y ZB(U &)*O3sDr!]g>YJN3Lb⇺4¹7/JWTZúؚ??(h1r8F/c\bsS+K$b͍|FuMZ64҇f5WM ɵZ~(qjm%DAWM0 Ήo)NK%F0(rzbWǰЈl30_OXD $Щ!XkgR28<"^3,h}D]g6 $Hdӆ,KR7}ᰆ YBwyFVi`d$j|3 p fn>ŀ 4GNL=F'FgU==)]Rmq],>xD 2 nS'eS: Vn]*+l-狘l'q%XW]'"BԤ*2~`C Rdi@Dt^3{Ÿ},Z8Ɏ,Q$HB=nyT'M @:'Bdz}s#H{"̝g$)Y癔(JDl6{vK([m lZCټ#et==.l$o %@P !L"ҧX缧IHJ-NdIdԍB*D`4ۆQEl!@IDYEej$Be:ڜ>-%eY@)$@n4BwI;w߱1^\//[6 IDATV_Z_UkXf`ek.Xvpe`ʵ}C쿾kCVZf`kx}VYjy+X=g9W/a?~oW>q9wYH >Nl6}ign0zۼﲁw'R W@jt_t*vmm\47<3?v"5:3sC@ϙ6K bm{wY]fuE##>oq~HQBz&.stV"Uw0]@a!hO=O{.c(Cz"2ywt7'SX];'|XUC|ܾ;z~]/}A|shȓwwxsDI.Do3->>aĆNJq,ܬm4`X$"z)8Z4$ݨ)[YL3F8gx7N63sD`t)MNT02azgκ|mhbճwEXcB+‰7jEN%\7ĆC¿Ny1|[vB*LlӃ=3oÍvI*5eǰ$׿d9lwdzvbS~/!&ƌ/N*"DCQŐ1ZIeRȲREMdք[!|+ʲTh2!R)3'hԺ`7&sQmV;JRjU{:ݟUY]]&iI׫zmYFc]f~L?l 8'M訽_Zz]J) "EJIҫ{+$9`J)ETg(dʒIJJ$HIRB.Z3|m}moI\VuPJݍ(#)O\/?ρ:P˺'Q{|7]Y￾_CƄ`g>4T>>e}l= Mp5c}7%tUQ%4{gTSu@ ]P6:H("UJޥHikD w=CB^߻_=w0]| 7x6*4̌Oeo`D!:l٪髊\BUeZV*azH@||O>3 x1L1G//Yuw;1n;q<-{J%/{="C \U捎 ˭Jya ƴB-'7? .K0iAS#1F5"֑tED8uX'z k6!^o2D+ںj\j@q"(+ "4n[zQϾjm ^P񽼿n-%սWWQЙ<_UiZ71zrb8?~?+BG<2UV {̸B-ɣq&V!=+e-ʅyLNQOo Bzb|TZ;nZMXn$8kk䦏\w͋q[P0ݧwm m _+}rdf8y/5yrIb [vݲ]G$σt`#|m2BGq MARM{ǗBrIygcu&ͨ-=-QoJJ& `X:.`\bw_ѦCA"QSw Ƅ]@B L;͂~E!ɴc(UHBJ'>(~=U?O"[ݰ䲨ƈΖ,YӈLď!Xqu@r!*&!<dz0wFj4k_?.j']1e㽌}?^_ҵSTEW:WX~_ ;LJ`q.Cc vcx+]d[7zM4&L-zF%XV v*ZTuNI89wj)TSY~ԽhlI<ǍIޑd"0V}mS c @p[x ۏN_'??hU ]zཁ7V=pi;Zn:T'9z&ʷRҜ bd1w35.#/ST!%̞dㆽ?PaS_hm(6L5[P?mzVar†ɥ3nQ`ۃ[Doa=gqD !f/#Als??Lp J }2}s7Aof\uyfjI`lP;'q_BLDju"kAsbJvXjaQBgH́Dxfʾc~@3J [w/2m8 }3JQRnjYg*Qۦ Ӂ]| ˃M\\CRG²aypQU68T8Ŏ•\@(*[Tu#d?eF7E`Hs[,$a4W]Ȝ%kZ7ԖB߮s?J@iik񉮑=,$Sx~;2WRag >HYo"`p3%. 6KlhKIY oTy3f@V\vkUm_3 (Z,>:=i'UQ[I㋗V5e*hCvF?JsxbuSuS#R""R\>2rz9uxRtJ~LS%|Rn>@Ē`$s߭#9Q)9;}/2g"Jj75j8r҂/$%*%pl9e+9g)jo{Q/]8Cc!KidoPJP| L:˝i|97)lR8Δ `4Y2;|=? :egmY_X=v Wy޽7#mQ+QK'hQ6~H_$>8nrP6y8>Ql/mg x,U뎮)-5} `at9~Ѯ_2jN/V}ǧ|z ȢzY |:$>%Ch; پ#]>_a[Ԋ-%g3)QIĄጬOY݆Q[\]>͞ /a~˟Oh$H) qGc1m<9yH|]w |m&vI_LH diQp sio0[hK!uUfֳ QZOD[:gO-b;I5y܆Mԙ^ \}H^㶏`Mw>{I2(>9Ѧ|Ȍbg&U5Tٔ1]__:8ʌKe;l6l&-fzz&j8b,c4фGqsd2 mK . U8s2S'B:U%07Ւ521Ґ9=Imc}ȶ2cjbZ ,Weu5 f}eϥ$Cp3D Ժ]m>ubk,wc}JlE`Ybj4L { rGv,JQ!xf7*d FϾ|ގ砢bU?ov(\u"oxܜ3F+w}r[)QYod ;闿0+7p+e'`q f.K:2)vh bRN;`V'J ɃѾYWS/#-^=4G3oى)6rK[~ :X[Qi^W?o/S$o?0e;B IM lDA1Kmx4GD^]틗 tu7*a^ƣV$Pa N _ڙ/8nWPx -?/j[*'op@g:?I{FΪ13uTd{õZoœQU+3Ujz5;Rikݪ2aG,-7Fbqf2BQUy,]+V?\-2 H8'竒DGO19Ḩd<1Ym9fA Ԛ&o>vD6n$i^eܼT1y'<hh\L^Z~;3>a:I@=B&xqP2\R*@@VOJ,{\%&FW $RkޑZnx]ӼIEzr ci;)D*݂<u!yjwuYgG(QW膣pYPl**`5KxIxG!D OP* AgԲ2'ի;:0MT=Ĕ1@_Dd Gb~~lE `{Pfc6wQLr9QYMumY -Ϩ7y̖d%~AXwNYۯRMOA\_=5*Njd֫0gbYU2 Պ qD-ߘvRC5F6;fR3f1ial/vODa?;2ZTx!C`@ Ƚ5( _B/ d7,a9Ap]9r$kТ떧a˝Ny\-8XcWJ"@Hy&L=`(׹1Aѣ(cGhh73&2_?rHv4 Cf|T+\1yք>]l2fv$`yAE#C]6pيOBfQ=6xRw>D*2ٿfkD YUYEmFPYIŀ'$o{z=c`O s>ʑA >"o-^YKlOQv lێ[}wh.@5|oyO3I7#vTASP&BF\|xywd )iZ`@PБgsUDZy/Na7(~+虊<\4X~}@r|£TgpA0*? 6 ]`51U0Zs|S]u.KA2{ֹy7_T {{.MڡvF<{G ƭEZmr fɆ=!Fisu|q&tYOvqՏsRC>%I 2^U.=OKLKNq<@cP0>:tFlZN8 l-XAzazSEF˴+%T}[cぃozoO[2[/ ګc=7&.WqUq`$adRe^V(iR%LDo>݅zL8qc& ->]+fd.om }WI^6-6') =P^ADxZ'*W)JrT˭c(]c2=ugYec$PsiG?kWZϻ2;MNJX)~9U S6{Lhx8+ExΘD}nHMڽ'Ij:~6b:(J}ܶڀvN,FLw]A^_ۗʼn"6vunp?P+G5ZrgQ%lqZKYF _.'i{̻7( &SGv tRygT0y`M12@\n{/)@lIisŊ-3HJdi+>LxIE Gzs_NF}~4t ? r s4nOn/!'!&z~-bxL2V b9#E#moMv l ߳_mNM?M _D}X +x,,,o<7\/~ێ57zlCp?2[Ɔ 9#$L駘է!~|uQs8n[n&ApKC8aV˘T%[ eo:vU9aW}UP)fb!۵M~gǴ }ئ#yGE9gG)xPxXFD?Vp]wŠ_Ch ˤ]-A~r.u-_1)B*t T:!'!K_>rUPnsjy&G_NgG}ȠP@F9@pp+t#a ޽ ݈I4n;3En%[[iLUsTYrl&>!9.3:w>jTan#41=C2ݗWq1 C"Vi8-UT.d&C}Ɛ Hp8En,Gn0z/Ak?$U5_^m% ԫ] 峠gGV; dQlS2!>A32 +Mr~$Q)aǎdlX;*LVdRh\třR?TR1ì0ELXz]IЗťjus]~||g!#wZ]ބL$K7n)TW#`d\ih03KgU%?bi{Um]a(|0depyg=}n ë %e*T XGM҉2Dy.nN@zNͺ;Hj mbG Vk{7pl`SlPÞO˦C.۲zHDశ'pF.󛳟s|B2,/zB s7[kvf(h& +&[JU墦73HidL]\=^[ tLF@(nDsieQ^b/P>#_SAӽgCO3Uނ= > _`@ĥg:IJ:mThkzFu>܉/5F*72Ab7lnΌgsavf~zV ahƜbH_WNa/ĀA9S{dž+-ت(CI }Re-I:;0t@~ym.K818CvH1KbwBZg m} '{DUmy<PU?'дՄ,(7>?' 冽K!mck۾Y2|ᇳܾZ!`nAPPSq)0_G̕ oO_+-~+Z1i|Lj/x%^ O_*Xk9t 047v?t_s[ժ+ʤ(]K]B=l2MnWE B.uFLMUܞ7R7)D$֍}=P`3r|^rP2L/[R\&$ yy5Ihؗr+(*C\cf;=[PhMx𹐑WW`0w#Җ8 #kLvYj9/ 4=Ȼz&Wup5]!\Hk5|zvI_* D*pfm_ + K0k0Q'`!i|VTFkP7XjI/HÝC}]+ҥ%Mx'sI^T !@BHK澿ۼff7z%I ݴ[wQ_ӟ'6&ۺN1@"8r . 3c ђVD`F">`H CDXd?mXXەf[K;,4#W$+VM:[ϳ'b#Ry2ԸH}/|]Ļu[>A=D8IHG:,~Z*luZ=E[zB&aYTk(=3zJ:d1]xYJ}aog=msq@ bHx+3ԁc_ +HsO`ZS[nӄy(;UOnqx|#aKC YEɫo"@;u޴R^9KME \F)u"F>)i6HXɖ %`(f]H;Ƀ`'gC `u 7{ũɂ+oz0h-܆N11EF*H<*L[a3g7aﶫ$B<:謚qj^٧V3r6!\;Sez Ө =bZ֮ xnj n?u@7V֩V݅q|GĶk'2 r2-|%P+9&ş8"C:fq:wUI0 лhVKUd4mf/̜U*ݞu gC Xo;`*K]IdŮdB݄@l6:bR3CD1RUb&Y񢖪UISx#F ?1:}ETtč!!H\v<>y ` t 7!f%tG›1o@b%G<8ly\ߛI7Q/_YeQ@=D}Q09 ?ibz.#19_e]ߐ!D+V Y~Zs'Q^}~#nVQ6O5-Zt&mBUNךg-%eA.Z'ÚD;:Ej[_JqoGخơaN{8ldemh8юª-5,7R,]G<5&S \ ?Y`Sȅa>ܻ Yc_Z5:ӓ7[.z+{W3U/ l=C{a8ܲ$Rݜ0,CC+ ?:/cߴEc_|gRdJ{xԿAw`3GP@AaҳE*a;h9?Cǘ`A;Dy2)j; ~y"&y3=^d8gLǓ.ds9Nh/cEd!T)*yk5= MasbhE(CD(!!n>%ѥDU;ΝZ1DKso"kW@_X?zTwԪOv,ߎ}1pTPT)>4݆TI؎Dxx Z-d99{ӈUngęyA#(wC+si#޼J]wQ},=ׅdr({%/8MU_Y1ϏfK]E6!A uH 5xVB#vMN Y\J7U #TY }g)CǶeoE5dx$lYy^u s7,М_+2/̹Q+p[oOI$UӨxx}ո(1#1!+<?Y\tkDe,!3bOosU#Ti}ʹp~p;#';j -` Ͼ^"G{"8LqLLwA K KoT+yu7XV>K^~(d)z41^9|4a״2s﹥ [oҎPaFag5r(S6%Rz^㉪ͅRd|#=/f] ErY>*w3~.³5Q"<~;!ݴqWCܮVB(VvE8E¢)h Ȉ^i*?D1$Չwx)? dN/ (jQiOi'מҽ{Kz a' Jr1"`81UVH#T\d!$ i5=Z @REa^ٻؤ&ic'\ hs]~YH'Lܿj4!cwշo.;w T%'3V̕w'H"_lJ*`7ik;76- yJ[s&MLCۥzvm #ѢMlK\?&TƠ%u44AnWϓged>S#w&[ &Z*\׊ffN7r Yj`V[@ |%a"J>Э)Msd{ed*o"l^kC-,|#wաߒ^cAF0zi`v@WJĥ0֌Ԇ E l?n S1a?W-cy"m"]۟2/n>'1].e;_V;`(׵5^og414_;q4 L]:ާ-gM/ n%%%MiY-~u*;'=g]J*;53]oAFp6Q\_ƻ/Ȋ&  Ҽ#M?X)("~08GpQ=Zٚ sg;{ 8 Z9WQ E(|t+D|-i)BKֲϿZ}NyAdf(v r&h[;o?iK*&ZV+@Q)vң;CSq_~؅XdtzJ&MD@CKEKȉ$|sهDaQpQKbFA o'?Q#,%~ *݊\Xf!Gj7e V{]]SJ8D5Hjã=or߶Y4@\߫$ 'SU;J]|vBh1 $FF\g$?7T׷k(r7A"#B'd٪%v=e7`3eʳd:+ QH#jǝfmYJ 2=amkN@V>MgF޼Aºt^8/OS awˆL-yOk$KYfgL_x.h/WQj@t]<ڊP,@ !D%"祠BF~J%`'8ay60syPWxd$"4w9 9zC*ɘ>a!8By(O*Գ {9 O.%L&"2Vfsdl3h5 E"/[p.H!z @xUKb@'Z` xŅL(v B 9!q@p!L~uHtVlLT#((\c[ݰ!;rև@~uy،&3PemjI;8) UXrߛ=f3kN IFaE3pqBG)K]Z{Ih6ֱarUڨUnxa1UfǤrf`q Lԓ Bư4ެhJiES Ȳ=;帒c!2jC4z]zЄ.46wct] 14[1Ø>nXT춑-MŮrw+pI_HL8V`Čd\o GY-QՑq;F+qM`R@c8@R3wfL10ĐA1M}O9ɅΤi#Ȥ u`x*5<`c!;:lzO-q%t-^NBR5CXY)MOg^YbA3фA:s- 0ϕ@ Dsbt+bUX,0DqcEB4C.rHBp ($ [!dfByC9G$#l r\T|JayѭHȰT 5H2VVDA焈\n43zdH$ 9{0AP`#xm~9fDH R1o% 9P\p.$Ri%ݭMԔ1K+UzɈAňcFJd!:e?)@SjYhi*L.K[o"$C$J8ˍQƔ 9%!\JfQSY>zab*C5tPl>H#)fw9e5QgF Kl*Cffy)1}IgJ>4BJ`)ʯ7 ~WTXZ:>Inn[2m `r|J)3IMV.lgU{!a~$)$ j C4cG$HCNXcRnѨBg^-{++ĕ:\r۠Hnvɶ;KS&;+_s3vf'߄n6wXNc lO![! iꙌn)@^yO<#U !";^J{eͲ6ƌd/@d`r݊,2uU);Y\Ytơ"U(h:c(kIŹ\hF|P6i%F䯾n}Et1PRE>~tU^iBPkq/Md:Vg" /9G9}?sDr\.GH@UU-sA|2F,b!>%+m(~2ɖ)!@:Tr2S#. XK rc5QayH/7qO1!D Px> Eʭ9a.ь10Bx.IÛ>6Э.t Be OK0A>{ "RTूiZ˜TAA(y)  r!dgHI7#Ļ3FcmoD=Yԭ19 Yf,h`"H*@O ) #kV'-"s[t;{}ǏZ L1*2DM'hGU6}Ճ_.$fI#)Χ ?3D?j3 V hx"nL$Azu{n8 'pID1FOiIG: 2kumAȎ iwg vs& yGMrʔي\/\<))ɖv#%V"4KLI͘PC,JEŏE)(1X*u~`7(Lݩ:2#t)҅+A}ؼMvYfrKX+A}_֠7Nl2UX }#CRK߯ C[\p  "ya ΃J,.DRX*qi<ts.@ @!sx-{e-*Qw0N*N60{,D2Xijl&`?i;cd.VJ?LfU4 -˥G:pU&(x&B$jELgB3 W/)%E>S7P@RBuFfjVRZÈ0@0dj {Bs٥+DlR42O:lmFO2i\@qlJSMrW =d}'UʴLwiKt$c.Y,W"K+!6;w'֢sb-ŃƴsC;O HFCSLn)Jԗ0R&^cϚ5 .8<@C.H0Mw.a+bcc< R!,R),0rL"((B.qk@0/Y?Md%?QZ3sU,`.\o^ٴL)5So :@EFI5$q)lN.u %MR< @"jgv#YrPt[@+~%Dxw1|>M3sn*9Vj}wdhc1Dz0{,Le(5 cjT4$4c!r"w-ۧt?N7Iwkekv OXS~nkmݶںKLzn9{d ]YcJ~; ץp+"2%&-]WYҺ b̃;)ws*i`ͅ-gW̐ $J҅By0 $} .Wb ".V@Bh.|TAAQ'VDFfcAD&gfi]+>[yX_aTsCҺri]A"աhE.rP!V6 λ!(z"&HHcmR C1Asxsz.遬[jS |Ԧf5' )Po( )]ʏ&~ܮ.C#Zb gH >d#2E|BRI'yceĖ,+׋0:-u K;בJ' PƆ!RqԟFr6Bf<$!vآOXDzTQa4ӺW14 9QϘB:`Z+%u"5\_jH%Pturmg[.Z l(I=4cfmID&xީ' TRԦ$8FA2]i.笹Dލ1ڼhӦO9wm#ԯ?siӦϬHƧ>Xfӊ/޼[FZkG^bن͛V/zcxj6-ڂ!^e6o٬KH~vo]Y/}QmH=ojjޣݏjf{v⡉3ݼnuէ|_n;0ruSVݸe嬷 !?Woݲq͗c./gLΒ3M5o ?[Hh$e+br@aͶJ{ߕ {ۖE$랣?zmfKd@N' 7eɆ-۾!4\ٵ@5\[uϟ|bLS/>yA\^uO[ 5s۱<72O׼]UgmY/[טIK6n_5.x?j۶XE&'52^]#?_)js2~(Fj2%RUx&Z*H)IGDE#DIr0a @H|FD!xP*I3#W-<+IiJL猣O^d|fP9Ṷ(FLEP[uf#4ewd)~*:PQDŽ(D$k9G <xP yVExa"D $@f8bɤJ)9s Y%5EӰ0?~M3d@ 3h &2RtJ C۠L c mzHD^6ZP̽-cOw2Ȓ繜ϘHmw2GeǪMzu&*4MC*c&MR [z9(aZ(l672w[[e$J=kb[HJ{ÝQmiϬ9Q9)H4ٲ>g!HfMhPNJ-XI}5kX4,2\6M- ?Ub<፾so~1 T|ď?~K/?«dmǥpܝW\x_VD'i_xW?e0We^w[ؑǹꪺ~{^?%v(Sơ IDATxm?*> .:nC-t˄7/c❿nC˵<~wӅ~5wrSHkD|4S G2|Q̏.Ӽ&Voт]wƐ z>>;%oN?E6mχj#>?|djCkk&LnWs);=6ƌMxÎ"[d6>­5sNݹ=p넶8Yٗq!>ϒy4菧yӋE>m^?9 oxt d߸!03t6eSve4%1Ef&&'"g0rĨ6Hl U"C>bLN1BQh7M9y(5|1(<*f:9ENay^` #.dgbM|4&Sc]:=Ի˴=awKyeG A}}s=ZQ}㝏뺜hjœ&S?_}iwޏOAu]yoz>8-~Uo7{Au]y߯{>@0yڊ?oni{uD?Y[TVPSw66)Ova.𷟾xgO=6Ͼ<fցKhV Ī,'ωd Y%w׺k`#^߻ p=m` ?U^qt\έr~ Zs>g -qHt`}vQM Cι DUf?s䀿on }_XS]ow0 7?Rש$SMŴdBoZ1mqBu37bԳLQG~NC@aLf-3e'9Gi* *mr (1BP>d!z!\$\e| 9G:~3|4 HۦOM\ΐ`2@<3~Qĺt;Z>l1A -;nٴ̙J@^k_?z^uW>7$:}' מﻳ9g>(`Ϝt?}@4 2 ܁ԔrD($"J=gqݨc.e^ILY Xo70)ڥ3@?T*!2$RPdȄ@y(ȥ_IP$"f mO\’WEeKwN0a)4M}{@;/P!z'MbYP ǜu! % 10!7 O #"9g}y+HAqb@%!y$X6DDxaL(E䄂?F~V'M7GQtzپ' '-&oZ!+hg_Uhx8,e(+e+t.E2Jo%eph!bNI@aF~Cv](d|2ݕ$?>? J|hX Pی)z\MYP`}ٶZRǐ@'c;1*1/`LHͥrP/A.Ktg*JXR?Pc.J1~<2}{8FqLd@jHЅ*m5 &h00i똓hcҖ@~⭿9sEw9EU@1Y\red/6|-g)AoR)Uh&Ih8`(G0)Ӏ f'lBDQXFsZVY1z990Q4IV$8 I'qe lt+tƦ[M\M5i3N<[vjo M `ϐe-XgиկD* g+dЂ5rf%VjQ'e ev#fR c禮nhƏ[P\fxs%L%鈇O0!㪽!iv"}[ O ˧ hi2nz~Tuu}n|kȸ{뇽;˻|@֏G]Uߺ/zuuS޻`7^`CGOZ/jj ;^beCB1|۷ti<>^ GkwbeQy%~xkKPGW|zk>]]ߪW¼)72sd >HK \  @* 2*0O8cQ2T<q9ߍFwWIof`B,tF-_J)k0EYFV$d/7˩e6[Ni [LuK:4)b0Ol^^wX %F3l$U 3A"xViVJFkXNGL%iO b6Y|iD;Ak~MCASX 7ʂ:bMİkXM(lx5NzͲb^7Yߴ'~0OfZ'{4"jcJs Z)ٸMA}榡C4~}͂nua}gei_哷WfA5ҍ+~;GG_ k|k,|ݛ~ OUv1KyY/C^S+<ڰ4|ՃGиwñ[ԯJ߫)тW4 -]0 \${#oez"JW0|ePQX\4/] /5Bx%_tיo>oǯjbwͯwb7 {s\f,˞ +[@㖕Ӗ.T{}1S>yy4_W-+/ۡҪqk\ZJ y\ rBu@d1@ =DQpu"bK]Л'̽kmUVռzꁤIB A2X)BʅW(SqLY1T%8.qTpp(SyL0 H03̽3ywʏ^V?wfݫW^Ͻ{.CM3< [$ Szhzz'-N12k}ʓO\|) o?fWjN;nu?"( K+;Տ|Ŧ5Y*0& nw||w̓:@o4fWXr|!MZ\-tq?bq<==?M__?gΖeN/~w|'4A9c'D6|Gք} ScK{sw'Ekha\ЭżGLg8%C<5{z=O"ϩM>F9g(A{cbKD6dw]1bH{p'B,9{F=#Q\̼,frF~ $pa֔ͽ/b>dW! K:C漥6}RO+ {|x59GaRAc'*ܔ ,ѐs "+EH'8kڱh~ƤvMx;^'je!O,b E9 r$ @3jR>^E&=WkL2פa7"SPI3{pb3MKRWx/6AWC$}+*R$t~kaf$ũդ+nL4kU;zsZܧ(1IU΃W\"Uԋua'WspWg3IIDZShڿ&NME-`/z*}*MvRd,&J(x4LGȟ,H'ZO12SR vzmwŧ~KÒ^vVoPQ8DvQ t(V΃8ܟ0kNAg,3pzr:ϓ; ZOcDA4b0jbPf(˺p6NNxɵ++Ѫq IDATt ?k-de{1FBg& c&OhlT݆>RKNEUcr-я{?zVV\HƐQCE{JABܨf#c\'9]VE7E  cʖXfo!9u/ v_K?gwCײTJH YQ\8;TzT6]bF>a2yVA#419H,AHS  υSu姮zn>5g&u[Cy 'ēKKtև/,h°, r7)5a aeNxj;•N3UH͙\A]Td綠>ac|H>(0#Pf}&d@ܒgg)NnR}h RZTEvGE+RZj;rMp:z$xmTsG̍@;9=k3W~o}V$ujHRM„QP6S@NJSU'wT"CW/[KGn JQxtοp 4 鿺|X'_zr *9{֔g_ %b˃f3Xœ_ f,nn3i04c1v^fD4IcZ Z+{aIC{$Zd6uw*c"Ү`J:s4\nf/ @ڥnXjc/ua. 둍! k6 ֞VȹN@hO.LduH/:5XeDE!GPJyj)N6՛xބtdA}],Jq-=>TVN*<MVoza6jP94UE|kp ‰(w^sj!IR_֡K.æ:i$׊Lonxfkd3 D߲" fXIbFR:yJO"(nx2OdnLdf%h j3kW}Wv@UZ%"r.@ѿ:r%B~ uj 1D^‘xfk6(LK/c+X(5I9^/mu;<[k!~-Khl,KމC8eچh!lQ~eY!_CL@+ǻsb;_,#"0q?";`qW Z7wlS&V-Z_1+,E#yz^TY9u?蠛g?-|U%ڀR(]URZ|sd3.QMmQ !Q>Uk38ӊ:JhlMuiH'|mA-Jq{>ζK\uT΍߸OoH<{8ųr+x5RP\k(/*PX"Jk,T3]MQFFCE{v J|1Jz bgи8OtC([ 7@ll#?殹,*;6"R [p !I>|c+/𪤨+ R@&}U~vؖUj5‘4M:Gj&44M{Dགྷp4AYeYBv{ֲ3i9 ; `YGRy'!f/#K8o,M_^>MKH!CDdq. CLѦCZXw $W_\e,h0[ n#yLDTݒfȢ;ڋR6:،V( y n/k&+ tS/%U - V>ޒڂvsRNhCG֮JC0-(?_ɇBZC(%4~j-^FGNVPS6ъˆvGWg\]ľebcF7l@}E,~@iX٣<q`us]2XU5X <:\+Z6ίrd}=AuK)XaDUnRX14y;2]OhJA~r,LW M0H(4Dݹ܈lJկb=iHٟJEwHHt-+'¸@W2O$+O+,V%/r?xʉF" R8"98#B`@@ a@c,"-!!{]DE2uy'Qf"v#cB DD#h-4 n!rƮ K9]rRQ+1X 4# ]~_#-x|Z饅)KٕW=}E`tiw@rچ%+nt׊m:lX\v0_D"+s,&v/JUnwTIP ~=3(1JT[\*z  Cղ쨞H` 81/6TZ臧:fA@H0>z`8dcmڃ4`@ ۲- 3A-^7ڂ|ףk,ui}j]m{lA4'&P{7l 6Lvu2(QFQ EPẐ(TƾHEwv-LQP4& 64' ZnPrLo7k&LjejXd=OT5*pfҖݷ\H-AOb EvƼn_ ,ˌ;43.w{̧Oa`,?,97^;LbT,N8ٜ~na8xYLWhFN1Ar_R9)Be`q>u57s0K:6*&޹M}gV۱FuYw?PQ;)Öu+x^~aS,('jw= &LljZ$/,JyjLlpEګBԔ'51$!0$ioSXWֹZRQMܐ`K mSe^;QZt 6ԡ=R0;K@n*䷸)~6EM||S̢r\ +ޑX@.RP5zm*Wf|h'(Rq VbDI~8dVE욄-V#,{$ NjjIVݺ dC)F>E1(=5l'j_+f>O Ҙ\S8t2< ƞNcv@D~O9ONN.=pw'v#g{od"NӢkB@k# qrO4IF᯵#5a@qxޜѕGGY1H]#"`c$ҁ`̥t5=N yſa uR#!#V"zN \Kwt\ֲqMK[Vہ/hFoTb`M? hĭdTDj3U0eZBC\8]&3jTlɌ [yR`Ye]bMy3F؂ fZv,IgvU@<ͪ?΢q㟵аƃhv6#TPƔ?ˇ=Ma i*ң3P֖1H݀/CXJⅵR0H_f(^͐3VjJF;|]rVSIЊtHַT5ۺ+f *@!Q#=0}YU礷T>M7RpT_MBƛGt"4![9OӞ'|eGt]D~N"yaic qf&0 V9D9rC66_qU$)e8#Oι'[g%J9^[;֒5Mhy ''`O/O|Ww݃7݆vu/<_O=gO$݈0[nwZ ]{h[eoj0j*"ߘ).A.tEe$Ί}Φ dZ)9kT@Z$Rê _0^VȔ$-n+YnҌm,kwR M P%! & 2yźRɯŅ<~[l5yU{5[g$阔ٸѼps[Ȥɋb[Y-kdGq^oH@Hjz/J)m4j4Yxn`p_M+7׵W6M!u6o@~ hs7VL$6fJiiE~%MkZGFT#j!! KV P̄b$tlQ_LpTV9-BJ8*bHj! AhtP̵~5zZC%d\jJaT\nwe7iΡ4{cn _H ~֚a=8Bcvy056ƱsΦ;aBx @ ͘9Mavrx )ֆ<-G;>L+h@Bow>L"ߺc{(-@r :!\h3fzYLj"p.GƆrސRpl'䲨(ݡMJɿ Vֵ>O՟%vapJCtn*fB^(ۼz 7\1kWG{JWY<["lh`uooUr(C [ S8ZXnQ LZF*=(TSQGkXr:қP>|ZY}cb7Cno)gCtGDɽ plv1M][=@IeKI.5XV@g}-skA@T Ik O1G9yj5ix1o 8C)ɴPɠH d.8enz^35!GYH:$*~BԲ- =l/~_oPf!;K I!%Ng=Zuv^]Lɷ(OxW61l!*o "y@!J ¾_i '~oBLƄ:: {㇗8 4MfEceysאj " ®k @p )DV^\;O64ZXmb_\p{lK6.=B?Wo]c B9=T ꋚ4)[ӴDLXJŠ(#xM|-(VuK){ ^QPi%NrpǴ \zO;MzM{q 8WX=G{RP _^b ~Ϳu/ȋkY429@j(z-<ҕ:nbd!;z:{׫(n-_걪b4bBM3TrhaZ U,Vl* fiȆ`P.!d/*4k.R)߼fTLIShZEf6OzJdZ)YV.p@sݣ\Fw;J~5zQ<7R3dPiuZ~D6]4v'gggBõw3g@d83yh# f eYѠhC5pr0 =-H>)? NYkȧP9449Rk-B O#< "9!l?^mnq|<0,@`9)VGUjJ@ h0%c `,cG 0PqH1&/bY󷶖҃uc].<7)4F=/QWtcnw ZJjAí9@iY;auv .fiMuZe\ӺZ˟EY1N#C QiEyS6+)ʫ2Ud.P݀:$I= ͓-Z4&X rn0x,Qyk*MXZIwXv*9Ze +1.v33Ă @7ztԐIuPk jn5i[)\C6Kb]Z\&Ї$--vZ^SXTy :j^wP`הܲÖ>lOzYkgJJX`j[+|X{e_O.%SXJ  ?۟-"}yޓ?a`4irEMFAVaY24IZ!I˟mw!K5/ :#o˪q@>gy5se,^%ܕfBY9|L@Qfz#u.RBNi!LH,qQ͸BSlھZlxy|%Uu[ D#a͌I9j;Vj%g1OIH*m, +Q_gWbgY`E ;L_"L.RQ9䍱ƘpHn=(wvc~SPcv 6&;!A`̒O6  )!tΑٻŃ< ;&,>0,=ZZDB(G^Y9.8?X -y#AY+A}PG{ڵ>#>˯z6f_&0W.?7\|~hgK\F<塢ޡ^oa!nɍ"zwΉB tnT[Cn gPµr*DhF :P8=G,I6i7C&})P"pZu,旤'%)i!B;:YyW; @ڱsW_뮰qdɒtfXY"nNdYlé ]Xb]X;w 5_&)wU$g@.+i.Ԡ8*Om9?Ҷ+ Q.i\q&aZ $:Ⱥxm,5S⌺mHKx?ֲ (PV2 HdW:A6MIAtݰZz`q35XQ?*zN$ǰyD?@i'*QnP {Fq*SǍV|`W!m4O{4֒<õ'4O>[iΈ <yc,g<0{ɤ-9gT@Ds6tDD00# aa6,iO CRawXPkoZ2FV|ך!NE/ !2eG/* \>}yǞln\R%tp5dۚKOMBs$pbxUrզB"{J-q@HN@6LjqD@;(fѨ$f=RRߙv+`ZBɤ# BPiB oVW JBFLlȷvjYGB@x2M{ca0.ˌyއi v48v~-l%5-eY#A<;BD^'f apWS83KIED h8E0bq4iv N!|0ꅋ8wCI؋N\ݡt*-٤`fr1ΑTp%@}C=~6oϋP7MOD# åNE~ ZdD2]/ZS(R Iʓ:~u䳀Yu!Oe%WRݚvCm~"*_0շG|_ry^ i S%#$ OϺ$m[€UTNwnK񛍌vLZ D%9ds"W,9cp"ᙕ^U: *r<4IIg9w(Ϧ)[>Ѓ^dj05*`j,UEU`pA Z/U= ;l"GC@6RHd=b[Hd՚M+M(_{3 HZKsKs$T!#H6y%ܢi! 2#-=f4X _B g>pq8pޕ>,r潵ޫ3qY4ϳEß n<ψM43)_ !ZNe1PHk+!~ " ?ٱ#?1md~6P.Z͙Af_gQFT 6qSu%о䡊|RAv\g;ZXx 5k-58d:Nˊ@SYIry.64uH5y"^|\a[U*W3qֺCU#42Z|̾Nb-ʤeOh\m5dp7Jb\j_ &Gd*+1lq/c$Y|3 *B^d&4% [h|Tl "+;he~ݧRyfi`kQ|z$jK|##oɕ<{f{9A7$vz+PJ.nr兤!2E/EHݯҫQCg})Pu]3= qV5L zBIɄc5T26JQ%fTܗѣT nbs*qarEx3rҤxҒ*Z=yD ;89pvZP''ή@NSd7M5 8",ݝf #Rjֺe s؇`É|8Z" PZţӭce+G=""x lE> y.@s=1H`2] o~RoxÒ]tW:]_Ity:M7TYn0G҂.A:BR[9d K1aeU$tD4{k@ V -="OBnBuq0s:|Vo. mP7ۚPF\`BOUhk~s4NatP7eؕ0. [;1 GQĘ=4fbU1 )@T M_:w"Mm:}M4I#a&̲ W&?4Q(o̩Y=a:BIJP fBfZ l>&~vGI jb;FtSh.TSs[섐J&Bi꿬]Ij_BtTQpnj+csqOyKH%IΥ<%E@UHtRu@ 24R{@p1XKDg<1j y"@ 'O~?qO>mo&gkqxUBx2 gq9{+!B~V,8:Vd~`sהΗdX=>1cFGs-p?5bm[  q8>^*=O3oHQ(;7Za$0GzgXjR᤿ȧz@r8iX/ In=N(/V* Y:k97nӿ C>l>A#P#)QRiZc5!o鐵*X2TY~ g7/L b}>Ӎ;~GkcHd,Mʭ(]WƕTX;nmsCQVe&ٱ݀D O<һ\fMO0=,hʑ*B'& lr"a1t^!7Qc*;hAޓ~D6M9)h#UͅWL|t1iLWŪg#]|kU9"p\,8=̏1 Jsf}PzY"ʢ.*W WzAٮZa+ e x~Aܵ%&Raj$<1O 6O& ^[T1Gyr1q WPk~'* CQˁ[]T9&|{?V? LJ-y5[+E@YPM>ژHAژ۾J&G%)j:hDJ!hRiY6%4%nţLصhY&};SB]jCq gϓhjJl{-]DQ+$< *t2L^Jn#W[$0B˂BcG) &X ,!\I 1Ow 0ZԥNyK&B"C NS12$Oq_~%j- ~_1Kd$q<.ifB._D@*,e$j"{_=!"jHV5sw +D`d$V,QA}]  YxQ&h#JbTgI`9kX547mu1*u:o"'yjЂl;Aʱ7_#eԱ0$U=03ƎPdqgSX )vD~YFkC%8'&,f ^`9^DMH D[3eYLBB]^MZ9Qv .X*.,uJ]wiڡFӱמJn'}ȣْ41Ib 4ЏmH4-ZaP Z$R(u؆e'4_FX*< mYSڅlTA 4r .Q G wy༠;No3HȨGZ?HQH)E?ski]<`tEy*IF%4Qg!5ѳBO*"V!y=g/$D|UȧR)gfʬcm(ܠ9a1kk@fQkҲ 1{Sb-tѨ!7ˮi '@% +б~J\Im u Rz󖆆!x(sޟi{OZHg>{O4MDhhpZ'΋"3)Sa< 'WNT(LOC,2+y> ۉ,ޖg>tnDwC/ׁ66@\}B(y '8_ {X+qO-oXd+XTlHjCt:zs-k&tfHkuEwmj|*!{;àe֚2R[cWEu㙍?LHG@% V.w].*缰&4&ԹnU v'!g13(ψD$Naz_KYᎴ*Gm4Jp_ًHSBt*鬒LtT0W$Ų1_*=1[[&NzA+6XHV Ϊ"cQ*RH*BDiaUP*a^,jVai[ gSFD}V M}j!)򮴬P{nȵh ρq@IUїWume,4=(;ѭxX<cAFą 꿚vVi]jJoQh&-<ٟ/.HR" rR (7b] JƯY )ݸ3hCh C(~lOv19 #ow aK+m`AqZvݿDqG #R0 K8˪!́@րc29@lF4eE`J@k7=:7{3fn3hk-(ș&n+0!Wkzi6j 9ej aF!G0q\Z[`"_Uo[XzY!\(@% !ܰMDnz]BvetIy-wNNɔ>R#$.Jg-O$3m Wjp4AV/[[ Ow*F-xZF^T0ˡVQ$3 8-QūUWJ Gz{ƣ(K)Ok'%XD03#U1=-T-iVJY42$PzE~]Ó5R4f3CNE Gpcܒ^_6X:5")ªwkaoRjR]0ڂ'3GoV+nl)MLWT,zja2hة{Wr92i`0nL{AXLy34/3wr4M@ɻeЄ=hCq؅/2dt !UaBal5jxg~:g>q:M{:eIU(߇*Y[%oO`%`Kl- LMC Oz[ҟ\AH.;o)4K{EjoRՅNdH SؓboP[z]G#0$Ubk`ZSc-B99j-OVJ2%|7Ndh KCeǘ~'/#BV9>Ev-R9)2(+}}&Q2g)g25#$KҘ-ډ,-"z/@CE0=Ie<Lg J@jgH3LZ(3%%#3.-d(>z# }f][@QxG6 @#U$ VZV hSRw&u.wK Vq*0ZG}Y^#%)/<XLLbtS(.VOIAb@2h:3Ibꉐ<`-Z3 rGw㸋" "';5v{,6.i6D8/s.(Q:J5C8YYD4v)Wυ䐼0^"XOvx IDATw,B伏MbmQeVQhbi 7Z@2c^"V6?Tzxm{J)7럫}٢gӀ M;r[3לt#JmՃ^/p"*kєdMhyۨeXSf~n(P!}XiE1p׮odZJl٦H1D%䱥|QqqTwLal>u@TAjSruAwS6`er^^* uɽ QJtVvR GKuשy+X,O'Yyۤȝ~x߼w,dłوY:p4Z"&Ey-aO󌀞ǯH7{FT['T9(@oW~{_oo3G=_xFvWbuNfx]I\[jsSzA(bƻt5Zd }E6/nFbp}a8]w~;}o}R#O*.~@80c R;tKGbpf'P3*40I:G}Ko_K_z+y^/a0<W>gC'?5O]#<AE;qʐ9 V-f g-gOlPBi_J w}{> /yI}#7y3 R$(7ۣr=+趥AGS6k@|sZgAk=R 3 UI*["%o >[snG#kba#q S ų|gp9D5iD4:Uˊm0o..IPaR+.J![HDUU:X er%5T"Wz&O Ɉf[J+oXԋA㉼4Ea4d#~x4,LDh "L)0y{; "; `Є8 x_ NC#/ Aʏ0 ~Yx8Ƙe8ÿar2LnC::CKox <;aю;ьfq`01!F8ק7 5/a4/{g7<x ;nw?2huQ&{RN&&;[{oE7onWw ogOwW>%׭z 0M8@(E-g"{~mǝ.f]X@#*,Z6߷_ht_'7/?g5dtpVGJiSC}$ ﯅])ӥ1S+ J@/\x;Kw\ &yR/,ˏCя~oڿ~'Q(9wCV-E)6VYYNJ'HŐR/W) (ORճ\eŋ_7׼?ߤⓟ|ǟxrUW5\ԏFgKUN;t=#LT.,恳AD1X6JKɖ篾p8`'oMȟ by;[ceNOƻn;o;x</_^8͈ v~nwZ l^ጙA.N"[1 \pwՓҋIj-8ڂӲLWrrZ͓"~PRpBJ.v6ZW־զ0@h45ȶRl }OdF>r8DVDьn&Ǔ֚a02cNO/LDp܍<m;9cL ϋ pjm8ie%޻`C,,K;!|ip΅9~E!p ɯ$Z W,w-y28xt`|坷_sӳk5"3aݶ'<17۔Heq#iY{_w"䰮}~rٳ_=+Og9lD^R!4nCzrrK_q+x޺ܳgg^v2q׽r7ïz_+OTLE8 WHPjm{ԎhzcvK\3'/ڵܑ.{i( '/rUS|d~dBm;rK2{?3ϰ4YK-8rJsݡ> -SݮzLNy ZR)? YN؜eM"iE1,{𞈼=/ yGޓw;G΅^B8/_N 6_ VTz<(s w~M~kS{an-.?-q?oO{_Un{aE'`e[iY}gٌUeBc|~1,6eOzٷ?\-k}{_xr\&}=w='?u5`lgY>ɟ@Jl*!%t_K_|~{9e3?u>) ke~oۿg>sH,;mcת(!}E!d-rǗ}S/W(aZ)!6}ϯQNNNmoszz2s>G>z;n鞗7uiKsW`~/}x?+Γ|][- B,@Sh,jRRroջ+|Q-{#*S?y뇅N=<0M }ww}[e}3O>d3X-T2D:ƥ_<]ӆ+rZX@2+?Hjc_V( K3v^L 8 <০$itWkW5He0|CMoXn>!.~*P,]4ݜc޶<p[ZK^ FQ؄v,lɄ:#].+^񊧟~8c=sKBϨbqkՖu_a0j*L<t֮,6=cJj_kjiǝ.&Q <94Mlk1@hy ,ذ&9ԍ`iUeJ ޏv]~@ûe8f}ӲgD xV:ZjWY{F[rU{_7o› 0fRbEQl[-A?k}`(KVDy '޼tCwh7!egطP(T@}è4_Ð ,^L%@Ƥ$ Ie""wTA{jB a@R"rI)~Eif:RJ4%}:zd*j-SEapsuIe]^^~!׊Cw^>0zeXXeli>rwlNʥRQ ,YMgl֨GFbξVrgNC>4d`9uد,^yoҽ>Gix#wǧ&|L~zĮj҉3?=q+Knosb#I_^j!c֘i(/6@[Pѹ~k S";mVG]jU^400_~-vUi=;󏟻g+V-p#9cis5 'uE{ĊŶ!{f=iL]ʓl ,3M9{XI_Z6BNv Bz,l@6;YMoZ d"hPexL=7zKXkhD%>_=^׊׆^i3 )C5ԢVD \Cgiec[Cv ќ9Cw{_[Iv3,tFEQ񵋓;oK+"n|^Ttm5̒ dVp SҎ/01vokEȦ2 ֩{~i|{C޴y̧nL'gA$9vXq]^mY,j)Y/h'Y^mg9JrܭbQ=m"S9Mdofsf*ʞ=>i>[>@9+a-9+h+'zݱ{]Y62=b̕$2 HXJR͜9f$ f DĀHf2I"H (B0dySI[+]BИ檠e 8>+ӣ$IQٽ$"oV0} ̌ 4F+Iyl.\ٮ')ORS$SJSSHcs&"ɭ_)UZ'L͹2'd\m A IDATJ~EsF׼CWmZ;>m~:.xix^y͚Fe΂NF@nXZnͮf?oH ^,ˡB西uMIbRϿ~wx8>ks*s(/ ~ zi&aF)iffos鲥LdB3Vh6Q,xtt淚"ڌI7zI^2cF>epw?ltK.)ZE+3+'8Ct=M5`d̃( lj6Dheɺƹr͜rjdBl[EME Y8xƍ_$7=v`и3!fγdnCA B;2+'9kz Tef Z`miZsm Zof<} X- ,m3j;L."[ЈgdrL5 \8uԲž6EP!X "l%U :`,BRBRE TȘ 5HJ՞4Av jL[`"cr kc! P"hsZ*@KF Tf8@ Pae`mʁ, 60bk@I*(Z@PX|Y#at#-($hVH!@?%D'X*70."_&jlsydòllѮ8"\w&'鞅*nXҟ%-ڿ^^ۄx.Iylge: %Fv[/7| )gWϊm޺ۼyH, T->|S{_ uؾϬ7]}>2zVDɷ'1szV j;+lL즤=pqײmxc5I^4xg``ZcɓǞzqߺ-;zF&?0`9K-Owtś7,/|ޭF1?܇j_y< XȞ#N`֚r. MZ~Uk1_-| ~BsJs烲;p3Fa j٪88%YB!m*rjY`B@-ܑ剦BgQ % 2ACz \d o_sт76H"h#Lz Arҏy_)9@sꝝͣTvMJٳl{&lѷ+Uo[S>zjo$2͹Q98t0X or2G{\"f4{y^ֳ|_tw\4CKfW!qbt)O#IqDD@ )832Ib l6 @X Dڼ,I{7@L<-* !j,j>aX3,@ ^#R 6iӭ"hI  `B$#q. D&yzzPŜ1`ڔ&!R DR)%)O=qn٤ROeM<<ڥKK-YrCg것KM[6֯_~e I%V~8~g^j#4O:yc\X6?3q-߁~2~%02vPjP_mՎ#2.lǥ_~Eilb}[}᥾[G̢ysw7\n I*z,Ǔ'gǞ ===ZZkߏ˟?0qmԩ3_sV`3Jhv9|SW}LCa>E9z)?"S=rn]X}%2pK_}ǎˠ>=}Iݭv-UuDVֵ5H̛v}!XYt ? (Jc95:ʂGJsC:;\k^*כ{aɱ߼:wp5azz '0GO6ܖ[U03WԎ56<_Yl^K 6+)X? u(dޕN4s|])Q{:m*/Y>R+BI+%`PshgGĢf;8)/yLQ% [<{zߑd XDRo )P%)04!IXz8O T@4Ird, ”$  LeErKaXQ>HQ;payfmJIL@CˌY"e6 "2@٤*X:y&fAYٹYz[RW@e:SB]V, `0qe< F H$1)<HDFtz%-Lj1cA "%8ߨWe"QXy%]"Z~Ea]Pƹ:nK.W,)v7|_?ۋ6pT#]boOvWMiYk^Yo4'm7Km79%OsҊ?5ot={Åwmܸq˖-Agy6cъ[*hm[4?/|ݺu&.X<LypxU/c_{8tA~ZKe`g--X8n{ʨLh4ڎ޷ڠ2_skqQ7xgF,;|Hc T*)Kb1dI}f`e%GQE̹?37ްrRW:*L]͛vxϿTzc=SYOCO| 6J+4B*Ntߠ y5W};_v׋{gÙҪl DX/%f&S?ݐ i;30 Dl*3DcowV/i9mȪQ"=DRS}z OO^EeʛL@uu0o_$={=jtu6y|'Ren5:~EY.Jʌ.) ia6[* 2 xFv~gk'1l ,T )[a)}KrMS5[<,=KdG Vc㉓sJPcYr?;9*'H~jy<B Dԝ<5=I$ 4VR$ }S l/2ƘL&ل+gA.I}ڳ hITØ> _ªQ^9hURdg +Zy0*|]Huf+{"傫iĔ&2IDC.jL7j"ME4^X Ҵ٨6"MEH+IsT7EYRgbR>UInڱW:_1G{Yխz ,[hL,%έye/ܸ3MNj - v'y C) 5R)GQ .Zlv61N֢p ?&ʿ>rѭ7~GS]x>s.o-[LNN:uj*H/l@1`,\|ae|iΝ;;ę#ߙ~6 4cPLE\u t3MJjK zv(ёV59Zu_w7a(DEAXc+ qk6X)cG.pS__M el=Ʊtqy4x뭌-ɚ&Lєp*Xúc:晒vV.0dZF;/U@w[/|4]k;7|3g65O>2v?7zO87Fu4|\\-D}ճ(}V#(eR([ v;[ryE/l[@+3j]:Rq-yhȮ/;iZ'F_(g(rF꿲G"aoץ!Rve39"e0(;lNLfiHe04dQ)ġRy9m0}V GN4X~; Cn۠u$=JRO ~WZ!07Ƙr7aD0"B0 $" VU`@@ZUL0#FEkq,ґМ\%$$X 1(HU~V&1bfc, Cek$d.b%.\a!@aE1lŎ" a+^84SM|gfEI;ԖYZ#)JN4z03ȭ-Oҟ{{{7oۼm녜'1|[+t>rwu ͍UG}K>3Ffffn|wgie˗9oM%~kt* %TnJZkƗiQ,DVQO5? PoO/9e׮6Ac`MR.Dz9ɪe,Zip-L._6ɹ-ƿk3 WۣW,$`o/||lвѹm$dǦc翥>5SVOOL:uv4Q4@yZFTF xbIAWrIGlRKֱ&[ZltmrPgGg٦JٖzGlIm` y*D!q̓P'?'Lf}Toܤ4I,yZI3˦'ICHEwb$!n\{)ځ[J/ݭptIU*ݼc{C.;2'moM/$^[ӏ9qq!R/L3?dΪ [S~g-PkY?w!;K}$;ԁ0:wcOi!#Oޝޱe˖cǎMMM4gh!93Gّ87Lh:P,Zu;_8I D?Yڐ]mg%#1#J.!`!i3[dnXd4摁A^m[/^'N72Ӕ?ܡm][C$!)']ϝfxȧ'NUKc}RqVtvs~~ӓbBEȍLshљJUz7N;k4WIr A%^tC'C]gjϜ7zz{^̛+W_|g}ӵMk5|`4Uû=BATQĹ!@Pz飸{z0zv-jZ۶DĤ["yGՠlO$ٌSӓzMyޤ9Q>|Z9(.C^Z.쭒NGk"=D|cKh4lS_^n]aQDLAUL͕JUJ\huc0"FQ@ DTqՊ~&TY/{D  3G+IBX#"HDD'`EDf%mHfA;~n^Sf])a զbƤ1˥>*HѠ^;K"U/LxoIa%YPLq.#D@&$2dYaSHRHrPJI@$חIAˀ)뗴("6m1Ajsc%0kzDΓ^7JP$ΣûZ ֯{Ց02 YxЪ/~Sq'0fc-"JYtL*ɗ7HϞi.l=v pbyBÃ( ÍfrzL#gƖ,:|rC39(fcGzdiM5w:xdp풑9 Ğ]ͯv>(ߝG 2ibԽ߶.WNņ\];W`EZeX9iSoujMC܍ _h(@ybFǔsWgϿ,폧^ˢNptX061`AdD@  R2IbfIu'0"@ cq$"d 4WHÓ R! Bu铨;7\10@;~UY0E3Ƥ9mpk[/DQ$Rn<&$1u=aa%R" )ei^RCAA fA !X2O۽r#4sIRr.v|ҫ_1ns%Uaa/<뗛5,ogyLژy4Y[80W^Çgo8.`_;~S_[~9+bn_bK"VKϾg8+f_+ΟkFW~~xqo2Ko'<"MTdʙ1z#lP AIӶ~1Mz}_‹k]_{֯%(DY\BnH+Ϡ*珋ֵm]v% Uk1R IDATEZкs\CC}UYqtɢ!ۘطUrϞ%K/\'|a7 rdu|nZ/ o~=O :tzÆeYs4M 72ǎkųHx{hOP stlF5-k Do{m˘WP!Y既=Ȟ'dB\hd, N=4[nlAL悢v&mw6Nf`jHZxUHwў: ?WR;zzXt IxzK6Nݾi =ҡTNn arHOkWIc7-ꫨzזř嗋J:(8ҹ]ZC. ^| -T,~Ӵ3;;$kJ\F˚Pf;ҹ=82X(W܄pf s@D$MTJ$$NӄH"cn gY1ƤJWDD_-@dUHTm2Gq@D\NBȂ4(Ӛ $SRZ2`Af3dZ\EAf=@h&a .4J)i*x*Ҕ'8Lp ՁX\r.T;9  kR|=[zl+n_v"}vS'aH\W-_] `ljz/<P_};oSo?M}kVS3( |hO5C}˔ w0 i,ϯ6#i"i|/:@ſ|ˊK-R0!vds"4+~ԭ@lkYg*G-Y0R[+턋ΔR]He#M=(IB`$e$R2Rq ٤4I=@$՝Uӓ[Z0Ն25A"">I7 2WcBdOQQ&_Uf1ԗZl1WRi*k1"U'-AAu.#?$!3!Gu>&@J)8$:+#Ve%%}68opX#ͬ4Uv\JiL_!$;惕|V_qvi9VLlAuiGN Fұ>tXHޞw[/@\ԩ9=c6m۰u^l.ӡG:iޯ+ ӹlF{2h[k;d)sÍyr&X7//;ā/  G=gJs6@RT*6)*N#*)S'oYvrOf!l5cӣ(!/߻jĕѫDm4SbCDfۑTZ8JeQr!,2Sƅo= @ADtzl'nrU56Ljdg3zD>Ai-?~/©36Y|tho^YZf͊-KQ4mz>:! ߴ1*q*{(ʝ|,fAWPY<*cDd9lc@]vjp02w.SU@G)$ݸzpK|kȿ<7>ܶf$M{~KzFwzyͲ͸v#GOW :u[Iws$AY~rVɗHejܚ0 9,]R/2 P4r7'z7%ZR)NZ^$9X'uJLo,V:Z1w.w/;i0?g#A|7dٗ+*H JUFda C@Jԃ,D Bc|FQI2VfQĕJz6ߦa оdH HJaՄ}F,AE#a(rS7\@Ʀ5WsJ:նag!ffNr^14}l1 HH)Ęd $01ģHӬozseJ )! \%,!+f~dd .MԲ-L`&E&/?xCg7^u+˧x"IJٓxSs@J={Qon fw) ,^`^ݿ2mv5ͮyn,/pr5vsF ˉy(Q10^)351_6lK-Yd ˙ R3ǃ<0n)͝p1(YEJ/":Bb@+֡޵[^U9_(ݨ"aVmUt pr@AOl<> $r>yu|"$I4ݱ~/mVɔ *%5=. 0"& N9j祸Kñ~Czj3[̞;ZyIJB[1"R.0WF2A[.7z?ʈ \s޼&0B0K@0U{H1/f -O)B۷Fb /eiI^Y+®+(MTڏLg\V/u=[GO|= $oK!I GEU4رHve> +-mˀh+om"@_nTx@qlP:{POFQ :H&A1/풗I(ױ1&*CG6'z,\>D.ֳmveP5ʖŹ]^#cZǯǽja[FQ Y9C^"h6z|X0D YeLONq]x{= BȔ8~ תiush-?*ͧL4ge3MCb5(&s.pt|bYᝏ4>6>S=ȣ7]җ=W_ܷokn3T\$77nؐv]erKRٍ vVkYju֤`Y" a0GY-|DbоBAr_%@TqS ՞8IJ0 +)OU 2!R>J7 AR04Q4H" "R 3~]3)I"D&I^b! 1T{]I[ r\ $"m"ƲcQ_zF솷Sԩ9Ñ$=Xӕ.DD  Q<\c&i j[RX4+PTxg.)geΥ :bK=PVZGvt'k7/VJ̱/qzO{NLʽ˝{(ğY%,sƤ2u-^"ӑ+捼_>d5A~:_neT nqL@"C(:!1*)aaDD;fw(KNi7[22ihw 15:k%Sed-MF>33mD!?"Xd]>QO Y|k/񃏿43&\&?18ǧ庍flq|؉G xx{%T(G#郫H!f"K_P%G4g6YQՊo@O0_n ?h2$`yg5͆#(B*pԂ͠ zMk29xĥ l{٪vYgT#cm}׶K=^M=u5t}?U*'8~jWKFݙF% N|_~^Yf|N>NEBM2jeLyVI #_)6qYo]>mM ~ p]6o6O @INƩPR&B`}Y=Y`wf{1풋Je6$~D4`/ /11]O>KÀ.&k= غ@1\o}{dΘ 4O咭SNE\×̖P=9޶9obPҪiu ΅2BP3Ǝ8YωN[KZR]$o ʿhh:!ߒ n|%ߖʄl~P_䅧?2 >Hx8jbѕt(7ק 2EDcɉƎ[M^ $35= bq_J@ `E9e* ܫ8|kΆ/0eo7"[oSQqpTZtf8j^z88пd8zL…LPW t(`Fl֚"i͛WРDOvrCKJ{s`UTIϟ6NQy;oRʳg'_-~4xȐ5:pC!)3RAݢłPyCvH]#xoDSe &ݷ]ۮR? Or^ afׅmo>v܇Q*[Qo0{U ݿELz$dsEliF`ٽaFQ{0zŽX߃*sXew~ѻVi|||A$@ *P<[.^ *(" ;LtBאo솺Zg [+"MTm i cza3*qЩȿ߷e٠MOOOMM?|d:wԓzRk&fRoƍZjz~r"޼f׿vC^g^!?OY phʌUbkJmlq qESު)Fu浯y燵6~zl?Ag!h$EB>Ý'-Okk$泟K99s^V*{ * PO;bGI}Yサߺoy5+_D9A5^_c-(TW8ճv1 D@ jX-/RԬ6V*4WhTNǞ|.rE֮\tᣏ?',쳟{t ;g]8ן>L1H) o!E&NiPx^͈u9oF[Ծ @pk> />?11mʲO΍O}w\1U3gxs}Z\-w{n."eoQ}k=t '֬^\Zx.^ h&﯒؈hTIZ]4 G6]I0Nk[K9 l{mz荷Gw~oqC1?@^U!=]> 7gSCE{gr(*5Nf38)mez]mnzKfBPֽrv^;C[>n_+o:|$5/t܇HxK BJ}qMw5 NʮR9Y$B<BJYmnBL4}<O^'}nd߸4 d㦏%I#̵z{zlSXk 11 ^ƭXz,@g%<5eYfN>D$:(6AB`-kDTaD$eYu]׵$Kr뺪(a"KY@BИ*DcLj%rVDLP5Z"56:Z)@D@k.X"ѩup9Tсv$C1 ȀJD}QD%$;&::U2 0Xz@ΜU5r B"?q 5cY&_/.O/'KW8jz12F{Рr>B]+WEWh4Q :"ADB[˰VR=lξT_~ bf[l|eN~t~>?v"E;ѣX1/vAQ{ F>:_MRC5O|BsALϾǫҭ ꉉɥn֑ɖ-z|%/.Y.A'tܱWN|6Mij_t|/>?n7'I-s=ObpnJm f~QSSoB$0ON \]+?þGͪh &$!OV9|/g?У&O|G%Sbm+QrSaͺg?}պA"*Cܗ+MEeb* Pg&y،,dX'F'$w{}'s/+>%0+R< @mv{++CfCa"T[mW5ތsD+__[0w prϑޠ!@aQBHR5sfn]\}Y*0I'/KsLyCⱷڧeF&N@*2Hugx"DofTOQ//-/dwED,˲,͛W|K;+7(둬;x.@⹹Wyu֭YfrrbrrbbbBDyOOw' V3lQYF=#7]xe=D*:6 ;VOqꗾS?~z/Fj0P0J"-x#SW anno'#y71큨XcGO|PEuigq͖d޹&$R$R,왿嶇ANn/BĻo|| +( `4m_dh(zo@&ϧOڗ}+o0N$F Hj^ۑE~` )A_j9~*/roλe޾s7^Mut…Kw<6;w7oE}a[mC8xuT,5Mm@hM;rrz~WkrF`7I>/O4ʷr{Gyĉ'O|p˯ |JV]tHџ#H#<@B 5o`\Փ+\Ҙlh^?\.٩jw %>Hi^D~$~`P^ $*{, 5E_DĪ:yGx IDPK ^nv{JB`UUfUn\ !e.ND Yf^$ZZ1A r`69  W!<_,CA91uuUX]FJ$k)sOa3 ~ij<׿wxȾ{eͫ"K'_s}c< 륋zKZ:=i`amS E6*z y*b (j֍Å.Go=~u6K[l>."...---'TzN䙟Ɨ7 ϧGneRHP?PԀ/ 9t/[3{_Waqj媒mU㜀=<#~-}q=ul-8Z8D:|4ao&7ư =M_mⲍeU/uݞ:lqk7;;wϮk&6NyWn>Ɵ!Mr0Eh|0_`cҙe~ 0]bclX4usZ'S;y1u'0 y({16j$~4s%We¤ s"eξ"Q.ԱǪ&D0&P!JpDTeLͪDCd뺪kDʜbm⥅з"UvV+)eeY*,PɚR!`<qmL5v204Ok>֛XZO,!Q4mSiXz+_EKҦ4GlI {lMn*gm.p~ӯ_z@@~b3DI!% k>xY$*+Ҋe2HCc<\{`gi8.! ,n .~bnW%~lŅEǎxt%M=;l"|\loH ˮ#@Ԁ4xB*70m îHծJcG^?Ӆw IDAT}gz]EQ,//fYiӦ 6n_B9YSo<ϧ~[?WQ AxX2ǻı3]Jib,pl6[$*yPOUGb]u5-YC<~=ចKw}oޠ?/UOB{npQ8 ([5!LA%&qTrd 6J_0"tP)95`9<4Z4h $M]y~vr39ޙLwLMwodg^O_| fr0UE즫7ē^8yNi'HEdiZIm900jcܑ,C|-`StD#|&d-"CTFX K4K/^ZTz*oQ;}^ch|,cM\gZ蒍W%,\+lVt>6@Qͼ{@WeA*UkWU)I; @R[BPtk)eKDJN(Jn5SW^W9Mmk0Bo5Fh^r_DjIA@=#z{.=-u.-Ԅ5Ha۴f] Ê7+-?GN N͓kIp-~ ?u}WUn/#yϡ?{Ed_.^8|`q7dps9jVan>5Y Dm0qz߁%X8c%!Sa9rx]iuM*rnn(+WUsĩYxZlB./$. |5t,Vs?=t/_mo2 7^$upeYr>54.{vZ{M]76" u0:pi*ԜFw>9w$ s#oޠ8v7(@9^itvNk<[(9@ls!^O%Ko7AJ?wzc'"Kni$:W>xgw#ŅVՍW?]Wn|p3 ߸voK}[`/2!0g4WgG#,b9B*EJ l!icANAlpr7BΉ%6g; 0u t Ums=ru`6s\OCVFYE@ch5FK ,9+ gn9@ƿW>mO4~`"DyYEUU東~ Ng}yUU$',kP%)M$*8IJ벮lBFl+JA >Uԙ(@$@D$0#D!P "R7s rV4a§em,aXA !X^`"+^2B]ScI Ҁ+FLT׏hrD}+K@xwfQbiI g.,R`'?|RQF,2xp fztVs"4w9s bȷ6x^\zqd]b:<:! =O_~v5[z^\|i>أ:et0Vdl{I _49պ9`e;}CõFmܯИ?c+"#spoÀTU}߽oO=r 7p?sκnQ}`n26O .%o r#KE0*޹i35xP(*6*'QjMc#ka;'LU>#Gwf @B?4, ~8d֬ofQn-ׂx`9:2$kw_ےXXYb'O''ƾPIܶi=ڳȉ~P$wtݷ}|/K^;x\XHCg:Tyt%fL`"*-5?uA`I mmM4h܆d|۞qY襪T78,Fwt ĕv0mi!REAsxG/:T= + !@u~ _*P,D,@;?,"H!RJY7'TUa^U؄$ TƗ+e$ !%P2I&)f?K"DlNi* [miF>b$'iq@"0knັ|@N80e7ol$g /]-l|ljCK=_z%blѺExb:E{ p3sB|$=0͝X4ėN[|?;JՅ7s/;66s+ǒŝ jvv`!F`:a;F1G wWQ[͆!pe`(LH3. pW'g۟}뮻[nݰ~,f4iiu8䩮$I$vX}9p S 2DaF>EpU:?}oxVZG7-1fgf?m_{5oࠏu]?-,}n:10*/0AUH:s:yQnQ"MjAnv-O׎Ǧ;w_sSc9pL-v="/^x \i)a 2ZIXlf>5_6lnxI`k:\͢BMR^0r]VWV|m7߬~&PQfٓw%_SC%.1j)҅mޙX R{?hbe3'>޶jR ::tHeQЁnc:t֎`5_N=nY/9ܹOS" |'/ҶZLO仯_W/)\uj%Xy8q}\̒ަj:$Ǥ3}ɥ 6w:ja V8Daa#3\i-WQNc&䉓_W'Lx;sæ ^sM7޼qɩ^D,QN3I*BmG~D@uB0jY'%Ld$IϺ,Hb&}t3K,DUU%Pv/R;Qk/M(j)6c)C(2uQHF z {'fiu],(2bE$ꅶAMphE32M)Gˮ9 f-{l"kIF &ǟY'VT Ȭ8X9"7ޔ}ԱUg<Ě+v^;رnÆ4M)P܅ٳgo]]`Ӧˊw{.u?2/}A̞#VUJY[/KaI.1< f+xsGgg?M _1GqC,G6ȏhNuVtk*߼4B2yd}sK#gnng'ռ\cy[lj-J"!u84Y3ˬF(ZgBy@gȝ|ΕhDHy;j8~őyc!g_wRVU/ىS.>~ /; u-1Y4u#X#{Q\,vcް"Γ3u @ .ws9_2 8U/TF`feU0K.:~IxsC<ˊSא4͒Zֈ&I,ɤ$ImElnR !Ꚅ R16H`UcyjhU霊%PeD$k6Q$8^ӖW22j5dy hR/}Fۊn ,ᑚNH* tZ\bom贱.-;l͠׋ôBKs{&pYO$ iJ9G_xn?D  6ȳBp^-zUVJ1Cb8'~yŒHȼ+݌Gh`Y\tY__ڜ066vĉ3^O'UD6Ǥ#7+7FVQ%`B!Ǘ)̛bi־0(6 QGݐčv|BD0" }UNWוvzo߱wݵWUYUƍ>~k߬J㚝a\,quFh[(Y^CZ+l2V2bIΎG'^}tl0ocq{V)1ɹ0JA/@YK>N5bs>h}vն-i=/u77m:pU?ɱmd$?q DT G[Uzܑ 8?,2G\ά,ʧ%lP(RTj檛)\K0v(pZGǏt.J1p W[ }CW6;53~۽βL˹~)}($SK?~ GsU_q1-q$V쯒xtQʘJTǩ)>7XM'wL7чK`@6M4O~ hbf5b%%iy7rP )$kfp,(ظ` 6ՌRR(hoL{m9Cg+Ȯ$"/SHf\N@]׈"*IJ))&"Z4z@ @fUU()eY_$ truC",d]ղ(@hSؕTz1Mc$7 MӪX(I6'PҜXԾnSUʖVo͌IĤ Db QT6GAfiGM@*XG|֎iru2U4k@hAg r귧U>])f7VŅK DmHXWfD;7@ۺa_؋Ǯݱky(:zgc4l/4e> ^{spi~[i, gAsHےkݥqzӍ/?;ONLvM/ool~s[=fp6XV''j"3`T(;,l1],Ŋ D Eo,I,~AR @(I~`NGXՕZ $RcTmk\+tA$6lTNںI5! j"+1Hc Ms \dQ+&;64>Ҙ2G4#:IͲ\8ƪԾ^151$_ p=BE~||ZޫUuķet$T;FhVނhg Vl-)F~LRNl)NX5˲|إO/z,̈+Q JfZp[Nf3QQKBᘴ IDAT$PX],4͕=??brr5b8\b"qIdUɃ;?? ~hs[; &E$SWN$r(A(F|iw~MPO`FSpbj֣4JrqsKK45tQLlLorw^|`brbjzĚ<$Yz%;Q_m$S*F*&h1؜,tՄ)dh1<ϜX܏$E:$wwg'k?}/SSj$Kb FGWYT0,Z*'CBMAmHurWx/MaK|";ӌժu84Z!TïlP/|80\3utja[{)WSx83HFvYf_t-;ɯ$`w*3 |h1z 9akEQ zmmTJRnX~aftLuCB$OUpؤ.dOi8.Fپ#|!$IR $iRW~uU4-RYyWUE(DES󱺮,$BH,ρ@T!RJuBf#~`,%٨i"uON @ՉiMi4 VJIB$uM VFIA5_)(ME.Nn=:Zf*'d"CQ=\WLMIS21r g*HƓ !Z3W3'K8 UDtU 1\!pO'zCQ'+Ow벩bd0W,],“+L/u+8u\p} ZcPpIQx"A/c/ٺn&(:ֱvO=]4M1M4MϞ={ЫgOCk@>QwwCg:#م2{6s=/'ߚU\6'W)vA6'ɐkհPF.XFcwQ.MrhFhfyv盋g^E;M(Eu&>S#HSa@ *w+hg. \EG`L#ֱỎioyi܅ucsM,0B /(#vblBhr*(0z+„epQ{'g^yLYׯ;uڵk&94_:yܳ#ϝx۳L̓Z~̷ǒk),iHĈWQ#`RI0Yk+o&0U'0$z1;3~ 6]brnnnffFfA?)77B󵐟]R4#k R]NJeMD5p, [,RF!:RARE_qhDh eYRYEuoBGuC$6a$I2eeP4nd*+U[e,0FU,@ՄRU+P XMZLtˊDr>dk|Q>Yx.Ng:f٢~AR6F6T/3@#|9ZE#(?m!˼ ּ۽Ӈ^>mؖٔNL#UIYuŹb%SG!/O?;<&k=JU@ P.3GB/yKueKEiyT}~¯K.\tǏ_\\|nܥ4;&kIpSs|C;LbFu{GxF2^2,ٲaFo;nrm$g dر}l|ښ@ͩMVgGшcw%5w5Is5_9qCۮ[5ۄזZmbkAQY%q&G;3^&2tBї&(w͖ZWz!8cGo8l܁ͳ#:ذyA5?y{ 9V)Uu\Μ?9s玜=~]Trr4K~nїDNo߲vM[7Lm^nӺ5yڊ4 ye/[cR,w3s+͹x1c#1֊F#hс,sf/#%]uLš$=߰atahs?ۘ8no$0#0 ~n'9/0<1 \&%Qg୫śEQ(_`gl)NgyQ0z]u\VeeW=ͲTQ;~G "T$1v]$-VXybſY ʲT4@RtsX4ʲ`N2/T5 4J)07,* D &IB$W֤94W;Lziω΅19~,?|jJE}C{5쑓wuIm-Ѭ6R]i.{%Ue|Iʲ8jSL.yf.ZR 2[BH|&̥ FrցD@K~G^ҫ}u6=OR_:_.wgff}#ト Y0Inw mXp2baMXk-ÂAk/qlC:9 :mj-%;F3i}iH뺃~Ty氡wz2RP F3ndC=xKZĽ; C+"gn26+z~聉zAB ć\wc dE#{` =;C+jk{ߦH$7}%NmNY"DYCYUB$ӓBb)T:F$_;rɳMkn\mqܑSbW/u^ԫ{EWvdi"$Ϣ}0-[,a dS@8"pqab9[A50f0nJ/vIݷCPFmuh]n-&.\ַ-'ib9#2H:ϙH"`7nϢ(@Ұ DEQ""!4k d@76zh) JƎCرIIeB "Iԧ4ML*8NӔ@g"RnVxu0ׯ"_ mb"ZD^35uߟ+:Zc Pd\]}X)"jSH U.$eUUi BĊ$!R% !TS C2q5H)Qif6URj[!D&T$D氷 2E0{X%ڨ6 f^E&xN`7,6LaeZJ`YtOX#`jǷ:C!RF+VF^nDp=22hAE[|A%~x>h_>4\!-G o/@{CL:P7r 8| y7v`9~"HZ߲Al)-F=c`S>:5 *Oh 3@?8=)wwbߕRlodFC>/7Ʈ!g+;P6=z=GU06|J| lVXߊM&R$IHgOZ95)128Q$2 +g3H{^,bo9vV_,t~RxZ x!+(iDˡqk_xKsp \L&jGmB\N2UU遄~C!0 e]L[Y'IER" $u.tNW)'pu]W 嫎˂F"$ 9IÜI7_ND*IC$eٜ^k٠0T􆥪JUpߝRݪn l:vZ4×o^"=-[wLΫד0_ʞ1藏P•k+_QF PbٕChU7{(s@1\l\#0*ho|)BAvkuޛ=?¡JVVoAi5;RmY(pyfj(:acMq ﯊QaM]jMXͰiH= #Ά1 ߔr:]1:-׈J w!ؓV仝 nP>5Aiꡂ<1臥7cФ grBZRN`DJ>M'4i3u  [cPb3dy6;]zrrKQ6y.8 aHNGnmvד+hv4B].dđvԝTIBy;yGL f?0"@ u.4HJo$^ۜ,dȢ}ސDDBIӘU]ɢDBa,N PZ&@54$As4r#@`;́")ܥDEiBsdԓ\I"m20o Vmƴ1mц& C>hXq;=?js H4fN+dlHDW"myGPicՎVO*/=e[@ +od+ZM?[y^ƫ̭+b=4Ь"x"RxSF’g,P )U #\M&pTZ.:VV&s `AvFd S懑lzG?zDܼFdoLn(V|$^ ?tQOl;?(%#3veSNn N(s4̀Z{`3uvҖq,Ȩ+,+GIQYʙEQ4RVU.;$IeU6.P~GRbc@((8Hij8v͕=fíM̿2敘HBkْBDmβ 27zj44gUtߍrirD)4jHV} 09lMOܚhcC0crș+l"asW+CDLx e4irG+3R1Zէ&_WpsEM:n>^Z+DYvFI EЪ p4,~myjN0ظʢA1;p&g\M|gOӫk2h%p^*KږD'E{!55?z$J$,S&"v:c"I#7P%t:cCYGJ)k (:;J&IוB@"ʈ5vHƬXc!DTm%4IJIR2gM)}tc`jՊiѴDc $*0[Nu$I6HS`3o6 󫔥NoZfce s̱ ۙ eYgU;|k^Z+my>lt_03N=3{A[13ܠt ~+Qo x1,ɕ:!ύUC~nyVsX0B鬟U y +3Em_%b0s )(N)vv t0zF6FD3jsqCKʻmpÑo@ZOk~xN{swpiۄ5a!=cF7f 貏QԌX#a V>B탵p`H۟z v$,! Y֯zo8^nk >[E Fݕ& 3(A %o>BMC0BByGH\ 8N5trY ҁFbeFH XWDae4 \N 528jq  j(LMi-@[tEd.bfhUݣ66$]&@LJYEA$ .4-)2"I$Ie(e)IHIQ$H(MRD!e]Uueu-I6f]P#vT#㬈 Z.kNys}1_إ $)ꊈ:y*KxZxH$Dt躖BJq xܞٹ觀2@==!`[4jm+9?eZq\ (yJGK ygu0UӍ֯ B~ *ۤ$Zanm뉣é9EjܑӢ6at"s8DόTZ=Cq%aȹ1*v-V<A#AAe丫aq:d(JM}zJ!݌GQ_#%(,E56h@UHGZ%'[pz!oy0>rp&q\= f}QFCZ"v,3 axѳN=E$x ;ͯA/eG`zZ)D:J8DvO+mL|Z&7鸦%e^] BC IDATasEj<0Y@Leҟ'_]@T[wJҎ+'0 QVZݿ V{Q(_qADB }ËDRu]#&P({V34Vq*PYH`Q:95NZl<$mv>_;U+`,+R `|Vpe9L 9WeSY'#y K/#$ d%W8Nuz-1ł1=[86rkcѠZ4 f7]\堯96C+C|ԜqKh)\@!J7^[{U |M)/ )ْ^ l{>biwL~Tb.HiAm6+ S!]b3rK2BO'P2#m4Yk^ap9׉p2_TVg jP?|37m*bfWG'!+ ê 53$y3"ԕ'8k-V` yT#<1ߢӢH{yvE2*9dT@$LwgNA$#SAQ 'P9v]&633]?z{fg-tWWwWW%pNt8brR:!" ?3A(X/5:3 1r?#8p\uZ70ݭ kn‰QD@H @a˜E !$(  9a 1|,T#,_,`lYe˲!F QM<"D)7Xek-j@,yyv0S,Já 2, R#b2\M5)Oq>5 N'0?(kdc)?Ic~ɯ•#aXL }5!=7OjP qX GA-vg0P5E>cE$} E)SNr`2Ry0@(9 b;Kņx8TIr;B" n -)Vmh%-2UB*TElo:Pd2?IbB F~4 kf emeh:7T}7h!K:lbQ$f\4S D'3Q*Dق-D2Gʈt( ֋.MCJNο^ Eg哄<5ӆ! XсF<=kJ/KNAUCV-$YtV\UK(J[Vj- ^Ϩ\[)w=Vv5HP. Єlg 0x,i"F)b!h3s؂+;T+Gb30p.7G-{+훉X49(ؗğ9b"Gl&mcsY6V$5/ Qܟ??A QGדYe+cz(51^" @| ` U$K= UҲiqW-$45.@|_? ד]-)Z RRZ s个LEIΐ• M0hR3X~Z+r欬] UR6Dcb(a+~~+{FZ@]BW眡@"YPf{QSSSE, EN=+ݾE8͋kdάw]\00k\Rw(KGuY5$p'qbhť@2(2YVR eE\7(؉3FGH7yr0KQu>K0_g F@f,r+*>g=.8*WgY)>a$[ﵑưN3nQ"ljK5(B'.3r.8'8TQJaw'/r#b#eʾ&DDR]'F @7tá1 kҢac1<0fߓΙUFfou0wZif]DR&0`H(%ƣ`0,"y,fT"`BX!Xqe%T<J7DnggmxT,PV?eʑDx6uZȳ]V㢱L0onAݬdK+8j+WgrpC<$$ QusyM6#|yp$+6 *QV(.bruR(AA4MR5qIO;I^$L(T;g;lڸD|*?Aa^ R~I & @+7A;T)g d|E1 D}1wޗsHm`9OWƊ J,kѱ+\C٪ " +L.:_j3xZJ~dZKC9#8+=f@Kv ER䙠7f3:஖5TX.nLr#.Cp E7+y]cS=pNo֘RHG7VHw˟{W&;Vf7ZZs[6$H )`̂C@P} áp$ADBIBBbii ? +r7 @u4"R '$RaF/w Õ*J5MC~/048L& gM fDs?dQ95]lYbD 1,P"PcPj_׳5]@ $LMt] zHtt-ka] F JuըkrXj6;C1ӲÈaeVaYg4M2" Usm=sDg>&t˻ⵜ Mtc*8^WkC#C0/r`-TɷU&Y  (G"h*J.ThzjYy~)͙לb5\+olH| b|l1heT\VR{`<դhR8a&ދŋ(5) q3(%HX~{?hO{/,?x)8)9WjjzӘŭ[AVꍎJ8_= ிGPWۅdu.!3")2.׿Gk+.b(8H@qYV&$JB q7.0*D*tK+ĮD~M#KO|(3B&"U,}XEMv Yy-@Q4/h&gKrHvX)SBP@"F)bip*AdiB :~`>KL5B)uFtMgM*cI"qw+Hf!ԾX+TB_WۓM_bUjEdv 쫀q@q3cF{u%؋H\nfEI>SK-p E1$-dCxudLam"5^*.{kxaTz8(v6gSGT3q!NP4f=ar܄ Q_oԕ-c% P˭:7qV0gwNu 2M-  k:tái!&%Y %ba hpR4D@{/ X=ĹWBc8t]gLCyTQsy #$|'8abRR]B-`MD 6H|MgҼ,fþ1D&EhYh1f1f96غeYiELfl-iELfXh̲Zm33X fi5= g?o\r :>~,>ۛV*/"@zW+KRˣxėhR[~}e*^#P]s8?*;a+E[Lqa@wƗ4H'I<$1c6B G-y^U.u©**FQ(yXE (^0@nR2TÇ=* gb@hR^D$d$?T"h<~T1vG0e7M9䆻"SDAuW,I]%qm jwPd00=lT2PQWLsNf\'lTxeX!J@e,;E/!dnj5jWZu qw|ejeD,#bgY/+iwT[%NM.݊*O;\,mID's"(G5^~] ZIKKXr9Ag wlVi1v}AKhcbQ ~ҫu8ʝr%9Б}.k rU[z*/G⚦Ll'ښQDtt{.Q~BzH !䄄DB G1D+ |K-vN-ˢ0h:a}32:ܚu݁IK)#1Ɉf6T1 Y81( j|;g_1Ma{;14$2.JBP$ W2J CF0L (7 )b`!`R(!̶,Ylױar˗hW?!C IDAT #yk4 x^Zv8JrM=`UaBz-?ya: .o z^UD.b pYe.wm)\iОq0Jݟ7yAHWOyQ_jz$y.Fx2ݞ]Ưcѯ(-TMQQG7i;$Tۼ3NU[QdQvW0TQQJUo w//HRYN7Q>`EeObp'u9VWvΈZV!>Nh89ɻ7eJLtqIAu֨Neў{αC*A{a [i3p{̍(fmQU}k'7ٴ1dSABիo_wݻ$SGaiBjz-(l1u~h(,1@gD$OMo(s?IxX R2pPDQ $IJLEi5J hا^i? yAtY,)yS*lK X-fifRB)e:c&<w|򍾔RJtD4 Chb@?Z=* qb$ !vZ̲1DdRTV _٤Ս\Yn-b 2M1,?[,bZ&Cˆi֯iYi-1,f 0C(ϓ(C 4?y_~\U2N<2o۲eڥ? isY㜬Ȗpx.¬BW>|֓yfvښYOH mN+RWh; tݑR+9ϷVOV +ھ}_i܂ƅԵ?)gW3~ FKx^'\P`f-[6]㇣vI߬O?mgjB]Pd9yǑ=YEœ/1tes6),jc+Qk8o]ډHqaZZ݋>xBO53mݸa?}o[fM |Ø[%x,;t(?;yuCV.K>WRr8hg߱uu~h/{^GJg-RCF6zDjԌ%y'{*]w~vԅ¼{VzQ+;|Ѭ{WhJWvŅN*T[{o- e%ضm?S/!{t~e_L>v0?Rg/M9Sxz\ř,}s&' sw;uͺCsO\mOFV3zc"@~ow=sdk\ѷK=}n^r4}߹ܬ+yUm٭g^7tզ߬v=}l2>s2OrZa^Q_:p|m?Ym6Ъ-˶C2O:M`޳g;ud9C:\Tl[=~>;=|eș-7TvwUmTd]6`oRPflKpc=/+Swl߰bѧѹN9s < Í_z* mc.fl_~ߛ+S]o؅;{-+^FځuC@_sȌ 1SqGTilby⫥DSԳ#:[l#Ώɟf}ԧh b'cnl6z~C቙$vٜsǶ~9:mFrfaAnaOwWj4es ^q_u+3ϫ ʶ:._+ONXe[I"%$OYUP9UBJ !aC1!4J4Ca ,F=i4E) =I:~` ス똬bGdqhG[АnZ@jNd1Dsc0 4"Q~pj 0RM1p oMB́3Ɛy ;1F)a l'0v3tB,J k \uER"?Vd"C4Zckj+2dLlrQO=*hc_XPI>Ϸgl:cǮ5}+벡o 0? /ndo؜ X _}ZG <;M++ߩp |95gae8;ݿu-ZQ\-_@1Y8(5B6\v,cަ&#>;\g`ent:kww^gYv~٩}-n\P@BQR|?;i>xS-wI>xVOVOOe[΁H4$ᄐ}G>|-n\Pp^ˤxԮwot$Mg2m>kAZ{I}lWLCyۯNsJ5jjҦ G.w=8D^~8k1mkN3pcN dЀGt8Є*n}ύx|ޖquG');\$gYxE+&T[ꋯ-+4я81do^լߊ<$Nd+c:@LBT{t\Y+3z=^1a]ȧrܻUQ9"{>)Xb'j= i3&q_e!L jb_|d9RV 0&l:Gj?i$^3dqU~i''N_kKSzEꤎjw28|"񷧾>m,:YL#Yo IVjGrvWH'! d` D}~ф+ijmW[15n~2&-;|MͶHb?/}5|´e*44էfviB$ZOXɃԾ ׮/xO+mڨG#j4@9>5CS@fKOZ5U?c8XgNŧJsyg+OK>((g>Uՙ(ɂ忐V҈ ^Hnyٶx.fUCoԓ 7h\~HZQ^:L?1qŕx/ݞJk^sZ=6vOK*te\QTL<%$38Թ1@a b)W8$(wD P?ejt8KORTfֆzyzCfN]7[֜Ek&^Ɓ+ؘKG1È TLӌD"R (!HA JL{3Fw#!߅kF(RJΆ[4mk@ ̌1t0_1 C%lZ"S/m2MSע\"@ƐX RB,b"XTi!M3-x5%F-a!K=Ő1~3CkZ<1ݿ1PEKXŽ_Iҵ:mSOez[ ž(eIծSGްo Z;]}IOq7@鴔#&/_1m'Vr)ek$ݸm䮟cESJ) 8Ԕ#G# paO?p[ C7o58xϥ/UBijUDoM+R1yk,Y!vc; aY.VWr/_n{>6]~Uk&}sٿ}ih9jv=,Y#QQLSKMKj.Ƃ\4cKo0P4PRXd*]&i6gFZBHyk[ .FEٻd}%T 7yŊJ`ͺwnxtˤ׭po[ֹ7[ xdqo$qǓh3 ƾsk[ 7!gk0'lI/X>bwU,-`۞MD +KwoÔqms4N7]&д}H+u”s?7=٥~ŠTJv ; vd=vBG\oЮY_uݒ?7jRrWZޡ U{Lxա^*GǏh~hZ;X r }'f@@禬\6֭hxn̦4S [r׭Zag1(@i^n>-*5lވVt<@ĝ]?-Kyϛ[ oيuy*= ov/<\já Vs뜧]Vt`;f6yhݓMXVRMp㽺pt77n!{URN +*uk@U7,B)w=R1!Eeic)f4GغQ<β`vP2Vm2*5Uo9_|p)+^=[<8Z |~mڗGv|U9nU1@olU}B\AGqF%e,PoE׻hs1_6aHh5ohv] 7q <.'VQNk%.-,-8<ѥALPsȖSoNZ)ul>珮wɲ5}-P͉q9\~7ބKM1xAlcir(`AsHD3ESB01Bi }'0 -p0 D*$$SbLYL4- j@2Ll?*E1DW$3Ə!ʽJ ɨFi"yP(x%@,4Mڧ@QJA ˢԲ,]y瓛d_ZGEO2SU;.>LrR$!QmCn{S[ԤG<AM ]}/֧-*?j54zPok/N[[U%:7%1Hqa8/Pq=*RAnf-;j%XYe:XaoqU(7{ڒ ,xPv-*f_zDhk۰mojs<3 Öm F8$TWU9ŅyŅ9)o5*V谭Kl# 7mv,8d:U UڰBvuBcGv:yC8]Юl}mu"{W/pC?ZYN H{̗L_u/#e% a @s~W:v@?%+ыۿZε(!!y|ZfYpdk+4lE iiʍ9 i{(3R!`QB.,9JTwՌҊmFj- \QqOfzىL xFn6j13rO-ڔI'7>T]I^) t#>[|<@PZ{s gdMVFG v&-[LQLlvE/~)xJe^-&bf1d1"bRD4A4`4R*aD!ȸ} QzP(MP9 {s/u\.`cuۢ uGb`&q{J9dAAd/nSw!, ?bJK dVMΜޓ|r տ^e%?KlgJoHIMٷko=Gg?=$A ^V5(\-nhws0~8G?m!U;p"2Hq|!iC͞< w,-%5emԋ7& _{xMz̆T}74=iouX `-|CڴhպE6-Z=O(?8KuFc;OV r -рY.;;<& v:?eq@ׁasmTH :%!iRn_{>~*C5yܛ6;oz]IQfZ.M7Lo=wg'l{onkz:zxE331 (TV_N, ii){}gmhV0{竌Mܝ IDAT}׮;jigt_F@ҀUj`BR8ѿ!زFl$&L4pL_G1qb#v)T/BS2HՙY釋aJ6-:t=h A@a{.g㴦[07־S8pvx@"y贓8=7֊˜MYL$eIo 0y pA. DMPi̲ !H C]QML{t B8Qp}=B@nY:"h-DQ]cĽvcZlۨ1YY};~conapBu76-p;'cR $c,o2vB-4m|uŢ)GOJ C._+5L_dYiZ0R4I̘aZaeYiZm `KoDIx$hL2B/Z;\(F XR`tRt7*,+;=9=z;n݄&.ܿtDt8I;QG1i'*9b^*?xT`g$\v%(,|YҜxwUwnOɒ5+fMY; زgvZ'L?x(ç;$%;rG.5v|o*\;ޯgM"c+>vg^m耖8|GDF WU 3 -#VMJDeގNZ:)Щ|%-gUG@****Aafg3vԸWC$w^8y$49_ޝk Ԓᆴ>|FN=&H_ޝkgJlVQsv-HD2 rJO iuo+:נ`{4J3헷hA{4HՋS02dUh"feBZuXU]g2o4%S(곜7MyO'+3jb0 kjJ0GWo 1U+Iz:BMnoNvz5SٕmcfٞZZ]TT+G@̞tm$89ggJ5+8ӱLD`EYT)ĉ:.P_Xu8]RJ$RUĐ]\3 {}LbڑZZc K$4sg" NW~0dխ'i E}yYy^{h󕃱.(.QiY$Y44o묯OUd3E!dӿ\+%eΔl (z5Nt;>rn\I- M4';fNrԔ;XafV `vT[&w^(yҹ-vi;8|[@ <H' V\ETџuap-6 َK @:~1f2T& c<@ou‰AlݹumA+\Ǣ8^9WXʞjt+ R\Z`Bc[eQ;I*(IIS3uj1 MѴLˢF" L(kV Aƣ-#&r״,f1@po^?A(VyDZ16m6 Nj/)Er-z(TM |"0~oY3F,kܦfĐY+&~/_W-N״Z;sUQ@E[d!HȖ. :ĵFj4`Zh+k&븟>(~ɯ4Izƶ0nyo.?}϶I]а[js20On]l׭~|я;{4oݾゼr?7b =j/vŤ^u􅿤ޡuc}Mz⤫+/\t`ͤl͞8Ikl_'O;C_ryQ+?1ܸ襧qV wzKK>Sа[s e|)ngu܎E!UоPcЫH z;7jۻg|^ e|f۩0xdF6IƋat]qzVq_\;[-LtlIWLyxiSm>YT犃 qۑz'V'Z)g vm^ O]'7)~Wl$Y[vKA)˩FKB{zee(+5Z|KQQ8z {0)/ӊ }w7h Y4~^2mw@uzt0~@]n q_n;L( $Ow+8$܆`yNG[*pH]_~Q),kDdgR>7M~i]Oܲ[Z64=Δ=[c}DžSs,p׹o.9b».C'}ť@Ͽd,jߔ>xo[Z="ca0pL6v#ܸgaVE?X=[eD mBHl+QaF" Ba48p80"H(LJBaD{=? (chۆiic&2 mjYNmےJ(zc@hB 04 9c147-n S –e'TaQjRFtͰe2Mvi0"?j:bBDG@Ӳtd(Zi!Dרe҈Q#3+?eY  MroVHO6_>aI"HȤ;"u}=o[B_5l԰Qp!c;/w=YK+8a n5/5.m{w{_Cf/n@m^r!D$y]] Woq8ݴ`(bOVZYf"2R0'\N| o-:r礅k?"u_|g޶B {Ai~ެEMhաW(0#@=>Ĺ#jigv}?bїA0~w74caB8ݲ1`]<ޞEm6e ߽Ȍ(;O0mޘPphUSl?,GOiNXA||χj̘? 2.ٙi?Eʅ{'y~~ È_luHkU23r%D(+'&nl^<E,,]Dog_>9W4Y3s/E7Y@J{㧽4!CFkJ-R^Wk:wT5RpWי9ngd8j}AU( zIȖZY(wA: ]Yw9v" fA ^%BÁ^|̟:mcgi?z=?OU#Y%Gf-)/tpȺHс7+s>?Qq~ɁW;fg.| V#!8|!Yk ={/$[2ymל ^Ȍbtř߂$]O9GOD1a0 .L0k,A73VQ~?gC#og98;EȅmC&,izOz%&pe8b[!z7@?a:1;´YK5Y^Qc`ČBPrv_f, IB(lHchZ0”~\=jO;{]6׾]=6둇7 Ƿ-ٕiyjl~X=z֣꾯e9`^͢cNشpf-h@o啮#>9i,5TRl΋Hʵ!2SZhRr5Ax--@ 5-1%z1"\ƄibA@WL%lRd|0=FIۜܪ ˏ\B9 ]'i a{3˗R<| Ed':†ama(sC[ x9'_D/4BF52nn} CitW1=NxT3HN`g#,ֺ]z! @NIdFkW_*{}X)񿅛b1yT=,ô-{-RϖSiehbg^K(Љ7k/'{1ǂ GP:^ Iwx)Vbhv~D@G%0Qq,qZ)( IxZ)P=p5@Tz}exz9PK/7)_;A~T@.4Cz}<]#kse.:ǥ΢8e?ehw)L'rD*@q[,`ܢ՘=((Ul,Pq1)oA{`3.{($A$YkpD6sˇ9ەp5KbbDM$O\D("36 2޳tz;wL?ι}k9]-;ܤ^i$I!DYVʳBF㺞8Vb|D1{܁ݶk\5eY(@]/̎P0V9B:+ 9nV:"0`] @ ,),eٶ(iʲlFyCVQN @/mrH,ƒH]A CevXAo%)\I 8&=Xga=&~ê)7G ERB`{J&b][߇DfLY.j1KbF$1wT'̴a ((X)cgMˁS/8{1:9xA pQc _0e8dii .J0Iv_0t~r~r E*) <)Ujuђ~oM|`%|;YU|s"y9pN@rGȼɔ=Ǽ_GЗTS~e)r(ߠ1 Q-JxLqz?F!sWJ!'1I݇hw3Dɢt Lb+Y!pHћ*6_ P!ɢ5o|!L&ΛZ#p% ѬJhhSoȧWe)M$qY$0Rs8 3MS`&"PkW]O HB]Ә Ե!,^fTG.g=͚Rvʋ}(Q-}`^"*PH)UX[}oOƲj"H~hؐc0:Qv  ueAp]'`ٲ}y^G({A_pX>7dl-!]EMx fO0kk -y `$@@X npuKtK'^UO.lO]H3gk'j~vC~;qy0aM"Mډo6~ns63f8 `14IsXAuy|n#uHG~lagLf<~4@n i= 6:9DHTjFQe雇j\Sx427$aYUMS*G@FIS$9ρј* @hnDԶ @"**adQQ&Y$:8vG.̇HLBMq$JDT@ R2(4 ʢ֟Re)($WQ^NRRuuǨknN4;| )ۮ%٩}꺮zaaa~~~ǎ7n\~d2yʓ{O~GU~#22(NȅrMO9mh񇀔̍q.rx+kgk8, ̢ށa4HX'Ј*P=Oznt~(*hTXlu5+kj(Xh!H$ 8}D ;k:W@ -U0&Cb@2V.q2=cEPMv]gΚD$&B65hF)i DQQO&J*+BjPNUz^~3_F?M3!TZpV ):v0A;.?nZFDs”.7y0s&줔hm۶ܫj_؅g?x)%lN]6Z}67*m۝;w_<_?NNT3jM7G2r=RvpB2~}3 DDtEmLʵ>HƎE"ɶڣF~#Edxxj@ZfwB,Hw O6a%z%sw/[\#ol3[΄a^soœ^K!e0\^kZ͊>3R[Z\?/v! RCUöq k%&;@A 0߻M7b  n/-_:;ThfEl,P c"уV'ɦa0+6d M-8H %ƕ_3:g z)4~r%lr  @l z{γ/_u:)U)cT;mBm۶oԃkRGC$`&Xe/S͚fV{D1qhgमd<>;DoU1ܰR!!+rJ\q 2DHz5lsMKu,YCARt)%"iۍe6̠KqM3Wln "qݖMlڰns_aa*2J+&?_lS^;~m>_r$#Tpɦ3rسg x9=)X Ic':Gca^$^DzAlS?W7mx'_;/hF^2Z.^i÷_4-bTmIhVC,B(:P" >'6l IZUh]hgmn~ن;Wo-8fZ È"XԌbhÓ MlF2[S)MÇW$7 slOۼhdFݵ8!kpUp@SӅS+3(-vT ; ejم|h[h)5v. k5=J"q>;c3f .co\cI?x#0Z34}61`}]CŊK:o~g']>WYoo4b+;dvn(+Оb@6Þax<ٚnlش@ƃBm_ӿڃ}<f;8Ь2igffzb!~>`G_sPrab{_3oU?'CT݉@6\Nh!;w XR^р|76>7wsi7<(GvXznrc)w2ʀ7$I(>eYu]DGUڦ! QeU5u΂*˪'Pm,Ea?n+b˲NJ( A*TeY"ZY :TP7ea,K"?eYt]>#Dl, H2aT(*5;t_x!~a;vPHq4*wM~`ˎzӶzBIu2lݺO|Sj tQ9얂̪oH``OҢ eϡ';kaM Q}֊HуI>-@)~w#:Aӽ>Y*QnKu/>=>?'0["~|@$j eZ9 g a=j#:fYcC}7R0. 85)_\}ϯ۟?zӛ2'\{7m|W_{@ @A'\a/ݿy1K^{6oڰyoO (V\b@C ܙHn"6<$OPyc3>op!fT;rʀkwv IBNvMS(kzBh϶VyB0+T3v!Zxբ`OtR^QvðzKiV FPe^~3?ͥvخ*Z7j[Vf͟-,,:~nۍwmaզuwMnvۮG,}α|Gd^{sV=#lҧN=݇92j6mD'NVHȮpAgE_JԳ/`8iC\lM~5=@g5^<&;y_ܫzɥqS;"Y=Q,üzuy_~3 ?gzsLܺ%O8ow=z][!6vyi˄lD31Rz{?\ ){ܻ[𜿼{=Oրؓ{-|[3?ۿvo_ۯsnlXvb_esnl-W~o8o|?O..Ųc_CW}ʟ8WFsS}ծzw/ssN͓L :B ٧ }Sl<\ww{g)~{lDe.lm{9j8 '/;L6jw=ZgM|2.Z_:tգ3m!Z  S)bPYLQUm#UUԝXt$x<7Tz5mDxnjʲj JDY О:ʆ=J-ٸe}ʔukNh4q(UP4+q4eQu]/y??b.|_rk~]uIN}Gccxݩ3pBe Tab@#Gyj /@hH)tb ,E$xO~l?yK$fAi藁J9u`J2]FĈ̛\ e PH!{5nuSEIJb"mcФ+D4UΡ$DY$( Q$eGZ%~Zg2aD,Q[V9fVl?4+ծ 7ZFD"i_\Jmx_u\UV}-|):LDG/{' xܒqY mviZ@l;زޱ.[RngZGx։m:kؒD6j T~M5\G4<ؘt+ʇZ,sJL /t&H#(CCpZ}Orna0K5õ F]d b跗;L,dwT59yc!>H@C`;ǚe \SLZ"̍ YAC0qZ_oqZ Xyӏ;~q-PDÖ9.y{P/@GW%.=W"vZdaK~?O~s󃝛ހĆ1\ 3@OKJ6%6dtܻha.>ѥ[<@҃1p-\T7ަ!c==㠽 F贏~#n.,Kb/]{}#+S_mekBƱSF'57_Q| _poh!2F$𬧤p zG{TB>|hje+.ڇ&F˵Gg 7,_Go@4jD͔@ّB=@_G)P~LFV6$IN"bUVF\n<Um=$IFz8X%" Q9۹,+p *@H-ڝ&ֿԩgWUu]Yڶpِi}zs բ[fg_DNCŖP`NJY &f0!вeva>UP 㥣Mv{ɸ pasbkvpߺgm۶}cEȟ1yJN/ 5`X)n 8$1 A-}o@H{}i1ٔ^ tg|`ݞKZ>ȬmQ8<1gY~ Qeha  n49<q`1/nF0TڎTM7}l-7|Ĵ(mX!^'59. PV@H}D\71{S|3p{v _OQ~w;%=)A6߾j#u[僛_Dv&D7PT"fv⊁l(JSzio:o/:hK :("Ǔ~1j|TݷC.rm̅'cூp K<7WOu6ÐD IDAT̋y9TTrSc˿OREeZdI?Q^y_}W=M;I+,/eg_0A4|r:XV6qvyS֛qHBVe?E?MN;JdH r9 ޫqX؅M t>%>pC5˸h1=-˹/b$v{ ]4f+ &8Edy2bj"fv^1pd 'p.Īin-=$t뮿' _ڵuq;wq#tn0%WYן9koysN~/\0]ӈk/p@ 䂋V{m{0XDW>ϧs =/*;q]}ś?ϼ[Юɻ}F9ё/~#n V]yqw+%(w (Z{5+'~[8wҏno:I?<0|; Y3t;=D*q]~ʨJ`zRyO;&1tȭ1( d!'~3V0[> PUŦՈxcQylٵ..D::bڶ%B~clՐ,SҒ FJk "(X_z~{UmSi%lIuݨ*Fv̕ { k"&v?McOC͜5~h5 g{bCBR8NE }$>{  ߻}%zg 4OT H*Lcg55- ߠgM;o3qe t3 Sa|c0&)0#?XsImOLDiE3!6Ϯ]}>reZz㖯7<|/GϿl.4w ]?y-yrWn#!{ުK~}V^W]vΥ |C_G!o_vѿ~3-nEW\2 h>{ξݝzʉG9\O~fq}G rJ@əlv}Cdz9y/>v'.Kڮso [1!mч?sg||:KV-_q_ 8ߒ}N9 /nB\AK2PV&&튤bd[O \ʰfF` (X6cUaݧ).:r% ZK:g"P 4M\B`=?mPeaU#\BeI֍TM`R`+$=城Iі`gVB+U-֛Q˼jqQۀ`  r5(*J e!eYڶvfnnv;q\i#]ns  \t|ߞ<UبާPl$7L)X=Zcq)ierˆ6E@DL 9߻w^}v` 3sxܠDJL.h|`ƺĹȷ`ݹ/s}?G;x_|έ5h-K6{r%vV#`򱗿dY3UGX~=s=q޺ᖏ%?|[gd//yU?G:9 )NwꙬɞIo~xƉϻW3`a-W|;U/<[?}_#( =$hoϜuοi)o;w:B V}U񬏾yU}]}c9Z3.s}UJbq-}ً#o=?ܱ_ijH`{;0Լb9P:\ $΄vg&͜JB1;' y]N5/a_qSEy78#6a`Ԅv\a4BʧEZ%I,m(fBjTgq5VoQEQ6m.RduSʑZdn۶(Kw*_$C K% "a `wTlʡ)H@ٶmZ,RJDTJQ!=6@Z G1 >to~X|_yfեF}o\×]yͪg.P =lܷn}o߽^s}Fs6l}}WUҥK˾wGtI{K>|z6I/[5fNOJ,\i;=tSXzπxFZv#lRKL!w6OW0rzk^=!d/420 prr 쯔Ɛa h>l!ְl8 6 WY{X}cf`\gb3j,E0J "$g`9f"@]@&/ވ@+N o~;qѝLǸ2}Ix~ J ,#()Ô0EK4ϥ';nϷ2R9Jz4O1d!%h-Y/9eŚI „#[`4P뽈(%!B{o{ޥ(ʢU%ۮ*a"AV6XA.IequIZg2SUBV/ڶ-J(=V[XAN2*(d6iעAY[[u'd&:\HQUb.DBT}-4r͗w=,olܣ8Mk.DFMh >{rHX"zzR}^pA_V)/b9iozCr0YPy`$̔ ;7'tR?$dUPv@{ucDRZ 6Y޵a;iBAS=Tc`#9ʰPL)D*=ǀ8YJ+|I(b RL,h4a%NA*- 5mj0;йE1H!N8[逖DK`0hѻ/J|Hy04C5K2 gizCf HQLhx_!~텤pC= pHU(cN~hHsbz8}GީaxYN>Fκ*-K|xj-y=Hӭ{${F= N*;C1JՈͰ(i& DʪiFu=!zoS4ϵm-% !D; QeUm#I>to'GE)PX[IYu]!*P !$(A bIt-!b+%YV#]a"Q mK!- @0,_qjh`~~w :)_h>r7e!s~O8>c[X 巟ʧڵk`nnnݫV iII N{IDIggCDYйv{Ӡjgjpe;Ӂ4H PLVs>@GX26AΊqv-,|p4:8>dX+Scpޛj̷!@A=JK5%%mt4 ܰ X97Do6iJ6ei{0%]()/9RO1{k%\.O;~INqxH*1$CpVoDϨBg,efzC"s*IpAE.= pOzI~2| f$*#?,)R_}H qM_}$4:`U& s1^#{^~ ;bA"Bu=@MӀ%MӀV DDN}`)$PQeg\J~˲̩zi]*S] +f]D+0ŢΗV˹ fb~w𓥁zmS5k#njR/UqLʪEYԭ\eu[}Fܽ=Q׮]1W}=kD$ZCd9ԫt`4PfzlƊ.Si5 5(䡗hLC}Hٴ@ v D+0Օzp} =Z2HKxD"s(X[:Q-l"m5vR#70fAigH@Wɠ[xgƞ b8p ՃxM%t.v+]kr*}g5L&&1C?2k[/D, sb @Lb8? ('K4d#YI3KqzEK ƮźԧQ}Ӿp375 LdWWLP e~*Hm G׊4$~X~bu%9"<#̢#-$|P:qE{ao'F&̖Wdϱ}ΚqMKUeSL,ޡ"QցQ)e]QQP67`$ m(]7fmd(FrYm}Pkֻz(s h(BdNrV?N`.8㣵gy [}^vVm;@ܻVsayf 1_?׸Ѩ u;oFUqaGSy[feY.[?589~sɪۈ0eJYr5} CL6Q+",S/auȿ*~'ZIOSzh;sf=:u4 Q|ΜAH Y&qHv[TYof v0)2NhޢӡȄ7. h(Y4-;WJoCOS@`V9:Pupb3Dۣc7l_d< ޞ%e", ǚ%70(KTf,0AL50añf:哆3D wQ3,aA2"zB. Lj hp73}5Bd-G*>;̩i)Xh,inp }T灄Jlo]RIysK6m쀠(ѝ]Nh47,(~`4.GuS+HT(C@¨?նk[K厂9]YۻMu]#bYo bl& ,"iPu]UURʶeYuiV:`ԠT>Ec!D@׭+#?|}ٱcN(F1 $SBpy"d&_w4DE؝XLy72 wVCrZ ( K#nD gF N` j4_jk~fׁ` gn)T; lV-T'닧qLپ>p4 Ht/y~9aU t]6߇NnBo`v!>'л"xGMsxis+ca_cdlNxxt@7o0S cZ<熐 㜵;#74S|j#1&Nr0|}]Vj+VM~m[:L쑼Ӛ3迊?Cd!⻏D)bAy ;*H"I45ԲD L&UvמE˜UHHwUyus {VfR5.A/MKIݶeK`z]mlt@iK.PJs5k(T8n-r$.k׮_\{Փ"0%)rJf"f,{(9m\<@F݌P1,;6CDsIMN}b4aL~FE:2~/lW<NIbA3@φF=}h+iQC5Uc[ gw%̯\6n-$gyD/29jT‰CG h$9``u-$֒y|c,0m0iE_R"ծN)W!$%Ak5_Bk^YDlih2|Dm>/$Uů. Pbt L'p`|D ŕ#Ԭ'*;P Y40eqbjΈtGhqI ld0Co _$c69$#S菧R>ю@VC+*v.3ҙ&M (}L QJd҅y @Ī5MDXFmH`Uڶ&b4V{z"P b1UkeYQQ<Y-+Sv.bBH!V[ iDDr{l=DD:)JPBD–Ckj!H"Y#-"EѶ=sWm.3:9][uw^vˉH2K)O"REQzm^" IDAT.S7Ͽ9Ty&R.`"YZ'}R/e,Jʦ5uXW*36S$Mw9T`6GPiz4-*$"p}$LeXNA*Mb}6.5R}4_;wc⛪DFNJwc"ݶ8]<)| }G%y=j\I#ؔJtDt<_dBtF v5n\)fzlJ(hO#r]u4.*Ē!{1CAn 27E\̊.QOUD'8uFҀd @K&]AENaaS#ϲ1@J (V?nȤDr2i"^7؏WO%̇O6Nd =Cmi32dd`[ԖɄ,۶@.6um45ijQwe(f bU{b*iɜɌo$7#*ֲ-X̽_YBsQ(ioB >/B^w?B|ssskuw}8u.vby(~n@Ĥ^B҉eD!Fg'fwgܳ*7,J=b S5M0U&LǍ/L{S#Ekq12̷2jSZd&c$$x|cdۭ6ܴMOiG 3@QQ#LK?@@%$satFܙ=*P-n+s3swS\t^ޯqIǭ¿[8u-^ <5[RtY\bR'V\C! ч$.Y,Q I ˦5%qF* g%PqKN0# 7K(+rx>ys <E cf ';kV26PUNVgND"Ǯ]Al뚛)DZ HN^T-85ȋv6$',{ {< ;,qG@9y 2EL>_ q4%;-~`/Mx p_nsX01z?tdq0-<#or„HL2mEeL[af(=~!FhB1SjIeiA_c0Ah/+HqI9k&L8 e;Rqu={Φ,-P!wh uDIm`Yj/ "PkB g'@B BS*F,"U݂תYSELoՍG`$,SMpe}1º`NR WuɮE !JCiOڲB ƛ7p-+>?V+U6meŊZj#v`_wb;̎)!/(b6kYCHAWr:s2H٣RI30P!BUWJ ~ |л\No7̜ f#-vH!9d:m3xLN̙kfd$,㹦%ID($H(ʲjFDe5LHJF#sUԵڬ;t2QQ Q [jV$" ,R03PQQT*4VKͪ ˗DugwJI@UQba@Du][]J3Bk B@Pm-.|'n^7yܚ--_|@+q .B0W$UI75i~rɳ`=Üc<Ԁp`r[ J%ڼ0М?s~YvTg;+ .tVcr{xA4A@H!2\̂.Tnj󓪠hx4)c2`z&s~Vޞ"%&Jr ϐ8m :!4 }i]KRR@vvR m;z5uMf;KKHEZJ9jr^C$â_ j́XjaY-&ۍĪi.)[._۷9Jt]W.B݁4/MMҪY6z`IwմeAtuF@ދ:yE'#7?fh :'/ ݳzgO)|q34֡p:ϏiMmcQ G kPzĻ6.E@MAEgcP;UC)8ޚ W #L:Ղiaq+=0&[jt 7:Rm[5,pc[͙T֗$/,= ׼)?"ETYv;"Z٪&=/nOKv3R +F9+X\x]rHC+$,sٔ |s݌E]qs8ԕ0!!Yy7k<я@\$܀/ "(Ҡy ,Xn3#\'r!6OrK}0 9w]^dzs"j+x&T w4Q\Ww" >R\N)X ³pWV#-RԙD'"xEQF+j4^X% ͥA<Qj ΅F"T*:*PfoQUj'pI!"B\ߢ()X"ZV-M@hoOҾ!8.Q(d'#JIlպ)ywАkV $H_'oiWo~9{;S/w1d ;)Ez N~nn?. %x2)gJyRVKwfzcZN>V! 'tPMՇx Ơ4:Cn~mesosFN 8Cj&.OodHlC.A2)h]M3pA}ƍ;C4m7آI6!>8]> 1L='Ԧoҫ7zJwcQH Me+Fd7zDmylO%>\Rc8)2[dXgjx fWnz0SF ERqi[;@92 1f@qA7Hzbu|OI9)M*ykYD 8IJ R45,8)0Uu:$_v!' 4APkE!dUrۏ[|-EQ:l[uͯNLSH}Qg$nӰTs άPV՞^.ݶmYm}jS LP4~,̯NH%D,ЇI *6Ǝ`\,(+7fOM3#3\E޻]3a]tH | ͣ6;)a&jL7ϒV](lة1pSPe,Is#kUTa=ҏ~}.eg°9(,"r vͨ#',S \`i#ٝu/d(e4ar^ ,x:!j8KQ-SeH#ak S\^8PPղk K*x0ܘٽ 8ܒaI(ˏXLpz&=ʉW;Mr!I9|^ U v i;c^Jy !%@EFuR#(Et $F̍m??85) 9 L( 1Ӳ԰9 0(!#o $ɢ( Qt]([}.Dv( &,MTtQ4]ۡѨksLTU%$'WuhNQvnH!Bq f5 X}BU\mw#vׅ042Qu3њWuwPZ-[d661l If%@ʏ̬5 E0I&Y+c0f `c6%e [j>sNΏzzvKܾ:_]vٻ9LqwA@q Q@0 ~FEQviЭ\ʸX"NewȮ5]p),K ܡLhሐ?Q6SF/HLi.>" ʠvqa-LCWqe'bЎrLC"U)~ Ѩz[7a"Xu03FPg^#!0#F9(!\]2*i޼ [O+'Rw[I v yQAgR=0D19(ozEN+`^E7ȥ"~eSIs)#Q0VB8exh2.e$slPE)$u@l9yŕDK[Ҷ^zJ`4cT2z 7Y23/C9p25S4%H(fv4ԭ?!`6'm϶۳kDf6`nٕ@BQ4`I%5qjݳOáMˆљϮECt&}{uMHv[=M𫉝؍L0&WE4fF_Ʉҥg2nNF16C}J;&b(YW2E = ΩZ\˒J 64ZETZ:R %@ P2-Cc2EJE vϬS7i +aL٧lZ y7<|6wE({(!vկ#h*"D,,ET [s C$}Dxdߙ7,vXkMr#?d\*Ӥ:1sHBpPߞNVEF^_b!2_Q8 .adz()6hc֭ ֳ.sO~a,YL ILя|z8t|J,cp =(`n+Bk<Î{XA3NXZMeN$.3ejˀP➍>vw\;\!8w_)Bԓ %DXUVu i=-D$"N(ު-3ʲ, o%q[D,R[]m *H)wҕ?,D(BT`Mn.n<N֦2r5zR [~>U,jƫaQSg>MdW̕nee8wY ,Ԧ>4=}x_YX#$G6GHP[F2!gNŰ\@}{G H)+X]ލTR'FhޔC&Aahw3-$.vB.գ[9hI6c2O 9Z8σtPSU?Q Ci,g*!sH"ŏs%C}p c AFhfA{@\4$NT!@ojFl͓}/Wbe+ܙRC-zЛ|NF2) OK8xWOi9I!rlo|s۹)|cֲ`  bXZfM)e͜0iŠ8ʃ=SAr3%TRۖy]W*i6,(K\)u-)m;u<;PJyTnwe.*>^jՅu im ;8ʉ3 vh7-vg>r'AT?DNvʁg>'˭Wά̃is-j2b$ㆇKjaIg TiU{h!ufC` q^ԕX9 sK@0U:zi.'y"L4 ^FuKQc#Cb={;;4c4tTgfDni/=֋T@w QTO%0ݳAZr\.GjD;|׌@w4|2ݦi+#z\k+}>4 ̯c/~uP f$k"rx>^m "Gbfξ]ja9P1'c< qKт*~D_g y2ܑ&>]WHX6l29Rw\@8`B BЖ2z0!-'%#0j.<Ε.F;%!$=4߫T | J\H/ I@|rUʑb3Q78jN<' Y9m>pk݉aɋ0pBZ%54Ga70b.9zKXiJ>3ҀR}:QQX!O}soGNa8=)pS?~lZH}LҤ%ewf081uCge d+sTY~ĢUIS8=憛 L ң}W=+1\uŮ|S-_E`Jf!a#P:cBx YgzSU(IFC®,p ν=aݪjZ]HIP^_@0ty"43[j [, tڻmThuZmL}fV0E))%ӟH@ܕd#u}kTwi=*EDmfHo3\(Ù &_>NxZ͘N3@Qg[Le.PFd'7瀸—kL.Ldb F'dSTfϗzҏWX"s2k3Ҏ< w:}Jz\({J)1VM IDATd-0gҡäZ~Zz`7͚LJ&\)|/X2$!-mvP:3keI֫vHlHJY #{dF5}v7^;d!' w[hKn4,N1@ys[dcWLF1.GwG}pgXp2T9|3M6K9%L}iJ@&= {Et|ɮP\Å|FH^'}o.o9 - ,3 ?n'D3JXI,>ȎyqLk!3G}|13L1h }D/bIl@_z4۠@)%TR"bUOfۭv@YOfNI3ۖ+(ˮmL9JF%>og2(R?N"" 6l4|(5XfEaB۶J/""!|O(Kʺў¥Ta(dI}{ˇ7W([z]i,Ƭ(ͅԣ-u-71l0D4sZt"=T.O(z!d77щ퓌j55q9Ȕ C/eXq%~F,)3ݎP5\N%<.zqh}Y-y*w`@HaH2 N!#G@hh/3c]#VxA)Τ0{uH_w,G<ơ\N46Ϫ=$YD T>_dq稼┓/"x)I# {nD 0n׷UJ mϭBĶm̱]u,mo ՛X$WvtRu6._h}UPUUQL}5(lŲFnX,3uuז~j XcnLk[#o~ڮHcX"$tDt܌,Eu+М~͙ e8dM<σ 2A@#jJ=/5bȨ\Y)zS,RC?'uLܜCs\`Nc$-Cttg[vKH:bF:P[.2K67LFI᳙8?z͆Csuob43U;qp7`.M SͲ GYlGt1_G`mg`˗7؛#չq G,hi_XOӍdH٨7GWa?ω-1$>LѓAaɐtC|G&Jc>oS6 rM{APnn86|PN M?dwBV~_3VђvWD َ"IJTU,CXg/e-@8L *U( eYh SdDEI)? ifMVI&8.+R@FVEanQE@e%`PP% # W4dYbRh}ZS%XF"+`Kvi҈rj<V=BsH"/@?Z0 Yd XyE'匥}a*a?5z x)V0|Kļlgh.ٹKw롔/;z!!)5Ė0}f7o2xɧ93gBGqJؼ)g8Y㟲! A !L/#1QC(ZcaB:q&\,Zqv/نfDWAoHrް;n 5ꄳNf[%5y|^h(oF"9ڇU=7ِc>βM&3x<fN,n C%˖-+o(kwޮ=¸Z_Gxئ!3uyDmlv`?woΝ*9ڡVwԳ v4L}WCʡ-XBٺ_|ݴjۍ۫&q4Fm["ѫgt 5w̲!+>sTyt &'GRdiͫB|@p 1iaLCOs74#)0<O'&Jr]\w(D9ԉ, h%E0(/zE/M#(sڧ1Jpžذ՞ֵ q_w 0iBan󱊟Lq|84qٶr#5r7zC֖7u|xCY ڱ`L3&yJ0Q0vRG[ES`7Z@, QFՍԊY΀b@׎ -lt(Fǝĺ`\" xOQɏς GiBt +?4,ж;+qi^Ӣ*!妞vGS#o6i (/)t!)*2dng*Œ523NNcI1`Vf/5bg6')0,ǟL ƺ XL.ߌ$܍Jj_msPdn!1.P]$@M*,@m5_H‚TmGDEY#uͦNqnKvhI^< 0ر`[+Vˏw僢;IskY-H ԛˡr5zƱ%J;ǹI6 :w&p99f c8G4Fܷ{6.w*A!tQ!VbN9&WlИ󜑈fℛ1AlI_$ڦADSL._9Qu_Q̜!S-^u7jZ\uMܫ쎞{݁[ƙ#bY(iN^]5W0 { l^K/sY.̼W,znͲӤ=(dXSy[ɍRX)z-6 Ua[b1hNeT.%TO2c0bFDGݎOZAD1vA˳J5뮌2y0~xAԜ͠\O!m<&&+mS yNC bS8:ٴF0 ."(:nn="3gsЭDMxd$!pLOzR6eu3u|(t&8*,\?+4<;gEwC1v8/f\h ; HpǬxRҿ9T5hO3!&S85SfIR E^$'CCRs !pE(MUHRUUmT6QX+OdbOr7􂍑v-[V4gF47u]st)Da¡I*>N?6ԗ+*I*o׼5|#'Nصk6y979(7YE'g9q}}_ʃ=C$ᷗ")@!i^4CyazY&'icBIAF &zH2} 0ӧ1Ҫ8~/| G):@q@L:94je wdO|.RHDV3tV G.1 tԈZ ;*v嘃Y fa F2~#Iّ6m+C'.'_G>]9=n %<2c|'pNB̔!ygۢ?c6SԧgQG?$Y&il'=tԠ! 0*A<%;w߂EZ4%='.Ӥ4Ft2ޤXc &'\"5mgstdXm.eaQlaI^3} 6LvqtE!J Gd/` q!6gU5E;t p9#"&`3:4gWG|''$dO=k:سls;Ỵ[TdW]>q )w7@HGaX2\#S h2X% F[?rD澙S 1of!0̗qH?w`vكI'(XgCh! HaHdHW%*+s (GmN/nl])i0NfFJ!b]O6ɤZI@Uu(my/"BYBڂAdjE/@QQ*4sOƲNiEJHuy̿;EQ(mK m-JHJU*%]C @0?~.\ԓtRu]E!  Qhn =v*Lնp۵M4Ms=~Ǿ_|䫏Ä?C ~;sΛ"W.'O}Op,,щx|z7/QЖ¹VR8;[q>"r]T/sğ ÿy6$n"J*xHꔑc)^%Sz8Dٛ|g[YWR DQ%i -`/,Oik@(",f)+a̼pppTg](\W$T~r==dm$#i / -a@Tli 2:[҉İ&,qF Nw(15z4CA5M[dS؛@uq9zFSsZE_Tܻ\⩓v-wO&ܳkr/\=,D]Ӻ픔jr}ݚnH){[?-}/^}09L@ ,ʙ1SNjœ0j%B m۠>i !Jhgm"`f@: IdD!$w]m`pAhOu_],KWȜ@`]2 ʉ;Z?mi5c0'K7dimxE@߈?nnlKtiy( a-w=OPgg=[PtYٗ^L?)G W)V+j*M\Bs>~8 Q(%_8/'>wlLs!bQ`Rq@#Ún,"K\Dzq ؑ/=[Q*Gaa/G>mQAt`'>9^m,;^&6NQ4Lp5 0[t}i?ra֕goD@)"@)H(EʽU)R"E@+!:9@:_p@(Pg;"s 0=a(f;@R58ߊkWRX}<< da J.a<'hu]#cq u< .6>d(L<=)"Y+ xdϻ)ꮻ~?sx(KQ(H)$)RSf_?$R_?wcoXO}=^TJ;@`3o^(p0yE+35r38iuL G0<$R Q*$ l{KʲjFuR'm*imu$ if˪nD엕"B}A-еH)*o5ψJ dG:"))% !qoe R Spf}b:xT)B$VnIϽ&~, RVH}>f"/G"8p`O7UUololw]w ,]+o~N:}Y$ZF`dXB̹<JuR5gO+ɖύx0@?4 sj[}lkاRbgBOtR5}mp?TvM#F.GNܶFF&Yø;/EjZVJvC3#UyR#.>)+A51,(hY]r>1q_RR" $T&djqK4f.̃ªpldW ]&}ItbLQ'7^4JɅw%SA=`y3!r3EQ?MN8ѻk14˪oiJËyNA ]u0i0m'j'}HRY{}v 曚V7.(~o&4N[![n"$xۓOxqsZ~h¹>ܿ/?CV9Køk=ssЫAfWTZF6B!\a 8C|~Ý%1>˹I42@Ͷ"j)eAJ5͌P jYmZCmhQr6Iz]uu@,d9 Z+AY,-Ps,V7+{~PJ(ҥKImϪvQ{+T]kaWi3uynY^Z4N"! 9el v{Ny{ʤTU@J'뺮k(>7E'[Q%쩔D I#bg1!^2Rv4BYqtPcdio=w#mnV4 BFb5I a)1u[0~ RoIQQĞP+v1\ڸ.IqO?twg6يlpvNqjT.ԌPj;a'kBf-~Ōqv; *jj[ dh\ C@.mzkjDr G4pPk;g&QVWl{pK8IeOiSOA}Dgc_tq`pq!9GRZ)ǚv'i? V9V IDATg~h+K DTWEQ(D!-Ow}ʢD@RJvmY`]f]tͬͺk_6kH~MWVayϾpYs`P^yFĆ;w{Ћ]gBtls|̒7e!  XF(겶 꺶]M'K@zH D1qeY: H>Hy@=[m[-V;A (H*.$@6EF(p K5:ZcbKжL Yڦ0iwvaʞ &*}5ٻؙ|d95 4|J21,q5ٻgڥe!N60#S5I(D]Ojۛ[W8p!s FD;B9lbXٱѲ=/VZ!]"@G>_{\?7ݳsGOw~qGOߥ *P(vrC#ۢy;\|[gĺ)FyG/7\2j6Q[P({Mw׼{gJ1btJ ? D!p?3lx+aɢ^F%x{jqY*@55uU>4Z8.c~?l"@PwrGkN 9Clq`ݿv߼wX#G&+&ˏ݇:wױ=-,7aϚk pa@'Y9꛿3D\" @f6Xb`RsSK3=4%h_Y=q|P$uRvD !unP~r2(PBQRYZIYpxI+?q.L32u/u% G4*/9əŒQ'ÊL9ZOff"@mZ۶{5B@1B=zk["@8S̑$0M9˹ReNl c1(Z/ɑR^ ΕOUUŵvRr2Z,z[ʮ]E!,|#pxñ(!sc(_h0f\N0 IP@/}SS87jظ`l@hcPSh2'qdM^!W3%fgA ]`uȈ wRJe{ݻwO&xgo|ۇfuY >4󃢣=f#=\;`㖭PC>ߜOΩ?UJP$(a!p@`]/,&| l<dDT5\)i% #7~Zdx,ʽ@A@lVj.le[wcʾc~nrfp3Uko;oediu2Y.E If]3k7Kۗ.n??~/<}i&MNRy4rX6Vl܌JGYc{p{ +`C1'Q:܈8K!ZwTJvHvJJR" e%G1&p(DQ.ꪨ&} ٵJ) u^5q9CqfCNDEN@kW2X8LK%2hIXuXiwlYmEUm([j&mfEYU,hJ)m2!yC˲T%"TD[U۹skSːHpJѨӺ\; :JEj+@!EaD]1MmDP*h=^ 6g:܇ZŒKv du,HI%l2=♳gX`ۏ(Ł}W_}l4R$ ޜ1yJ 4W{QRk&on]vo_|Vf&ﻗ\{=xk_~!0P ?:h԰rY@ v!?G:yC"h`+eaBBRIT';QVWґ#G77W>gv w:) $~;͙`ƒCPu#H=ю+N!B`QRRg@ L+me:2ԶK\8<Μǚ'tgt 8anYqߺ)Q [-0v,׮\oJoT$eQ-Mʽ{Jck{_է=y^-s `p`3>nN~(!q4%9*/]\lF6?D& @@"ڦkZdR)HI\&FDUYN钨*(HOrLكDT9Er"8/O[`G!%8f2((]HD$F4$ R*jI $賠6mgy)ةR*5E qg&2V6Y wĴo+ [ևH"}б"BҳGEᏀv)wp:,Uoy @{[*uX\rdQroi.Vn%&e4$fm%ۖ>sgϝ4uV^:wD_AF@* OƩx4:kTo'7hlPͧyŔQGiͥGLuF,ΔNb_Ĝ l,}Or=ۍH33#Gv1Iq]Z>|w\w=w7xn`vؿ>|9&d֘80>c44XG @/\ >ayReO_zLtʎoA#Mͥiyw5_h CS\3_xM;78^C&eX*gmڤ#4S`2mq]nFGAifRvEUM3#D,R}'p3'mfDU]M=")w dY g鋁X!D״H6(P!q:[Z1`iVMZH(% E $@̅v)7M Y :#Y%=Ȃ@vsܷ?y uRx[ZG`&)ER* ۶uod I%>23hȧģ5,krRy󍊔t`^|}~s/_t9Ԍ kv~7FRz+ѥ$S/a/gtb(%5qyt(B ,MJ*odbڛG{׼2{UW]7+k?7Ͼ|yӮX~`6[x4~କ$c@!^#)+3v#du {zw\9 5y\~^B{#XJfxͤ\k ъHj;*Jک+'^-]B-6EK1Pn11+0J- ?`iwu`0бoCnWH4%xk~O8[ڿ\Y*oY]W:  gm쪳JjAv-t nkUae8kջ]KKˡަLq]brGd kGH'݋j * 7ÿpa0Z``zzRԴ @R4XR:PR6M;ERζ@<: 6*H/N]&Q۶煮_W9, E{eXߪ%Il.(u<[{5Bf&)%tv]I+! w<7fpfrV=bk/=Rh¢kw,{1W: ˇR $\'='!c[k E Z7'N:~&8dܔQuqʃB*ATՏ";^ wySl(JnkO=G̏o1;IMbC߮wr'ߙ&" );sbU)uf 6N?u~^sflڿ9ڽq;{G&J5V$+xdNlXv'elJMNxl6JG;b]eYC#/b>4z< b3C:QN9ߒfZ2?!e% '۳-B~! Di6@4YZjF[(KG]M;فR@PO&M3sZ%uTrv!Vjm[!DsQG)y1GCۃPEEP RI eQJ)>=; H8X& @k +hE1ˮ#RTYB,^^Z޵{֜\e 7-᪪VN鴴w3,2h)ņsBÄ\u>ML$ 0:p65M۵' ?ƃT2 PCGL9=rƅ'L8QQ=0O]4GbPv*`S| Cfkkgz702ϩ=A)`ͤh$ Gup<`n$jbn(Y` @+k]޴g> OÇ>ψk~7w] 4k|wʢ\pySbpbrCCDŚN <&r8HݢBtIn@%IIͥ]Wqg!ѷ~ǩVZ GW,B۽شg2c]Ů 1]^>y;z6, s竺( S:_X@!>|ñڮ=vS,IUX1Āي\͠Y|+TCFlo2^*(Bଙ! D)[VL#l h)mv/%rȵ)>a֜{Є򣤵->k7$ .6MDEˍva)eUUх}d眮Zu[[޵kO<|fmmm65Mc舺!TU5Lƒ勈Nĉo}[ʆfiOg\4?_C~y&u|R% J{3ؠ#я?g~:R !S@/`Qݡ'LRмFDYMSfY 9Jt ˨s Dd/DZ&UT+_zў_vw@9f馌18*G LF.4+{魩N=n{gw[}}w᛿w<w?U|g^uiҴX7~`$H IDAT4s9]֣{YsʯQ?l]v%d ?JI0vc1qeڄ^@B53)-H*%{g58=ߥqŶdrѥY'~o-U9ɪ(Chw2ꡞ-saA=zY ,dG.n1{T˲W EEoC 9ag[O]^l}ޕɖvW{_l:̂BC\O޲W [ < f:)5>)xC}j1&*?{ώ`?bPJ>WWWMޮ|"Y)(ew/EsUitfu~7{zOk?Q4)% ÷xIa짣!7Js7;Eն IUeYmyt6Y>JQUu6JI,ʉZ) 1Lʢ]@Qt @D-LsX;`])UfJ)bAEQi?!Ϋ MxFD)I)EQN!&(m!"PBMc?(E(R(Ck3(զs=UUv߳ mhBx{@y7ͺu^K ڳ@xM1qO#+sgTP$!rc7_}~~oyPz`so +/ ]CTK'͞>#?h1ɌSh.%z@.\(^ {}~v@R* &F\Y4kۑ%'{+r)¼wk4LuaQ8{BU"6<w͇n>f)R@D)Lh,^39 vϞxrMv?}pk 4|b-Tη?hX QkR2$ʱn֥*:={|xc?~p fy`L|RमAriϮzi׌%!e jԱ2RJ;M\ٽ ,ό^)>Ҝ#GĽ"/k#KO`=ؽ@kPf)R]:i EAJ @mf슢'D,",ud5w]7ܺVU, J)s02eu67%CDNӖNI|m]ז*%HBpcD26Pos=m%ŋ^v$q-'"/O!O.+ɦ>@)$*!R WUmoٳwf=ۦM !bpO4gq悳m׈pp0꘠6Q-* <I@ &dlQ.—3=)K*Ξ\Ә(7˧o}~U}Jxiq:q5/jK{{J>lh'tP?{odk;I|lmW'a> _խG\>~aRjeeСCl gC'׾J F2 iLsݻ0< =IA; ns`Q,g`+kv-^Dw}خL:*˺_T1{ߑ_7^=ˇ>~_|Qg.wO|miY"E%`e|oH+ѶNd)׽9%rU(CdBAHrQaH^]y-u3$I"j7k!g>߼X*PP\{;^^b=2գEBӦc.NEa`st"Ǡ;+&Wmk.y̛۴*E UTnSO'%~fW \(Ѩ hryi=l_( F@$%﷖ՖpUUl[IUeQU]ېR@PMkI=uUmCZ]SMPJ!B.x(QUuDYVBuRvTm% P HR]k ,dBuUQ"NuEQ()]\TϫR̾Ma<:o;F윮:%}s'O[nBۻW|75ǟyd3]9~3H_tEb3S e/ Z`!>s_<;j>*w`e `QIa*I9E$R0qdsRe,m&+`^.gsΞ+*容yɋg\{fv#Qf-R81CWٰ$#s: /yIGTK9C?ܵso>oox 79~×vYO_>ٽ]YY9vV೏]{z?ywZ0b]mQFӫXb_f SbZk[ȣ! kOg dl;(Hj:Fkbz;?>[nҟj]Ճ\||]<́'8,iʈ>;h߳H QooMʯ5K)Y},KAAG2Fkq}FpU涯*$Fei|\ŘvHpOD?wMGש+릘'VTufS-֖2vhe6b=l y[FH9`fzbStqf[mMw%~U[O:hpN_nGfٳ焔֮?gRE->+nmpEvC(&7`쨷]p'8jWPH0DQe" !ҬV|6JNfƬt%ҴW2Qorb0XbH,HkD4BR2N{> !Ri(Hf`plu+ YHa@QWpY_lT)AҥQJ*ހ>dM\v#@Dfm,ļ(BkbU ̜ 9 rtNmۺu$3/,,|#14oHKKKZ<ϙ٘@̓;l&}[{ޡ]eUO8eK}&~WXjInf@V `׽W_i0?|_lߺwZ" pD-?a<0j:4QUiwZt7`s!͟k\ꝿrֽ?~əoz܆ů&2h0OToén#[s"t/@t~Z ]xyS? ï|[*__޺Gl}z~X΃] 68лpLg?8y%ZnKߧYdz*s(H6,ZAo{N`4=^-͵v㭲 4[.3So=/?uvc|亵m#g&~.k[{8NLc 0e2QWƴ3CF׃CgZ'2xB|YJ#dnlK -"WޅGgG]'yjXp;J͛,-/yy Z)αt^reIטǎLNLٹsz_e/o½.:|SnHz5WLO{S-o%syD DT3u \}-D"x<ˆ˂DeYhkRd#3̉$ }*U]>l5j2Juu'($`)i4=,u[q۵ܦ2&ϓnjc_p3Mj@8m~°)(š,\ULJPѫFgXBFBNdK7$ >^}LorV-{^7lذann zM8s_ySp|!Lݨ`~OvtF縶+$Ut%m4I67\Ӱ~1*D.)Z{R7ֿ""l/$ &.f[yv_GR^L_l1d5KFiG淉K 5sl} wh|m5kGBb>1lOLZb.]99DԌt6>M1joBU n lc}gw!bƎbM+ B5ζ0Gv0pˮx`eoظrd+0 VRB$ `Ye)*q̎Gťv?y`c`7nm lӢ2Ee֤oCDi)+::/жe_* 4"y>`ȴ,r!")f*Kd2132I !:crU@l efֺd@\ŵK-`C4ZkR(4sbffEAXn@BJTJ$#Nl)IXYY1ͯ# lTM[g\?\cfFqۍd19KcmJl5(F$ISSa@cǎ?NeSB4˒;YwzL} $Zz 5{9y ۦWkrmAjO@TT*_q[\n:ΟW}y0 nSkcM>E@* q+/lyP7߶Ĺ#8}8 ){/P^?w)|:ou2b/Һ$*VJR+EVx2_g3TE{CtyD],].;4FUl'oKA  -;lv9\XeRA}z̏qQJ~"A1ضua&AlCkw<öEڃp}Âr1@P[6e>"@R1Xpv|0XFGYVReI(E`% T'x?gJ);$ 9; Vc椮4@S4Xh633T%kw~ةc݃@\5` 1hn3rGᰚ۹?wu׃>ԃ{xk"|'J6|y}:uE޾7U"$1L6a8K3iҕ)"W}ࣟ<~dd $sw>Q6ے6v5mu:fWWĊTT#6.Z;S>?;uն]/?|awڵu;^?5{f6y%J,[9zZ7NE%Fy]9Њx_zh'w_s޻`v |b}|pv2g>cǎ1r3g6BcsB+"4!#@.I"M{EQi[YO4E fYY|:2fU$eɀN畩y2!"s%/Xk(MŚ+E&c$E1B[|{( % ҤIijn!hk6 IDATu5NYD(FDKhwa!s0]D|4]77G gq:%x kx7r2" MZZn?[ zgΜr} Ý`lq'q)TQ6 !UA,{'DL?y%M?셿Ï<@o7rʗ}[?Ŷ= ̗k)**<FK[FLO<º ߽Y]}qIWZ=B)\V5&}wu]7x/ 7G£Gl>^:l@Q,$R&Bef>ȻY<\͎wK~ma7g{E9#d~OjO{hxǩT2ڹ(R[n D6cĨ5,&Q.%[w趏\EYlCQ(K=r֒emVZOi}ѻ^{?1%0+T2uPD?^TVdZim*U@L[džmb&56\nG~ Ujm;3bO/ P0}h۰7)}4U 3 \g-bee,ɩ)f>~-;Ξ=cdwhYe?p=m ~ 7|7^KٻTm!R9yu#N m[wn]0!Bn\#&mGWJ ̕@Ҫ|(P`q ѿv5B ' aVtw3ZgDpAG2FǖW:6olFϳ~ o}'ҩrh^3u;~lG3;w]{ύ6^Ǒ2j牋o` :@0oMfuL | iMYl۶~coɏVYߟy;vb/ͤSZў:!hʂ E9u`#=ׅu`T iZ:2! keϝw/ҵJ:tج\c,^}G!sk;^xQ}3ѹ+l޴I$;ۺ PPp&F F6񍙱~WpunIcx gG0(kL- s_-ډD$ZV@Rw;NJ"fo6Ϻ$½ Z+R˼R^TX/n vt[ǷB[n#﹩eXEhDm#a}6[Ef5Wܼ BH)EqB^xXe&9Ϝgp%k'O{-{7oڼq݆1֚En"!pb'b؂f9.,iBʬ!0z]V@`Q ġ!ywR/<765`VWVSM޸)~Wӿ%i"Ty^Tzu7f=x=LEE;m {izĆK&cǴEB]pf 1F&roxzeY0L$j'O33żȁ+A5 *.byy,gi&iʋ|aiᖛn۰9$gi}yCS B".: լBX ##3;GsR%FJkҤAxA.0s( fFʒ--* ZFGj^pI@R0FNlZ.2K(L#kFXNMV]ߡ+% !ʲV-&؜o ~+++EQEѥ4Ȳlii5>M3fgguHX-zo>crKi7y} n^_]0㭵Y eWTB<Ȱg3Y#Կą&6ctFGf̣ 7Q4]Ƃuηo?W]w?F: L$.Jre|D庹3?o\O]HDZ!"ls"0Gk9@dݤK5I6Ip 0Z+ep=(Go? <^W򕯜Xf;x{3M`-&:%v\{Z]h cUxP]+ZL'x`vUs$VDo&Q#Bw 0rZ*716qp#<]2Gb9 0*PzFkF7(!bj53"*7Dmr!xi&?EPpz30ңU]@̃R}{^B9IӎDiMz6/w(rzjUj=; -qhvQlu3F߻wS`ML"wKBc|-d;L!Uvyo4OMO™gf禦++'k֬]3=i0,Ed~,?eTk]1"Z_xaغuxihÝ?@$TˍR VJE▮ĺU/ a5 xq:H0?X3WAMYj"IfbTiM~YYW9$d"beB&DI9 !b̪IfpVD)eW@WJP]*gp˴s֚91H8212PW(Uh45vD0g M)diOj(K/IkaBո*BWW:JٞS׺6uub|1W:vvMWuuQg AOT" 3~aֹ+g?e`TM@J4) S 2; Tpl_E$9ɼ=aʠ\r)3s7XL:Zr ŹωX bJϡӣؕS-fI3{Vd]#rXLqsϝB\p"@=y9*ZxE~\j3vWhqKwI248D^_ΰfy7oWKqㆥţǎ^ś&''ΟD//.\@DeY95@C)UҚ5iVLɬs SS|n;p%kE 6{*h(UpXyП6`oh; iGяRDd&ZˢJsX `.1|!"U̔+LM@VD4vZk\QVZ_pk+`TѺr`|GWU:봻3Z_u{D f&>dAL9KJ`ʣs{^u^}CuF+rd`F <4P}wf9e!tA ;v]!x??AMZ$Be1UZ^݅иܫXf2"3 Ի1yܠ{)Hh\̄>b E*b< ?Ss &uϞ׶ò>2nK3y{2k;tpJ4j%G Vi@H|e08ؾ 6Oo]/ZSLz̊^SYAw-8_Xϗm5+Gl,J1QpPw] nTWߌtV~i:@[72,Lիb×xy Lə@# ?2w=o6sG3[=MbOVVl6K2YlB|iŁ0I׽? 7شt1f%Q`ei~ͺILޚuWm҈&aa۶)@ Ig.vL݄O#/2 MtgUa`VX,#@[_5D stM*V|{(`AP$*#.$$X&Sc.@{jo }c֌2lg"zM3Ū{6]VNM'˕C[bdk/-Y 3gH{Q3ۥH7_߯-NX?fe^zP7i``8#^nj}WPô+!d,!+YT-eV=~ Tpz!?ƇB l.PIWvWUW]%+~ҟt46fnܲÆ :bl6&HqrJCN"Ȁ\HA]h}ʰ5TQCaJv f MJd4EY8 f@TfF@"zN봲zX& {oE傽 (D iw+GV N,3J]b4 +zcl;D`gٴw^.Sz]d13fqre˪?>c.]F\m|$IVEŘ*}'"$J+h>\#V_Y4Jf^M56kk*3 vC7q@xOv B]Y21p$n;[hGA-@4Z#j[̠^@0r}:2XP~'@-q#JAb+[HwR`& Ǖ ½54P"&%ԭ&h7KR'[\HD)OrW u|0{:l#KW9t3_;s"AUHmߧZ5`vn>q8rpA-Cgtmm+nL搌 CSW$^DS, JC֟kyǿ۾koh.\O  @YBA;cRjBuyD1zag0Ҕ}ı_z)$YY5"z, LDZBkIE1K{T̄cbDLXk&2!E"5(0IRٯ`,ݮrs@bɤ4Z+ŧR , FZk2̡qZ$b",Pi-dAJY!-(PnϨ'9EYbdX-L ~QK#"VZ3 f^\ྜྷ<8Oà+lp̜ܚۯv9VW8nd~^B?G}_&3FUǽM^nE82I3l|_1n(S$.U1 R ᴗT`*!wIBqւZf @F/0 C҉Jb.'Ib3:[اHxؾ ClsnhĶW8b6G, űV/VrblWO';[f_Mls 0Hj TpFff9MdZ0ö9OF,4C68 U[['&\=qKqp*R!u~;Y_ٯoN }4űGR\:<{cq;:6 ǢCA~EөMqBD% K.Vuj/,Ԟ3e,k_G}'7мooW/yuY}龔:cyiz;yD7־,fB*2QQ GEk5b]s~-D9 Y6weif"@h5gYF+[uAeLK;n2=ƽ6Nk t!bI:IVy:g^ݳEMڪZ1vH?@p&mL*4 Dtԉ\qw܇ IDAT~/s^I6?z# <٧ y5Q jॄKğm(' mhR0ޙ .?W(?KbRSZm9LtI0vpW4zq&4Cs}*BX]\Rh}֖%Ec޹sDMmp EQoUii Ի.){~&~->\nһ*j#򄧠WՀ|䤨Fĭ 9470*KD uYLc `mg3 :P1y8gM@@FuxcU\ \^ٷ3\hocWUc&}S7f>tTqKqpx#p]>3}gnu+K$MGO.Nm[NtBmBWM|Y.q:(-9.{6{Bn1c}v=uB]5Y\v?s@/OHR$2R9M޽qs(0`f,Ν?KCCT27qc^:cf'ph*^D`Vk.`D]Cw`g' @!$ժ$MerzEK{E,_h,ey>@@DaI)LRi{!Rs5xT̺($IDHDmy6S2Nkfh<32L;EDf`&ֺ4p՜a?l XQ!+jD vvE^"FKsThy|# #]ULTj?Rߵ> )u^ڛŹ C/\$ ZRJGd"20ݝ_o<|޷G֮=t襪a@4giMZ0JZa]k{/0H~yRd,3K!VVJIdK!@cnIt>V3l.ؔkr A iHvFW R4M'&&>"}wqGk=/$`⩎Zx0^C֟"rhY=Z)^e) PZkR2ff"&ЂE2 6Nx 䵗 `7vZQ>?[G|$Nx}g\Җ]oT1gKk/}5 3~2CݰuKy/?r}^np8yv(rN\9yf)nY:bUI[߮|`\$z#L/͒P iyGS0{&vO1$vqBI(YJi.7OZ&ZVj;JRc`:O9R&y ^p6?vKcC}/Z%l<\Jg_=JTD<7)3ؗ$+Ri-L(mBH5,˔*, )Z I*H@0"MS+H45싴bNf67-cZLLXbcl0Rު@ !Ll윌 vFhaR}l2!"U9{E x9Jhw8tYeYc,Zߪ3,Z 2y{`:/GᲖ0š XkZ9 I*{Y)3h}Ӥ&-Xځډ W/*q)p{9&4}ܶz3_zX|~O=Kw޾sjb1&d@>csWݩW֪eT&ٵ"c:"ݴptw}2 u7ҭ~\mHϠHÎt5ͱ[(qL?Aj})ؽk+8Uo{׋0pQD4 ,o~: n0xhE}NZ'[.IaXP$({ {tJR3͢#y7-.c %tExG%;?NlggF鉶1[j*zUxWI<ݨA)-(wuKh. vT=V^#efʧ'otUq)?{~ЧUܰwkj~cDŜ+Y LD2&k^s;?brч+R\73)-z;#-bM|mAUIPJW&I_5nrTZ_'*J:[o 7n5K+JQ/RʳRlt~Ӧ^/kq|+n1'}[[pC*5N+CF]gFGV<#Z&nL$޿u= JcLeY=pYL,DșQ&%0h"TJVyeS]y#c#X"iFZ+ǀ[%؋f(N,Kwo&ؖ5}#Qɠ9xjoS9KwY77rqS}_x.)Dȩ^?K/?Ӌ,|K&@wy*M/Y@+Z]wiyfS(`~/ٹsJ^yYjMJkg;mlh9"mB PI#I{Yf̲T <{=)1wadRWbJqo珼cXH=ެoJoָ!q]fB,+Y0u>X1 +punY2@L\BVV\Dsݩ,0NH6X7zȶ:kuX9-F`GTx&sר aFC鴰Jk6/VuC;1/o"f(x蔷=_]?)XX\ k̬ЇGKs/1`t~X۝Ņ.ktMaQZ[Y}Y-?LT.H7\du- '=K8Y_@DK赿Gs7HW-3blzv-sٞ(& .68󥥥1/\暿OEo߾OƩ0rӉFpV)|s܀aA#yvxM 8$UdֺlTXJQ Z+Qp|e0"Q.۶:!^ob#w[ָ-p9B^M+JbZ6ͨc Zfg/*]fiUSSSDz0y&=F`@N"D[3;̟?uԱS/ ?:Bhk~0䉻m{X0 ށ:g`;W{G6Ân4oz sz55֥&& MLT*&@6;[R4ceYLٛ]/~nnsϝr^ZV!kDv> Ծ-EvJ klQ^ўӁא`r1>lZQPZ3CfJ)32ۙ@$$I* 맽L i@,z&I+-T"2R @@`(Z% .mR$5^D,2fE֚eA:^ {!1֕4"U2#o @" 8MRrz / /O/mհ:[0saaK} -oޱcG/S;x|釾ЗvvUJ7ykIê8(K#u5}Pu½qhdh_ 3N_x{sCy~nQEEQ9r_?#:y*K,I-u+:ofD&R(ן[Qh)[PPϐ3}u{3Nř7 \xBzbJlUL$iyQdZ>5T(7i,eV>ėVʗV.l=SUI3}'LRh<ĐVT*_.\8{]> IDATGTqKTkj2qRT(t Bskq`GWQ-7݀sb[e X\4Pɲ..X|ٲEzkHJtٲ対j v/gt}]A\$e>x^Kal% vևEyW[o];h +ա#;"j[Iܻ,jY_}9Ի 0dȱ a!Iaï.sl{Wv nu!4Ugb%Ir^x'x|Q$D_om`YN'y/]5\F`*)7co:q(hBg*&MyZݹzެ%}k ȷ_Qe[i+Kn{fiB^H[D` mqXv٪H d'=OCOG7VǴU:6,j 2+]w~|'754d- znPq `hEJfgg@6VD? o\ P A>Q!DE< +/*P3畏sd㭷v+mY}:|$ jHh즛;on[_~els# *!f,)<ڄ_`5p~]1J*6rDv7CSs›?VeD㸡;VjqܐBT*8i(߱Vkk4MjMEjׁ֨@QU(M$H~#)4Vj{CT|$d'? !Rmf $IT' DB i*ZPe?M]lNA7*ĥq pHˁlQ4W~z3k/u`Z1\~ۊ[GTŠ. VWKo.32)oOȝ޻ǎVޱ.pErqmmÎ\4O]=E3C餓FaV8iΙ3g>/G"D 16,ߖQ8TeX|֙dvHdŶC} r;ɁB P4ȑp׭Q6/Xi 6xcSöh ;!\~[2їiUr\EW $$}t$q,PH!q\'$>Rۆ:BvTU^GD(SYS1[2lc :UXGknVu&aŪm((O /;"xA_e~d S̹\&|cB[07hcwdԒ` f_$0f^q':.Z(s5oG,׺I| !_6wo@K %7x>4bx~6=?;uF79zUp)-Y1K ?LI'qp&(<]p"6.k2S1D6ɭӐK CBΟ?n9s{zMFCۆ:F)`?[^ByRٌx?7*2T{fZKUQB + Hi7ygJ#j1y1iP"=Xa)\ŝ"X_pN Gv`UGǕ Yy&Ys A ^PjaB{ol˻eLxɪ 9QFt RxC+Æv|Vmg{n3|йSXN}Dxbv꓏qRӾFZz]ѵwa*> Ӛ&yrX,Ls,9,5癶E\8MM϶A+B_I?.lf!"Fjhh72$4BTmq\@!0DB(FFPV5buv4UcjT'Y$BbjBrS[I-,,-HJRg/ xHjBx*VԁR L(XE}ϠOiºHy>ac.b)P*Ȭ}+ICIDT1͊JTR!mFQ*b+߬U ]S#vsJ>h·uLgP>85B?&sRPMp9T1L)"pEcK\%|ٸɭSefO[9!K@mڌ (u83TR&-1}Qrh@V*8}p( Snyd0l`#5J jN`H0>F'0|2c}*_C`R6XaKef9HB-<6g?KDA="7 Ѳs%!g>+(Ӡ1Ψl1秔:-]H{Ob^`AD$ |͓e&!ߣ*"Z2^X N?B~u.Nڪd7m@$@9\7FD(S~ZyPV>jF!"QTqCjh=j@D$L!R*Qlq2*Q$4B(@Y|UJU:[-[,)Heբj@&R蝽΢FTVP@]rĈ/7nE1=i@W@,kk1sZR~vZ V@S" ~@@*Uts|)ߝ*GyiWَK~TCoo#1\$m@ UxA 9)ʃ̐|[ܪE3˝!DVOpd-z! I"h4L":3ƫreBRԬp6_9NZ)%Q}{:obvYHc3JŲsZXժj܊ϋF.VI=/n1bDZS{EzOOOR>b/oh%8ؼbN$Vfi1P$[ ~/o܍`̜I| +֍tq8f.#:,i 4U(\Ъ*֝ 7?hS$-mR\2t1Fޘ&`̗Ij+M8S,RB! L<8)le`? ,1:C78('q^ȨZ٫֓ &BtT,9Xű b2g{+^3ė'_Bgcǁ).Y W"B1 {ty-8<:M9T2ӎ nnd+S9RVSMϜWpʌD>$@0S9-qZb4yǁe~=S&x!cYwRQ+ՙTh7›P2,փYq۬hYHfK< e6O&iQy.c,+%i\ 6HP:Z!n2Ab|G0,9RDžNTƖ,)ʸ) *gl S` ݾ?)PH#s5?iC\{Ղ]0hXl6i0x&Sxu5_{+@bBʗR2_-1c|IJM,F$M1B$*b 0iQu|XARW7(4AQT!Hs%o5e;r|lvC/J)+qjݲxyx٬d.n ![IMu%9dk.Tb;{?N;K2j < 7Y#\]Vqkּ9_IeWXM&Z֌#5BСTH6㸹gW.ȮL4Cګ?;_@uj#7T)l9Uyg \@p# 5.#櫐MB-/(]e樇([>mO*9AS07Doa7omj;fv8ۗ%ѕI^k:̇>,BWrp {G?N3ҽH6F]i)6QzE`^٩])BHbBTHf: KAR 89`U([/p5աVZ*LfZm TA{!0%YakHA vRa#b@Rrz|rhǀIھa.@!nvclQiO>?یBMPasKm-~X@vA+-R[Ca<u5tlpEpą$ll^. FIsSZOBĮ䙄]ʏ{ 'L,ߠ$Zu2z)MŬpDAr7$B)iz+UtXL >srLmUDlY•i3[ =\Ja!)j(lfW>pJ c[paWW[lV[ŋg+F#2MSZy2sF"X%q#jĩLheJ-I Jި Tqܐ2%DR9VExdR1k AyyBnC_>SDUjiPn ]$%HurRJ@a(2+վe*Vk7gnQRKQ@Ȋo%)Pja&PbdO8bǵttDhDQ-g $4SI)<[7ڠTX Cy@ukޗ5e }7f[W6F9+Zz!WBmy5U̖Gʡ!@?2ư/8>%fppxIds>w<#"sr_ % PfF`9}+h4wPm9bgG}: IDATIoj*29΄# wzlXj-iG&A)_GI?rH+*3 ?5H,t"O-K,&p~ϸv/l M nEi Lhҥ+WxY SI$qi*eJd䤅@R A@JӘk*H$i%}(qCG8nm5ƍfR_^\Ur(嬒99I pfJ)#ǂ }sovO\t$H8曇MWUԳ'GQdlr Lf S?5e3(\ֶj bkY/PӠŠͅ5HLRf4KQ ma fw F+萇ɔ.8H,-@ !0s U&qQL@iu6f-w Yly4/lœ >k,`d^ _uk-'a(އPDEӿ +'?.SgI䃱g2fn Мt?o ʄiSk,gP۟wဇ$ix4nB8!8 s/LF'F>U+;䮠vltf燲 .@}*lv-Ao@@KFD'hC}R*8B΀WYDQ^Lώ,uS4hFII (A.$IB ED[IR5 "jJJwRfQ@S[ D$4AR'0B!AR 8Q[s+"Re5AwU@o5G[@9g6[vAϣ6Ycth'BfQ +v ݟ|!7/k*n;Kݵx0ސ9hFT^5z{*'[$10-Us2g#9|bJS61Qr,sOArElڶ=ٯ#3)({w5v~`2c33﬜ o=eb⷟pY2)?9k+3h4=8BJNI8@lUCV1%Gu/_伕l"X}Ng8XY䘸bf|&X=IOڼ4Y!esԢV@doiYDCR[M84h^D`'ZNh: 1h<̩|;7_dY|JWGg숵'C.܈RI$IYJ8NcԶt)}}j,5:ARhTeQS$dF] in6S'*zqbC9d:R|9cY5jSŒ$QM3 =Mؠa$I2KZ ]Ld#i3 0?OҼVfi7Mr8)@A5RrZBB^u6H`ȕ[_@$} ̚CJW<'(ȍqOBaj؏D-\&4%ƛ-ld¯ f<!g.ިd e:db:AnLamJFOa{s*1ȫpny~ 5jd?;|}h`p=-AALY+0ΗIC6lQeQx!x˟2%u3ݚZdg&g4 4{P], HpNCn , hp];,UTM>}oߪ3F qo) U)(OQ̈^H>/1tK_NxlYw41۰V΍3-:|EdxQa9Sjdž] ?)q–|$VcWotlXD:[G]PSDL8B:foFJ7l_$8c,3G1fuC|K0p3/T*/4mQʴ,\ku*.#^bK.O!IhKV֔$1cq! DL E$LEZ7 2)t`RC?-8lx䥉Xq&[W+Pra]+&9%JS=4齎,k0Wf;ǔ-L G(Iw+}<^TQ $b^MnԾq[-gjQN@d0P\CD_W?.=Gv^{-λq\^Q3z*vȶVr%qI[,"mج|͓tHeԩ)X(JrKW(,+z P,UzԟuܚBM6%Qũ1k4Z^RϩMՕ(Sy#ׯpɟ퇎!Bs];@Bg2jkWvF haL\}Y)ewY[:{ؕ; G2ljsY^9;uPye/gT ^3Tѧ |-=]걸\|T(K(4hTʨkim6 LWu\wdWun,_7e(wѪ᎑PW !Fak_nOLѿXCXP\ަ8uB_V2c@F\*}?P%q]L@': ˶(ád$sv8|>sW@[c)ѓ)cbv?Cv>Z( tɔkhzYT/;H0ݧ Sڊrgc[&g.Zn0LzRE͡L0-L|)~AhY*49;޲_qi#=&1ux1vr8[0dzPdœ>q'҈=֟+o8i`ﳺJg_#SI 6XJ gB{ "l"4T'Z&Pg<\G5WM2)2L!>O%~ET4JV"iH JBEAHAN+\x@ol.CA%CmѢ)O5dԌ-?sF3D|*TBgc *L.2rӶ*/ʒ4hj$0ؕ˨'YIxNdd|6+XUt5mbaDϪV!ݑa:Fq4|áмs' fb]0YhՅ@m2Q5$4{jqlvcxA6^ |}q[9mo% d)(/.zy5g󆡁A3'Y2uJ5y -tk8*\3=7> + ['\ Q0(Q9c-4c4L񭞃u>ZiR1ɔ)UMPd.Fb3?.ɲsQ˒(ۅ|N}83kuN<e ?|rm`8jp@pb |֢_&TS(p#IY_|j!U*hG|fU  ,&Rt8J2criWBB]bH@7|F)7 83u PϬ˟Ix8ˁ6 CV-9w<y6!S ѲBP=Tʂ o0h)")QKk[Sy-b0K9ùzr1 5weg׆'1#BXU6RKᕴD&[fE!uDF^|a`EK{{|DIu#ʈzpgՊN&.gkN斸 :Oj [-y)UW axfyWedp<+a ?yw6- IU~ k8m_{aZJ\GK-ЁZg,{Wi[U ܉h6Ŋ nI;dIv qĻFop4fP>KZ>S+G%pDcol&Fݥo E_h5UL蕿h8 V- q/!J"8R v;Ȣk`Ĥ6{B_Tɥ[a z|"u >pA0P[o{X  )x)fi 0D S8p؞eOg*eU*[_Iý!;bW}`/#oq. b}GLfj:|Ё\7$@56^q:*Be=b-g~_޼bN?:h(USfn~/Lf)DħF~#?"cQvfʇ2SC$kij__%OlH1Z8v8&,zC^vA*%ƧYjN.wt{/ ?w#. X%։zUOYM5/ڽ#/q-*O}\d.IJ/ㅐ+SɫJWRL|.k֘,ʂͽ^ө9{ZR,:);۲3?b-nTR>f bgft,%C9n ky')l(D|N=iO ΄0y\xOoweMȩΪkݜjZD4IAŅ8 {N3F G#H++2Fě٢ =rv,QB Yq*EY.YFcl0|Jq?_C+Y/+v;F WVǬ"Avl)C;?TB /?8. ٓpԲE.Fւ?G<7bQ_,k ZHS)~Nҹҧ@|? [?v($D(iVnE"7K,K(QAn~ZkTbti-cQuSRyDZ=]#**R3ٽ}~E=u&eðƙUQ8j"9 >{"3gpF1Vկ xhƘf*gt Jjj>=+JzD̷R| |Cx)_}?te>ߦIcZlg.Ki; J>w,S xr>Rϭ:5V.5avd>=֬njWD0UU\<m-CR0wDɑP0r@"^hҨ8zT-3:2 aDz"q>s~dcSؑK"aL@OfOԷ"kl$04a~Q4cK΄\Fäu إ>]P#nԹL)m)Dێh%b9?3롊#<ؐ5})HRKbRuټ~[]CnoM Z+쵳R ZH7Fuu U9g = /Fa)x=ENveSsNr=\z8u5CuȬw-ZDZx31Mn8X!D1'`wIڛ*E2;P9 V4;vz<ԏodqǾlF҄ͥh<:c=lӄ2}rqt>,s)5P&Z|3dp;|?R'n}> W|¯i*5X%c=hʉ:<Eeѯ[4_~L8:pz5:>;(B ցKT,C58PC2'8w[ޓ; UZTh4H͗k͡bg|ⶢֽ:t _j4wQ>C9~ Mzce9.:Wbq%>XŁttT/)Ϡvy2_ {f_ Q]?dWDN؀31d5|i.Ǧ9TCE-4gꭉ`_wo.H1BOs&4,|~.{GUV Vʤ 2PNH@9[;OGN3a{~! +&D[l\,UcT^3ɠMz]GD@$@e9R#71,C WAz_9x,s8;V¶ʽÓU:Zak'AJO"櫒!&)JʛFQ]$Cvc#Iez@ l<_EhRͯ1 Fr 4I0:!`o9[A^l6"tTs3hB׾rAttܐF[@V?y. JL$:V+?z#J2L_Ҍ:ely>P&/..6IGTI%r$`}w03Z"I6HxRe목͔^@/Ώ5fe]6ߐidY (;=^|sE(n߬z+ؤx$Fm|WdX4Q-h¤Qab𕀒mƶ<DG C?݀ѻR |>A[xU2t2hUmsOV}Jz^yc_ a{ucG]Qquބ ]|FºC~ʻS QrV|Xܔ#7Gqo)C٘4 q$=ufH-Qެr_jH8h9Fy3LN`579EKSNsKAju ʜCM9BnՈ_k*TM7uN"WP+!n ?O4CgPW` CAK#:Y9فN#Ђ&xȱ&ix\|?DgBFx^~ľ办lć}nd^uATؒ^()18Mv8Ee|X߬De :Vݹv>CV-ъI׋68wķN~St,w_DrQ_WՇD<0ZF2 L˩IPf뉞'BE1sQZl*.,܎HPupݺ`J=@P (>|HF4aX)0gE%k4(S'iX?3I1 VNtA])m]φ鋇T5r?% d~gFAیu>^:G9DSz(J`gfHq{M)B3@է*4RУjF.uF@9ؖ{ܘHǷ~ 9&lljAm9)+Ym붿~hZ<=W ~K ;2xK& ?+894~LT԰q"?>F{OФ\ F㭍n/N!'14E&sR ^ _0h^AS n5 0rߺD|8;\~DN;9kpMRIQb|urkw/]Sѭ/-OQ>m$)=S ~ȴ&<~dqVH AOF$z΍ i/]]a%MzY1-ܐHY'է筭c1tt#}iMz_]74y8]<,-u?ZR'mQ׶oG/dO@fh1.n[Z&3c b s<[[#OIs7')5}/ɓGI@򩃁{[qLOX& mp*# 5p3V`(_%w: J+ۼuM\m i (^:?k{f2KTf}u eD8zڛ*ڰjICИc=DS$3Bd,,q7cWb&@桥$hj~>:]^ 'U}dK_vq*|]n &M+M "k7߳? p<R2];tұ+kJdwxIfZdErl#Y}5il)z[qpw֢i; />Y]Y~4Q,̖ xZ֭#uۯgi3TB5&zvw҅]<67zWN,$K(N!!dͥDŽT ҅tbF?@2:MtaXՒ8|v^X+n{[؞j `A>x[GZū,%#u&a[E#>vcsXک=n9Ћ I퇅rSOTk%0;"z3ąL YSmĀmB^;#qKA$޿}D7&8%+ߙzT?ž<c@>gMH٘Y~Ma @}ZY݋Mb;uPK7ky<~, 7"lۆ &TzJe fbp}7:/37S %.u_H`Ghȗ(7?7"ՕnXX&O/ [6 ф}XTE5WϷŌFF[8k8.ӛSCÏo.ݜggfx<GĈo޴-cdAO[heOStsr~ЧqL h3˲nuq$[[]@ˀ d4]ټ?]h!2(zuSqs-E8#9@-4~D{zĄx]q_StIp17)5\7Awr{CbRO;r>@1rZ, xYMS@E*'^Y3Օ鑾>7 I\!ԉWeEF 떧nϭI{o-C/N1- zh'\)ǽ3Qa`y+~vU9p OeAlaj*-Ϗ(X>NQR >ɿ-3tPbd?]NlדP'B\}7%kog->w_ĴvX\mԞGkzamǝP.`@LyJ9Uw8ڕXГ\E3iuF͓En!9S⒧ӌ'Yz Ɔm,߻|9j{ΩWsGvt^Sxe64qwR/L8=hiU0]B.q/_f5uqvBjB3 J*$Tr'mM[;85E/rMdiN$( Ԧ b;C1[h0*_޾R),G8)9vd--ZFcU`p$@1^~47 do /''պo^+Fw[K_2@st8qO䱋ߺO5i9RB#pHnk-toe7͝v6*;ME0~b-en׵`Dr| '.zU?ط.=.@e؈,A6I 6hw x۫.C)r/,w*%]$fg">4RnMZaID6ecItE* ⦼6Ѳp3X*4:khUŖ8gϤ-.l$}Mls*,-~83 -ʖuoGO[7youMϡ|Ur&a%GaTx]hT.o ayI?_wYf@&j}QvB-&7` *x߫5t_T>7W@g\nRI}Fݍٍ .a=AL6h{t ܮ˸e/0[5HF--LL+]-#tzki݋ lIMrڑjlXeW7Ls{!KSK!$ڽ׍+O\ v__}xtzɋ~K%UuÈ5^V <0RmmEԱ=}0LXw&v\~\"$l*:7~bS CrkٲՒʒL5_gx*lD.Ek8s965IBWهr] k ]pӱ!f"́ϯqK}y~8s%ljEw(=k SctdR)6F$vML(9QVcQf%K'ǣnr>QG,0҅#Z&f8͠>gH"t"=is销ll]kI}R-uq;0N밈i1fo@T-S&0f|g*.nuxkX,,b%waĖeZfB[˅ˉ_\ &j.| ;;9!rj`0ϛZrʑ܌ntg=_&pڸfiNk6100#rBr)1l/J-]sGj c4(cY=*m;$ܞfK[1 \vS>7+nhլ&ͩ-jBBA? 5c}Sg $s9A *Y쇀Q,T= xlz"&[3Fmu-~V"0QXZvpE+Dm)"J'}?NϮB|Ѥ0Ppv\% ƞD# 0 e1|fM<%ݦn/Hs% OQQ4D UqxI&72E$ANgl+glWO*~1 @I`$fO~t,l&6)(0.RʃׂPWOtZIwo θ_Sc_$y^,kPLFd_FqȄKZWM·jwDYϏ$=˽ܦ] ]igMvYQ=}!CDZ RwZW :.?z}0a>XI=TU6*9|f, __ oE^o3LŃMQr,H&ϳIS@WP~y9iؼzKeY>eߴ_yY >c.#Wh~(+KOd0tAkU!sei^%(8?s8ffNTManf|j7zh~}sM`!D XW2rxϮW8ȫ_ONDLF552 a_AY>|egK.q[WY.b:>YʢE@ߔ9S݄}DzþO^vAO T~; 3;QiT11 D i휜m=g[ +.22GP0dՓ\ZLeRېNTy;XBӛL_tµؐ `E""hyv)gui1#wg`qw5YjsC7YoXl꿈 Bi}2q ϼI^}gVxQ!-d$ixձC!؟fw6x>eFOxό=rf:oS-qe COe7gY3Lf1~}1gZ2/ Ғ[, Cd.̧=taIJLu.>xք()#8^O@i且8R|zkDّ[}3zҙ ldZb 3 mjqDr4xi_,Ų8@%TKVޕxp_q7{^.`rr:t{x|r^I>hg#lLFKF߻n`HPcpmUG|oG&k<$?ȤEB/5wnE~_Rh<|X;_T~"SZX[oWM6OW.vۖ'8X HjJ@'W@AMmi稊]z[C9&?la'㲐Y}srKiFjbcWZ℃\oWTj#eo1-2;8 a1pĝ]a`"A<V!onmڛ뾅 `,g7KNav"u pc9ز4ǿoߊItMByRKo[?aFDaK7RN17="r+jZU\ϝZ}2pUnvO$MU)ia1Fw$OcR9Rˍf^۾!1ƫ)^_v79`=ѭzzד;;J?-=ptT/׺ԢzxJivZ0׼5364ql_zEµ D$} mT5,Ђ<-5>;=!u'MK>z.>{E%^2 /!\puԴ#v97dPƑ)hĄ-e0PeeoiJӄj/sRm/u:v)8 cMy4OJ{Q +qrxz 6 ة6N)cA~qNJ*Bӭm:iG& ُY٭>[Rm"T`C 6fV`^O߸L;[Up/ιXu%|҆l0nhAH$i#( :nxh~wzfNfk%:b,T<&u{ə 9-Vi-BlЅ[K먶u?+|YCa '4;˽/}F스M1V7=I^d:==<8'^&imp@3{SLOKŃ_!Zx,@wNI~%=t\kbs)Sw=4sRML7Ͻ.V,ۄ@gr/-tIJSYVU ܮ,NY}OˢqwQ1*̯:N%&![L;!i$nD0uO~]uBȖ|L@`G6Φ߾|&­@Oݪ̈-Վ=}܊JA2Em{Ar!2_  1aщg+h ح_+˰*y[}%SB~'21z*'rzهUH_{;ͪReq_rɛ`}X;Ld<sm!~~O@ ۦox0W)u+td_*ŗvͷaq/"miJeNI~>^\sw$# Ot{M'cERe+D{xMY2u3QF_]>t^ިu7ĕ%#[1T$f+<GpݸStܲJjߋv!CedH+R䂈qDuEHV?%[Ewu,q0]6X8=ee(8]O<5MAhq@pHImr@tZ>ŔiQTh7BiNAW:0c+e|;@G٢WI]Eպ[iҗ&˰QK6-."S8p*&nNM&ܔ񊺻V7B7no_*{ ԷV[ggഝ1yxۯ7n;T1v!.ֶ]xdR /!Bo9ZIol"@+mR*&(*7OУK#hea:wRCG<csnSIxhZm{g?O}}WOO,nko}~ӟG>W~*Ξ:oz,}2WlF G\Kd,] .Af7-=nS M=̟;r]T[+jڦ98'M'0{Dr{"%&=8 %u^#Ib}ZT &5# ƹQ]"BsOaq IiOOy%:q*׳P 85"N4=a=%enf|~"naRLnA4&!\ j057d^2Gts_TA7SaY~{qkyp*󇳥 GJ 3E9$CxwJ.Pe :\s;1 q1*VD N-)G/ ږa3Ȍ)cK)F:#wBä NܵiPf3S OxJ12pxqȫi` v*EwBs K)cڰxJ@L^Џ LK2|_ ^S'5T.~o!-%"2j`F"%km"xCX&i,!r^"/ĤfCs2S;S滨ߦvܹQlQ܂i$g! VqtY*&Zb鄨K*6UxjхmsJPҺZZmvO>on:|dyc$%1 ţ"9lY$;%|c{puZx3„%䶒2CZJ¨2lT fwLdOc3-el >s?#%xzqcLJ61l -uq}<~ǰR*91=aÏqV1λ S)c3W-1}h#Ry1QJ3mʂk|}f)f>Km>ڰa.1lc3l0S͹1=>>6_k36vžĚf91l죖F&ap.Zs. jixjn>s6F`(n`yyp _vjAfQKuZp/07{͵] y 1knfancZ6Ralx`Hqu 6,޵7g1lZZRνssR99!TjqC{.4341a(́Y-ٌ, cop 08jin..4w0R!B{.;v7}}fJ {@چ0oFF3[gi꩔Z*Gp}WR*}#ãu5w7wb%[8~P&֑2aMEl0'-|}c6nmDE1M8j\TTE5櫐& iö'/Z]RK%&"qWnz=H%p&6.N:݊@ UtF t^qI:V{p]lvBOJ(s̹> Vm7@ziQwmxV@|m`us72|XmQw?6`n5^3hW\K}p"ɱ{ UJ8qp.0[? oOE\Uj*Pjko޺PHܳ i'Xuy=`Ƀ6"Q)۱ sqڸ2y (6ɽnOJ=8M\(c{›n}SQ Sm j?/ihqV0 ZfDņ)b.)a PVQ]6 G5#~wssȶ]޷mk+~үwk˿k',㻾ۈ3u86i@nԬȟ܈"ef9Gkq]88gw0`c31#Hf!5'eKTfT"{Ix3NeJTe}S&K_<-%d9 /\<]#d {-f:lLT69ζ%J =P(+hj8R) 7"E4vvx$56&}f'bH]csG7=QǾ>R"Q*>vwdxΘo#}SS4.icH|cX!(YHfcv@$[cwЛ%7a#@Iyv:O@[٧BSU3K"M۹ $^K3JTPYJxWb/XdKgȇ1sEFba55u =fДQ=!w/ H? JΌ|kɘSqM rdqUc(>=C1FNF9\nQUHا{ eTuU~4د̽ zaσz?T% GRT Rs#;eԹaw= 5f.Lϴ aH{ITJ3?/ܗx;ooyk/>_yw3 F; U?=W*EU??"˒$2+ ,+UuO $H6ȝ=h0Wc{}IYJ>Ûwxn!DCLϴd4DU򓟼W+81퓟?ɟ\Sk{?ܰX!_zAsUնmՃZswHyk MwWЯ򺈴!szH][+{XZu>Kij)M[߸A67 Ho^FF/"};Iלo#se[̯VסCpoGi+b<XwQ}QhEF&nҹm." :j|-3}q۬F{N}D$2i({m)"LCT"w [r=֖~ :yq*OϪ-eH h -^;UdS"umHYzX7֩%T 6Am=,8ׄv9&%gfK:| j*PBۺ]Tk *O"dVjkҨt+"qyk,"[KضS| mp 'lPUm)luʓSkVOy|u$S8kϯ{۴^C{d5{m}굶OD{kMZ >Z#]: qdcU[m.m!C`fvb@JZ*";J)7ME[C)X4Apw3.grCUBذ&*&vo p eDZmPè#Gsɀ/e_[gN1q=1Cf6׹NUU9z8mRV &8ܷZUnpfN"ߺ皛( r 3}ݺ[m#;K)H;l$%AXhfm4^kzjýMzz U [ˍGҷ 1G#r}͢7{2ga\m]8zߴmޥDxk]#uwg@K|f>rtth;- ֖hT\_nC~jo/Y;h- 3qN޷ E --B v%qh)[m* 8hBN띹nm0X={Ơ`d)Z[a4]DJ̄:\]_ܜY;S-Ki wvU:mp ;a *pFxߣG?~noM蕇>o~ۿ]Zg~ѣG>׾5N,iWG~o|>ߜo3'w_v<|̳<|W~Sߍd*$bZx3zۚ,gly|/"BgiI]cDWOޒ3xĵ,Uӟ IDATFp;[C$cG!i$&LұO\"34&.'3`͉n"2Gs:6}Tb+6&cPk.cb=3_X)}s A(I2h.5s 1csט.a0$aa p+wBN&&%;b3^e 3ע3͎xm>=qYANv4"̳Ѓ>Ґ&K عVIQƒ9wjOswhæ,MJܐթaNk!/:7FO#Zr $Rl\hIcMrdB ؠ)<{8Qd3$QI}(u@rll),_G`z!Iw<6 hn71gw'RWI=w@M9;*ЏXdr9Vȼxt!SׄMz;1, Z#7lL~wJ_5 cht!O{ImG:[I'GkuEGL̂=6Uyjl;x/-5OdM̛igui,uÉ-{#kmyQj)} ?/H#r=h6V<ն8ݝs$Y$g|HGVWZ`:lڰKNl@=M !%y,tKR?}xvȤM7NtYMK/;Їj2z#=.~>|^x{w6g̹^ \WNH^{]IQe."%0~1ʼnVQ 4|D7Ɯ1`SoZϑ]n.-cSi2#pM:ZE'rH7b}rzAa0&6Á-#DlrKC"Q0hShX=wos2=[ĆcɅ8pQaM9qh">ֶrZMwjL0]P[wdbd-!ybuͮ‰;};1T"VP-DLV7p\%j(z[Sfˠȃw0?;ydji};.w頠u0c7;ELsOV[]R>-^ޘ6*7}P'Bw9o#zS"f$3utgV;11ZZ*$"smP:r/{]" ~hm208dci[4rMKzR"DPi_JD0UmXUX^/1eKS$ȹRj9~:]9#Pt9se8<0hNbӦbd2JVbDSRJ$3QjUfYol3D#th2|~|}qϏ><D<̃9=~zpN";ٵn v&f;DzB"D3yu_ekQ)b9(f8OEA9;マsTY2?|;WYdϜm{|> s>[j۶g>Sk@5Y"я~vssݲE׿K׏e֫ [o=z=}_?OB~flobYlfQ] aVbSXjg!லi~/N׺\_Wl1b?kRI|d)/s2v8k%h7r!@,?_Kl):vyn6y\^E Q׽G k"*pZ}D.sYW,QSuIڎ1?Rb! 5  L>ӎ|1ڠGԊ;|]u3N|_RVd-wϴS%f2O:hqFht= lG[ =%fwrLNf|aM` ugH(j`B*%Y8|_8QWy 27f :u^u\J(cE !uUe11eD!2J鴍!RRX>S ps}u5AƜ#]"h{𯻳jc$_@_B'M|dRRxt8? " ,a;rƳxq 80i}hb̗nm.ۤ#N>izYxA4L*|zZʱO~`攟wRrLRVL#ۧ. 9,2q~d߇{v3rz3%dvO ϥaqFz̚vwā/3sssqr!F hQуs!֊*vvH=v}-U\^iFԔ)dgvb)쥸s0'pZz\7Dq00#£}En6s1R M>e2Yi9iN$x Mc~A)"նmW|߾w_|vj뭟~:} ?[?c<|7_{GhpW†ữkvOvW|`P"-=p¸=ly?#OX GyPQ.qHs`7 &O2c]z>O} _>}}s?? gF[o>jc䌡^QeU-֯ⷉI}K CnQ>v0TEDE;rs_{\ )[fWw68ɚ1=$>Gm=ahm{FxL8sݜ-`eCf%>x[kf15ffnQue>F>ϡuAK^Rv'+> !rZV) "u %hrI=pĨfV=Dݷ~q_LL=u n4O}+)c]@ 7>̭&[kcHoui0'أ7(KIU'{G-}@%ͼqG[1[|DJoq11i@DUٿ>\IbݕAᮐfu˺a!eCuNX3y>{aL+9f-L {4RG`=EmVCv(R~B& pх4 vcvSY,Ƈڪj vQ{ͣCzE^Xcۢ\&mGT՛ͧ Iy$ZjXiTm$KSR1IާNq˞>2(QZ)J_Զw=WO]ki~3t*!cG?WyзEmc$\UtZ2;5-ì ݌ D"@~XN'3#,ꚱL/ZS%gV#?}0?:?_ >|7{wzWWWmru׆3QGLO IXc{:ߩ#lhO1YXT!㲌,e](EG+#v}&&Y"U ,wIocMr^7[;}g},l{89'ₒe%s׹S6ăr3 `T8SQoGG:~pm` Y1{'v2Yد/kcڲ~ɏ5 W$Ɵ=C lbAqJ=X>q]򠙳om̖>gv܍l48bS&mM@!~ABI<Ǚ{u*n;0ʖ8>rrg(X#"uP(RnG'FS#/8|wѳ}%p7K ꩰhwir-em"-`~m*XOLy,-2Gr띶!kΎͼ'?h[%Fyǣ*}&OQG|ix=="J q.ī|@S֜ Ii{'-=)yi`Hr@Q @gޢ-Ť|yDyXlPd*m۶s_j6̣sd>gq_/7D_?sK_G8<#s4<L'VCE nZ?i:̬ zcyHtWR,-&obJ}:BU֎\~hK6m-r*.>9dt __2?Cz'{{$+z` a=0|g#o!UQdg]ӏY%BI򙕵Ic82&D(yfS@;! dƜ1HWk!wU`n̈́<=pֶp)?ɸ11%bG5k\DJ91x'1 8p2 Ϧ|oă-z'O6븈0!=#tE"b{bf?YQD3gF෵K/U2/-euƄ^bb nHxǞ2'MdJE_anKm`I˴ftȦ>)=.;1 q|9N&G]ػֻeP`la,LZJb5c4 բLIbY{*Z5 Tx$v:k|slq&-{ t1gxÁgpO&- Fpb!YQy8kN a8 ֲםIFTg9aNܜ5u%Hc=^kTg_kN^>\D& (f?3~rBD!u=- BDtl5p^ {N9s铢20"sɾt0ҘkĶPk9 uYU5}અxKH8 grZV 5FkđDI{Љ";瑳RO`y}{Xx|g0Z(Y,v3BRp0Z; γ$_Frʳ&[zc>Cv r;:m2NQb?sGxʙZb)%!uqP-g^o8ͽ^jus}%z=,j}, rh}m' RO;fॵA#Fp[I9}mE5rf"i#)R[)YM?MggުK)Iz*4zI48#x5ֶtQ׏gos32 N`6Ωwx3=w?z]~瞭KrCNWW`Nӏضӻ WAԢe)tAg82`2L{4DrVK6ļiQzc t^T>&wKJQ}޷ /0'9'/|&+/ҟٟه>o/W՗_~!|c?o뛛> soͷ15L>y߈a|Srnm䢿qĤoGGD|6iZ25Ϊx9Fy}@s٧KNz^\E|7rVIg>hY7>sҘ٘Q_=X_BImo%/{#Y5~SG SN8QĚ;b|f3y=ߗ!4==jPQ3҆ʬKg}oYeU$91.2ϧZ}E-gd)8=Xf?TP7Q+?Oh9l'Ktž3JO21h˜;ynߎ>|tNLIX4sgQN%ZB({'65 $NI'1c|~9ߢ7xOlLЕG +f>0&φ<9xC' nQlM$s0f;D^Ǚ>R [Z\r;6;z55X[qVmle;7u0;fOugcT s!'NFhxjqy\\J[C9" )Vjayx2zzأy;LXAʋ^C6QU$!&e|XԦn5AEC6fey0l@8]O/ztK9: )烅tR =;5i#q88=u%G 6VgRvEP ĺK7:UDBP{ۮN ?Efo_Zo>؟l_Ͽown_nVynKSZ&"HVh֢vz Zx?F֌fJB8]I5&e.e89^7x6&/CCޏO[VGG)\2ϙ轿3s}|s~替/|ݬYd{.Go=ɸ'0Q'9)C$$c`$8T96g,j9@yna}>xI{'B!py'Gqs '_gl]K3zҡc.j%萇OxN'=go]pIZZsY8Cm5mx[r=f?n>nAۙW~`zƬѢ5sֺ_1f۸'bZ֎A=b=/:O˺1Xu.(eU-sX,h٣5fG3,M6A:gRjsa3TK6ז2{⮴j4"PG̎q ;$m)b#cVXЌԨYuj!&":Ƴ)s<{Y][OOkQK}8Lp k6{xvhl0&I%3yy CGVib9>m"L?[gU|T7?m'-ˢ>5ό.[)^JՐ|lQgEkPZ0Ik@~a4<,1pa?vTm \X @o%y:DD!EO"]*l F^+IhLFDZ3v.ٯa H?{)uPք7̍So)kcF߇ԛN ^ZUm۴(fOf=gViHb")#{ p?m'ez)̕G>UANv91<).[9Jol~fGySֻomw1؉idB\!H؞%hFs5 (r4d.F Bl,Ŵt_{j.Z;mDjժUO=O>;psyGӶթn7jge!Bjo=zg)yL!>M?OD L+G'晉 Hd{zƁ9Rh9/c9P#Zi 4AyV[g<=Usx8#OXݚok"G"&"Z\9'7ZQ בoff9rs3[\qܹ=3c4xU ]\+EY}y. ` FS)vkɕt;5C*/V 6lɜ5*[WGnWv.ښM{e'`uK"}yMu/F-4$cSX*ٯjT[1ۓx+ĨZPĈsT|ml`bz9R޳ 39U\UͲF5hh$=%|rL7ɁPZqɾU1׵8D2m-S-;29~|M}+hŃw;"Ze d0]9<=gɲ̶pÄTRz0ES>=A`{kL߾v[ :j7=OK厜^le&{3i#p ^sDNj_qb|s\SM iWEV[k|BZm]^0ɘ*qy|~b1ZW-?Y53Ҙwz-$rn"RCe#3D Y"]p1W%H]G:+oxʳ-ނ*xaz^z|>ɟɟ#;*V<;wy{z|3/_}k7-`2_K=wM=vBZB(#{,6!X^'7'伸9,fr箬]30Z#^$D;qʊ)*`?I`E>? w i(1C5홟֢W}~)BU}zZũ9ɦkHYÄ8I{*6q I}hYuz'/]}I$}8"o`)zƘ@ 8bT?|N\ED}F? pp9'ku>YD-6h/~ɻ}'p-]@(@g!yS }@$FGM:&؏9m0o I=Ж~T|vw.1n(~4 G:EVӣ/Ry8[v.>XKyv֏㼜=z7o}]#ZI_~>|k_o|ɇ~GO>=ҕl~8o_yww{taj=s]{^};{Cp0jZ(Gq S?ފt ,Z`) ۻG%jaa5UHYS CC]P0j e*,w( ^n[x#W+zxQe=suE ~\csLPQđa3EV-%#~f*3;L| Kk{:?B}cvPlioOq*~k7:1pmB"w=@e/?Zr돈I*Y2o#E!r?y_Xff-!xcYn6BTx#<ߗ\#YkvXYAy +:-8jOCrưTiεVg'ق!{uToJ󾹾boH#Y}0u&ruwvc[xY{~_e6jX F]*Jf/: "ȷ>2SF/#Z/*?u"NRN;\ ] E%td핈ˌ^QqG5VTʃs\Ⱥg|EU]V@sC/ڇ+=UխYBx?R؛1TSwx}'UR8L5Mu%vʓa ڏD>IxN6UlcG [SJ%EZLTRwvoknd#ӨADa*L% QXp>* l)14e6rfmZZW Z<&dWm*@%fȺp׈*ǸlثGʇ[usX05te]6E.ǟp@Ygp+v'Q*K3! }?:_Qʼns~tA'rp~ʃ|g[cblg=7lFZa4}|hܲo`}\nő >"%v,"} g9 uP  ǿ<ٯk47.~k ?L\VY37zf^0y>9i-q*TEu+FpgOa}`㘴sc^,<2ͱ{Ow֧MȽ2a}8cS?|Bz;$cL?jY iS8RC2d3 ;r&޵ V"8OR0$}ч}cTM/X`|obL,8„16yXBD.QcI$̈i[U&|Fj1:OSI4xxwDE${nK6Wyi S~Sc^33r'51+:]B߁B/>0%A2lΞ'-ʉ[P~?hZþ+Y0d]uXYɼ AzڳN?oi>lG>NyYȈg?z|xA~д֏֏9D㫀f)I#Q_.gF۾d䶗QQX},sʈcL0ϋ\Y#?5`/_|Og?;;_?ϿksΧO><<sFA9c2m&<>IkH5cR3PqJ;p[ prlI=ܹa@zq/sx蛚xH#c=<\i=󣹣hu Z}=xgS <\??ϋOnY=Rty#,XYR"%3B}2X?E}%gu-"3[*4s獧?^\ճڙx5Fo94g&xX"xYȑ}S`HHqͨTIښ;@d񥙿w?=f;>z͌:3?ۨKk~]I;ckC(fS~R'_"CenP t55zffs+q^&<v81bծӷs[$w~ѐKW_GǮYg̺x\r`#y@Gp=&vDL5 ].wu~8Dȯ^-8MU8ŪYC''Ӧ5ț͚䇻-.,^%DӘ(jIՎӫ\_3}9( {c"-bDvRO]h64;Y^"~N弔?9ǀ; r_NwL7 8繗)95,s] Q T2>/"n6j O;sϞ~ݿ}>x{O>iveO@ovя^>Wя: M[k) ޓNaMPVD["2ݮ6[vՕͬeƊNiyf?Y2vrw* @_/x뭷66s_LE{cǏ?~z__{YLj:tŋ7|s_8?_?{p%Sٜ~}~__?فSwdZ7f`ƫ$ O=ʎEfA+=OԛR=$+ ]yc#<6.[t R+* hnƽ"/ܬ q7TliaMye!f9ޞ)Im8Nn?ٕimÅWŝRv VcL$ <3ÖT ]CGӚka˹fqciBm  s;^w)X\ ׫eɭMr`f͸ɚMf-ojkU(SY[1mT^d{Vc*Z!.%|]A:Ig${nS޳g2% V?dq,=i9S$=w;X^z+œaB+>9Z ]jkŞ)^NZnkdC?} ߌgSyo]6]2`?"u%b#o#ٹ*5xbյag-AT見J4=}m g-6}AEn<|~]녘A0}uiÓ")\lheǁ@[2:tncwA Vz-YM7wIsWWzX&]I2wbgfsьq,nXdOYz~xxx峧O?{o};7|~|__Wկ?zwwժw,~1ĻZ|=VO?bMIR\'\ڼ@;6.k-\HNWb.` NC'0xdL%V=XM8aڙ$߃Xr!a6]zMp 8RqR" ?DŽ^ΏԻ8sH?J9x;`5M\|#{yr^|b ރ(lp~uԠy ގ^3Jw#u [t.z 4 s )gwבFr>q0|<8KRkyNM:V?Um{rST^<7`'&n6׸Y(Z˅)gF*c7]j 9|!j@|=î3Qk}EpwKty:sku-E4s.gr% qG*ʺ=9K r^b{s 5cBUrsZ;͹Yjz,PlǑ'AfJ]bN0ƅwmUpz?pFpGb2-.H莥ѐ#ҺbG!?W-> >βaGpGj]ɜ#8Ak #"Kw?s=fz(]@Mu#>VRocNmJ[K~w7ZgN@CB 8R?kGٰ,9;AH Gmvb}}Dfnc{&4¶WD}03gšQNyTC<ĎS{}Dޏaq4m8kրgd!Rb5;p@{"xhHR{ou%"'؃XS6oyLPjy^ccEܯf[4/cx3kӣw:"\nF U4Ϲߙ:T1u<@K9Q =lhntIydI1q]D} 5q퀈Mgm4a 2kB?)ZfbpiSEqgK0Gi86݁8Lѣ^q0k3狇O?bj68xxT+ yçiq#GP|]VUGnM{q89\.88Z}sR~dK/_rI|5ff}}ٳտ2|֏?Ykqf3e8m[O'$أ$oT[`>3g5#O0 qHg@g@昏-=}+/GL qCH 2\Dy `5+ KcqP5 =̅iĠFR1a6x}{q܉F&C3Ӟt ~34jJL.vܱ)Cp<ۥ+yn^.zCy"{x}agdk~rJFcӠg$K\h {ޡzPsKުE^!9{/}iOSѣp4i.sl:Ћ oH5b)f (AYTQoٍWw}jZȉ{8FjIu gA<^Nמ ΐn_Hhl8pcbƊ>* |8"Qk#p/?tJ'AZfَ4~L>ϓzg%rcğWvU13>rM~g`͑ڼw8/ Kz pg8P6eD-2gn38/!8Gح@p9GE]]~^U8vвay^ wm;GjƶZ=L^Ɖ]F1Njsr;XE]8f _ƅQgu3Gq*j ؓBk|֢>vp?$?|͆qừ+G>1B>Nqi:6&B/݌pnbz 1s|{|sl8Մ[Bt=$-;;(féo0"̼Hr#t4u\ZZSJiiʒ FwQpkފS?1ǩXEU[\ L9i}=SN<@,,?fo9k ",)DFyh qUV28Ă^g=%o{y'Uh!Y/?)7Upf%\(Q-aMldMSvItP‘Bg>]L̙yKhW3#eա},3aIJfe|Oy]ۤ.-egsQ|Bh5$m_/JD_4I &O8/}rn*1^ rX _KDivTLd}9q,ʾ=3sfU>*qhzvqt=sS%#ENK܉/ccQ^obri{Z8MJb-ۥ" IDAT'deyBw\)\H6oSeh#=dPL)[ȓi<*6v|!֋zjs8dhWsI2h.o MMۻWMTt&[ط-,ۻ*lc3ZRHVtl6{Z(EGdf-7o}>@sk~5Gϳ*ρv"dTj|bsL*æ,G+]h~viO^l[5D:őoz"ͪf9& a> Z ,}d&f[!w7bZ}|zgݎъ'g8Ƙ3"%7pk'rbcռē,W%oFnT7"I>3oōJAF˒ݝW ܖ&6j $pjx>JS%ƣ׺/2_QIh#yff ~+c&}WZќpZ6oz־*^*)%jBwl&Jt.2M~5S)|N{ŋ/=ٳ>ٳϟ=gϟ={ŋ/_|=e'+ PD58\W޷ag|EMAF,~|6 ᯈG ~U _9B;0>cP#wu·?x,qvTG n!GT? T]БgO٧ }#V[M2Ly$ҩ57 Fopd2Ⱥp\"̚{r{q-}#8gwh뱶s> >U{Rw7"愊171.uCc]GMfMʹ4bcKg'XB;ٲYoG0˟o>g?)5|i_ M6T$LU2nq/ySM!+{ ÊC-g %9Y.S~yfa@V'Ue}ۊ=G"nzw?u:jjweӑo !vzzHChL6,Y9/V2SVRy%Q<@ÿp=%q֪J||hъSCHE"v%%3WcD:-w_T$0.lУ~u U[`֫-vp%!-{g(J9WT%ȉ"rInWe!k/д8O9H(}" aud,^g{bh5&_N˙trE#5e$r@B^jȋ8Yci R/^Ҫsk^^W1lKc]uUT,T.(S@:b2"hɿCZcnnfgp{֢}Jn 6 =TV=3SrcLfh>G_XS4}3X5(QhR !aUǭ^.m|}~@j"\^[\_菏OϮ/_Қ&zF*+C^w( <s+{X'CPTYU6ѹx\Wa9eTqf Q>|z.37-z}^zׇ9 xgΓw##88>8''̽6:R㍋/_ȏ1-tb:He`wEa$ph~_K|㼤/ Fk/z97SU_#q0>ÌD汸Op]L*71PksŷEMÙ<ƴ2'M~ 7ːN8Ly-;Z9 טjMƽ4 I]2f&4u9u ^bpw`<1!x )L58yZ^},Yx [TPޏc{պ8/t"#mK`n~F9S+>iAOvޗ7wo¯f m7q~Tjy3CKxWKFas 9c˗x+n^xW|}xCg}A7=mѣ*ʾGB{iK*55Qٜ2lReJ֜=?|TixسbXY/"'L՜Q s?0 E_}m0 -ύ@<LLҡ>?5/>[frz;mfz^}0gL<]:WzF@pz<8*JY! .Z׬>~-҉7 hyy6ӯj>55Nqִl)hLу@Hc0 Q7#kͲsk̏|GR>$kOc."fW]Cz;GxHB?uOx&pW9ϻ7'{O?9T0L [`&ZsJFZ ǧ4nsZ&sȜW`+`+Ps>ؤ?˾xokM!&\]liܺmX&zkCO_}ĉo~9~@o/Pht$*[)δmV4?H0*~MuU4y$xeHື8d*!mb< tT./v|nŔy>\)8q"/<%btD\2vnoc|JA:43z9& bK;ND^rn/lkӞ=GFuɸ ܝ&hIX7%Є.</E$d)&f<:X Ț&s7(6Y`#JKN]Jzc溕-Oee[iW5@-ԅN+2WlT;ElqF㴮PEO1х<ג=k2|m۹a!llE9V$Qڀ yYCB5-7+w4K[;hƣܼ>s 0{ Ŋ|UQ0Y x0r*m[/r9-7v8iS!lJ=/}N0gpe"ߞ ŵo$#$1~sW玉Z у%7#y3U <  ⁏ٶX6UJ(xhkjU2a'~M+~k#u.ɼVJKhYER ;/#XT9d Hkfv MܶS8*9$QqFǮX6nֵ!/B+Le?;ezs EjAKj{1 ߦ&I7D+!9Vݪ>~\PsuwY)샐o \F`{ea[Ͻ"ME/7מ=s>?ˮ<4i6a$9'Td2ӊ8U+^Q-ބq+U_ 9Qc]TeW؞xsim]~?oF[[~\dy~?w |wSmz {].|0mz^r^*Z;.tڔy:5UD].wpf#{|弫u ˅%~wNW9/wF]k~>NT:#!.' Ν4=i½,龜dex\|\t"*Mb>YuwT#/*=kv3CU:զgyM/H9NjW7p3-ɀs,tzrwwך#gVp=Ƹ+{GcMZ#ǁ]%f6ӊwOrpm}rYww\㻿0p惪80Kysx& V20߅Z;z,{嚊\|uqEtGޏ>)q^#z? F|@+uG? uw2|Nw#5\7]5$x{qb\.h#Æ/<>pQAc}8Oj]{qoBEvj6e/gY uU؛r>V=AF _aK܎޹QO7rs_38lcq(mr8ȗfLҴK"pFȜ*_:89)ZP^:j6zR*B;F^+DN 90 f6ל+-z|Z2#ƈΈP<o,[7Р\kMK\"~Limfe=6UQ|HSs(IeyvFf'UOS/ IDATd&FUϟ `Ұʀ`y~~~4N˖rvze#@0:Rtc*Ug%u=]P}ViK T܉NW+t7sU5k1nE0ݼˇk-o&82rwbH{ ܭ8wHWDZ؃ B5J1RpSO&R3G)\d!< "،{pJ& J<-iʽޓ`l1o{ >. !E7+ R z:̴. N 2sՠ#[:$kfvEvsjHwb "ТTf8=CC6_-B, yк^eIד7J44v7ɚ?Cܽc[ܿMxrMxmd'}C TCs>)} 6iv:T>7_F_ltCfwnix9Bɕ, ٴI Bg9"2 DŽ:{,=k9m`Y .ItUk֬:@9G YBe6ZZ{K"ĝֶ{lm9k\US /eT0mCĨyp0k\+p5F+-v(ͬzYXDfۏ)WW x,Dgrz1擷<%-|WdCt/+&%̌9䥙ܛ >M)Nlyp4x5)45ai,Kxkj܌HEk)X<"K9iy8-jViDǚm ^/;J)Kc8AodP!ɺ学t$q#FP4Ь4[Fw2f~V^u9 Q#5.8ZXȀ!XQzSl<̄"nI\v~5L_vk>јʾyG -™\%~?6_Wk>_f_?7V!Ӟ]ѭ@C1~6o[-hJ-Le9Lu,)XZB]q hDLFLX'n岎 Ub{VW{SЬF| m.Bmmjk|X9,wf2t׫EhQj~ =*h wd9-rp_%w-ҺXkVFHjK ,ϵ:dWiA% en=lXg4TZ@ gitk=-̱kUɠ0|rJrS!_m9|`-S2cQㄦ#[зmVc5 wHt:7@ZBCWI4?(<:hYd fm $v7Ecc**A=Mƪ/#*Qaӝ#\(j5Bu&k 7Z ؤ]ʃr`)#ȯ#fAw1>og+bZfQoIQڳM pD?=*}E[Y& U~kq!=e(ac1b9 ]z = 1~OyJ3ךeݙY1%+8mWCAN 47Ck)Q,G%YZW2מʌnyqe!45c\းKUڣǸ3tGf`_(1 c`rx50{z/fjf:Z!证+5-z@\ ժ6Zs\6Z{7У5_=G&• *n^ƒE,/B W$;E` ^^͙5q?W74oiw2x]bX"GknüYx+b؀n%1l aI.(b 뺮kP#Cɴ[p?8m l *,Ւː!Cdށgg81ϧ+7Saڙz`W@^I}@f;;_k=Sa477o~Ͽ?=JVG̠l d9;0KRe4}}F_Y㛙/wr!ϭ4N[QuxJi t:B6m#|k$0~Tew@mw>jFHlB_fY/VZ,(,-črp"s? 6YB`fY;Tg)Hn07)VfmaڶXܽrϨȉѶzrU D&ZSloYnйt7iz8Ȥ ">FUѬ:^>fXnXk]*LL̄ 4_%/V+KMP@{kFرn7U NR=`֬_XuG5?H= *L3;\("/%3QÕm\|mBkhMJzJo7OdYfx: WQ7g!Tj{uL xgCxz%pwf^WӼbAQMŌYN ZY-55=ʻfg^LF #)ìBlTS8@ !9Mef >'NҚ*ȹcyƚ046ў&k17O␙kռ6ZݺIJz d֑(bﯮ!pc^/5`W5sZB>ԅ+ 8)oQX| =\2E{d5`(tB۞Qҁ zV xÀIVi蜪9}毜JI3RZBaёʹaPuߺĠ0R nj#:bCʝ)טs'j8Frn0GJC  $Z8tz ib{:Y3F,=#\7@Hу3iFDcJ &Ru:_1,{4m~y+cgկ~ӧON5_7cfsW^ل Xp?kuAVkn%l?#L ?=037B&E~7H^mÝ4jܢs`0RACϛÜU*Yv fTfٸ) Z&{QKnas]1yh V N[#BۊϵP„Hgr~">]cHcHhIB~hf 0=[|<ؿ0BUGUFf^YXF>ep`_Wsi{G۴";>u}Ҷ2]%(Vt&buxg1q3ľ/rS-|8qޮՈqcS| +FN͝tWc'HLs>Ʈ]sz<>!-i ?gZY+[@6jaƹ;^Ƶ4fur$޳>uZ`3S=lF1bFIlf˭^M]Rc[k+ǧ*cFtqb˵PR>QIIV% zX?reXkYАTpo XPh 1M`M㑆Eyl1Ac"̌1>; m)ҋA]5ZY?1#^8_Gڅx PzV3Qk+e]F+61GBzsa*x7wyȮ(^#w^_ Hϴꝙa%)Fj`aBuo;Gk$@*`ݫ3ܺUE]`տ .ּL ϯ mg u"fטOZo-~_onI䓣 Tb j(q]۞yӭ{L(A VvU:$Qn*4NMpfߟ]ͼOπgW̢] bˬ-^ an$/):-SN%oZˊ|$~.|۴vm蛣̧ Yf0أgXÊ*j}|"Cx֒nrcB>]{+qeUTx6|fne{(HEMtps9G*]v"88e T Svk^{>mn3msJjw._0itg*;ADǃB%͸9D;& 8.L`dx PcDMFpL|s4BKƪܼV1=:E$o%i>-̹mZ`uWmK%ȠrlxK@llpm[epY f_:es wdPUNtif*(A/\-ufgPg+#܏F[l!1Own=fǸT_4k\=-h娄jpef)ؔ*%u+FDSr2˪Nk _9#6e݈2bF@`v=2OAuitze)"oB !C .jjgۍ;S U8.y`᎐|y\kT뻄@aT8Vɝɴ*nIvqc7gTӒ .:4W /]ø$-sjL 'u=OrSP22eI5"'ڨ~::"@H;5H/a+zk=_Vq%SnY˨ ^Du"b0b̋}v;.GӉ`5$m/_??`K&ۿ_?OͿfmI,2Ƙ}d~31p^rCiEmAc͟6Tex!,eytQ7͏fsDD<aJ5?3."ݾ<Ψ  Jh7O8+ V%Il_+pqnmLJE+ANk q 9mK!s&V0tuoC:Š1m+:N4llTM*yӪ-m-okڈ%?u$Ц; ɰw{v:fdJLs.?0HQvӷBzܵ9 DPnXwmPyɶgfkNNc= *z:j9ԚMQ3|RuEF1Zu6ry.|1.i#G@4Tj(nz4!lghh"PrDSɹѴ#M]c ;tN363c2F Ю #Ю[Arwǩ1Su y)BىZh7\k\(5~^VוBab>hʚj0me>Je\U;?f'c"=6 BSP+fg螸g#ƜQG&}l]إ]"~]f~~Zny4MKVGz6V hTY~#,G{5el3K 9Iz+G&][NOvMK<03B4k e4sXJE~BzE\@Ǻ9sReׁiڧPɰqs[H 9GD!n@ά20E׽_k@ " #%(u zEuΔ-TCtdҁDU@|y3C._ so 1}xOBL/1M7HT'۱V9r#SWw *̖pة:Ok"{SڨIM÷+ Z*BK4z|1Zgff DX1k\p3C9,ĴYcpeXtz@:U7'#[UfjL%ԎOk<[Lp~@[+S:i{XkoQ %Lz*]Xzz0$#\3'͈!lDo#1NuVMavQoA$Ϛܶ/V[DKUDyafzH_k]x^U( ,/<1= k2ccx6-"^}dэ+$Aء,SwERݙ}]x6^k!kg.pu$=B{holtPكO^53zgԷ6ф7۔`wC9:򸿴!Z]@JK&S ݶ~aX4lyYn!WP^fPf~)&h$]mMήn{o o֔5LJhmO.dOpfDc?4Wt675l&0̺u1:aV([~xq,6 ߍ79VzK6XKОrUW|4ժBֺe.KUFެGU #zIʷ8\߂۪ИzG9@kxg; k\0R(Uf_}v3sx 1bΙ(*#\JLq+,AҎuYil3 e#Zg꽂7z_&\ IDATRM87XLu7evd־wĝEK̥|,^=жI!.N#NFY>}S$ CJÉz&Oȕn+˭!}Z}oGSrA~9bC> \y #xHomFJoqvP{m3^3"f.̕z=lek]г`r٧+$6x8̞na1t1iyx{ͽzpxd'նPx $_U=tF$N(4ҺIl :5BAimsqeh;z^끴X{Lxrm7_  e`=fWp'Nİqsos4p${_>9)ht>pќ8$[aD( 56]zQ+m,@-n31s !99Q:W̙FkS Jgz 谋Ŏud^rU"*F7\ :Uswa&ϯ3z#ql:iO?:Loi /lfv _T ORw4ݿ"F)F2(H?.fn@&۬,JoOiJ .du)seSJ]\\X-u}5>dq[p%]tK_ęGhW/u\`v'32SLNv\iEݝ`N%+xo[Z12~)2Q}bn.Jy.YW0JwGs[Nc|~0t] ' !},miM/yFb֙\&XMeMdq7[ZSʁ鴘hbT]Ր0'Dͩ3c@i irD*l~U-9Gn'Kb%0+R_}<#jb׹$g.q؍ll*Ӊpfw['Pq.vfe) #*.?Yk4NYwl,N0DP*8|=ڶJDXT7珗Z:! Kg 0#[)'_i`'gݬ:Śٵ$AAbw_ +XX 8I )HR[3_890Ȅ/:-s㺔c-;pfmȏSKt7n(@fa3IE&~XyFE[.tY''c&O0#3+sV)RQ6Q# 3Ю^kcR.f_9' sZܯ(Rk>p߯_oO~g'*}CF"wG6+k;ݫ}f Oa3Xz'[@# ߁l+N;r̬ sQVƙ;mX nMaJt|]c)uX ~Dp6xH7 Mhm7|fۊG&j%)ƤK~LKL9>޿Y]/j@jȹy.MQ@<(+PH1F˰ OA+Dh1~. ٧^kbVF3X.Y;1㷓uk A蒬 Ö1nk{Xχw2Qx{UfbK:9O.x{MDCju\b)BmU4ԕVFWf}1!sp/9F#ӂbQ8,Gx(3e^\׃Qꈁu[Ë&0FD 󈘙ͣ!i ]T 2%22=WG+U@Mq2c`>w姙ՙYQs#?mK0Y1LZDP!z0%"&0fSfp =xnfkz̵~c׸.,º }=L: p,D_s"_ܸ \ȊJ**-Fb+5d7QD̮287qEpGNViCĒ͇wq.cDg6Q1p\D8gbVIIFXsUBR~}tUU.˵@dTM5A8;Jx  (mq\^ԓ\[mJ:E=af`bT4 >p abE/lJiIk3VFrs{m>;WJVz/pK1¼P=uqv 5ØI[3nxpг_FO~s,j|>q3;/Pݏ:?7Zs}݆9EO;g{+­(PjƼJ;QJb| ٰǴ7 uC%yY]s;xk O3lffo+x#bvZeǞRVvؕXꯙ.8wʲif=M_B24:P<8vef:O_StA̍RZ!AwAmľ$i_c 4Nw39¼ay3*Jk Qk.N1sâF--LQN$v3uqE*Ggef8EYvg=1h9ɂvUi% /OSaO3[Yt:kmE"\\gC5҂JU#=З*"\ k[Ξ %6 NU`>CIw&&x}U 8Yr=-Gi5=5x1)&bܑӔ$5J75g#+nC.n捐huƌWIQY Y؆c8`U4){#._i}ySm _Y 2><҅Lg4V{Gs0is39 ޠ~=լF&iPA}.E&e{wߘ}am{{E>fp;j}'B{e6k+\_9)SZsڄ%&;T?Pbc\~2sļ\cv~Z)(Ab~2[sZ5Uc@g'cjjGZlSQeì BﺮpsDYXZة_яJ`3;)f(uy'q>1_04U u2]7ے+y? ޴(Ty+L}_ccay0R]~֥q>ũF4.Ȫb{谞 ,k̿hs.vP;c>J"ZK:!o?;<TWt)veƄ~('k3~m^e$z/meMa#X\:=}IS@1暟CB!cVp܎^VL囸yCTxv yB;*c(uޛxHVFiӬ*iTNq̓?kz&7@}F Y%F18Ag []SΠѼ3wDCFWƗthpBmrnBFg_+#m+PŪeoVSb"D)) Ka3Xf[0muw:k"gLcJ6~+wAAiF7v)26  ^Ҙ;ǒP!Ǘ91w^9_q77mf&|?Zuvj/Xۓ2Z{+,2"=:"p 8y@n+ОaD.EoX"mnpn{EenjkdJ-辥qD?Z5(|4cVmI0J"x3egfZ8pu 1?-Ӟq\gy#ʘwTWBSrm+Ü =|JK6ZG*nD6ȲER) N)0՞Tps`GwP' hi"$so, μ[O%Ǭdm}z\q1sN~ |^ל|=_ϟ?^/|>'9wog~h=J5~2!La[Z1_;CDݞ~y_.>%MqOCPzߑ6qN^uU rt7p}WRur_6)=3N_Ms4Y59NuHC2e -yE;Ho[beq- T/.8noRr{ɾ#9!j%qMln[ y##{hN61Z.ڶH][Q7BwP D87 3d˓3H@ckUZ;WQ8{[bjSv\[kǡ ([EBঠP޹ wGJU;Y壭 ?ڋ~- 0QcX'vC`u0"V6K Ll+-WxdFw̿{NupL%չ'F WTO >KPwC}}ݵ XJ'a"X/MG$6r&*͒D^Pd!N\@ NJ o1Dq;EЊOwG&%K/P~k9X%U2J:}c2v.)uU7F|ЇsCU[]i+?_˯z\kl;6VRL\|9'v,[`~կ~|>ϟ?w|w?E7>N) g”s3nv[Mǰp񈙕 G0L+l}nn;|2e=PwUI Pſz/^KT<>{۫MmٗyTʗri4S/(6;gxK IYo<MOQ!B3 ']!!>r1ʬs uuE^tQ /[\T1%g"m߸DH:!;TsoZ턛nZrO**> sGة(;vPzLH rǹk*a!,"TĖy/%UCquԄxX˧hFzKMnjJ$.${(N䬰S7H̖TD2uoN3Q ҝ#/Bjeԋ3lsAf-.oJ0*L-m>ųF̷'I|B[Cw}7,8ɔ8'n դn Pdg.ׂ諭O4Xݳm'&#ЧhF&On$H@"#%xј2b{FZUfzUظmS t&΀ijr&IƿV)[3bB\z5ml,}S^]S^2 715yVbnq1;lN@ET3t)Gmp;&T/R$93_=ĝq."_ ޺lpV;5/IMS)ҡ4Ic`3|>\| E IDATDKa\L1x쎈Ofp[Ep)0sn߬Rdk{0ӎf.1)rD _.䎢M~hS7gkl*v& VUC9w#AkL6ѡi5-FeEҬ#@a5v"8m.ͮ3Rqcr^#DTCǿyZuQ_lefݤDaю9j)ru:ל>Vl]&&̍N! f#]87FYҁqQeÙ2?߭m'.rϵ&թ`]cI_,.(QU, TAs[kfi1ӶV@b\ZSkpZ1XLO͖^jEJ{ey=x̆pwϲzgb}WsĽ̅m\k>nci!!j1\8o=-<^ﻖ@\sز6̌?6p#l';H7Y  }o~W_;;??.3Q?*BSPYvHwW߄f߸ ֯ùmsmA\̘e.kԡnz|T:hr X:܄~t*y?iʨ͊[V7~i ,![JP?S4*e%,U!)l݇Ay_n j!0Or ~6ڽ* M32!VII2}sp[ַ%GƬ̖ "oqyNWwU?xI@dx/ Z5wo/|ϗ_}/O~'ߦe| 3O#7 i)eG<ܝ?/?yq[af'*u[Zp7x?1vdPtw읃/`֎Y%Ktcö?!ToeZ Q1npdMC{EL7儃5(ΐ @䇦k\V 6Lq2hM~V{`dsz})-nث+Kk-r ҕ[n}p%-Dxg)?F4%T4cz^fRRpgv60m;orްQQي+w t}?~4E /0L]Ja37[6*Ne/Ssfҫv٘s-{DZ4(+xQӔtFv3{q~LG؋RhZcA8HY͇ uBDDJz"Zv:YH)zji#vVŐPFqS! TC ^쪺9ZR7b| xDW<m_1,-5HF½P" "ZgZbn',s]c*sʽ$ k CSfFF\WQ>jRVSN@?z%^Qoet(W)x:}a|X6bT@--|t䉺{ȣtz#+U (3 "ilfF/ݒVFQC=r04 ~څBxs͔J&amΉ Q߰(JLkfIR=_#Up'y]8wZŶs\FGXHzLKM'z~c ]$%f|k_hk:4\Oo?}X^m?O?׵`x{+m: 2}2A+y=w1p ~Nl܈9m"NT~o![u =Wn1lV0BDKo0W_o(J‚[1 .ܕCJn(vC~s{ '# 6%<fGt;:l $-+4]Swkn7M,\)IegSA-V3;?ؕ"ٕ/.Qr:yd1# s>tQݸ 6ja[#r<0TޫhST"'|SL#o3YI@S%xSi婿&\T R8C 7Yպ޹*`'fu`Ev2@r[4A6Wb6ZkA4])EtЭ@n˅!¿=_ۆa F^]Փ$UIh:3CvR`~L"aQ5AiFalԂND&t繐WcsLcB=]\~q|>W=Ok|ͧǧ͖487.4WdjG(PU+t]}`7 &L5aJ*|xgb=D-.u9] vR`[7{EߗpOZ pv[Y'bo;KT(9Z\m/ϷuhN_.h4݈=q=*miYcH)XC%H(r çбOfZә6N"'R^4?ib33oMfss}o5h6Gkh$}#!qoV(*MJZԣj\pW7KygkeƷ;!|0%p:ὢ.]CR?)LȼCCS7V{rP|$"IV$o9ۑZn[S#4]7ӹ!pmwP24O=u\8=8V8a&m?v}ypbmv!W3EZmc'(%uR6 hz)o8- 2%*}Fw|3`mC:ґ  Rm~҃&# Ie[E}>i5Z7Tw\UװUqPR WZ+yg^"RՖxB§0I [A1 &f~ɸ?nNr6xwچkf^qϑb nS&I8;+Cwd#JLޙɍCNIڛR>h$thU,QbP/||CNbݔEOb0dс9nT@ΩwctX;[96\71[IylX:CnvI1I5֚8l'kr\D$)'(127),+ƽV)brAfV lƀ;f{ޒSו 7H!!'ƅҸէ:t EħIvêXI125nOge{'u3X`g3efŋ5ʸt.T\~kUG?(.eU=Iׯ$ѾJthؽ ^U|^oӇ0"~䘽=HjZ,-< [kFIv_ k k޺=;!}qV>nγ+Eꛢ8iRuvhf9=gK^`hoIm~̶ٔ {V 43 3Afp`ÔجD bgfc}aVw7(ͺkLN\O5 Ys}WCylk@֦p+TZU@"'$S0gp&Ytl$1MYN=QˋZJZwϥ?*!1a6t;j6Y`gH˜HKD%{Ζ)7wǛbeQ8@+P{-6ݼbS/{'$zr7mͬPݥXG.qJmtGSR\\7G‡,eP`wOOG;ln 9v؜܅B *1O=O'תU"wd/5UhGGʁƐ<&:ݼ4f|z5S?yh( Qlk+t-9Ð_N^=3'kR͉hV֌{ضdeȈX %qS"C&l+_=t/@ۨhbswsOysi9T䗗'u]ؿwx"2*ŦBJE9c@'cz]bw/,ja}|4g/4˯qS/3KNZ@ -d{b5*0j`e8|i -0I3j14L;(敥.ŕ: W)kH.? y,^ Ɠ )yJQ͸xxQT]F(]^%K^2}>2R63)-ixŽY~r$drkB*͈&x,=LjYː5611ޣpSЫ zB^7n X񌾘f"js3핲#֎)Ydd``Dd˸6Suc! odZr3P"0#r]EcaFtB~Xj94tV(sdYa8OڎG#XشMn0 #z@zdJTHY>O10XC}ˆ:A^EXg/2֊q-o Z f((Xf@Hi:F@5qp#mK?ژcϬ=dzfSctVG`+tqB|mV ,F%BfQl w[oR;Aamr̭<=Ok(e=}O>yf]xX);XΈש*+f#""FtL;Q5"찛 uCDldC^>!1bEEwxg%8W@ζ;F 0Lonb-"tWUZF^2^*T uD1HQ;zWpahP3#zWje)e$3)! u,X, U'>?A''?&_P#c %#I":TjH֎-rSxsNK{ c!Or޹*Iʬv+G鰂.JZ6BJL[1NĴet:qSQ>8+OPMDA1N-VOET,ː#>/\cA:ϕzy-;x-c^}Dj G4#bRȢ#pO mh\-Qc·A*Mp h;E {Id9dTOFAMN'HM"<,bcnnn|M_)Q2{Νtq{nWu %g 8=|T9דO>h9r?P#aa6UCMܲW,i4!ԥEfs{,A]V%E-,a͍_z/L6eGlbn&ww=gvhg7hԃ(){Hljo1˄0,>7sB~5؉2V8e@* zm4*)nVL݆.!g85H(ǙOU O @l-9ˌF09`]C!U!T*I>N+88=pLw-R 5Yk\:[0bˆ9YHF|({4<obc?0PCҲ*~4HH-cpZxl5jyّ9~0IoSױ6*M{CfqȳUѶ$Z^Nˑ3qO&  ~Ɓ=F8 Y rp;)oRNÙr)hQƫi=TXĤiC[ۂV \~,<;/VU}Y{Ԡ"3ɍvʬ97ghC?dfAm0Vd9PJX]/.ĭUa{T$5,Q-"aMiA`1>Cedqe3y (Nݹ~k-PGR_J$O:IQ78}$j]m['Qmw>j2_j4'RM%4Mj/3BWgD(\%Dʝz)W#ڪ+TIHڶ"CS9n7V4SÖ8!JbZu k *1X lrbAa%7ix2/~W2?|ONo>}Yin-7 {<9>v 7^z-3ETM3FevbOwvwIja_ Sk V{qq[ne Cz4^1CkUU罾i)S!_TśFf\\8iI^V'./5~MEdnV|> AMyO  JG|(KӼSG:m]euBr^EudX~!Vl&A9fj&2zDv}-D*Tۏ;0̍~?sEiq[x\T:X؜믿O77N[o~{{ŷ~ {x}Cɓ;w3}~?҈' 2 y z7Ia˞kGSSUY8Et&9L~ZD$Q0r¶*!#rt甁ei>ZHwHJX6 )0Ŏ~&sˤ խΥtw5K DYeWs!{LR&좳g&&L$Bш胴 (ќ<t\y~Qna~ H,nvpfj *e vQrO6c\X<̘Ue.ϫ}7&bʎ{V^t;EXHv!YT]Iz:u& +xQōac6*|u'Z)N@/~Ah;p O WWrw m1{Z.bvmmj0Oku `$TUF=msOZK=)hBBL.թbX*`=0l:U3eG퐃=:9HQSUTN"J2⹌=Lppe`a|-4@Bff1Xr5S DD_ Vam-HDHDRوG]3kB<= {3'a"C-C1Bu:ϟ?7-bfFDU?~_}왪k~vOϧs's?1PX/Ν;t z/Ǘq&-|lۻsI eN8\F72%!2 K^Te2Yi$"e5S] A#9.;*x6+ !/dDO{!Ѡ΀!B&S*hR?3\ē"^^YL#J&Nw#^'_Zi̽P%$4/𴱫e%O\ ;赳p6|;#AƹhnkJ 4{Z`"xZ]>Y2xF;#Q ]@,N ׂLs4Q4wP+$ݎ%fnjHXQl'8vgcGZcȋ cƇ| ]\RR2З=e"d1 `$< Rh'npLaZla+y KWH ` )m0{F3Yqͦ0a*{KLQd_H,T; Q2`hfP/FUyqTp!Xwk.Ȇ*Sح\E$kZe콊0s+QoSR3rQH,yf8c,ޥ+cgV_XшZJz/茆1 +$ItڋGLE Hñ95 jvvEaVxJ) [s,] "9!dx4U~] hM36AywlkD*"(VE6/C,~śoDJݷfWr{;?O?/~ӬŋO?Wճg߿{W⋛~y頻\ es\y9Vh+םWSB*wο]m+{E]bT\Dh2-i8F:Z{-ra魟2xS]|~1 [uI3TbbT_"ތIuv! K,F$Ud ?- fX ˻t.L>alj 'HöGxQNy?`6HA̵A^hP6[wVv0^f3> /v2(/sˮpio2E]H:! 합Q3 q. s) kV 0X$Qo|K@/IFXvt3Uvpub]VK(@8aIˬscD4C^ps{99e]5"2Bh3ԜJ(ka9{I+}~sQ>޼݃b־Ѿ Jhh;so\V₍j*#uUsY>`,ɒu#< 3s /goѕnO3u I@f9'100rF{aϏ"~.BԚw۹UCǻfHi6r^uk]b 62ˌYBc1kO[6Ĩ܋eTQ sg;0R4tǬLr9nnm |0 SȊZ:EHZb;L#ۣ=ܓ}깟Iij0hK~0oX$0EojQz_1Hٴ@ٶME {^lUV^z/0HtZ* lTyU),¬u΋r!XXVhx# ?f .ό Zu.tֳ_Mh<&88WF'DZ r j RzY&JpJk9`/&Sv a|U> D$gpXs֦<n؛® "id"]AS\Rn3e N.v'PDKjUSv H]fxNUy!Yx)!5-ʋ3ڏ!1T)Զma"SVwmF,fAI!MgEl+DEc$CD[k&-KE߁Π_z1&iߘaMkZClp}7S?w3Mimid5M#t䰫j]_]~/*^{޽{?~o\__?~?7|蕻w;L9Y_:T moŋgvm B.Ed||w@.=0E*eǹi`Wm3TWJrxFa43>!1֖H*k- SBcdWf"2HK/G // DC&TÜL3 KiuB\- wl}?;~a ]``<''0aW p n\LFW9PBOfJl:VlY`9+T//7g.>JGfȊgU4s "HՀ.IJ6X9w ` "_䨝ab ekYYx3r "dSb'xWWW@]YvO8\_e΢(,)U>,xAjy9vzc`DcgL EFBH1 ld e=AYc )^I뵳jvvAPXpÇa1X$`ʱfȫ2e.^TJ3/T@(jnV2OP\2/R04|aχHE6'y!}> >0:UAw!UɬAf,KU1 IDAT=RZxUl\Djʟ/[G/qM~Қm}W_@'o֏|;mۯ>;s}}}mzꮡtE0ͤ !"L DLn!ì-n9 z.%UnJN83E%LsGuڪ>AQΓxS-2[iU P=FQ8%R:"`# fr)ܒ_EET'.aJg7yD!UrdP+Q易|bn]YlpQbmP%'<2 9Y",&FePy>^$Ç9u -0p%wb<}mc$Wi2^P=-@1M!}RV,E6pgEL'CdLs0Zٻ;f^G8R8 <tUդ o2痹X31O 1a""79{hy\^` 70RV)B1Gʅ֘{jȸev aye.gв@q;>HgR6l,AgHPP51Ԛ^ Ʋ$f%?ZDŽȱW~![NR_r'|;Dן|tzwZkmNdK.ɿ; ~D-,|#yρ/n-e˷ķϯED6TJn̘%~GYH, gB$eqJqZQ@ؒlUK2W2Y}k)A 0y3"pK\ 4CtxT|衮MD=|oꚣ|AJp$$1G\13ABQFaQefwx,a%D*Pb-BA ߀;g!~ڹ0W<掐-/h/Y0g-ɞKI'N1dz`0KFPCȴ.ha˒LQ&#N{JxN'0/^ذ'"*ǧ~+gEmnF%H 06AZI)zTU(RhV!ѝ66#*3Tv /#$4E6Lm^Dpڼz0d)هpYjy$90A}J 6[\S1^2;Umհ1tX%1 AUhtZɉ]`'6N讄m^?P>D=x\=(n9fw-”vUf"V#3;en:壦]2[k$.D4j6v :F-wA?E'x80U{0N+$C n ^MkO4 @b C*L*QmӓyǶۮݽK&vegU=ŽK`R7.* O㸻xhvfg¹[L#"εD++vL 0EYkm۶ڽ{"ٳgXrN,q0Rk믿v_}kzX 6k$n3x3`=IW& IyXZ9|?pZ^X1*MNsi8d/=4<!GQCae agdfϑ<C}܅003$\1oYgSmrIX`feS"M`LBeeo,h܂cAE9̒bTAMU &#,쐳ǘ pab6[+|edK@>1Z2 bz?8N8_. 0,LL.⳿e=x0-7 d.Z{F@ő€3$$ӊLbAxa@d{P-d#:K6(<=impZ(Af f P*9Ɣ d(dՌhTgy,d3J}-XZYe)fD\<h(ˬ۫/s{0T470%PAAqL x mɜsŸ5?&R %s%ȿfO208u?;OBdBPG`Hf~#'٩6s&dX(6*))`o( df0Lg߲y2O51 Z FPJNk6֨R=h maT~8K 3|rϟ?OШQ_*Dzy-m 44 uD.,M&C'C=4S_8 OBFpKPzεQ28=eъ1 H3;̲x>MUUu:u{qCΆ1i/Dg`B(']]DM?c =CKJްtZz<=HMzNDg 9/(Q"D?kåF^>xK ֡t7_6^[{ndfi- nGDPGP\e]cP`@/l1;/a }D8"-T `liP+ƞͣc%prDcq8Ff{%/(/(2˩fN%-3'淅@w,_p.ȇ:#ENvF#iy$*T/3c'Ww8gAat vؖZh:sLV QBqC9 lxs y0`Ys9v`/ð,1#cgY!( F6%<̅~|%xRKi {JF<+I@!}ze؎p/BXt3Үl2ܫBlpbE4HQQkSag|-dqx*ğ8U(h A$^cA5P3!DB= Wޝ.XU\Di`.Y^ZM ZfZ ?'Aoh?rHmL/=HEVk- c¾2Z vi-IR.gQVJ^v,2),0gՄ*,q5ǤY*t& Oƀ-̶* qQ<\nD:|pHTߪ{xKewB1=\E5_ |<>g ±t9;9!rWi{ǡ4%L ˍHx4 5YV!p1dJLV4,HGʰG<)]x%/#p'Md6}>O ęfģwJO|Q:W ,:sbHW}h9+Jʃ߼8WH-Ǒ0uSqQ0~hP'&{H$zA}yowEpa1 mœX"c +yR_UIAg[m/qb274埻Q/Lp+h&?0G"1h}Dse 3soR:Kch[~-TH+ع}/rʬҕbTyp0HpXqUO wsY%fۑ ٠i*403ȍ lM4@nrPc\]]Piӗn(od{Ego2*z\ǐ>Ȼc}f|αp:ph10 c@ )m{K% 2Т}zqTXπg o2W00.lw gEhҀF @Peoʬ ZNH0Y'j'"sesWGƵMJx"f^X;cTf+>j+*˄A ennn`-#-'g9Lz=+Y6;n''V]sPcm|$(V3~؉4! glʩ9R R!y#Fz梖X'("t 0ϲ3MPJQE^^O@^]?,ZWM={l3|㏷m裏 W<#T>~tټ`fёu*z9`d MRmm:P .ˌN8o*d,FiJ*xE `#qu^hFwƷ)2̣s1 븺Bb:Yt:"F]h.xc."p.=j! OܬJt<%:r>Ķ9vX<(X?n$s8S.?oT`0xn3Oٰf3]D5j~*nOh3v*ӛ+G̜䪐B[\,b&DN j9+H"Cr-16YVFYewYs䠑jTL[Pr#ctV\͢{0x+aCq o zҰeIYEZ3~<r;-K*RpJF9tn?l{Z$sb[mH-v"8)ѧ 㾪%缰P%d!<[nu*2 XNQ:LZl35FpҊpf'UIa2cmiHgFm>t|"Bg AHs*l 8PHHsUk+&)+= L?xZ:д!'0DFz?‰&YK6$"+崓Gmv<3\GrSDzuZ-RuR$#4~pT~\kУuT' j[!&bf_4ՖXL̢=D&ƕlۆxN.n*H3o=oԼ'[3!t{1HS R)h{O|ssÇN'̯]ʵw/sb":UUSl\_ϼ Kle{n'v7x16-/?.v0k/nŠIf[^9@CڜvCrL'?<9|M縵g5<"\E/܈< Xd4*EFĠ3Ejv IDATCKG1@71MkVρCiXŰ`tJR% H{60䵘 *aU~Cւ*9!SQ3.P n*R<&Û5^Zwm6n?B MshfGe5¬swi{XAɛ]!r&BZf"&m{ťuHNT( %\-6Ңe]cg 6Z;s| _109^ lՇ~kxo}޽ϟ _xqsss>onnnnn꫏?XUӟ/?ϟ?t:{BUB}٣k9?__,ָ;'2 )EMULEE-kQݑp2I]82\6| #3D~?1Js&vkb!v$E|.⭪ yAR&:7eQE2aj1k)P/p򍛚J#0j*O3mO 0,g#-Q|pA 6C#2*"v~M-V9yedHp.埖I:A;Ef5![2D,bh!d1BHPU=IP߂ܹ 1$ ؓiC*81vN?K,JhlSUSswm!kfí '3iI b:43S:k> 6+d/;-/Ҿ\>GO~>~o!2x nZӧf'.>_0Uxc@IǯJpMLPER13[B|qǡes CDghJRX/X2:10V..ڿaOpvAƓ4 <õ&c=5YO{*cPCfblvE݆:œ/1uaXڠQ@CfzIkFZ8_ZٟJf&8tJE $å4- 9P}v3b"%v {Ifq[(K/C.bVdbvnB4#yzO]#ga Eh(=d} %sr$!II )`ٔRFs<ĕB%"0lK 9id"k4t J , l^e, -<"rHdayn$j30v]ou; 4JA%pE``*<ć;^݄%$[]G:R^AHZ7,+U% }N*GHehD}05\u\I |we3o]%9*p s5kegTumkn1<Y|LA,tl#""mĎoy@gWWwsDZsv3z AA[3x9{ѝ3Ei@{צU 7}WD ΗmmKmٳzÇo?_7|7|_}'Ok~7/G?tss=*A_ӧOUuwyLx~Ԫ$}$ĦY"`cB$R!ii)jR-x1JzϏf#Ts(@X0(.enʱY&|mԨS|_Ag"u2jPl?mdG$8/-e=>+liRr<#K0na[ _W ݽL }2,&|ŔسGzm ݹt4'+BgZ`NbswwfT)MԴw6'Q'|?h&Z{p0is-h72,SG`Ϣ2cl׎^5r0L~~kM.I aFiuÙ]~hjZ[j+ VK -R\p#9KAk?sM: I+ԝ:EN}RnyixΨDfws/ @k6Όb@<Z3@. %9u n4sDqƉEBZK*vIoAi2H[1|LS-U+i)M\TXk%sBTt:]]]kkPxUo$I5 skˤ?l)""1ltbIv=Ouw0=ppf.#:Yp+Pݭ5߬Pr"hx=nT!̑Vstff=Bԝv}]\tF;!Ԛxϭ&:ǻy>eq@lxmȹ#W QY2l>gu[jV+ÃNr1ȷR!Vw 4kd, effsHEDh7e5hxɫ뭷oM޽ÿя~o<{vΝ{ǟ|I=/K.8"rpR%S/\gO[K2{,<\a;NPD+E>'vi_fq-f8 c^c\ -CaA]agV#0"fxHvd74sЄIvGӌ2ecfΖ:11rT^BCK#h@/EK):4@pRC< ߑYe搘XaM 9e0Pj tbuq;id\{}<(~l ~(/EANDN]"LdvĩXebևhIvXܽ ¢I! M80{o[o& ~9+ѣG~{{?~[oN/ŋܽ{>O?sl: xBxȌdpB#B] ;ՊP~ ߣS 0'fZqa(GQox,(3h\MG;d,{ɔ PV ,-Ȭ&wLTg!>p"xOURR9.*Zn,bcD2Vo5U0aVS:B$'k)dWdʪ"_ᅌMFl."&3=.-ƍ KZrvV(٪ϰi4)ceQM<{ 45K9^f6`\פ[4"2/~,2Բn8U5Qlj{ԧ.OIB>mAڣ#ĀY`j-p1ɸPމ9sNUHT3 e@efO={MwC^zOv1,Qf}y<^ rlZB6~}^uC~ E1L~hU^8▍VnۖXL j !{ZjAeuBz㩁#hH-eAh)v|!1 ̲Df\"rlDpU^ψ4>eIT=\R+H-@&T7 P&}{P .m Z9GXP3]*MEdoC1!8JĶmfv>Ҡ <_of!<|UϽY[ڶ/]QLcTG[yG ,]Ftv0={w jDG a^5k:N6k|VMJpۚK jO#෨o2 pLfe-O#Y^ T mJe$`hW֜xT!fNGƌwOz9ZRԷ |~o~Ǐ߿kݽ{Ν;777|~ٳ}s?|]_wt4~Ve^hm|}Dߕ{Wg#ɔc)d~u m[uz9rłL.[]]`- jR.3b/3p|I;)5{j%+H{q~XV8_%絈:"!"O ΓB#(JYD[,_?r9Dą||=?zI0lɆCY ;u7Z^;Q2S AsgcJ^AR69E;n3=hkFz7v nvm#րrJ0l #κ*6`xq+<ǹ`&4+%V1eC \Et}lx>6_1|"+su@Vd\-ȘaW!(gJNLP^[DtpT\8ʒft=%"mkcݍbuq0=Vg`-!AТ]Bҽ ߰1rN#p!Q7[!}ڮ,T C!*no ~fŠ ۜ2Ǟ1,`0. Y@+ǸRgR8T-ͶrF&ޥ9Wִ,e@Mjϧatl;58x.otLr݀'!XǺ8 q L';,ÝkfqT @3 A#!ƭ Z(*-[Tڂmcw`j{GGg=Il$bQdZ5 [01N"rl!9=qУ#s -M+rfQay,W3%)Ocur:9!d4i>vp˄yXC)ƏCkZyb64%y葵PD2PZBvj^sKhsJ މ Դ9 hfYɩ0˚ָ-`m緇K*UMp ,*@H a 'Ǻ;Nѹ!NRLהa9Z=s^@# $:RӨ'Aw|ZunZ'M9.jr(ŢLܲ?&J"45~9vқp61_U#9^0&"("pqW"l!ý|K=\V HDt:G@UkmcmtOeXazL,'2YFT%NBԊOBG.et 1T`Ү fzgf믾oIÇo>B&G# ʟvo n'`/;RCB1Upϳ) ;-l3}-+T6zܲ^@HAqF 7a~U]G.,ƈYq}5e)cj{ȹF#%(ݦ-Bdg``4e"Zd:ݣKmD#A&[x`rP TPx9Ch Ck$n-QceVCBUAaIଂO`9Ǜ&h YAJc; xx]W,sbPe˘ּn=B-)A59e%&w0DVzh3pwYҹ/uɬ0F|6sQH3YB!#(@r k e[xrnH,]1FņjrB8s~>^Us=3-+%.Xc=!lj'`*Pܒ. 0)kH%G#EqExY!9 D$NB̍"z`ŽNcT5b}/*x< ωK"ޠ盡7U3\ dr+\vkTufUD~1>3yq+?ᵘkofwz-JiJy:o[w*ЪU/~jn\[Tj@I!q3h,^\)L%͡Kd5P$ RyͿ6t4STV 2pCW줗7" LįUehMU=U;PцЂ'p͕Sxoa2x]jf)h{WP@}dSC^}"aJyj|ΞPh:}Ry;H-<*6c';AHBV ax^t(?+fYo1Bt|sA r\,#0JD? 6O'2K.Sq>g,sP& 3?p)1!0_~:7>0("B}qU2gO綋j@8J]וDQJ]if":2i"B\ҷp'05 } 1?ùhg_YsZ:_c䪫 /^᥉Esao} JIፋMND+aVY k`Hft/%u vfr_bs$ƨ!K=K(K?ɀj&3Ox63fQe=mAI6.s%qg<ܙUyc 'iXdU6" rJf᫰j"fV(&:QSD|x"{%˶i*7LBZaT~]c?, &l Y^Mq9B^TyXe1MC̙^fG^IK0DxfQ)H X+) ,-~\^hvϥ׭kfOܝwl00+@pwm^VdFzQYq%Ca)Yx^  EkAo{:C"Ck؎U\L|n f6`F Yzx>rwͣ^1^iT-g(e|Ro-BHyETެht/2D])/G_8ӵO9eeDZ0+Uf]ja7jo2+ƎKVe\FEoQ*ПLcZy6Cwm@Z1tl_)&"U[< ]sP+CXBJy-6 <3Dz=9 .+}Um/ԁv,#ȴnRE3g(r#z=9.f(z%+Mrlno0 070pc|W- 4梈0(򱺗i_]g9LH8OX{@F-Y:ed6ez>;s()Tn 5Mm;;n{m|+lkUX,n"KqjA()sv,8#zbXfT@۱}!ܳŨࠉS"ns@I d'%M*W8YG}epyЦà"[1pJUP3ԦW3LjYF%.Ҕ-J@$8ǭ kN#HmpZ Ҏڪ+ľ/sGxb$=kҌO\0 .%5[gErqC΂Vm+bNГ1t4%&P[h׷K2C|5c~]x 2zc̓b·xG.Nk1nE/q)?YRnOfݍ+(|XZ~sP0F,P&&HT_S¶5y#>"_Qe3{<b;x;UcTymD @4LPHG3Sqe%-bm@'wV#,>K UӌsI<1dm~X][4?>i ~% mު@?m΂!`F{W]9C~`*aT@o[.UMdkJDh$7+~ѩjFft-">&SEDIJTPb'<"QId,dOES r©s$e#-^sO<@uP L+fcgSaЅpD:2JжJ`QQtd-N;bR1uyz\b!CwkA I };A` lx} ،w A˳T!Cm%ܬ 3J]5;F &S/gF%OCC7$JWdd3M'nHW3&L%e׾6Ă_]l- MdH-:B9؃t\U۬E9t/" >k5e!MQWa H15*{nu:&:h-wVfHNDrES)̰ QlUiooos'Tc:kܙ4/EK Ӑ-{g Ҝ8 Gx5L*㗢`CXVyH1, L|ᦫ)-bE:PDD2/x̅!/jYoDm:hc# y j y>0;MqO |5:At;\\5=]ƛMi06P!xgoD 'v#r<%D#w1ܴn>҆%" 0=s3F@CM 70R6jBo6䎘5Zr~A#a`#vı#tH92['`':P/ya gOz!a9r 'o&/ P4 iTv|e1ETٝ%<ci: =x@&ƕ I-Qf;#b=TJ^U}!$U~YDCI:G̛rP%ws˩HW%@U?9r yW]gP<4 s9#>s_-Y7$մN#B(Ȍ3מA„Ux$ƧRja&{o$cf%rER ,$cc3.plYRP}(*89:96O&L=Ta*xk#D3?GαLHGS-%>yiT݄2c uà| lMTy v<.j,Tװ=+8X!oeധJ ɟ^{M7Kɟ8[8ryjs,fܒtpZT\USE\ƘyS 唓{beUMq!*EѲa2g[,nxh|Bbx܏F/n?{S7<(nۇfc6֬!sauU`u]#'t%ZCn$p>_9عLj uj0,;*{ m 0~zhYB &&ܤ@v˧U!7ܵ90m-X/9jB&44aWtF&fryXCRGVm L Tz^54X:rOȵۻh`0>69ْ8kI,IKC2=.w[bDdL `{ʮÈjd;OLexaXHN ӉXioj7'Ш݋U7Y„%mM{*h8q_*E$H+B9xܹdWwn~b7;k: Y#IG&1&}l'x+ .2 'V5ɦ.rG[3WUm\ٔi~b,$c~K{rIȊoŊd_OZ򐅗i"b0R6k0.H}b\).+l%ޘB_WRK\v0Fœ`ǣ~'H7e^7p?Ugk[%]ALwyps:t OX{L˘+<12u<`Vm>j^(?3|G<p-(2="|>t;YD.(2sn=v(SrcQ\9dvdVն Cs~EA kd29X5\YKo/uQͪ>|a1*ƢsE:1* ̭0Mu1u*L1a<*x˱Kj&_\[8-g4~XD˯2R_?7 q_q^pAdnJMA *Vy 9m\eCe&<0rYz|rD>8fy2zw2.nR Dd!r~*{QJ9Ƽx }=_1%s< \@!A/~s5r3TDm. 8YR :wi6{{!*![ h(!;[fx@xspDTvzZo^0R;WΎ0$Ҕ)l)J)A|s/o=ޱC mW!eW\\$r=7D:=$qk<6P FI5p"M̹]g8:a:-FV ˾YlEM>ă,D^~e 3g:9FL>0zVj (6㻗҆q5_'j*a@ZKU\#42** Ѽ" d2 i^OD𾯨fTdR=iϝi@hHP\ik}zy`Ո$C'(\#"TaH{)-־tu8`f&bxCZ!C]6` $w>DB+[Uߠ]( O&^_Q of#D0lw8;"^]?qZâW`rVRQs].&P2Ղ^@I  h 6Һnr kk4*.hYi-=diW })[Y*s[uM-(y}8#wU~ATW&[We+w[6U&yvkmtg܈{,b"e_LT0xo:Ns3Cm[4 1mTF5* &vϜds԰Qd4w bN^Da b9Qk#ڝl `bH2C#l 3QssYE֮'GNͲFj mL8 On[ t.iñ=N[zJ(D"e*}NT"XBw4ke]ܠkX5Yx7Ry1DB܂O.2"o_g&WC>16R"zmY?O8Q] 0@X2d}9!WmFQԶL[&xj}`6"F#6N&r,ṉ~~4FJL *8PUqBQhƓmg KF Ѡ LΖw*9̂.N8`H@NVĨ?ܚ [rxz|Kl3q%L$i3' JYlId=9 Ggw(3')JDZv9UzP9JYwOv7Z<4I-D\haaRQ o6E,BDERD"l(vb Йe_"*2lmիNܖ<ئOȟ2QQ^h];) ONRv-|fF!ᐵ[XDZ-LȌ_Wi`;(^g9 8a-T mBD[V&O%\:[ ӯki4 񣂉"%qkc26L$$n\M 8c\T"`l[/1dW("k)շtZغsŶп9 69[bUxg!ǶJ!l%{bԂWm k0)U3e^]v3 ^r̸mj&y\RAS Q0$~9a5+&g>WC#kP!=5mC?b-GٮcCe'9i1>a~cҴLc0LZ75וC qcNemx-l'Wۦg{>+ڿ ?Mrp& 6N0ۊ-1Fb!<Wcc3`%Os\8OX*T0'c`HeYjc;*Dw<K$UG˸ٶn=u9+ipHfGmf^@ʯc0@_B+!:/[l2<ЀFauԷ{4jȜc@Ú^,J`.n γJ3[zot4S"6}K A!C{(9kJMs 97ɱdѬIN'8Lx&N1g|.}VX9*\ȺkR'hYyrW@>LO`cBɲA;E)k6 *N,pC<8pYg64ø<'s԰`4D Gb;q,#@wVAԍHvq"<ؕ!G(=s6"^~7~y ƲR*=4|<а  &m)yևc sw7OrϾgJf+J4cm, X@ lYo# U`|n5.lp0XMWpF fF$yAELx^1A ^ TVʵjB%vZ.{c#(;{JLCu],5AFUeOi^} K"& Qyw=b )L|n:C ,w:;b|ʝd1̬ĢB押a4X|] ]3)Zp^:q4#ba&rf ] ͛k<fɄT~ΫXeyR-gTiv|뿂Om-@}OS V["w.fzMDPp ^BٻiM:v>E$ e5d Xqm6-s EXv%?ӎ쁤94]-0Pa1} ctD-\'~92olޭZ`6񙿒E@/䧵ܣc0crn]Ia %[yY]C?L͋^Yދ\e%k僶!Eдe,dL,P'3"aYcMwՄmR!-pd̚ #mӁ 17rA_-VP6+&֦ԢY:KZr>%buGaKnm#anz!D$q2,R5R/d 9sY-.ϣ ?5єy+C?Se߭4T<@`2 \`O%;[*f 3sx L&|ؒ-޹idK1RMӘ6U |&djF;b l9r,LMۘ1f?3;z flR/^-!g'zߤ12Ah,|9D&JLhS c ȴڱvQY|~E1D2FS+K$q0Ԏ}:yV=<(zW^Pk_ |Pөbeb IDAT 9{Lܶ-"ݤzB{K.Rh>Չ7r k IƋho83~teE)UGh"@n[Ǜ Ɏv­4=3r 8bŨT>TQ^&B*.!wFYwC _c&<đӦgň Sn]䭩Qw 2r56jTO8,zoP4DzaQʘ`m2`V)cHVgH:њl71׼ܺbֹK|#^l;v~ tޜ_$Eˊz4 ǁ$>,DtAGFDd$D:Z)̅cNca5= ^b-3A0"1RɍqLcX]p1;M *z1ZKPi 6TzTDJ|LVd$!tz1NLHkO'-~!kuz%5s⾮kc?rX}+;uB\b=>AΦI<<[)"iGP=!a#-|\ɸK9763)""7Xm`#ȺLnrZܝf,6#KOsCͳ9˫ȫ7Ts8 wFNhвHPǜʾ8"Pɚ' Uwа|=ByKA($<|kQ@2Qlo n8`ŁrQY˱49m/4~' Պ7κTކ9@Fgޒ0ƯGLu}8c󫝷<7SadAf3<^{`q4 pR",~~;RGG.m| Af=@QlW={h`P9^-ɟ0gLn/-g ve ̆7t-iDG÷eˤϮ1;0Gx?NR~#Ы0$*kcn=ُPu`%̍Of0=Q [im-ХwS [zp|juhsauLz0.w 쀗@$NƇA{n1sJ4uL/P`ɧX`xԒ*$Y@&3оk]R̙] b3@ey* #A`٥fCN mjrWBz r H?uXb!*55G $qis??~O?}<oooO>y{{{{{|x{mrM_ׇ><u=|^>|׿ⷿկo\[΋uM3T3dFt Ao aЯ,E.#µvxjj2\@*rU~~IHK53AX=oDC5\x`9ަ@՗&?) -CcwNroŽrBYl#]Bxn&1JO )(DTtn(Y0BTPf_<"f9[ L2k&7fMX\Soȃ0)nh]M!.0 12g0V=,; 8UTz%6K\AcC$GGAX-ERHӢi{˹^.,+%T‹ |rRp9Xa8AO(4uZ3Qmlks}P(JKd&j | kcFDC4Z d6`P5I㮹XF)ofi«q|!H65ru w=B; [m?}hn$^OTjJ|>_Oa:|=_ܙG?;] 9IcLSځ@r뭨GUy~)1ٛu~j%PPޏ|C**ED)ؾ=loB=5P K%`Sf}-d6E1OԕIE0w)u$}Zl{<ץ9우Njs[,*o-۬S8el~{rX21Y(:Ջ<"^c;ZԔzO1p+d=cSGZ.\:OBTqGQ <-9QqL9EYƯ6p'Z;l˪2oyJC'ڿrb㓈HK!":SuNL2.*"dܘ7xŧWA}EdK4 sfHU1xތp&GsdW++0Աs S$݈ܰۨOa6՞$޸hVvPfmʚ4z̆Ц^C`Rd<NG3Uuh5D%K!zZ\3jп@#n᱂s;iT^I m@(-T,ly53{Q=b:B)Q|kf7XDBf 5`+|8#o|?c5CS#Lo~O~g?駟%њZ-dz?׿믿f9:{u]'|˿E0) unj&z5y?'Dױt"2Դnzr-2 `ܜ 1"C(BI~tmt6DU="K6߾rp_31jEIE4|hٔ[fF-qQ+KfQ(&4(3*۴Tjﴭ{_3➻.3a.bK-׭'N`[#o]m*P&4&9Xfsi@KqK˶s?\ N| Y*^" i AŐ6 8,̠Z1kDT5-yo8 ;e`wا{L 3҇EOLJZ9s1kLo uͱKeӶ1Y s8mvA@Yn^FDi[/Zs;wzܑ-0Θ4%gCtks:T=0?̥z{ 㑅}!`/_MT^J28 YVY7R%,P*'ԛ;-UfWca5tx^Ǻ_JPg/c-= ewMf~~!f>)JC+qڠ͝C-lEdXW%p]6{- }k蝘QUSuaZru>_6dkb*HQҴ!2l:,cwnFI;6u~{y~^_ -܂q>ݍ?=f]z)vmtk;oM~?Ѐl_i}LW{cDJ"*;F=ZO%#=";e*֝:p&+hcaP-' 3c<2?l_ =N~AƯм00aG6^ebA Hwv;~᫯ywߥOAb[0!H #Vd'@ msU޾'1N@k0hQ\V0Qni˴er ux"c C 1p9XzσX9i҂@ɾl(gO8*P3@^keU5 e/Y "˻־EZd-3ce}8FMi0#O]9o20L_H07򨑴d%U >Z9oM]f%b 0% ~ Z,ѓj9XJ8Mun͐T(|PoϦEGXK_?3{O?Om<[hLXt{12#'2ab:`N;⨎Æ|MYy¹c֎l!Ͽ4\39/dLG%2 %W0pR4YCHôc# cc,j;N2-.qɬ =V=3 ^Ic%K8J Ÿ7O#{ɚUT=bU;DTy10g;< |>|`ccFp+.TċA팑ް`>BYM:uZ)/Eq&vnͲ嶙 <7)il;܏=L?1GL[}$0 \ft;q#.$LMঐqرᩱB1I"CteKV/<"̥h1*Fe4䂝V<Cκ=a+ 5L vDH31s0&u Xj^X_ٌ1 @}jxɍaYpiwNz]E|pkJQXEifjާp1=WLc1R49MڿlO2kB Qz1Wx@懊jYz,!jm5[{Bj!~PՌ@]-q&s,=HaGï3&yGW`\vĦ=j0ɦ``3ayQptU:wFKTkI'Sؼzn -m9ـNH(p趩u^05ѠHJ5Y̝ʕIZؒèfI?;{s d``:bx_D6$`Nl ,L]B/Y`:L̇lF80-'cM=X6yuD1!>??~,}cx%ץWpWho߾ݟݻwfχhͮ`|pyȨH%TfXaዎ*Gʱ '+=v{?Dd*V~^*J&j c"W`h@dxQ2Y.Ma2⫎SFeܒ#1.nr^21KdK$FUE|`:bTKc\My\Nu7y$h2=je4 9Ңdڲ,3Qy(ScWFɲyqz`A[` |Ķgt)u:V Ո3c"s ϫ}ƹA%K%b -1@]VWo1k#ϓ! $x2®1عcLN6@QaJYDZyK3:5YXdJ2N`Vi:g42 F&p܀G,_g= 2ήfǀßG[wzb'sԣ%UTb_Hg5(M1h IDATETcl =|;rdI2g>3d(PY\zDܣȫۭ.]XΜ\jy!. 8z=IUf0xut v_+y_, Aіrppr {V?u&1l뱉 Ռʺ!qkG0ϨZjR3ow+[x% &-Pژ(#>BA3eF*sLO``=L)y!g~ dWChi )H1>}fUv!VKA r̐e#c ΡӬjLqvzVAJ9,煣_h*$ *}HW%b pJfK[w6YdQy^9 w[J""$n)=8:G]G!,YY)Y>@O ,Q !Boͮ9茁FBR!DWcIP…FeNof{5CbVX$rn*dɱ =m`x- ?X8ÿ}/?8/w|7Gk!sww$&qSkf!q/_>o~_"׭1Q;Eb4;q¼OĂSUBqUiĵs s&.8L3DNWA29hKY2uՏ2t[]UzS_zN55oTo# *b'֚*YzPO\4F^WVӓ{IDFd9VizbE4]aav}$):2s|zh4ɵ0uU~ bu~b͆ E$zb&ab523^Dc'$( hǗp` yϭO5$*m۾βDF]ol[y ,cNId_tHK w5^b `2VIx:`BlFFw;H n55eT<6ò 9dT(hNظ C$k8 4z6:\o[ZRҰn+ځ04Kz}[x4,~Eccf5*j-L<\UdQTE=;Y.f]gUwZS,[B{A͝ .fmU,/0ڰ;|f釁\WݪYmHT4vd&{XcwO wzsQ3"+$"y1gtnvk~Wy~"TY $RƟL6DON@:P@˿??ׯ_>|?=o޾{ݎvE{^~tOO߾~ϟ???}O?~vӟmg&,c5\8Mcyv/~<5zL~o_~4$a_g*(Lڌed=w|$d2M+🌜+>ng]0 48_* (@$ FJt<%mÞ*J"́Qβ$bHH`3$Xt]/ ݝacG m 9)/%Q dmiY|FLRLQ"ZwMʁ~mYѽެP336"lV)])FCY9ε {<)(΋86`K%!ي~폯_.Ɨ_?ݭ7oo^nnǫv{u{}{7߼y͛8ao>ӧ_|YC޾ͧ?&Ʊ?~7]K+ٞ/'Uaa /zïv*_$ qf?K',ٝPܘgCx!&A+]凬&=dzfɶkwD޿qy _Gq1B3qO`v.[5)muD\z_G7'QByqv|w8q3 Dq[ H{ DcOaS%y5k6o,Ke"a{gTgmuAd38ײZeM e 4C::!s-駿nӧOf˷?7o޾߿{oMqUӧ>~!wyY{x:{}uҹ%|%Y.X(ѣyEPCL/1yoH"YT#D9ԉy۰KHr&I` #;.юWhI>lǦIHh`͙hZ#S AZeY/Tpb_uΉL$B?@3dxP > ճ:emwqe.T֋lkG&4UEd0BS38mUdsRďe[́ ܣ2sib=(lŏ9 ?l6}b>ٳ^}BxKh.z235o2sBX;r#h@<֜Y4FD$,܌MFsKtf!&z.1vj!Y S̪QWڴyT m1Q$H;NW?ϭeo ~00A搘x#m!Y 8j syi]1, TD#F 6]t'WfoY v;DZF_^HyyDhM]cm^;u(P2O!F qW!gC\빷YT|8/OEj[Cg v5ҶIynyB&?nj ϛ5Ds0bT<,~arSkM-CĢzt_#&!aq;zCϢGf#a"o>,w>szRgZTI+fnY/}|$k0\8-=A}1q v~Yul3Sd] !*1H~{73pwC?}ɦ \=eG͸iX/ݲxet W WH"BFDU yJV)3l9pEK字Ce| /"GV_VL<)P^,0\ y-2ӘWq'ƒCN2e>/,v<Y#fs<mBJmY̼荹.QPtQ ,tW6ƻCv +f=7 Lz}Ql;:0l$Z K!bڱq| beS.~2gV8_&@fr 1&?#,L!v\92W(_vrRp  gc5@z6PV@N}vRiC9F<фTYfo@ѠF"TExL2f yO'5b=(ŴeuWCQa%řŽ.-)BG^.w]T)YCK8$Y:ZXb L-{YSuJfp+3"j? b4 {+Z_@8d3{kLRz3>IO{tz!t럅`2f` CuIY\"yv>B'*!Y^\ iY:d];A{͘:X=E@m^wTa(b蕭Mj=~%3*sOw}pýѻ{{*.#+߾}OȂbrOOO>}*s 4goVLt/ͽ&~cp;#=".jHfSmoAKY7-p$}ȯ>ܹp9"p,D)`do-skPqqnW J"2-/ BVTzJ]΃8c,ɃWOx@}y2pއ^1ׂC#6K8^kW1&6Fc B-i>BK5u{qaaBpZ_K4v{W43*|( WD#gW?uR[T!gsS v£ 2̄(ӿPb"Aģ6 05[Đ)(53<;6Sj-4i! 홠7<,?gѠ{.2Blb|V920f?`+D9H4kOZK~YDZwW@¿l3xhqN)KDDFή|1V*HWմL(zLʙU>4$5LocfZ^vR"va{2AO3 1.S&؍x&*c^h:=jM!aGÖ=c f-̄Ͳ7QDUL-[˭ѭHy ~֚C[tqhݨӎeMp*+yۘY+ O)Syɸ/Jav*Rf Վ!5!ʘs;# TTt jV&ۉviY~q{.xNdUv·WxnË\ihG)޽쳨`g)EQ/L@/Or(p<#m/0_6DKyƂR+-C-!hdGeJ#v pV(Ÿu٣ފ@شzJ;*x/^˗>~.rtHYd@f4y6 *&b!l$TTGÔs8^L"DE\$38w7QTPvFUگ?wHŇWy2mKM@Ij/o_>0~t:%!/y@(s<f'YV7]00N>haRtΐ~N[Z0wB2/QJ19e1ﴉrby}CL|ϩ}b8+>`e19ǖ><1gu,Y".G? R]}N,yDhB Rk嶍q\> (k0{䱋\։uyhLaBLT ZDPt GDDReԧϘG{F>.Qz}$sb 0{\X{0)VBJiLe |^t샊 g{mF/z\Yc?EYůa"6לu `(J 0P~XKnSV픂@p}# Yu}GieL4> `102wXud D DϱzwiB 9*蒕 ε| c,/x;aie&b+Lr?NA x1w^;S"AuK( rJ)M!-0z(\~ij,e0XTkR3c"̩1"}rj fb5XN KE%;~q0W6TsYk‡;Nf3ս~WdIp 6qģ{-OU"LWH'qEPڑ"*|Qud%,jlfC"|Z9"BE57Twf gdKVΜs`{]CB,hE^Y1e0`&*!j*gb,]@NYXʺ+/M('-"4כ%3^bZȴ,u:⩙h, ۅqe):diqrrġ]E5yɱQrt R$l.32EnaKcby)QΟN0֍v]3L g~ƖKtbfC3k*;IsZH_r1g5$4bT{&XJ4Eq ^V ""\½7ļ{ M># Gڏ=tvzX|bwn7RL|zNNc gA?jg\<3Q t| ,]!şa8}xM{f&RW.#wȣ!zKJιfXx0?ĸ꼍bwIy@wSu2A[p+* {^ˁ[E 8r.S==t ,m֎fIvZ" С~2wDzDx,^n|,$0X:?&6y(Î4{.2 fr"#fs ܩB*ѓ<Ng5:3dEW7uQUA9 ݻ@ý>|(u=f,w? fW_=J w>P(Q7 R1ΪQ/_ڔQrP۸<'|,,`0bv[RitͲ 0x Ҷ|nL؁w ?;Bqd-3*Z,J(fMl x@Y被P,jK341=%^*J?h>)k$\ C0Lm-]s )p:o\,o$`uxdWZs=-Kj 8_7;>vy(VO-i',I8sr 09Ⱥ:-r)щ{f,1=W>/9KOlT!JYNQ@ o5W愕#?겔`6 yJgU Ze$> "$4Lx sy)ux T$ŒUvQ8wLOyD鸼Th̕ib=gA &5x3ӼU *+0t<7b~b f=-$h$6!纜:SWh'{.m-o@:]={x*. I079sqa 5gQ$=M5ڪHtd!FGD͚DSXE Tղ6 _/$jfExՎ,}e>tgG0ت$ʄTab}U<:1o RU&颍>$`vq˄(;e#h~:E57 -@eTS딚7;}?Jt%8u4C=A!_dfX(|X}pbq;ya˖V^/Bw^N%c[2Gq^x2u``/Yk bS!DE >Ie3HV>eEX$OhklRK _b2f2y1ɟ8j(a _J x,'ϔO% P+ó.XlA/X}ݝXZ#-!a#rBg`] 0 aHwX`lRs=ݧe^ s,a2cJU&g'Q<.s,wx9V$h0k_l2,.fc`lͣ ~ Z/|c{Bi,B#*_&v1_4$K4&DQ֕ina5EΝFΗ9!EV>$tQRIVg);6'Xb8ffDVU3;d"rno߾=λU|yB3ښю5k֎'GW8$;{U];T/7VZq˗{{2q1ˆAX/Cwܿ*cA=i&d&] Y8[NEdT aG[.D i"!3@EdRfDÌak|wỉyKfs rY0 c@.B @٨| 2TGf}s.pNAUYkQC,#'P?'Ax !dw?zG뢒_R&"Y)QMDyi,:oSB[ >˫8c;_tɘ#cS4ۺ1n̴ 94K`nNVrXHd (0 M&+/wqFs z<뗔0Ly?7(#"FYI|B=fSusGa㤺@'P(a,L٨P0´nmX%8DUz(ni꛵a9΍-0jZӆ]L#R 'Ӕf6BnyXST$YQ)畞B2"z>|ԑU`Wظk $c!3 'zL4h3e-{Yw.!cǙ sJLc,Q k$ ?qBhW淙g-¿jEK1 a^̄,M*|fT1l>B:k |@ dLY=ʺ!E0pS nLӟUff6f^e$xJs WB(-^wC |!WjKP8c8ƈB}6͎TԷ: / T'O  Yy{Gб)"^<Ä saGHW)2a}GTڝ+sW<@u i(2 HZPٹT,ϨE\$?ޑWOdM:nL6{߃4FD!**[A8]#G6i\mDGZx*]Zdpg]!3,[tx]܏.jӛ@>"d1pes:cONx &\1ǩXoooqܞEnaa[x쪝gai}?+/vZ/ 9w dA}._Y>,:ZxLH։C\PbY`ځ ZFk܉O1\#A0#C6jvk!ߡEbCL^HV0h,6KWrC!} 1aiHyq-LVal uX86)ه/Ȭ-j/<}^[-G`x dQ7%gQ˷qO! i dkMf IDATy%)e4 ]Ly<`ݹ Oc䡬GyD~?qOЁX%D O܅_9˘ax~rx89s[o1w5@C@$n }a~0sKe2ǭ:`i>0| |EFgjgb1/1058eX^`|t);ƈhRS>ő0Re"8Bu9'ET @Ny&a(eLE2oc8XG1o@ڬIHHsus\0ۜeR#)`Q@ ~J.43>0d- 6"Y< ]K8xG%Ѱ523ͣp6FWr \-3 )35x&d| ֹޙYHIάfQ~p?5t<.!)WfzNۏu-L$IzS}Wr2I.Vs?+f䖫ytboᴸ{m$XWTߎ[D ͼfJF"\l؇26s#ov҄QI8pY˥+8lmcjZO3;eQ,-g#)`#1-s+3cA(*=λOe1+ݱvBƑ`^) !r煊Z ]IbܨqVYyrf 0WtIKg1H P(w j2E$l@fvDS7+deW%/BU%;`FPTCj#o+(HGҬ\ DLf)O2bX!й🂆s1ӎNySqR{])l>P\4bhQƔ]/ڞEݳ8{ؔi}fQPAVcILJCUQpDy7ꝮAON'mcZ5rNiF@ܽ IȐb-\G,?zH Gv-(ue4)%S06V(ddD̴ }Ꮊ>]2\4 saBRN5[R0P(:L7 8Οd2:6QdKtHN a̐#PZd+Rڠӭ'W2cUI1ܽ+9rcin)cl'"Nๆdl.ws%.Hȩ]Rd{_L72 ƥG=cS͐o1DX4!y"4AYv0IsRZ6":F 5.>F3Y{jCis^Ƹ.Dsc*!֖jPݧNlV8wP{7MVɟȁMw^a[\:66*ns<<ӋtS3P,sZ9 fPi׬J^yt?ˠS_孏)c*Oܥoh0؄իW!b̤ZDf:VJMܟ,8YEuM" e&mF{~%J sbvI> kF;dj1N&/ Ufw 툉ẅ6`Pr"JZ5$U4Td %X▱t7IHkyaX5&bj dӅj'> {[ܞXe1Դ%oP \sB5[tJqduKYeuw 8-X^ cEۻ=KaE ۑlk)<#[U+7bܡfޡ'eZ 0YR%"\""#`(ED&&a'/@ w-0)()Uih]fXD JX[$N{Ug^T뽣eOsG:MyR=OBDʣ'"`_Aa]ٹ4D"wJzo~2) Z4.+5;@”~.*=@$uCEK B)(.$PZA!":|xA>YbEbRFHY 8S liL;e oT Cn$U7O֌PPŖ\bĖL]l>j-YUMIS:b8g9ख_U F~b)9&yӭU貐*P|gqu9rfvnqr.z583U01 =ɲɣ$p,Hr*3:H ݖ$fF K1k9|]Qn:-Ϙ)ѢB>L3MCHbYb{˪ޕcaJ(b g$PJEiM,Sg~@LM13N)E 9g $xC>CUd=dUZ}VZP r8Kl\- `5gT }>x^~kj>2~ԨE6֬c7~3"ǯ_1G@&aYM=aחƔįD38 77O s>YɩTc=gZR/R S+h,F>a+> ^Aʡ2H-e'Ym]mh4`]جz%k&2x؋ aH pR']#&=`L'vż RO[O3w %|\ 'Mp>u-Uc8m, 1|kj.0þecvܬR֯*{22܀ ]!Eam1QKh߼+͘E9SYsRgkO%2R8koc|eV:,w2f?)C|Kab72:+KS}9xII8^PL2#"x$O*%Ț ;/#?_|#?PeD;A_mS2ޖ%xw|>|7/s6Gxvl-ҀYSh0ΛWAr\?g">PȎǨ%"jYl[&;Bά j mֈWWDk6?5~'#.h^+g]}aK!OϡYW* @Uʨ6Ey7{6Uo'WDYI|׈s9= <@cN\e { ]?tD$ъySońDP1Fc%7 gU$tu;0 >mH+GI\b@VȈK쓭pp$п@DDnbi ~9v96'.Kk-DYWOiFW!OOO^c@&R( rFX3w˪ O]K|e z yL'~]IP,^OOJW1H!OJCx@V! rd"#APv$a[MxmWގ>#Gmd#U\'Bd".?u)f#{^G62Az.jb!,94!,d+O$ڎ163aY}+{R0Ilc-hD^BJy)v?^_رv fw)cZ,X%Q OZpViL]M) IDAT@rkFO l18Ad6dnnsc\t,hw1#/FCxF.=KDLW B~q ҲJ{!؝`v@%XHXppcŨ @{<}XP ݂F͘ 4` `وzc9pcAޑ?c‰btB)srA.+8t@l;|8oN;a`(K\ЂQX2ځL!~Xn*OAڥ"U!3ϧ' q?ʦr~.lWdU?`͎fڎw~77hbǏ~/g?>\LշXg~Ef=eh FVRŽAI7CH[9ĕ[{ {"J_-kdW  ڶ߾ 7yzȖaf͖_<7zev¨?5:ѪKu; UtHKXHX#vx=޳fo<7<}B}hJ]*ʮ:dK HqUi ?.,Sq˃ތLk9s{EWYs{v2pX_1g~Y+])Nꎨ}l{ ;g#s=C zpͺr2eF|YTUn䝿6 dPe{|u5]H,.&fͻLUJ#MwH6[]G%ja-3&Ti=aY9 wSХ'֬ D6d~B)Lůj&殧:Z*Y!\Z+/FȔd0ڥ[$oܠXyIc)*I{\8Me! Pa"K@-cMg9Xܵ `B=o E@.:@ǡ\&lk+TCRRE p12^b Yk4)bg̳MiCRʌdlT/P kĿЉY fnj6>z +k'_LD  0"eT0q(3! ]GA)(#C{qYT5buHq!gc5S5kGk޿kErϗ[nU a v7>,E%ϭ:8/#d#0vxr%1{@dG܇EO_>~ZBrEu^ Z8@w¯9kʅޛr0Vc}*Cx4|SɶE]#J/L EuкXƈ*S+K[`'E<ýl,D)7*4:Bs;E6m[ܕ USvJ%aYR 1$zG> m` rN(bB%9E7lCzQ'ƞkyT X043QKtC^ׁA+-AyF|[g!XCB ,Mu<\P>-tT: *%ua: 0$NSe|jDŽV6'Y1$ q0i!m6%@0hoh׎szS6)/ʐu:Wjٹ`UWȯc8l^K8p΂ C$6y+3: O>9S?yقi9kl%Zz89&q*XǑ`h0wo. ߰2a0)c:DFt-G "横׎nQIW//>{]SI 3<θo?~b%2?^}W~8D=J{YYA\)3foFޡ7F8'E1lx!nMEtBʻ%-#ㄛ x|O_ا#WQbrivHH^cih(P>$kL1'GJ{!́ Y`~zƚ.bTt\ )"\"$A2%#[61_qaEFLuQr2u*w_T(+٣p3>}*"dI4uLL;&:sC[GO<ީMfgKA#";l-v 1&AsY1cC em s>Vz;b֧r?U)S[RĖ%)桃J,kњ8/uJe>yHO=H7huw2SzG5]g"39۳TS̚tg׭o7&Eu/~b-ji]P[4.:/=/r-"" D"8}8""Yuf0xe6qL8ި2+؛E/z;?lfgxnIKȪ*vL継aF>LYUy.xKCluGj6`hv+IbT>6Z,ϑBQ4sE%03GDj/XU9FNza5ŴurKuI'TAZ#WY)ౘp<_޿Ϫ~mAGyWUEqy_zoc$v&Q.|oYZ}+T,AQ@h)k+r9h)ڇQgiWܑr{8tw$w%L2ne"yB\T[ WPDG E ee j,{CV~,2kj^T6V'C6,)Yɠ #7@^sUvXz=v#贽]2*("`;tT'&ܵJ %aXDlg˘᱘{A¥ #AGe,(|sC{4]V 3]`Rv$C&2Ɍb%υd,s/u HӸ K<D Y g g-:߰wOwN_i]3H]'9$E2XD)d#""SDڱσ@~]e} p,/cq[ҺS;AE"Ձ, 0P僑똽֚ś #A OFD&@ a_h ˟ٚ? $%gO>1c'Xyx5CS0pxx%/,dsEO'?O7g'?g{&E3+!ҚvvQfhg?~''G;~wfٟ__7yy;uqD,t|H/Z%y<٫e՜Uvql?L9Y>Y_?{Y>)Jres6kO\C-ҢttGngfd+xEtV>Q:yM,+Dǂv!E\G蠀$"Dah:_guVBpYϐ3[#m#6}Z ̬ ^exU`b̘/7+8L-C,g2_{$^吁dNŢp؞@gIxEBzb=0]j`&g~xnMnǙRؠg fa+ǐ)yjm s &qL!<LVR|t5xBMNOE@ֱl imFp#k1⍻t( d}D`kkL*x"du0P˲.Z})b$> Uհ+%F1A CeJxnHK[I9u Iy CDC:"e.]+9TwGh?j-.Ʉ-*E d9(6EW(tJz),& BzR e3YYowm}KR+e؆mkLsg+Uˬ/E'"n//֎wީq={I4yNb{~Ndt5-?_WSTx0p JE2>RB|vۻ-VJQxqڡ@N xz;7SS^ [2f"y'o10{bsĹ= uz%zjBW=AhJj6@>)w͇?'%SgekV:&cC0 j'ql EE3)Δ2c!a@k.wͷϗǎ\|Ho|.yo~o:?,%߅P.P݇ޠE}fE{zc/eyfxO` } ǫ N@^ruH@h+⍎<}|6ڎϷ 4-U\E(2k+MG~Bs<<>HNÎB[PP QHP$˭EYA>ٳfx >{)s82Sm08Y2 3fmV" -^a`p&#pdӢJҿ Vhtvs*HNd滎Y4J4jB`J,\6No y`~Sb\8Ր,ipx#:36^g^Ƽ1[փi%n` %]Q?x(>" d2=p]8'ۏ?~Tݻ?rњ~n7|7nN!WɷT{ϊdwe]D?Uŭ"+ k֘z땥z^+E.{&NZ{>IF&OwKI8]aV6"F UE~jI 4VCA_/QH{& XULgU)Rw/2P`JGې TS Kl 4n/xJ4g-\iEFܻWyeRb T ehnO8*f&O)O̲C-/b.XXٍKUxNMTo}LN!6fc͹Hf>^/>W@2 sc\ϋ9odXX;ّrh9Bneܶz*:ҊrՑ+ѻașc>QÀPCcMȒTS;DeIv0)2HV#:8]w۬zzpΫgT<˪f2JǑdN~4h A$ct[[%LmoB&I9QX5 `]Y# .6VKA8cK 1T5 `LYۊi dEyOrM %l44j37S[^qbqb+{Ngo ,%vSQۙM˙ͻHϖn]:}.οFĩ=\#f5w"8(eG.~S򧚥S8Qx/BE4BggW䯹GH!ǎ!L~4ӱ*rJ;_ԱC Zv, Q U,ªc8+{ϧOW[Tòy0+lI)Q]Ǩ+%UptWQEˡj,دBU>"41-;0K$0wNsbB2o_AϣnU\L(/Op4g=ʇ% ؞/c0^~ӚKݓsY3/]1Sj dfܙ yD6p,E}'Ck~ &H&܃vE ;:1MqՄ IDATA>_u8mKL x6Å! Z# yqir!:&sn[gVzyy~yy%CS:6OD~o8{ݟޞ%VF^5Wƛ K/Z8.xHh&p~}ocnZOsj!|5IzKg6ӊN)NL ŇuEϴVᮚt`WYF5{qWfF/Ct ty!ָ`e&e<6\VO.y٣Jg)uSDMZ`&?iGS>eιJHkMǼ8ӏs*meh3!P;fl%d yLw6tP0t#Q6K"45LY{9ZѧC{@:l3/,́fx1:r?su($+ޔFt:x2&BGi[:d_W_-7:H^4WNj.̸j:([U,YK|^g*;i?6D}v7&+T roV1]0.r ;{JcR@-^e}WꡰPWuH"aYE$Xoܛ)P;\Br0j1{[bFv2؅UTպCU.^<[yReȧ$3bFb<ޔ6~ݴU2^EI 6Vv ja;)t0s1MbIspkzC$Pʕ~+vQ1ߺtq'Pk޲cf^hXx>ʈR"aVhS+L'~(>$~Xq +kNN& 'fiv ZNR,ʚۀ$Tӡi_-캐Q5R"Xi7,ca#k}4|q[~ PRB\Ǿ(."1G9'PPiuGC3%;MF^)h@>sw Ų,+{V p#c-[+[; qB;Aw&%^ &A'4 .ne^U]2pƸzJ%ٔ @r}"*!YAeOy>zo~OInTޢ2JԄE%^(9 O7y;137]WWgǪ̮͌]>>SS+W(-GWJ3E䢙gX;J\\h=4 "孝;#."1b=h~ wZ,MNu5gRj1DtoW3"j#-я^{QpnEq qPS J${ʗ)`Gը4h2 kK;@lYڝpF73sD=SxVSQ ($(W "_dZyL&-9Df&,<鯄S-˗j~/"ctI70aNy+$7DBbٶ1VϺ{='=o2p,<)“D? jddz@\Ix!XI)_Xbؽ)ati?tMm,3a*;AR*kA)8gܛx<Ԑu(x!s!LDfS!PP]F*"\g[YJwˉӰcXG->v=&Se0llq-oÚfM$mNŠfy @`QF'!KIKל!lM!>8a~e)BZ;F.%B2/z#s7FEguC%SD5E O@&YInJ e1S-+6߽{ōHX/nq:ifMp}o?=?qJ_ԯ#U/Zy޾3{"wa] ӫ $ցV-o=hZ]#-ֆMm nk%R֤sjt4x:68X[BD$r8 ,&;1]m; yd峠zIkii2kaQqX:+^4QSj/U̱1llb3UOPJv>|񓝗tD":aC'2J ʺTaip.̀aRA$~5)-sLvʊ%^?N6s9±0`H6d/ x'!L0:plcK?sKeaBej>+ĝd+}򗤵,>0Qɮfg@ [8ҍ^}-"~{[@GY>͘A1 ud 1V1"pkf"[ELKa۸veY>XÎR !"*g/CUĜ7}VY.̚1wyj:Hٖ'EYF`Wp+{9.4k|b ?Go|`U Bg3:4pU".TGXh$S|ND  Eˈ9oاˏ^1)Ά lv:uvW.}4g0?q^&Ӯ++߁ٸSnۃz]hMЩzg֗'btMeC&t8fPJiڄ6?XTyeP;0>gVL#*= k|]㚸̙ @DLLP%ۦ.؎2G4i""T̩ ҂iXzzCZAc9 Mam/d{YoAKJY ˦ xND6$!CB1DR0OetT3suiˊ]M|i2f U6r#k 0y~CgfuLQ<ԤWɶļlKZ44eN W`AW\*JY@<";C5{池RYks Z,Y&>A~[MPq ͺ:nD,Hf)3'8@ ix˕${9Ț)>W`!k,-s:jaB{O:c^bj}nĚ"VcRQ¤Ϛg!V=XE"Ԉ507 c$6gj}mŔ+UA9+(agbc74P0s(9L4gàY T, Xh)03寧,W;6|ZqJ:OoOutjw"+3?[ff$``«hp:y2Gq~Tq$uic'`̒l`PeW///A #ExC(2EL &^$AJèH" H=AG|%V]0w1&%؞_4F3$)ꔽ@~;{g#Hez*tb\LQBR}ocoGQu @v%w8,`PuلHI!:Yf _`(_8/{"]fM ~Eœ"UM2'L.473.!(Vاƽ}bCg=f QWUլ"E`k fesw w3g~`Ue85Gu6CBeجpF*d.?)*JJɼYDnscEE>na>o)w˵9<3"L=t/69eŘNіQn“e5+S ʂdǛӡIq 0s_>4(3NU435ŖY­5)4>!s/[Ɠ W`Vվo9XQTUex 2Bus?@ؒtYu.Ƌ.mlVEu)/78Ġ;u)? <ӴWm+8[y#3.:#`]ZS,3v5aP)Dk$ZkFB_.ijyzzC4Km#o FY m4zE}S7k̔&opxZSp!4fMby~ lƠtU??$UFOhIQt A1Ay^~w]GYԄaסX0в2$t.TJbc`r *|Iddȕ\FoA] DY1Ь+@d4'Oǫy#h~+{g^"B0Q ̂+D=(UUOmzkUD%`o'4 zTF&D'A.qc3 xW/,aos᷀F9T)AÚ5\/.2}*ժl22cTz p5*H-P ?,wbe µ;g^+/Tf̋Hfw^ULRD*bFJ ϚM `dI <&#> 2 !h&rΞ<*yo0i}X Ln9RJ8Q,W#\t##|HXHa@v[!o? K&$rw)=EHd+Ʋe %[?W *#{w钱젲KcW)f1Rf*g2I+i8.c9Wզfw`zU'\ y38]ⓕϳ|zrT!xtKyKatX M6zO]HsjFx()frq<\`̠vT2wE_Y"bZgkD58sIG5U,։O϶bS3e0wӰdDh9b!3Ei33S(*Cxux|oKif`$^LjvIbc]9 h^)1c%ƴ[W$Ed3[g*JAqWI6过,Kw}קT3 )2̏;| ] {0v],ޅdA{,Wg`h;KYyj6و;%q":W*#W^侦4O^ LPye2y[ :dzX/lb!K_ؿ/Sq1({ pĒWX ac^C̺{>+rIg\zٰm[on@F/!%C8֌QMB;&|SĔ̬]jR,L]}Z(=Sc=V)6Lbƒhc*2[rLSѽ~ҹ+kT aGB}S`pPac *ouha>)bj'b Z8Tjqԇ"cgd `$YeRI)sgng<`ZXT+0>aJJJfPe/vI*cdV@8D @|8Šڨ1"+R\(uqcj8Ψ(Rif1/*l*h'ڥx~I ?fʿmY 7u`|q_ 烧hdYuqkOf>FcQē4LFj#s#[oU|c9gi YdĄqGD{֫hsԅ2& 6u:TGL[Pz |UD,v*UdNJG 8 2]+(+$Q0 HL љgz?R31; ƽvRgねțDT EkA=B3 R aPkt$dIg%rH@ع+Q<"cNzhqV t8q3ՠ`Xyv{kӒ2VAɦggAQ<~5:*?ǨbDͲr@H$jY):y&a.OBy6 (e0UG(_.o6Q\C"N7vd4pC8ۑ`۶f5D6;$wAfaF5UG!Reeya<,T>T=#!+Du3|Fdg2:߱]^}W o|}-S<0Bx {[M~vJŦ d -YOCkL,Ȇ@K+zO~͜J;4wl薪z #S-X |ѵ:rɴ +Kd ;=~n_3<<4ðk]=)7(\Y< v,e.Ah{Nt*|&A%FT52cXI`/OfbS5z_l |oR.W$I%VjM}8~̉mr&n\N=~ 6ou97钷ˁݧՙ0b2^YYCǮ/N;(&&+ӜeLA6,YJ|2w^-!?~ M)~NSZ2נWGqx2%/oqqsmNi}~+L:XsRۖމv<{=>=wV 9h^w^r+Z#QQV4=[},^<ȡMܮ U;SZ|'q1 \ٽKE8o9]V\sJY֜.\@d왻!mOd[qrcr['~ qEG_el"mLy̲ ~.PK;}PKx3C-Pictures/100002010000013300000189A474126B.png-YXSRҒNFHFH %%d@J:Ҩ0&%F*]* sw|޸=I 4)oQ@f DP•DNJSL - `awOfQz`sh`~oW+4q35bV8]r-d4UgUJntEDao}}bhUQDѱD}ь4J@Y(0(88 ",N&$q5Q p̟b#B_Ĵ-Z@(ݍf 3JEX.0 0jXl d ,-cC.@fa!roTEQʉ)_YC8 #A|$!ËQQ44v/X2GM\ ܊#|ųFdD3rP5gq/7k3:6NCn,.D==^Ѽ{HXE Ϳoo\e vn`nr'fL^- (.Io<"v.$',A)>rhg+cW?.4KxhvFثq( ZI{V>ϿQ*V**%V2V7PyzlMŤw6P(?3Kc&eN6o6H@Դv١ٰ{F 3:;& Zj?S($Rz aF%M߻07\Z„8H6>̻`LV)Au-ƇY%y_PlUF=_dT).jUcYUgT-ոWUX٭iuC2sۋ&& UU Ǎ"^N^ߪ"=ۑ>gUǚT-TܤxQV?yvA/#GeXSSJ,}BFm;wWW[?XRNONop7j+J@A|QQ0]:Z]~ݼj]i򍳖4kh(lYE_niPW_O蚷lofHht3ہƏݙ d 4uQA沋/"rD,f7= M >k=;LLN}:,(R,`WuVM|ثƿޗO/IYA?baFkJ7ż a1nm\.H7o+BkO~((W./YrIž6W{uzt5TE$Ũ~7MIz򗙟 ;_v7z?n@nG1_}R$}txy!*pl..#=YB%O[rrY4щ܃^};l;= G{CC->xwd<-3I_0PorhG< \-x(ێ GC246J[M4lnp iNx2SDט-p_K$ORV[%).aSSk!]y5w<:to0*듪_ =sc.כ.ۘ>X1yX!v3@'W V*eĊpΤɎ!o[6%uE@3yͷY<1?i83%N #ty僴p8i(PjU{9 U8,41; Rj#!4 stPاq@ЁD[>?ۃp{S9* )VZjJ8s D|X[I w.lת3Nvj+/}C߰QG@餺Se>XVy]Ͻ3?ԍH$[yh/i8B[TʃG.ܚ,}IR z-d* ZIH$!̓K@[vG[ccF 8Vm[ dn< RaFqJ(lfgb 5,Ҫh_䂣BR" HDRC|o[hB=SZ KJNskot-8&cCB0?Aw s'6Pq>A0RZPLX*“ }'*NE?Na1G06~lJĶ} PJAqv[@ӶŕyPr =;ě{f`!O{ݶ+OA>q$]+3SQ!l B E֟O !TA)ꈋ$Q:Es0 %7ß$ļo+2g웤̸L"Z_zyK/gkDiŹ{G$y/oH^g]$鵾@vU-!YJMWKVoGvcS`O?+l|mAuD`s pɐRaWxej%W,ih\i:Yim6vA-{"C\_rM?t4Ygyg1(4Iil۝Wa{([N؝XL0uuxq&XG%؜l5m Qi*̕K>>}8|0dv 4{Va̽6c\Ͽtu9Bf X-MS5ɸ;jRFp)s֦Ѿ`>b6˦T%̏qNo?ʭoQj U0Jyѭ?П&Mf#Vy,jɎ=Qh$ƫFw"j.+QI%֛rFqX%dޛmѷqiWlgI>K>*0Q>8aIZڨ0Mo_MZrfXsjMV'E)~(/rj鹌Σۅş5bgҾdےP SKeu;C.4q>}~>e„;ܿ/ =cezܬo/BTр,A[2F?]yZ|7{m,k q!b<"ujԨF;pN˼E5Ɲ\lM@V7f7Nf]x21u[ D#idfdG$LR^aj)"4ݸ4 wsAzB#%Q_$.H5 KBd<pyXn!`~. :e^]u|mZfq\UP&[E&L JvyOZ9J]h2b~G6P"Kl) טbSʙ#4h&Jeo9$@fyV[dz{r$Usţ_$MT +qtsq F;3)*Bz Od$B#Ш߁ Vfrn!Y+是m%j7c.[/ J{־;| [!r{-r6{v`|mM"뎬eٮ?>#zЂ|۽5.$R9-޽JHAIFTГL|ٲmC#♞#ay'gpMvSԳAoW| |{Zt>*| arVǣCrS78!Q~pw[^&cKF -\=~鄪L0:5T!4]>}:ij%v>tzZ'QCIP]+_;zn@]хF^W@h|{o%;[j3չ[8_R{.Cs2&qcHK=_cœ3<&SP;A'|ҥWb_Ƿ}sGw"\Bu0\0MysH[8Ǽ}e}9sVhk_78Jjg{7Q 40QKm+M]6!Yh{ {A[yjƖr-4ar8_ʱ.~ʌZԞ"[-2j.@qfh]ZO&mjgm6kXyYM?z2.ѱ-2^6WS(-&oEē8qt(5G ~)O>kC/|.Gx R-*;CZTQS_>}z?%t$'@}x]"cm{J67^]B_&p2LMYKߵŎݡ<,k6$#tck*GDjwt)޵3oTetlC:)3*H1U$ O@h =Xg$8y? ܆܄r{?C]3L9; ~C%M@1BZ-w}d"0y᫮.SJ'\U"vQּau^mʈ7,x ^B_Nd3~sAO=Ln/½.+Z|=.mzr9CHx&/r)[o:JбE7 & /!\ޙ.6Į=1RWꈆ~ZSI^wZw=Y I_9J&M-M+%k1ś:^N8(&Ahr~ -hBZy\ 1pKB GJzn,,իoy9HeNk|MҞ+%1WQ(x܁͘Ke:%TŁCi.ҡn*8G.>&wx/T'vͣX4W.L/E%[v.UZ *R9v^@}ݯY/ȱ `<#idADV)^?!Q!"(tJ@489)N`!R Iu}*euF>5K"l08aY`XM $f{4.R9GLMM^т&X JpeOT4f@g/hߜp1#9[CJ?=pيf\(_; ]MN%Z,fL'Y&Xr2Tg9`|Z|F0uNFX kYjrK'16Ǜa0O)N[*)GŚ>uٰV6eϼ6<= 1Yl^;l˞na/fhycQag_Y? uK<FxnZ6wWsTȷhVt|9!o?Ԧᣭn֨PK'n'PKx3C-Pictures/100002000000089D000005E47842EDEB.png sb``p b`('l|B\uXd٥C$jDE:%.ߠ̷Wu]LX[0a K, B |.!s޹nC#/a9sg׵}'+m2s3 ?p⸠;gޱ]EŌ S_}:-@i 6g5*9iRmeo91,л=mb{ob2xמ戛ߤ, ɻ^+g2̢1m=8XY-og1|x{R~T~>sZɓ Z_>ZSDt|ϻR64>uR⟜ H{X-˿qީ wC/o ^E ?YI;WX/&ܙ zff-bSwK9ﲿsl !Okf}zwZݩsVߔ'iN)s꼽a-v-? 31pƽ\fp,Xߛ-zjZއ20m"ε[Uk>hߝK_6HzEQ'/9Q0 F(`Q0A}bE?~.PK`_꽱PKx3C-Pictures/1000000000000589000003CE7AC8834D.jpgzy T;Y*#KȾ>1ɮaliV<̴R*"KOIJIR)Jel,ls<=-[s{y{+N'4B,t ރA:AB J9BHP˾Qux@Et4HC|g"(#MAt՗wLJ琮Gyo9-(ٺsǁ1ʹ̶Zfǂ.aH#GΠE;t]0Od QQ RR2RR"_(y拋ϗqiɂ]hE)@< P Pw`"( %? 9f / ($,23oC(>/?oF_)}i)*ɸӏRP9uk?`VZt m8Vz%竍JFޡ_յMoߕu惗&l=v'e׷}Z!}7@BG BA_w_4܃{.G"[G* \iP :qqQ50:{*?y"\aa}涁G_]R0P vc%!(lac 6o,6\%GUkqiR Tjj~ ūIM-TUN;cQ|0yWpzVx![k$γV,cCndTZAEGg>_\9g~foSe4~i{.lϘFg')td/5aV G5Ϳpۻ_{t0ܗBZRgvdK,ȑҷwm]$Fsd'V4XKKLҥ2f=,}n&*b6֪2+#YW|y5HN3eة)+_*ܵ4cAAP1vj*hlޥV ͎ӟ})ަyo0j~rש>eKG6>F_گVp@qVLf"S)3 n8=KtpVTKmJe8փ9~DX.u7Gҥ9hm@m%lMy˥ᑾS-DM9֭kiK)ރ))sQ.TgXYBdUدR8 Zy)=kdN+WV{R3vvb.0˥7}ٖ zʑJIdk^N9”7yjty(/دyGֆ;)#4_55\+>1V=rH_sov(J*"w᝘Z۷+;A*0k-]$=lj(Aڤ#pg~y~/`1;3δ}He t Y z)-c򰘝;1aQX`]u v'!,[3G=_Msc:2\x(?r d$O9Ӷ`P$!&"B$> D]a#,R|c aсHcg+p+% 獉f3?8FT"372q@`'>q ;q;Aߐ[1n@vߘp]ѿ, ; s]5Xg 'BW8 N_JEпSՑզʵ~K#Yx sQq'd;!Z$>^O< _E^(B.s $< @"!SC\A-8HPo0r0+8y *(ߤ@6?9yH_0sC)Vt jlhħ_BZA"ЗПew`(t! }D0E#/2;U q7 Z P=Z:5=!~ 3Sr 0S0pv ܁ tsr 7 P˯c'4<40W~Oΰ(=f H k)#UC<- uWjk35א661660ב="X )kݫw&Di阘hijjQaѾ{4â`s_EF)(|Dhfַ녃@yV(PWGkNﵣwEQpE%pTn7܃𕟪{~uZFFp7oj?3խ[27WcUP_0QPߠ@ >,*7 ~$2S%S#}#km@r#Cc #mcC#]c]u댾Zc`} nkelOS;60SЙ-􄩲3f@>tt¦Wu\`;[+ j1hY5?Pfa ~: ʍ4tM 5u4tD@tAhH@]#\?=}=lt MM-L lt-t -Mtttt0M{wzZQqtmӜk&F(W~Hϣ34661FR#]npS=Pbí[!12}$%HH p116 .!> )b WTI%FHj &H111)+}c$5DR#$5FR RDIuTI{֠O  C # c  _PIHA 1<4D e pж601ֶ12Ե11]ghe ]s Lr{KKHvNp!vjp> HXq #o]1x(V>w ȭE72",Z(]QmϊYaA !'? (3푄PyxD v̺<&Q@`$~ׯo'oNοrF'G™\m`fl_yG-~[l9999999;~ n9[<{8`m7X#/_E6Qz d<9*?A(S:A`n6B/s^WÀ~?mspsᑷ~q1Qy?6u?4@0aA`[c|MikpH!i&iw1'5ݒaw^y\#/6I:YD^C>^C"$ - EH҄t!#7h-d 9@X yC(vC>(: 2ʃ :TUBP# jڡNFIBQb()ZEQ֨ (&6T* G@ENrQk[{skT5YγGǘǂg=V 8<)>)>y>Mp|||Dc|.7_ooĿ??+B#;M 8'@+\G`BPPp*A3A``Wi͂}tyBh!]!!/0BYBjfB ¦XXTX.vy?.[(V.X.CZ-6Ghz@^FB~)#+dWحY NaUᝢv %RReaecS+UVzLZYb{8"b /ԌBΩ=SQ7Pߡ~Fi*UUV=0ӸѦ)iKHkV_]VV[@{vv~:cgt_*FW22p4H21`F)m3:kf,ml| :Iɴiu/iOkT[c`kki.oy'fsӽVqmKk?ZY[\N{]亲uS wxlZKZO[Q )5z46#F ׋w]z} n;8:8t|Tؓw*w6 ltxfxzW)WW:Tv*.W.Ž}#ݣS3qMM^^^&6[oܷ`K▗[U{wO6m(ۘX ~~gF-d~n=} A;0;v -vlHAgNjP[aa!a r=;#L#2#F"G^BEm՘C1]wEtς=a{Ǫg;įߕ`KD5$EAR^۽ d<>ރ=l%'F&%D>w#a'$?:}4(Gu:Iٞ405/0iǥt#9c<'!K?-yjé?8ӭg֝={Թs9ksJG3_޼BE.~sϫ7/K.Rd)EY/k<kẍ́IT2} tGn Ku8>>A~yED,8Aaa)Q)))qOwsZHp7Gq<(^xw /HxP< :|"I pwu-Q?l~TɋkVY`M ұC=jIsǞç.W<|fb {[ MOSn?z30M'}CFc}iF=R\'9oa5QVzJ@4hbiZK׃y7~͜}u7M(w5қO[x0S5'vz|Kpd \_ ׋,sM7:G뺆>N/(bܵU6*xC'eQNX&^|j+JNx-wQF FQ0Y[>2JoXgoX K7^ /Jöۼ璂uB~6ZR`neBʦ}Lfd+K2!&cKY}V1+<7^>\εWYj5ֺg:3jH*=\Ůw1j{eVfv܊vCM KϺg`Fm8; %ku(ۉu7#Rz"4^,I `fޏ(X^I qWwPO-f0pvE3EhrޯU'Lm;ħ#(-Yjގ&P&c,es.Iwz5 j.P4sRn|5F|Km.8fޓzXCc͡ϵ-aYj=(_0r`~ӳC%mR S[mC2n=XR{6I\h`/jRσ3YBqA_9s MeϩuJ<)܈OY>$yllzwL'j9$n+>מg I_9+sD\")Rhfmxn^:tsti9Pm'˴gH ̈́Q @wD,b0OO|"@q&=#C!5?WmxlTk)䀢 @b< Ö`{KJk٢xuTs_#9pJ ɾ#UWz]:n՝=.B$V@jL . ,ߎMչUK][?L_F(7d.+fט4U$,ń/13Y4˿: 'n|ʢIq1[ϛW7apg Zi2{jn( 45jDs}'_O.b GzDd8 vvۮ*;Tˬ6y"ZQyCN3RA@NX2hn> #>Sva#1 f2ni5c궘a0CƗ<;R3>OY4>Cz&a;Dd*] _1,n쳊qQ@*`KG:6+*ZScd+{T`w[>U*j24<0#ozQb*^!cƄؤaj0.+>pȻ)xDZ ^ VL?5JsLރ(Vh\e|%)xn]8ue i٫.ٜB0Kwzf|"f$_7U# k\cDx(whgdiP<(jbj _G$s!u/f{\nLڬV1C+$2Xwp e$QVn"zscH7&(eɮ)1eɅ3ۍtu7<!awW4>]>%@Wrwc1O«<) 1h." pFb顯'}*#P}(,&"w<ypV=8N9Hplk*bL|1EGYOzfrfNIӷ(~¬X>,oup,6פuIl6;64ql_QM2}`JӦ}m9q[jZ&Zen#{`Vz۲cJ@IR}fTǗh,~A&B WXt˾0ɚWq='P:g)vWϡy.&M熥 vmeu*]2)j PtHKQ U | 1:'`;| S\kd/)ն4`c+`|8*zw5Zb4,7!Á2f4 c8ARt6{q*Y1S/l:w 'ƚ3QR Z~i{0=Be1t̤'L]CgHm 9 {{LW ILS5⤠bs4c}g,20hg˒/K\fx*[I1/G 'X8Y(s }d~[b+P 0Py2Jʞuv'HUE0l܇ԉ#UZ_%\Iں\3TVWæ/ڡGH6}fg@~Lyg[Vl8ƎuSП0Mm^Lt nJ+Y>E={J6΁N|9@EFUOvs a:3ZF5v0#Sk̯8M-SDRS'J99|7y S˦dqUl^V٤oQ?A*Ơ. \̼@y΁AP\ *?a Lدŵwd`t&b?qK9Pe$ +&Hty5M1TdN|FfśKƺ/{.C蝂 F߫'gÓJbnN>Q<͂YՋݘ| (nۊ}d_:;SH41SPB8)JLvXrE흀!]Ke\3F۽7Lb˄a)}&ZtHN\*`q[ҋelρIXLX nh\B9xfip Nc˲ƳG_ ;XWX1#*Ïl,s7N@Xѐ| 5s6nw9l{@Jei y'-JDEW8\-Ooыj*?VoN6AX$fP٩؟Y/~iƼ.n:udN?^4=}yaT0{gR[KJhjܹX.I ya kR$@D܅4D|k(U| Uߞ($evW}\KS i ̎1=Fמn[}sTwW_MX.*)3UhB)2rY(h3~Rot<$ZL&Fjo[ b}_HyofzNw3f$}F*Q=o|& @d&́ =H`%%ɬ_[9?[7C]FL08BY&1 :a09PYJ8M O%\AtYh p;!P$eqb Xfq:Nkd졬$͌̐;⽳}$R j',Z׌YS1#tS&n3I/׾'[BkY7 ڨ!>Z`Aj&yI+K'~Թ[N8lK0AuBah0*kc ԯ3UN m'06=S a4 rθ3oNQ@1NOQ}J2|df[/LAʬK%E4M'} LyˁHRfXjR5M%\`aZUlXjb&FOwLΰmHێs"π8~jZQkأuk@F@?Q;ۂqv+dlfMH.'gJK3CU7RL8Lrȁh#>h.XM*6iPnz)mH*F@pgT4mu`V/´Z $PnÍN1=gF`'(OX`Ouߦ'20O @I #q?{ Nj`I tۛLP[&X(ta^sIhLi0Yk h˃c03[x\ZK1+ oCi7io#a L=}EeaiPUvapYG"lsGV8/ b{iUې[7 N3'0Z{X:O!(j "RS͕b4fĘUZ ,&xQSyL]LgR+3>f]|l^<#Ah΁}~-G"̈'y/H-\zaR K>!ۅe)蠹Eռ8Cr`v^q|B~nw7;/ zzqr#]'j _Utf>фbE v:-QckH}CQ(McvYߖJyW[p*RW2y.JɅ ^ol'>oPh[x֫ϧl$ۀ UO?O5qJw]jXj:XI8;J2qN吘ukFt7Q(R7XpLoeR5wV!`I@pOt6 ;(t;^ٺϢ4T8U;G̥~2𫳫b4l1ΑkEE\QGܪbY I=$8aov6(%8@ݭ4!`xJnf˩тvuʹWJ =qND;$nz+"-}F$%+Ɖ (Z.,(*x#wjn^]g5VY9szilb+&nR;q_2d ~1f=JTRTmCė2^+O`޾-͏l]G&piJR)\G<1@NerkqbgYŸ 4H_e3 e\ PgӓzS=,QDknHDns s(H+6bx: `$¯v:tz,0ԩJr+[/"KS염ՍHy}ciMfZOL;zE+Mg#cEG7z4JDïdE*T(U>_Tš@ܻWQTFeo i٫NԘʈ!xR5;/*ŧmXCó=ȯE=C/L!-z-!%yxVacz ?ozS%}6qS˄Fo҉zUur)djħ%fZ;xq=QSں;nn*lXn/5P'.z>\sJUܼ^mECrE8kFPѩ}Jq%u/\f;]³ {*O-[D~q=A7 Q=\t=F؛~;hz y8mOިe7/WOKbȋ2e"7ԌSؕh3{f{27()'HIl=v%I%-- X]s:,RJ^3Q8~?-(+€Y qlwqŹWָ%7KBǫz?Z? #JpU5O?'Bħ5zTĩOn9' 1wK𗄗 /~cxwQ4ybMZkKqYȣtRPws'gϗy@CQ9YJODwȸ5}*:L7MV3Õ7+˧O|] `WΘ9{@rOe F̥+ ZJGx*_Nʱ0N[D>dmK:KR!:+WګP\6=u^z4/ QJt_q l+l+GRVFr at{ Hw⣳FXWTC&Y9>{c x>QPm`iS rTojH;E ͈ b-x,%=ꯢ+l[ŸvK2O"z#[KtR]\8uF#{'):cnMyʃ&eFr\"S~da IE͜-|O B@T:XtH|t@ۤ6Rsm*e=)=$VU)OeTւ==W7?)zcJ'Jhm+"kZ%,#_WD'b $OD1bN^rt?<\aþFq?mأDg+Og[lGJ=th$YM96j]ra$kK es(-uPÕ[Ѿ*2^c^G Οzb௶e7be0ڿ$^2Il|@Ũ_xxC/'A쒫upPn[rE5ejCϣsBĀXtH(>}ul.68~2Wу4eqK'!Em" 5tO, 0V7c-ّ韌@P3 odbW{-'1XV+dtj\vV2.M:*a`Yu!73]w\_y۲>`_/Xg,<7dv,T/ӅSj1Be.Tϟ%u|h#Qg^D=$f,&2X /q;(~\Z!5 4nprcF5ޓR+r%VͫJHq6&[\)[xJkz͓MYyGan<{b <f]%3@\D7=8j66L:yn eNHyoU1a!u[$0$톧ޗZԭ#@T={"N"" {_\&{"ʂX֧`){x2OͰ. Y ٲihw/@\P8v !uk5p8 `{ЕrݽU@f:UDWqD,dɚWW5<˕JoR"2pIx7 ݙXGVX<[\%-gXrٟ8"jd »xqʿjR0Spra X)%V,ا,Yѓi~} [\cOPVy֑;o&Յ_vdkcD{Zyu1]̳¸t#]E"bLqiSwzVY<#ЈYǍXk(se&[m|Y'̫m4ثgnH p;~ XĐXQ[< L Ψ$Q:2ﳎmjε"p_x).w~0;n'aj1ґ:)sQ4l@Lm1> rDD81qiezynn!#Vhb[nBpVĩ䦺˓ , />wg}ungE57ug3/h_n.àT&zM&yXko[%No0[ay Vnqޜi63^eB+h5[򩠹}9w0/9rIƛCNj_{5yL}tŁP!7/Qe'Ml"⦓?!UT/2jX=z_wx' T؍ DB8ZX>yBf^b:_POu^ޱ#}:MЦvM!i-d$~+LFeu{<]CU߅ #wm9ØH}D71uf &2n,C!#,^^`0w|ጫ獌`0/g r=l =R/fs gIK d0ci Eu>LP9lvWou``' k^4S}ɮ:7bM_RO* ΅P+hvNNM}68<.Hu3x;su&=˛aWI\Yߟ8P-6J80x¤/VI\ybӉI/S7,]>َ }!d/Kx1K%_7~6 \ʲ%l3,.㾒87h 'Rߴt+jd[L{ U[S>3cvV̌x/,/Rf23rBЧ-1LR*%2d9upnc6t&FGܶu&0R1M1D9B*v4;Qw羿WzaOi"~aVC8HXsk$'mar!n[A#|&*F@)6a\企Sb^K{b.B|7%[߰qD>4y,|=\|@) @!jJ$@.ay* WX8+a@?vނŽBZ}z{!kHΉ Fn:ooЦJa1+BVʲIƩ=wA $F[PQ,x!`s%?Inǀ ԑC[nQ_ڂwشsyCR=J4rJ5#DJiQK{S~S#踂 p}Z^ s߼k*"܋_;&_hH%V8ɦ喫G*CoT6AW[sdӠBb/f:&TGeѦfIMMC'ĩ*)ms}ʒ~1CNfČUld3zbƺ6-gK&On/ P/7WGbXumiQ1e|M4B!e7q;tkc!8ea.?3~m4CT+_gc[3ҔN&Os6?',o[ě.E;00+,5 u4輡H/8;'=[3^ l#q_Uђe+^E`#;C{,יzZ.d}/[",96_D:wk@+aSŽxA[焙Qz?ɛ̸LˤA_%'__|`ܱd%Μx3 kBA#ԗfU/$C 9(g"c Y̓,;mˤ޿lv?~vޝwSH|Wf\cEr16 lS6YgE]Lx9_MJ_o^lHjh4BW3۠fRw-:mmH:CZ08 l02Ȝfb}r1g7'c:Kc~>ʟgqrQZJ3Fc}<$ArVHH-;FH랋u=qNDqd /M;TPeSmNU,歍4niTBAb9]$6"Ġ/Crs A^$ŗ&fֻz)_BƷIiyzE:1u\z'wl2 E< ع2; qإ1o[^>mN6INCOe;( k\"_MN^fn7RWQ$e$K&5!U}#6\ Bq^f|K&ǭׯyJĠ}R<0e >gp AW7ziQ*q9,HBdp&N^)?ǣU~=@$Qi3 qgAR&㥫aQx%鴷}!<;d}Tp8,9{ ^bn̉i M_F3X\Oj7<ĻA{^IF`l 2Tb P_"&Bj\͍v ƤC8(LK%w o\\_ps mU-VÇ_}mu)vP O OXZOgEyVM\Xwǿr\zg*8%(3PUY2l|['𛌿6}[sƅ5J< ?Õ} E8/,s܉}0k؜.n~>BR]MIlcDX.ʯ^L;RpRDnZuj} 0yBd 5nfɂRA@B̦3= ]ZOogk} %-"2:}֫~ڎz6S,ph>ot$k}L7 {N/,=l=uVQ ΡX*d!9JECB ُ 4ZI`̗@ ds>FRb%Ұ-\~ d/zFso 5  w64r:{ teT9Zж_Df`-qS M֡A2xLJit&qAhb,C`$ld~8s1QX:W3:@yF承\ ]t#!1=uv&*&cvA>% I5E>Г|P,qQYbn'D%zń?w@GQnA'mp90QB񒰭T^I~${~ U" R@sh@}c@Z?`&sT>O:O,@\WO j{j4էDRbY OIj3 Nm숨ʏK 2!a*V{1Srx4,s"SOkW]2wT1vr55@v3]x_G~t@w)d̂Nm/VO`xq.f0drJNk2 {u ^I5]$V`V%H L7H,~0ިl oYQaW]A O,kxt4WM%dT >yQ_^:!## :q2d\8;CCqۇ/g:P[t"RhZc^M8|^# 5.tP) I@N!ax 7&(<-ˢyJ0/mξغ 2hNO^8XQM e`Sh v *+@.9^Wwx`m"6YIvuw dC|~蘶oBV$ 5;mu)qh·V,e&u]P AlQ{S6լȘx }J[=/(~&QϪAxlp]򇄃8j订7HHW\+$ wʃ3Wwx\2oϼg \t0ʴDoN~iIwMפߒuM+ryĢd7uqlнQ(.RPKȂMO/(V¼ڜwLjHjc/=OQ1VyZP"z &zn=icMkK %ٵQ@:] v}lw!Wu #Vs9vA1ߺnlc[2I{,<ܦc p퇀] !͝πE( ~"gƭC.d 6cU'%tC1kŜmWuԬd*<ă# FĢ'g̚ }Ǵ_fi'աCp7B?z{ z`AFA= ]m&p_@W }I{|bӹ|*60|dP%8}J{?/[řW= tsWHmUwcb*m;{ 6O:W%lD7]d)WG`6"P2\9"CGvaGGBbqn20KeʮGikKGq֥w`YؚV뱴4_淾^{GK "ۜ[GYm=>-g}aָ+ hm7+,#ml)#gf> Yƅz:->.}IBxb,; F$FW-Mc=jNi[k. Ӓj .oWT,C]0ST/9=\zmP,/^ElM]˃ۺ{>ELWdeôU#-%5GĊٮdkp ]gOA%l);){1 Ә2X!~7dtcy jF//tvaKǵWmsYPӆʹ_(Up@b>-(+IS[PӪ!6H?>gg>)BP? ki,a3<*9 {)v"P+Q,(ŭ ,"Z"VB={RS%qglFGHMk0.ak2Ǯ=zгG-jgKbi.@s c+Zw}Y\W3NJOA_I5Mn(O蝄"S)Kg#VyoV= }=hۗ>\tPVڙXk Q8?%Ӧ=J AOҫىfOv_c ֻLd[>cIr~PeX\U[+,ϼᑣ2ވLc~Jk) n4e s0zQAB/\tHad[@֣bm*:#oOӎ rAbUuĥ_\6?'\6WVĈ Cwk;%&l&OꊆAk=vc93iZ+`4p1zruHDZ v żG%PJ%z#,@+\g#Ev%jYB)liAğM}ڬgi{Ʉ/m{N|?ߍ.(9v@kpr|_Yu`K@+ K'lZD@1|A :KtRG=' p}4DjH)f!i1hI6z dEHBx58|i<a,vy'Ј=VKZ0*~~m__͸J3tvlPxŁ[]{NO3l?$] . #rĻ:P=|Cb\3 3j)s18ha-?fsJ&[4%_2öa'%#d'N:i5>5G&!ؼЕн迕?2AXT4fa_{FOgj?*Xa`=%%)L *I,Ԍ<|[g/_T3_m+LFz릡!`g=vX;q9[-+c6 Wm攄qbN#+ݞI!= 3V#.hET3-C[z UX)=tk4LiߣVD'^3K({"\c{?teM'癎sS|9Xi.FndSo OOb*c.r #']Q#xMpE}Oo*4a֨ *ιdx;Um'P) 6 V)u5Jӭh͞ zH+KN 9/XDѱocHt`sD&?9l*cYXҿGJgvh{dŲvtrD}uzP5ɰ;1c,Aon"c4aGtoZ(I-׺C[=MnQ( ezh6.ZWR*􌳵5r |_DS{,ѨJA3'c:,EBo 6繍:[-BE*VhLL|5B\,nGD;tڸ F$ #,`2oME *gϜ!3IOYpxp_ŨCj 3Ou`XONRg]>蹒lXHjуn^ έSȱ 9s}ML+A\`ӐDEOx`9 >ƝuqaC>|¤5WBo=7 UQ>Y0z+p|ONst W/P'.T8nN|6ĊyoT=D_7:F*qlW)Y4D=U 1sqР-4&:X r'~۽K(ƕgO~37n^st?R'* ڪ`qm:5ɸ/\\<˚ z4CM+7?v+u1 0:=[dI?XIQ=Vrن vgJV h$4SNnSm Lo;={8o"*}?B >.;TFwԸ ?ڦ*%|ʝګ/i-0tOx~T#PWη #YWtt YuKzk{-hzzݷd^EN,N,OV ztY;5?QAϊãvyk]Ȉk5 b_g^,ڊSĂ S[QM67j@r'*N0Zeh2ښU^KCe<6:I tY H#7SV<ՅCH0tW17y3]&_؉^Ul(dUv:O.qy*v5>,~7Oӭ@o6w1k++#o=D4;u=2^+d(ݖ3L{eyۛP#"QSGRb?à,m\n^JI$֦M pe;<. ێ :7zt!l*WB_+J.GZlW`lMxNq*u.3RDZ,ŹK䛑<Tgoc'=Ԍ+#LCߠׂ}2b^6 4qׄL>6XÄ~,@'jQVKE%Mjg -8 {t1c=E!ڂ"|C\EF6ʭ>B~T-{M)e! }#ֵ8xuС֚xvHG tx|q"띤>di@O8a.'rn-8u ԏXU|h{ .|r3jTڝuힴnB5e˼i82Z3qvL0}t5-[Sw [N=y).c9]C;z.?LTuNN|Ⱈ91 <@& Q:pԄճ)_ƾAeb+%*O'ڹOe{MSsXZVƽ#q8ޭa(h537Q'w"0'X'U1t, 7I`z=l (]ogu>mry$^Z`x>r\UqS kI .+E@=z% w; 4G53?l]hj 2Oo5{.˩$#e"Mբnw~:< JQF|g|/è -?3i&Ir8*X2F5ғfQp*azkÂ8W'Cq_ُ3F_K (CR~]3aGĴ zaұK5d?.̨eN.1JOc.2cg,^шe@+m?,n⾛ArAh㚗ƾ 5UVC{9G"hJPʥO|{=bMuVʽFJW:FQ>Q+e \ ]bES6rl誤L5ZĂᯄV[TgaRK]ො1F^|kg.WRBڨaCL_/|3E~S[la"<4dA)Zf YOm-xd%%HEb\?kP`%9"j=Go>.f c y \g7gƜQh&c Õc(iŹ3B"$`P+:5r'C R#ؿ[ijyY־]%` T3xzBm`-Nz;Tоrqiv5R2&G"S]])%3j$kz ^ˬ<ueJo[z~6Micx^&WπJҦoPe{$Y'#{0Y̨:= #7 H@5}|eYC7\s׳Juf C^IwUd v=; IrH;RJO+|"1 3PÚ2"+u?.653Z%ClMMZ4+&!%kzO; o_wCEYG*zKVKx(D'XVnN5KruXruԔ2]K1}m9SO" snL<өɸ0T^,OذfkBdgrDSE-cuB\|3AP5)]A6hԅ92gCxe.=5^U8͒_IHpʙoȀDB=>GQ'\g^5admd==A~0L69i![އk}P`*#OF2u\3o4 jLRHN((T!#G5l57P^ա|)Igk7l㛐O ^JKYvX쐠;+%;C u &E#cK[ ]Ngi?szZ剹&M٣xpYn]ێRu$-a5 (Fj0axj﫾9&DG¾=)<(tFa9gBVJD'MɔcHYcQ1Mi͡s9\Xr4c|^u.32Nj ~L'&[>t.*d5v< 3<ôPckJW'i12&>|vw*S%Bπ30_,fa$']y#8ɦ0!8"Qf>\.Y/R3ܼ 4Ϗc.&/F=B ܄J:w xMOGw9'iS$_CZo ^L L E<ܙեI߱ɣ4EV;Y\ g$PZA  \D%/!΁7KX a;"9ssr4id^j-Kxπ>!ưc g`g|'~(Z+uOs{,O+$󪶥Q٫%)}~1 XA$[4w 44"rG qJKSN¸;H, IEhxDFDW253m9°], t9GX !nT돮KE ci?J%0!C1T:7DL`/#Qu c)c ^\?tK3SȑO-[&M9 J&.<n# t2(ߑ|gKDDatsXL<9}}&Ш`Ï1V[İ ,bviH9>kBtG/j落4==\;zJd{$y&Q3 olf0m{G`6:Q5lprFI.ڿ"Tn+}r2.876yq, %gk;9)i e/x!|[&C5T¨jvQf"ee-4wpRY_~w3׹[GgXpHCa8Oaɹ@"u[ Tw NI,~JZ:I+$= 3W) _ $#Gd;ru5 ~y _.ьi`ќ8^n\|tZG;Tj=/ȓ&L$ ?"K];c:$ަRC%wz0s,+$4RJtҹHC}B\,cJzh;)KV65׫3[WkBJi䑋<ʩKJ5JaF2 W:F!;Wv:!\HZ/N>r5uhHgNl>{7h 2:%qWQ };ʞ3[/i4b9`O \ovƘ#uSR.V]#/Xzx.cVwF=҅kK)ښD}ctSQNUsz20:@d(?];eg;i`$㟽{>"U3@RK=$|֡⍈rYXҸ\L\.jC߸Zj"]Mԁ.(m[/ ^ TYBOu@ |m8YW:T̪]Y}!\2b Š4*|&}ZEN}p+2@(A:RMvADI.PYƉ&\ t3M@U2?$Y|2clvi$E0ozXR9/dcr#SEiw(jSz,ƨCFneQ \ܦy9ID]xz`Om՞֮f쬔zXu}c  vé]:\`M&e&l\ 9Hk^ۋ4T.iC/] &OԙzR횙4&+0.X=ˑJ} '˱.vn$oLPy%dȏӫq* k 1kkAz)p$ēWyK"C:nS[f`܃uyU yn=ej vU,=-LǙ 5P&$٣/}ɔEh lzOf~16f#ʓbU׀J5t$2Ѹسr9rU~]l* q5w/arIPcېuu_C/y{=n?vBo^RFRdjl<ugpxkӵ  jL0:ف`)fܽ2v~@#+\E5hhPo&3<S@t(2:`Uh|r@ &;{`V|%jζ#fu'+3I]AǤA eEӢ}}ژ[fͪ>V5߽_d$R]뷎h\C=|G3|j;FB/ٮ;.ٟ@NN~":vLQ U5CZ{Rӛ ?,nL D7$%=-s"zB uH[a|JW>֘ZNM.d0%z*1"L09(u6UMZ%Il@4`̸qIaSD>j9L4{ـ@ -f%f{#Zg5"Wia4yl^sÓ1tI.a1~a/<*̳Տ;vxb"\-L+ 3 ?@CMjJf COZ/8*/obp%{A蒘B XVyp=i[cwA>MŸ*ˎuecWr4`aNm)a ׌N0u6Yҝ]310صxsdNpho"ywCo#τwT!XtB7ZZI j8Ⱜ%MsɞHw\,頺v{8C1m={ȕ}Ew'ZK#/#}Hs“Dι~JȢbTV)=YE-B;ߔn4iz*Wp=>RoLq~ 6GqqߘShdڂy N}{1\ f;QGot@krdm &ga$VN촡 o%0 7>P&.L񦑅Kmuϔ#gA{t*z뒌 Tl UZuZzrZKYƪ=v{4 &4idqE^+sK Mߡ=Q@WM|;wPjGE&?/⹹5ƩC1iP.8Gc6c荄oD H7%3Q#^wح74eI8v Gƍz;'D線Y6MDHn-J(*:lnm YϢk~+)D7u!P\?}vNykpSj4>Puuz 7M:C \&w 2nkN=P&[V۝&j a55Ɲߩ @شǖGz2U 0f*#AfMQZGS3 c(wRϴLMFG"*HXqVx eE?wҜw!HMoQsorQb K%gw(Z?f:hftWDi?T~ɦ$ObCYQ^xy/-ȊkPXMÝ/\{_Tw G"!.j[nzj* *&DNu5*zL"M!W0uEK=BsI3r{7/<ɑ~ o"f-Y9(|F?o%ZQHK2C)zW*P?(3^t#1Fq9vTuzPe k-[kدzـNVJ'7FL}MmmjQfpZV!dR(mYFuEP > "#e߼w/{rWfS~Oapn殼M+V/62b|k4s[)DN Fϼ|Kke%D}^>;91I0bj& wި| Q/) hĻ ZCX|Qk[񓺩їWp>؈'1SQWe_D4㱳H&vCA1Q5nm5uJrJ6q;ĴzL즻nAʵ$пtf))&j6Fs)!@S֔t7"eu'+oF32ZzSj$pn;fT&a}ғl窩[Y$T1)c/,DDD>O(koo4 )md")%?ա!"apmXC|T`jّizpW1Os4nhwbjq_:Ho0 _5gA퍵Wn/O`QE3ᇒAj'-L_j#/-e bAI?re -E.Pjێʒ3=}Eq֎5ia@$}_?P.r0Ur+LAS QcpAx'dՐ}BG5uٲ~c!,t#sv cA$ ~!ͣr?1e96jAdUZ^Z|W6cX*%g9?H o4z/)źo&y kO۱FM')=ϨdJsφt!أ &oo?c 旷sm}}_tRӕUᰴ6aM:$l?#fC(2C)CeG'Ub%MB/3r1MR3C- $WAXp28:%/;j :+=b<O @$ w~Zy ZV~+5{"5潀NY1`ShG_p&9C4[bD7˼MzzP0d ~ޖ&ܑ)I 堺1=VzڄUZQrk瞎)MpKiGpiQ:dc,B"0nX):Vk譣㷺RӷnζZR8@U1FN[u++H;I)r<Ohk yL!) Gڠh>8$]϶DNqK4Y~F> ,ԳP-l9au}%~:*Խ7]677,w!7EN4zEZn4'MZH[g+5=uff\bX4sxA*6S5RD]v,Bj/OPZ_ߊ:Hl>x7N7Ţ]CaE4 tL'), p"s. iYv~}w7A*T+k"(ou~\϶e`$'7ל6g"O aPa,khwST?S./Dݖw1mDXD"6F+nG@a~ 2F܍оJcLW^{(OٽN "J  l;ˍV]oEZO}?X͔uKֵ)8ZR飸`w85;?uH> GUg PG |}}J'h |?n}W##OCEKpvɫ37e4ØP[4'[|[)=1Xޝ !V$舺\hJZ_ 0$Zu]İE1yzĜdh;&1!)tC֯yp\Gna$Sh7a=~AYrZIp^9b>Xm)&z`68#YhH՜z$U)̳IZzIų=6c}b~6Xm/6kSwQSkdsQ99}JG*4Vؿ-ꧣƓf\DL8ٴ,9jBvv,5O =e 8ˎcc9OOj-d(W~p'\9~mXUNvZnqv*W mS'/{)#Mz 0c9%4}3SDq%d_a B}o;Jser]̵^F̺%t-׉Tzy4/"oUcU :p@蚠]@PHX߰.! W<GM6`'/Q'R"luDR9uGkT |<ۆ$%ſ4Ycl)S#F_S$ wa279 ,nh~PD)ٳ1:;.^$RݙO^f,˭. >B\ZH A!:b;B! XQ|]0۰B#JsI*Q~ZJ{(Dq\[GE(WơE]pW..PO?xa q36؟OĎPiM 8¡ͽRP $e,,wG%YrϯS"+DoKcVa?r-W܄^l*Rrާ&mCMbɋ5vˉ#g+GDYB%A BEStV =Z=?gJs0[/7CB-٭-K*+b, ) sCjsN4$^C/ %6{3c CS|Ί+dʸ7hғ|osAzh^O#cn>9KnF$l̟/ɟh @&㴎ڤ?uiکoۭӞ_=afbNPPyS~(Zc]#*K6G?cCs'^K"|=fx A nhރ3d(7_Ɛ3Ւ}[}m.Ǻ(>Oޝk<42|V-o2ϋؗ2c=ޣ;#Bq{z{vdC,JzUm`rKr51ˮuig &!{wkO]rIR|}LJ'o*wE>"þ%v|FcWNCg]'{{W lԨTW{aMGMJ蛸kwq5MFOɷ]0gLg`xBH_-: \~~ywSZzV2 (ț #G\a Xp4=%wEK!_R (QV,5X\WOAr~~?վR<(K_fTW/fZ]@acc; R*`2e{B!(NxT8Ѐٗ2m?6rt.j?= mª:'|:kzGA%[ ma( "mcqT}I\]輕K* 7Yݾiy %̩,%o+At |6~Z.,:Y㊲5)PLʼ,RG&Ex*TKL'uhW)٭uxj /~ɬwd|Q=%|U@ձzޣH'c2"[x3qA`vGN3fIk%YH@OYb)U^8OGe!"dNncc=O lʊ4$]+I +Ҙ7QQH] ד5)B0- ҏ3.AY )|{Ծ +mk.OW$\,nИV+ӓAd)Wd8"xptk9%_ LEeI ywLN"UfnlKN DQE%iѨT՛Us 1fk3Ks ?#5{LP[N(WbX9ՓS`WΞDk1u*mv/e >DtOm~Ĉ& AJqשKZǯ$!֒2ucԕд OAPgl,d\;D~ >NRT|œ|s DY#*|H(Gg*V<*"67+~"G/6PبbY5'1=!ԉpV7(,(r-7@K;WĴ71+M ސ#8 nTY>OI7Uҗ\9$ &bkvk.89ɨzK%eEAV)_)XngvSL8F Nn>^X1BчP;"ѡ#o6-HJ:<9"S]R8,:ʔlMsGc.FM=|T6!O (@`{'\{TboGA:BqZYz:~52!`ꐉ:ʲIyaʩRrcF5j-9p|t61 :X]LCܓ[R;~` s54!nщr7+ݭݭ4>㌼-7P_y:Z,~mڔޒsb!iӆXF ,{Ank,)C] nL!`;ntvi4l=]JX;wJSwua=/M 4Z4*~C!Xq$UVVP\B%bX9|_6ȇRroXQ6.- tƅzq^D9qև` h7 5->tDQ52q}0b'& 11$,~ 6Z-NӓMє0 ./3ۨS5Jbt( l g *+RJqTN=-u&!9%eYpsySc> >"Lㆌ gK5fԜxp[$TxŽLwiT;`)-!RX(eFC?xyvϲD xw7鬋Ѡ>v ˖"0x`P^KPClQAOy+Lw}#G&RϫLmiiLPRb`1!Ͻ?n`OD*5 W~l\=a\.P~Xo$R6ȺM/8^ sŀʦ:A~QpwEI H=(f$ gdꖲs*{<[Zy)~tB.@+jq5& a;R/=bmW846w 4hHaJYo"T NnEtӆwD36bk2mUO6Q)"t:CfD| &oƔc*bٚ WhS|j? 5Ԏ.04K7\e@1_O"&b bڡE\Ҽ qd0Fg+c4a. m^,Rg! %RӦ) ь-FHipl}@5 >Ŧ]C 7 ߙ݌$$ kSA%d߉9;{7oMRC#t!iR<[=%.o-v_Fgb^ln[qkBn`"ʃJOr/r rx켪;/7ĩ Zrw2kB:r c~65!}z~uOK$EjE"E%~X.2.{oGlQ5j[9ñ22BʉÐ Gx.4͏ZgɞqIs^FѪ?"K ӂ$_qx9>sݐ}|ox2(e5lB 埠 ,X YOqQ"H=n-mS/y"'0Oڔ"" j=*|Ϲc9MSN{Dv<(kH 5n0`fbMÀ"DIjѵ^B|)FEȗoFq*%Ϳ,4D ae<8WP}Gt}ok!x;3kL`=yb0ϊ˩Zg^0x#Ih+=6dr=v܈ς6?&E#&K+P$ӵeoct}bO}\\'Jn%WtIIx kЖ@yZK#?Kv#VAT烠m+h f>rUԮl-K%ͳz:&E!|{v(Ś=L*3׫^|FmDžFHEF-#VF{|Rzn_y󲘘luxW {$,e俞ˇX^'=ڹՑ:}pygٞA/U\_yi1ZHLXZR9;숽C5w /܆fq67v9pݻ˕ޘr&դj}o1zA3c:z Sg7k됃OC__K-,V#كP qА'O(,|mڌWm/%'Jfh퐷ɻy%tߦ2GTmOd`ےZLHǨgOaY~!4o.?ts8l;1I>jQKYN Ryd5J/%>lhTM)5غjMՖ:/%EexYTqśLrl+a3G+=K'`,S25#/E`.?pL.9!Gz=ˇQ |M_xqⱜ*ǮxmkOJhϻJ_E$#V$xQmdV I& B?y$SYO),2}U:+E[/{!]=q`n%<e qEd~[]z xC@CcIܫ^yڤUjBAd~Bj}kHV=괸L?Xg0g I%|s4zq7;:-5EUJNtH)&& ׳Qwܑ>7 %1o1~$ߪҫyFsޡ+Dq4^& `'4`׫4=av+#Eޛa:˴2z5,Vn͒&Q}0(c@eml]n&H҈kCў]BgO6GM/K.Ӆf9'm"i*6 {™@5 !5 yCM^S W!"RPE|W;tVڞ#:bumTY [K*}>*}yyEDXֆfi6rUYl=ol'kjϢ¶m5(={1:{^նɮd] ;g0e}TW:c܏$N`2Q ?.@5 x̭_t@ZϪfg|k'mǿYn9se#Qӈ|C1hĴp[OI=b M9ǍŽid51*5RW{ OЛc>-7k V**q!n QZ\H,*aIT*+)d\(EDEHJ D*@e5{ә9Ϲ9~:܅+Ea[FE@kgO˾wŧHa#:C;[:W[v#;A aUҀaUMNPdB8#l@A3l wwV`(4u9kn:B/_2@.r*]V цy'w;zժԙir L]D@rHW ;}{nh30uhGem?ƁKk+ }lF{N#M|ps1)yUe 9a76S$_K3l%߸NVถOCkn EL5Bq~cmMM2ZEH+Sv:kwkœf>T8zЊ3 (u8ϲl:Qϫ9<(lcdk%lNQs*ۢ:(tPwV{x!uh0~wC4ݨ.-9WRɡc)y|;8 ꇳZA-ƕȑ]C1Pyܒei -x^N2(o\5 ϽUiwE_XhíR#oXƵh]bI*X_o\6ef.#zeIۭyDE+}w1kogUok"ӸATك%E҂e"JW~)r9C7ȩI:afPg8ToœBuQrA2=J9d3`v9`=pʝ~.unnP;@^F.vs4^!)f2̌@Dٯ5؃*nQd)]]!{؊ 8l A2zBzM(yyބx7U*1=PO"Y\k(Y18*/(nohnL/̕ݓۦAP%X;^%;&pn*bqEw>IEJ5<&udSoYPaFPe!@;!hNI0-U/bքA Epau!O|8]~@h/OJ;T7s]B_8}*җEԇt!Tdߗ؉eYkCHC O~o<:r\58P_8:U|娄Kio7_u _%03kVKe;5g<{0`Ui/ի݁WΪ 2X[k ?TLD\̞#Y 0X#Z)"#7QkapNz:)@ho▬;l 嗯z3oq3~ @x`S BJ(Bb;PB*P6jˬ ]OT^28DIҗQ| (P;3%SK/]$F5:!<wS.!Frt]YWNF(o)Ǣr+lvwVBɔ/.uT~~#dYf$s82C38ie0p9J$3zx$' OL` 8gCTz݌{]4ܠu@IZ5/bpUS9Uj5=M1jĀ es :av@$JFx-t wLOpl|{0?+4L\^t.YYDZfTPb:>i5 .?~ 9|&AC/'h,/Pxj;9 m[W`+k8X=цY gxkK?b{}k~h%0B`4JMI2sriaj3]Ə.8 I.K]]MltbV@ci:?DFpGhU$?kp Yْ|B!mf.r1 [+8ܣIv4kd 1-ְ(2Qg9_Lr% }Fv$SY`Z1ף}k19w!sNS60xwSb8 v`/T'g^\۾B#l珑RI x9}6Z[ٰySs{PєN(?y߾ӕ7to pK0~#珯D]XPc7VJ:JyK&Ƽ:9@s-8c$"f%~e:sE4jr{Y+-:|+oNUoʿXZ|S;[7G$RY-(J1M%K{P%~i谎[B(?=U^:9d}Tqu6䆘C/<˿|muxN/8-st豈>ZӰ43KVTb y=v}~d.2#Jk]R &syo{عNR7 65/%4īI rQBbHůg'֜b+p+gom -ҕݪYK"1ݠ#5* 'g~5a [B5~DRӮN&Bl^6%9b1ST%Šs1½n=)l_;XX_E_ rJjdK ZUx1I*B\JG$N CcdjSuP'vvާxw {2\zzҢ;?:g.{^ PmWUaxBOy/|Kڿ455hم.nIMl EfEM3ЌOy$UonxMWNQR]U[3Oc[GFH[|{Aw\H6K .ӌ"xM 0VIm>˙ITLL@l%CX9ez9z Z0/@/Wi=,/V30˜44?Z8gZgn9]RrQz;|e^)El9ؚE MxטkpiνC3۫즯F7U:Fn6cUIz}2h|-(".clʚm<ɡ VNy{.[T KWwBA|'9c,`bx?JO(Ƨ_.{b vV6KP u{iO:$y{ ܮ@>"R}EGO@j:]VmxTV30ssY,D 0&R]Q{m|g K^#0SJrHjoz){>/.1Ʌ(#enQz!8飈 L@~F W@u8g nDIs?:x^vF4jER6N0^H|aƩ>hU,`nRBʀB P!)ݢm=VKuxڄ>@!sD%2J$({ɱeYU/Z LH?GPQgpm%g^1%톾(2u.PVg&O>?2HqrtZsS;(YlcQLUT9[蜵DX2ZUꆧ(w'~ƶ&h G^Dl}讌^[;6UZMZGv2 AF*n\c^ '7ƵdOI%"sbꞧ-?,O-,2 ڗf6 ɾ[R=&MдKX9`{4)ĢyKtVGX|tA׍LyCa#-;Wf^4lVLǟr||',R𝂔?0c:FŔ*1.A4oz"li G;h"3HF;mK -Եހi 'V7!aC"2:We8T3uWg22A%DRR75_7O7#]<ଜ}G%Ofvo$a i8#[?Sz@e[ՙj}U;;{^ xϟ< @w|=lx$J̺yp쎲?XK7JӺ!woghR*:ˑ󕍣dp76"Ewu)tpI1uxk#7L,2Rrп]Aw!<_c'ǥ ";>eljnדm(c Z;udSqۀ 8LFuW o-5ev 6B:k'n6T ʽp/Xܧ\8$bW7@)}>!ZV>8{luѦ96*(ۃ:u rK_c]|Ԗ}#]/b9p`J4$6 xi֣@6V)F]zO;V5\,LQ }u<^7s3V'Z_D^&cqgWCv:EST'v>pLƕױ?W<(54h+kaww x,`أ&燦u Sw|)M4w(H9N):֕k=G9H&'R4yMWiz6/$lduo'oLKPl gt\;tY#ba/Vfzp%Qh_U%4 "Kyv^?H3\g&uoLxɝv&iZ4|H8vS .iSA>Vykg rԉ.Dw`I޻ω~=D9e@3'mp_ {Ocvߧ:g-6{ fiykO>*UA Ǚ{j6x:p[y+깧?FPa"mR}9 ߵiu*gx-a5Yk[K_HMreTuvNMdI5^nyF^6f$sb ,xW酐_ A7vu#ؙ:^kϵ;פh{#(5/qhYʦdȭ>+{IYtHpεvc\V%AVF4 (Q9&Jf',-*mph3rMZ0lgI>$|zlU`JnئۊWε 5= s;)^J&dde x)WXewmHpi, uG=ko&$2(҃Lgzbwyhy. .)ڒӥ!b+mNp\͵o(\H|SLʆzۧR?bWrǧ,GMr絀aKK h8Iy3tOrbfO>M1g*Qɻ&"Vʄ} 7ي/yovKjJK&6po(`9և8#(c8CTq5} Zp^>MH[g^nH\uaoN7<蔋-Yn}8P4Va$ A@'uz'.{(Ozgk6{:/%]!"nk_vx)2 wJO.tj#u;߯>uTA%wD"3#1۝rBu{lԀ zi-zv'?#?֦-+:4T YVح۩?Hgix驽;x_n~(Fu,5}i_Yo–هpAZH*Y;4}UW~J冞!>Qr,YjD^9%EM4 ^Zs)u|Ԗf6RF~)uxzVV9ft")Q#<,tp %õHڋGq{P3oH"nR-_`V}hԎ)IL xJt3XD1`VMnX@us/ KP+a\NقXwD?˃o$4f /M8ֿ.jaï''$ SkMT.Վ.C<:C=d`@g7MdBX k\$Wq|i="@ I>bo2:Yҭa%}e?ѻC u7n{QQv c?(U㍼}u96EuvͽviݨPN%k-$_)|~^wv(_@lZ}' )]1ϓ \]fnRuwv/=,ZuQn<=& 7Wtz!cgKf #f7L m?0]&7 .|yۥ@쏟#RYv#؊E+,= zO%Uj/7kI;sr+KM^DRE #)'O X-'qL^qZod1}q 7HWiR#1ߍҦ7@&OUa?\N&n5R=AT_jP!f%KV-|  Kx[FP1CbNYMy/  %X!uiCg(ß2tMycU*CgͥfZ9F;huxT1~M gZ@MGB?>*X,8(Bʑ5XݶcDcUnW&夏ZԊ0@r )wgy?X= 9e/_Tk^ ?bg goSR:fP)6}W4alۓpo =;H$j(&&xS'p le-6oo/#_eWrrچ9|5_'$W"_;a<'fXXwPH'3)@}ϻꗭBfپ['GP]}i%V 0!cQ_He!2I` (U_ e#-Eܦ,1j)C)=94x}YAC)҈0PPT{֤r7nfRnTR䃥̗&fXbng\JuU=TGP*yƑ,4W9T&BkNx"] =C)!6wI;w[Vw}ϋW 6K$[=km~{A#S mO>~XDQjz!b'SB ,K [(9@r-؝6SԬL'U$^{hf#X+Nh7QG y.Q5J (RST@zSf # CЅ=҂Š~mpܢ>_jX&a֏#uEQV6iCse$jqPؙݹ$FTKQ@j)Ὥ⼡q=|ofYMNA۔0KU$ i2bkG(lՈ`|Uww;pRZݯnR倪VcRMHn se[~X9HY_Ͳ -"eT¿("`iqwVdv]OY0bzaaonޣޓd;Ubh>EJ5Of30yе)AR*Bh, H` š- t@u}1Phxl bfp 3zvOG+ {M?qe*4{f:I lꂉAk >AY9P4Tnz 5AOJ">Ax}bNXb6K-?;;hTQ_kyb)PQο()5jՐx4$tHNZ%ọaRdud»3ᑎA1E:IB?ghwVdKb5j-) mIHPҹ-*y},\,] y&n]NwO4>g*d)zŴѐA>8 %5-U)˲NJ4>1P'a7x$~5Dq_$(] ]% E.'Jn͗ (j1]TOa#&$\"g(ǽR(ͅV]SAv Wz'ybr J(`?B @ < Lf{(I@l祈K | 6&SעMdMp ԿРx/(m4z hKc9x6o˵5.2:'rDވ=0:;V+o?bM$'kT@Ç'x^|x g4d}<}4Y4^=/pFShA0w g,> F_@%x~|ɯ! Y}mWLnyp pQFG/f+R/_*d=$Ie"?P;x63z:6G%#ԫN%~ U?n XWtWIn'a78{F_ff"5at=ӽ {æg^9&/V.Ga$SfZa &{yxo="sW,Zz͡g}Gg=K-fq_q vvGnjA¶y-bjx55:J%Q2QJ3vn|74Jsh3CcCTd9ײV\9$zЇw6eXUE:Q&Xq'4UN W<G:yZ{~MXW)Uąh(FA6~JޥDZFgΘq\r2vqŦY#gVo6f9uW&p ?x,^r"R4=`ɇU](#`R%HA܃ZSɽ6b6F./ 9e]`SqZz 6pCȆ% CÇr FjnzؔCeX 89BfŹ+sG1f箍8 ;aAH<:.nɡw8ܹOKTv@t8 ==i[ǁ(e9ޥ{k3^yGxysV/a龢j|>fU <Mǻ)Y%-r򖭄|9f|M>\u2L>ĖpA-=:W.lj>U]Zuivii|J 6|%XеٓSgw6W@R&6H]37B\4km,m/̼Bx1@ZJ^1|E|IJ%)7;J󃡡1|/le_Źwxθr5{fcةwKɾ7@ZN3?IkoV8Lx_[h!" QCYAi`dKeFdt%~~ђt;¦.I3; =lZIm^FMVW%d,o}Can0ZI "a+ A~ Vr)R+>7 d޴ÓJC~ ) F %7j*OZ1|@8VfzUUnXg]ς類ć6j+*R~nr&N#2tqyU#)JXQ$F;cC7}XذS˹9!5ͣtSʰ%LxIc4&_-[*wȧ5Z?_!gP^.#qށ7`8I=JEL*@so \a1F|1~ kv;bCXR)TTܗ>"fG}ǑF@w7}E_E#o1% z|B<=kP@}W]"h  0#-V_?:E=X Ѹv v0HCWevurb,$+B sC Y@~'\5k&^/2xZh_d/@8eVבǜnָFTF]>h>M)ntΪQ+ $q*8#pL4e)M ƿx;+jJ <6 lw\sVJoc(pv{H4E K.pnFq˸+Cb['ZcEsCD@pot}B}8X݂!ppn{H? j:'H@88Yn*>fNqxڽKM_X?,HsO(b;v1Cv66iQޜH>vK]C:~UaTh796&ΰNPs=' `gK6z'Ê1vxxG-!XsJIhvy&gZϴ*`S]Uњ{v%<DŽgZ]ײK?is"&p0tM=$RTs)ٔ0rN,pIƩXpIc6.Xq-x1~NuMc{tf1}-l$zo^{HI_ؘ"@k2V{ÒaZX^IZwN=ΪR9ZWn_LVQN(2턬?_n}pw\ 5?减ڃ}FRcE+HX6{Mw6`1y ʝ ё]K<'_P&$s|x,~#@^ҁuUkR+Nrj+Czuޮm*Ǹ ,O˒L Tǖ{QŖCan}o!CLfԬ}U%ӡ;= $XWe.Žp ls6V䄛 1N!vki{:Zp]FײnK Xw'khY G•>`+o⫘$̅^u=لүܓ,wztѳ\#Q;OF*a$^)Y /OD/ ^%itد#^( ~ҺUlCzyo(痱 p1 KU?@&P )vK->҂#$X)8:v?8y6 YRxp*6Qn 68;a஝}".]˅]^νx9DzghHe|hhrԝ?1/{\d]vx)66{z`נ5ձ-:zaz1V; 1;&d`/Hi,<5/A p)D:+rc83q;;wH+wS|WtHnpVK)CݐƲ\:wEb5jV9yۭ#ݖ$&_,L.[|6 8b `s}멦[ڵQ ۞S- зıxYB ߴ7h;qCH5v'/ؘ'[{-\#oDe3z#- ֆՋ<>ÔAR#a{>%ɶ|^" ymk`!'g Op;'Y+(+k!3I{%Ԧ9ˣ':GP{ϸQxqXu9E5ZIncK''_5x[[i@\+\t7<܋L⌠*tV4+` {,Lq5v  55ƴ .> tXiz +a:2*CVu`f E(q4hyzם,Зh|d%$@c4KE0vs^vaF- R {F)i)`\8K%prӺ?.e|Ԗ}KxpZ+3G])X;#[N-o[=ŶK!3O%ob %ML$Y.K'i# }Q|UI$ćř@^qNCad@wCh^Uj%6P?e*~ ]z%;;Bvfxk Vr|+jLv39)kV'Hiۙ"\;rjaYUZJ>o!$Pͻ5!DQNpit DɵaF w)CY/RR~c^[GRB9Rާ<gI§ "WT4 <-վ}#GnH#!pÌy/iօ54Ka޺$%E9= D|c^ X b~Vu|#X zl?jf"h1i!Ny%\5]=]\,VFIT!*s޷ӫ@%My:.[,1*Dl+ŕHW;IxE996Z5~'ZGrߝn ~Z d)} 2@靵%-2fD<ďc2MMXM>9[7yj5|. }Hm<=ĺ.uBZ Da{ 0VH55 'Ӟ[\JUn~~ۆq]|2NY1v:'D?MM|@jcS(Ғ4ӳBi3$^ CXНBzf3 ud|SU_ 9\ddW 7&$y zA@WV*ot©e9R󢲂X* Pʳ\y}<uSȐdj߈Ok '˔6yn8p~7#Dxj[-6/аdJYmu:-QnQ<Ôld"U'-#pӽE7i:l$?C(c'i&a=t$St!%\j-L_"vJWF'AT|6b$E^q7^WΞni 3q'ܠ #n6wqTF6egUĖ|4j\_ G_Ϝc1 po!/^ysˮ[ioIlĞJp8ti:dfA }kqC'$P2~usiϱVg}ű!׀hN!18pF~wldX/o!z͠*VhrI!ѹCSP~巫V5?l?%|Eݡ%G2T%j6Q_-!z3{C',jJ}E:%_zS`%qb(s {Ay TngKPO#J7kKekgHkrVAlgHSc79EN65oM2|bA]0(6|qVּƧEU }_=~.C-Ƕp:w^OHojO+E}JʄKLb +N-㙎wk=yCѯw ȮŸŷ7|Su \{X~DƥpcDY˲?G o X ]I3tTmY{fѫiw؁_t{Ʋs17Wvh8ovbĹɺf^\uze&/}10u ұ21`0̖ph+|$YK D(yo7lNL|S Ax{k{W\YQ9,aI|=ܦCJ^Mt7>Lȿˇӯ7U;\(8IuN2Awav :u!s 4: o&]3WIC)So3ʄ\M [r[5)]_p`znUBV ;::|#!#(V&pf.ЍKkSA͐@tA mxt2YS%v@28NK/1K0=S@)'oU ^Գ_Y_rxJ\,Xf_vy A`E~+Hv?KiPIݒ~{J,Op3*nS(G] .+G{nEWwmݝ uy*?ۻd<c$ u@VQKE*I6!dEGrWl$YX')rjlX*M/l䥜VTȋ4^Ӟnz=yx>,'!!XϬ(Q܎ͧ$Ϯ8>4QM'6f1 ??R(~xҞNSx4.mp 3#-䄍eͮ/&v-7ڤ;QP萡oź7@TY%]/lO:KJ~X72-fGd&?i^k2E\jE$ -wA :1ۼxSMia3 KK^GJBaXe{cB?dn@[bl}ZIZ;^j^转*nǕF0Yb k!Q鐨]0NWZ;ٺQ.fG1( YHgH:# y~hk_ \-%L)dWa;E\ݨФ4c2+4~4\e1( ]*(T#o(ezJPjZI=dwz2m=N/}e--fxV׈,|]flA'Pq~NV:LIb]8S~ DnIJ~,SQ@LZka7yʓ5tۻ jL(Pzscjץ|zNPV("z%>R-F{9pѦRgnsny-דwWw 6aq/ւNJWѥ 3q`Ihp 't* &wgmfPY$SA#n .J}_s۱)td-kvCYxU~U+ܝgzCW[pDkV=a]S oR+ne5ϯb/-#Wc^E2VHf16kcmMxTLnjlq:ᴽ cK{a6%5,f ( :wA'G  gdU|وRtqiFI8{3n"=-DWո .z<Hhp%+uqvpS?t3 8yJy%$ aϛ59_Qb slY jUZ~cXq5235 #uB&AXrod|)f;?۰dEh@'(L&}[AzOz$wf;@l{ǡ=\?iEseO+3(>.Zy`;X? b3f6O􀋌S/Blqb]w\y6 NwTIYH٦Q"¢lI]9foiZಒmMOvV,Xr5yT4H MsV13$Dvc__4&q[KLA[EVZo;r'a] Tu=ȪbE <.9O*ܳ8[rn6$uG#*lX3*&a'm/NW'`ʣ2C^ODBJ #\,Jf6+mmZ7 AA@tY<ku#6wKjn. z!D?ӻ{{*Y9@9?]!o+V;_DmBeE%JA[@9S;iWb&/݃DF89t(vpl s"ǡ5ܾpO5,/tER"⥑E_5 Lcղ]Ulcb? zݕ]Wy㥉50l{w(25ʎ;j*=&PIV{طw8\@Yb>gpiRz@0Rp`n'@z%> &THsb_r犅ҖBG N[yħ S0qA㖘{E`#/Az:{V?~퓹#cn>r*T}LNs$Q{zV|U6scGHM~]5 H KoTE8 ǥcB߯-g&V-#UFdS,')>}@Bee%M'5ݩ`FP7/0sdς,gF<1MSj .Ntj ӛIϬG˦mPj' dCWf)ce9{y,<]{Ÿ >e7 Ӏ:1vRKv.qu|u7euA B5ܓ{wzAd+ ߩb/\BDGgx6)eˈ_CoC >=x[P3eoq/Tì- hf{v2g&ZdL`/$ᧇ:D`BkX7PG*2( Vn\7uE&v=o3}qZM %W̡ۚ._d bHE%mix$o6ufo\MX(~46K<|`!mJPS!._۪^~=҈{L,_#<lY> 4j sJk' !Rn=n[N@-R92VeXj$S1'1dK*c q;C)iDMSi_@v&)Q6{Rȋ QC=@۠:-6>~u1.15B-?6w<:ؿp:@̴]"l |g{a.!ʕuj5k(5:_+ tDn7\B6؜nz<ʏvuE;RŒh;/5?>xQm\˴nbK7c28b<}.>7H{ c–a-M5bHo@j6B]GM#$L4jwr%rWQgоDOXlaL,I3p7\kxMu+_ Q,g-pLqF)GLη.|hy\uSl+}5g eY1߮A97QCvt% BJ"/s s]QE\Ugqsdaw{,{` ڿEj /{UZY ȷxӎsE2M6*2^ט.8 S"3X:5tW^Vec:UBpQ Y.lH6&jJ(_\lآ'Njݎ0o? %4HGwkIb}>9s69 hU iAraJEA=c ~)I MM7ދu4Ro;w@'ٵ?{Z&,-dnjPg(Q޴gֳ,h0i}(N" #gJd_sc.zqVs6y:l."˽r,8`\z1:1LmD٪Q璁YۃSօUHbqԃaYv(>2$ܹQEWgDI6yXnMw-k@7İmT*L 9u}v)@gɝʆl׬BpjufPQ?NBa4&BA].z]jAcqYxM oQA!fOyp\'!QS[|Ř#>dFkO]蒺ij@w(!+g)Ha ɼoq!<,DgPJfMt[MR p)Apf3„W @9Y[&˛6kUk@~,(R3΢--?7pT|=_r:k 'atpwdJ Y$y]4߃pV? GqM A5n?f+zi R6n5u$qhw6u7}4vgW'|4ÏNhp~Ff6\~&{#r+GQuxT]"~" K/GWEĘ SK|J][ '.lcP(k#B[lNbo|KwSj6ԍhv |" He*jS '?þ~f k!m֚(] X&,^CIưw;> avqʭI?ݺ^xT'G"p1gљO1.g)h c?t`A}J1+Z߆j(T̺z/}%W! 6ړydBB!xOp9b- Ԍ^ȱ+L \`["k[Kķ8<3)8tR dkDE,)5z~c m qq4`W=Ȝ.8PnTBth-a`}ϛ V>lI<?$ԡzXE1{$.7qúυ:M|P7p&{&t5Z^8KQå<im-#L3Yw."+JNIٽY%x Ъrn{cKWc2ptg_W6Լ@ HNy Qt\tb#OgAET @xGP >"CUHKWuĂʻ)' =?5Å3L[|Lm۰f:W%ztRGz*o^O SBVTd`D wUȋ?Ggtg[V!id>5!^[fz=՟ڞВ2i矚֡ɶ$TXb;f]Fo9gdwht|3ʎ4ǩ%[FeY?j{o?n7 s5/FbhIʫ-2F(#iశqy'Z*_j8JQk zk-c{ٖFw 0sL4 )(IpN9"Z5>=OVG2^wl=ԇnÔj][H\YbwQwy E(@Z-g\t<ŏo 2)U{\(A/I!]Ai |Oz6A 5Gt ZK\ǫIreƉѬZA]w+ϵ 6}*'F]#uKЋPZB& To}­3kW-^够i{yk Xc{ >fʓXTO %Klĩ0.fg"eOScmG n٬E#K<.d2و8#Gho'/)~lfDJA_gϚ\kg`v"`Q1_uk*'Q"f-mzDN1a2xdBH%H3->kp2h{VcWVmqz и[p9^bJϴ{[%`s>P^q;aIc9$$()"_WWMª譇Duௌ缺>uyN1С|)d}yk Y^Ntvc_47,u"+o 9)'l[Q•^mH X݆:˸n;7Џ쀒@I-!YjU |J<\4`mm/a#."JVF|ئؿŖأ8Y0iCAB.j10xU^YtO+  hSLm"X^:>:2_dC?`rdV!%zDž&"WF!Gs ,m`(8/ K^ ? |5:~w YߒE%rZR2@$U0Vq{o뱖q`g'Klk_@Fܹn QnQM:tŭ{ٞɵ)i 9(;0w +4P7mUovHSi oŹ}~.6b9 4iMe"4''aZi{Tºa4>0:1/h/3Jl h6̢W= 6hG {{ȓGwέH~?LWn((=12T &i8Q4w[Yx`1a4AF Kon Wu[_>6AxФ6Z5M70byfdboZ,O>{Ou0јz,}I_TuMv3!wzk@|O~QALs1<*|EW+} h(HJ>G64(({q%B4⮓񓳏e~aLr="R##|` iT4XK:Մ=HYtVL&X+ښ]y[`ijI )zpSܐknڴOo¾:tJPISDX@9${Qh_ؒu-v:K7N7C7Mli9T@8rx9lA{o׎*べ⁹Nς0;Kn.!ܛZkqg¤בRhd|n1R5k=r_w PL.F%4W.c9"ru)@o܍E J?yM?~غQ8gƧ0;t%xn!SF'o.s˄UszcZ[lk.FY_j_(d{c$k&:Xw ~cޠ@g@0m ^\⟕{"d6XkLYĮ\_z +ϥ[汍S:3q').5L@fiXY I)d-Jâ|rWm_>)3xODȬmF;jRU9w1tJ]^|Q_GAg̚>̛R y=˃-:eS?[zt W6G8g9WfK=kf58hn)ŃqԷt-H m@hY/N.dxH~ ͪa&y6u2Wx5pZ&XU7Ǭ%@h ׈7]d_+L;S6"3ƊO7kRdpQ,jY9 {VEn Z ƺ <.XE+س<ضiqn`XK'abbʾ)ۮۋtBtЉô8Ϻta`͕XR2ۥ|ҖFb@qUOm*Y Np/\_1e[y+~Ǹs5~ݺ*,`0cry<1vGb- SMNf%NOe`yc=?%F_X93lޚ~}-1HFP^ #cTVݰ ځ9fݙipzCJAd }}Ucѐ._]i0+PBcښDE]@ ur k037ދ tEU?\c0ʝp=EomD_ҝr`N jM = xd_X2%pc޶n'|"6n50 Mϩ+5_XrVshs?);8|5y$'q!L?~G5JE #1WZo\4.$sd}k]O=tj&l̪ xf]B˴{_'O*> 7Bb8UDҡ=v4UV DH[Ǘ>&dfe}Pԋ~pb=9|TJVM}`SϷɊ'ԶkN6Wu^AN_$7`ŃB8X*ic׷wݳnz]i5;n+E'2Y{Wp}u$O 혖sJC9JyW~# &G _u+̚ec FcԷEF檤6|tBLCcv4lqtok5֮PՀjTwix~R,vgQ9 zĮ){&C;BVv^XI]ePfXq"i`%w+wc|-cv ܮįCͩQ/^BDE~$nvƝ6SW/bܧ1+/%լph7ǀeʱNGw 1kd%wIZ\ HȤM*܇?g y0dѧVJ"yHŮ7YLzܐcע)o„yބY78jw]5߱ -v)넹st&*]1SbeڧTsyGm-we׭x ~/JA u$ CWh:{A/-s5˒BnCbhE-0\k#="w9`~bh]VhoHBOvȈVp>9tsTvS|g64ސq+GTm j;/*l5jKe6wo{=U x<+P825bO<<9ic1)]YA^Ӆ |(tʅ#?#53 ]V Rds_ۉUzaXq_U $<L YzIy/1,qU\適bsyϼOt>Cc`F  byaM(<q)Zi23gf*g:u)S*U8C 0UhpukD99wmLK|Ceڋ,yaڔ=q#ѶLhɴ{֥)M깒r=j'v?؀^SsXIMxFyb-u*+Ǟ_&c{KJsnNW9XjrnBejp[+fL ު5W @S꿦ЦU;,궊5Tk&aC2Y:LviĪ۱[Ψ=}"c3tgI/ jPBVϾZELԴ0?S/Ҿuksj L}dp)i${vgPυ~a&LyW>\'[_WTSX3)3xob5_qz4^j;3^R`EngV!\(zh'h^oi,>m78 b.o1(eF|lm fq a(Q(a jj5:vO.)ZanPd^,~Ni7$яf=3oZ\ Xr[2%z CqvŞs4?oVsU"fX~Qig^lNt)Vڰd?UH& su.|u" cnZQ-r-UkM>گCȏHQż]lA2Y,>\M2EV?RGo{Y|}IhCz2kwiutJm:hs}3wi_3wЭUi\{7XsVlmwV?wl(G9u&al)Kf<=D-@kx|G͚͊s`|l+C:؍F]Dߖvp)m}tû)KGjqPO~"ѝ %y97ݸgtcZ))}\;m6'~Z2 ጝ_Vh$oWQ0$Ů9 Plؕ;.` {>-6ѶF6y/#UG9!`pDbζkj2-&s_ ` _Mh;;'jX)V.Is4) 2@NV΁Fʉuӿ Y!erO/iD!ʙKEo nXjnzs;q̓Dр8_r;+UpDV8z">Mܣ7w{G@c4W^wA'H}P64!AWѶWڜ˱}:Ru\̐Nº]y˹rpe3k$I7kK5ViWeqA<`^(}^ZtNz{($"ƻɗF.[mݼ?C' A?k^\}yv{K)󰥉! tr#~ImµAg"h˔Kn"'|)A>IvIBt8ږ[0Nj ǘOJ-Ū}'\%ԋPӨ^ ^VrbҞ/-:&7Ķ 7'FRg4 '>xUюסez̩u8 !r<0JClD=绣}z[JαdHhlK͹Z+-i-:4ͤ{TP3o\ilRi[$׀ hcw A9iGʉk@Έv-#ୢuS2Y Н KYG%WHKXd0 klXNHꄅ֯RWlL5>8WU ,btBJ?N{JIW':Mh@dmTgD]Փ'aNAxP![}2[oQp qj6!UHz]EKM'as V'aO aeoR@K.wi:T PY(tT|*6~ՃA2He±^w2HgߤV% #L<)Qtbba@|i2ast :ޝ"Gz>LŽeB*ʆx=O]YǠ6ybTJkh% 602Z~7ixF&96 ;#A `\ׁ^|>dtl%&1x$#ڄk|Js΃ک'DsRwM|G=SG3DOIֳƭ2s\&MTu`FH6 )"Ք3||}fX0= }7Y#)%< +ؕ̈́=e]]Sê <z=: -x^%M.[HpT79 .H(g"QZصvl#k: 4O,G%2ji8$KOaNk2t=5r5ɥqo$0CܛH/NgE*D, xbBD/70trkldy F{a-5i{$|lfE(a_˯pqa!iψeLHr )B#FewfLa/RbvSIJ4#&z4O9Cb0NՐY7># kh`dB 7k@jJwkxcC6Ÿ.}ŗIҭUܨlRu;yX ( BQ\Ge@Ho%6CoQ$LڱY@rT6Ȇ~zqA&rߠSŸ;wb`YU/ߊ2p]7&R}a#x6$[xeJW\wvLz'T۵ n)Z&C 6XYtNL:XKaL=u$/te2k{Ta,!}d)}~ia>+E(Cq>6˺x$=%xXLE9مm)r .[|Qq#B~s۔H-VSc:pʄ1^ n`7SUl sKE?J#nifRKo+fB~̠Gۼ m)-xb,?UrD )I@K4ѻppJS%iԪ  *& %Vت6f=f )ob3I}AI'Z5)Deu]m]2Im?JOdzEY+Q^$Sc3h vDYkڨ"o`tlEɮ16J7]4+q㜾tD_Erv<˟ȰEȤn(CcpC5*ڪ8s:<:/.@աw?DuBe7rl2 w"+oB$ 60ĝY=}“cGtrhdd@nxiY5H`*-n8bP6O!F喙=M$,:g{=xhN!KW屬X¶7G *)Su% L vmB*lsNI=K\JOZ'aH;YHA٬S Qo$1:P1MùvHGػ3K$CIXΑaC@%IR$+ӆb6r(Zr,M6kSWu?ӭg~?zNkS卌8;9{UV]%C:;x MukbHT88Ask vpn9"Vy{O҇+N6k~g0~CKnP.:!cXIT(Ps;Qc-Wꭃw=2$e 9 +^:l-P)m:۵_u;p^YB"&G6ͼVkG2!7~} Ͼn{K.PCDn*'yղ\MҪ;REvo2U$]3_-x #mĵ,T&'gr|-t+G>qq/Ov~TmCeZ ne$tL|%>g'b)݃Zn.$dWխnQCpsFq=*cl!Cx2lkPy﵊^sJ?'@7_U_´_c`3s-Y%_oE'9MЪ6;3+}IzH|4m2`3u̦ВbMCsc^ v\ 8M2C}Ģ-nC3;#r!e͹X;sZFd:'0l8?*Jp=cO6E@J&~oJ{F|Y2daN *]v>ɺfkSױ=9ՓٕLY&Cn]J1 (5iv3rf#] \|poFDٌ*޺/W]=Xfp=ٹ7 Rk/na,2Gj>qCpBg.eUf-Mߢ'-^O+{72>N.^T;w{*:дI+23vuGevzxh5Pݍ$PKՄj]&.E\葱wKS:Ed1a! @m@OA/훍l 8\1ಖ,@-jOdQ?0xd-sO(6bl{r~0Dt2A4t n4qAw_~]62 u2q/_Ln|fm^J8Қ;ZG]S vŒ o1x,nHMC#/TGUMoNIs ԢsB?";lv!]ue.VsCm!gRktTAS5i_>A(-N !Ru#?׹6o[֏Z%*ń!"$c-oҶo( fG R0Jm ]d]4,PU?Bg#o535&xi^Oa/ph/'Z0DN(` r]6DU J98nbp!e/{>h?&nx)eWU8M!Rv~Қ9aM_K(ZIiҁ=V 7-9nwB)uR@ Mv23 ]|x_[90ToH\\ 6C<(MRKv`7΍(kžJ_= (e񷑲ʎVU"Iƽ)/ǣ{4wmz5"k߼1;BA۠a.6R_F'\ՙ%Dr|_J5{^Hr8Vl<} W'~s23 U}WjZc=sOg؂wߵ+MIisTo\k(wy=#dPZvCy!F3Mc0oތNH+.SIK)eEbuHinCo4i/X\B!K͕uOTOXףv%d.h;ȴ<9)y\s+06򿼶. $fMq8eFЮ}  >ZEW& ?RG-oiXŢ-B~]]hW3eژD=i_Q(X _n!"jzmnҝ|`zdR;3zB)b/dY[JĀ(^:^>׸S寉X8;S^fAG L#RNPzpkf ye]z!/>94O{E*Yb=.gaK*ӌU"(@L+;PL|WC&y]an?f~隿ȝ~7GCOk8|ބ^Wf 1C7Ě%˪+e$OFoo7ހRgsPnJl?͇nFyU$ƒBp$tǘ;!c ʉ +lMSca/'NU{$1PSYXiQ~v(h~ƒvm )&X0W:v@ML [MEv XzsϢ #8kOM%O?<gAFO}N)Rb8 3XTeȔ`|۵ 3tNĹU-c!C/UuLM CL+YKTJSp3(8ܴw[^X9OGMT;WcoE\X;7"s9u͸ZZggT0=%cНդ ~Wk_{/o{;bUOz1SGNq^=f# E:{^'7~o2"ߍ 7_Pͬ;Kūi3=i$#a" wrՖ*c9}$z-RWK{&%az?  >*$c;y?n){ǁo+ҺIG >=ŧvoHyXɷK|Knvx9> UIyK8 V]1_]nPu sձ֮wGA FP%(>t+X4IJ&2(oL]O/@滖Ou||!{) N3hE]-V"삞۪}dC>GyZOKV19i+~yE Ir#lTݱt \'GGܤiRُ-ɟwbLSj-DZ-}ws$ߦOw2yoX/L_Wv`%>(R-Sȩ RKpPyR>ҵ~yy?zX:[\Uv)&&)z^]-sbfG ?1(|7TKWV7{ Q3D$QC(=\TXhCOga`l$$eSc[=-MtkED+ #9" 콂Y'8g .k `og۾~)`,s<փ9 ga>I%[;Z-aAcl:H[dw!{HRD:r f# 6s¯$~G;2wTeDzIF{ά0\S$2+,ؾZtkrPz^4f k])`M6Ms=:L'S?u[ N fzEoW(gf5)BP2GBѮd +v6UAEjbe0V<ڲQ;dCɭUԜL=bjY*׊mc6 A(冞ٹ9b+ryɔxiK[再r0O޸__Am`H%+ :w9.ʚKE?xi*h(\?YEwSSQOYBpl45KRo:N2ӂZp>{h;j-5NkJ)p4`؁wKIOQ=#/7W1S9RҖas\LqFRӗ?y71I"1YuLU=5X-䗞߀{l&p}=%lHjk04W=mtby>"Ѐ,XxiD[֧.F=SʔǤЈ`C}{. ;,!`IyÜpm"؉Xn} I^ kM 53cVнnգ-++ZAhRWJgkM2?{]\i=̡Hv4p2ea @Q4O󻚪s=f]ΜKL<&@ SkU^@}o|=ŧ:ߵ;oX z6e`bSI +^RC;,20!7T$MCח:rhYcމTölGKOPӴi(!|k\X$&+ݦ13 YIWCm' PmM INv@r] dҏh'flӝRp $E:Җ[@D3P}ﳶdBf-pKTQQ W;*]t {1l<6D50Jjs#TDzɻ:z猸%kCx"cy#U\3I$([c͖]RễRo^*0w0#;c]K6qv*=Q]'oHV'{B3f.W ,qQ0*6T,Eǻaki`PךC쾯q>x'IܙeRg11I!=,Gl6BZR'QiH =+Ig`j^9V$i-y)ܬg%(k֭NIЩ RXMdEO} CkRijT&jH.9c]$ث)$c($Np)׿bqc/KzzjV-D? H.'Iҫ]9ZQ9GjciϾW0P?0L)x'#H1x mFbQӚ%d~Cs M:Zϓ1xG3ML<l<0Zsё#[ia%=e L鋆wR2X'@WF0ik*:&{gkz'3T0VC$XX]\t.It}qbR Uv7#N,3,"nbEdT_+ymkɫϰ.>snPM/ǩ]VKsb_{T1 5ɣQl o L,@=}7/BkȱuLŽXXENCH BP8 3FrSZͣc,x֐>'aA;*g-!pe8PIaHX:= W蓥wD~钢6yf{:RE'!]1dz]=D?㸯s38.pS{lGx(_w\Vj*6UnNJ;(dY"*,rm;Ho 'H=`JlFPasPM_, "#]Ğsj3tUrBLJCP)Ktr""Q4> !kl%]qՈoo~+M =`:zm9gT[-75"oD>%ŎldHdb=/$Up)iQe\GxA*glq89|Qc띃ͮ.' gK0krYV-}`[uN&O̟Qv=k0_9Z,jý_W)dV1і- Z蠮`́J p̟vxlbo;ϻ)Z2.s,yZ!3v -]ƅHEPF M,%+x0h u2upafX=97Xc.x'dkn*%5hH|wufmgԎzB.mwV#+,ϕ! a*ŒIi2q'4Z{CP3i0Mhr[^Te@@yE7M`y/ٵn_mk'ϱ)EڋM/%a6WZߍ1ԣS44pEɑXw}?4 187\bP}D!3 D@9Rޅs(ɕ,L||ׄ[ qF{-У0rQ۱h)ۈh$ s@ y8 byz馥 eStc _*ɫp݂{o;fpښ\ƈHk<Ԙ?\Lc2S^J懶 dCJV1YPbI$UQB%tjŁ U(niJ͛4JI Y+_@yǒ:OqŃ{T(/;NƎ^I1_8 4gL)=}=s|:B?Ѳ\)kŠH<[gc|:]T=6r4sܣU ehHP܏A膠tCB/=vɢ?I41=eB{tIZkǏ~(!@tEK[PErcAڇ|ȋ+/egaoUQ9W:ljJLˏH !2m.2&MmXRo ?w-;X2\F܍1;yaup!^UO o~ocFrvL&cW{'8߈^X8s_yuOM[ql;yR\AFySk=6&Ym֪pkDrTtx!:و{ %A{N<.;a疣ʸU7рF|x㨰I̊JK tp#nw>^5hӾ!3Q @'PdCCQRch 5. zbl?4'-yprd1Y -xK$sR1ӔTPk izf:?8֥`bd' DqLAk^ 2 i~7o B{H߆Eɲ(׎MS9w 4,L}e= ki[?Еa=kFކ _vo#AFTf#_ 8H(c}݁c_a0H"ct3η+ՙ,'xG_l=A^`Q:PLݾk0ȸ> cinthJbX"358C`ko.>X0 tXKdoLNh~ W!JyN27uGdR741hu,@(bbliz Drv@,6NGC2?8/F9Q ʀ8ԛ[c"lYEs 5F1q. 2D\~8?NFz@bʏQ-ɇ,|aeݽ#F&Ǧg꾝KFt71 HBrmqb`{kqZr^WҜiw;`Ljz1KIP;̻)_`g<*V{Kç}' 2 μ*[Zc)-nq;Bc}j.67ED^L֮CtQ A^捍XtWH(|Xw"KJ< "Z5}nfh}x -+eaǣ6h t|]aF]64cI*F8GH`ukQWH{eƶ\6r[z:gaWo2m YgaC8:ٻOfxUnw";a oo|垕:T#b+4Kݔ!c_1 uꏒ ޲ŜS;(.{ 5o|:*ݓݒ!t%OmW|+,^ \pm5 czFgx% g(E%u 8nI\1!j9Mcc,@sGz?t)Qda򪼣ejKWu n+(_ztᅍ< 3=6i/ZYau'5w[$ UO0&_o4,XNUbCL|ntV6=ЅnPZhHэ:zuqc 0LOJڞN6z?S^z, (}{t2rƞCG,ՙ { a F4~|x8AWxV0U~dlXsS Eqj~;+(_ZX;qļV-e֬X$t]mFm[w :Dȓ⥸PߍA*;{߼T!\eAqbYB=@3 rgN*w<<6C]7W2pgA͌DImHL1 ޽ଠ}nzvbm áO3o[Đ]nq>vRżPu ƼɄ@ =8z1U_'>DVf_ cIԣK'Z? M\9Bas?BN)!gy]S摸uF W\'&aHhH)|Yv?na9nVf^Rt|KTvt|V$9.;VO-_w f)̴T貑 ǐ}a kZ!jW=A# [d 5eR^l)a3;%0k|RtXH_?BWk59-2bZ)XA? T KvᖘԶlu)OC` c4ڣBn[ŕϼRdg‰߶*+d]3j!`r`D}xK+S..Js6`ޙcfNGUpR !0/HݗyRzmqWn۶k_,ls@Tp؇VV/fBAD-aǖ =$@;0 =WJ{<*?~\]iVnɼk}1x2b-a)]"<^̧KMl/e%Յ>"eV]޽~SN=I*>ZxقS 0|V$)Ir^cy;'脚wЃBJmL\$lNi{A-/Y.WMdcP- ,6pcNp6i΂]|LgX!AkV,&ꯎSB!pD5&ɒT}VCa*) 3ۍߪ?K}lmc\%//3A(R}<%raQR/OHg~ݞ8C99o 3:秖)<4~wMk@IcۆlsL[cI&Wѿ'n|˙;3Jw#D 6o[%++P;$y1@#*qG 6@j\mF东\JgzoB|;f,`;Ƿ]O$.`JJveeI4{fǾ;@4󏔠Wo7?`4se] WuO˭QS S&+?HxH uI?iSUW$g@ 5`DhSE9%Ezg%;jl'BeUf5QKnx+3l|?t3m\# %UF|G͵{zuF!gȂeB)Z7_d#ή fSSZ` &.̡`Rkt"I2^99ڃ\s;UR+yyY<τ~`y @M^ mor..%3DV~[WWv};ف6ڌP"3oC+|ӏHD؎MyK(Kf›ᘗy:k艷9/!@;@AAa).oM`s3XdtgI |pNV)ی/3DüWN*qdJzAg m8 ez?f 1[D"t/j >{ׇ0L e9NB:IZ7UN{f.qA)p=im‰#rJ6hι%,̃MNbx 0,2mD %_ūJ,a5r^ex"{8m6L1"+QG +" ;<1^8 Ƅ *qȥÿ0F7jR: FoفuB?@43h BA[\,kq_e (›g8jGkxVk;X≯7b-d''_BCy _‚uFN3Īf ;LJi>>FB?. :!tv g Pk1@=='H+%h%r|PDBG#Bs*Z;@'L|I+b عǸ jE}ep#2VJ?]EDS s#eA>ACGu[z "<k kzFyEZ4߻ ܼ` a$9TH}JI*W%ߔ2Č[``@H2™?]A0dz0<7{o" i 2&ᚭ@0,k `) h ٷNqh\JLƇ!prWM j.,l;2Oc^,`}A~>$"croX+0 z2@, $V|,Uk;ñ|o 織 {C l/pED!**!8h )(f\H#(8'`&KB+Fq׾f~|cb6Ji5|z U >ݎ؋#fSp{xA(r*Ѣ-Q)9K fP h-yMyxuE6D'T`"5&`vp&6Xq׵ ‰_|I[TJ{!>&& wT D'V+EUMmz<+ڃ`\F YL߻!:?pgam>X[m }JPO vjjjR+2>01<1v>fя7'- -}`W4m鴍Ri匍mQ526cXG)xSC$G/)tV'XwKqc0(֍֔H̠םz ;+xO" EK~15 EU^SYhYh^O8ʾч'({-ÂBs}'[4q @U㖋71@ ΁J~J 4>E% 0i9 + ɘ5 4_)E}&z)k6P i'ypHW-.נtEw"B>^XoNY&@fD~>'f͞(ZLm~tR0@25d7$V!Eѝ06X*J9?S9v$W"=dD@ġ[ܞIB;"hfjG}a&mto\o$+ʹp ]XXӫ߁1`@'PN&= ƺtjZJIv NŨ%e?n@>|ۀg0*Q[sk [c/Eݏ̮ryu= ?kh)| !pn)V)2uGY>{q9q Bgd: 75nl._7r 6?N>1Ù%EPX{Y92Ž0S ϟNh>rv!jFBu6P-2HeG՗.:?q[ Ԋsb}m1O0 Yu=}k/$o2#+ 4V+ZԃZe3ERG"d6zր):#6c$3M^^Yb-H6+2t(%T3,;߰$7/Ԧ҃=$J& 2""sʸx[6bLVYSC]6vPR4I;pRL&<3!Yw}?5zavt2Ysn3YTG6-4fbTrŇSK|ZTG]ن4~zRuubJ5HM9~QwIds. ++YYIIFiP3]q/^DTu*jE:FKpk h=N |un#rfom?ⷦNY׻ utg4~}+%,Ȩֹ5ҁ6$iKAԨc8lKr/ 7=Weץ|Mr{VγeiԂwa%+qSG+d)^nlZycf82ktG-+}(`A8řHKQ i<|M+rzZ(Ky6x吏ś/u J9* W6p3_E_3p^|ˌ%6l*UX+ G]j D [\߯EHzKk K^qpM9k5OZeĽmƨv:GfE]RKџ,xbmʸM(_rG`Řd0u NzI9X*s슶#FSG24B$Bsb72cmj ||cioG)a:Xy+gd8g\ON2EoV :IƜ',Yq>m pD2E^zmzy'3p|k覃VAU)Ta4W~1ž; U33h+Y &J-cӿYՑRǖO p2 CƕmCz[to9#Oq/*Ukg?nT͍TLr%F̑4?Z>MX\!+Y{7ˮ*{ϖ%%1% QG];fp)#߻\+Ĭp'z֘%pVJp?]' jI,)kEKV[1kghJ1_+PnR':4zEEOC N:\dw}ġV%d 0=-TkP GP9im>`SzsMk"_}]::ka!|2Q3Q yS*ݣmzdqrׁ+ZoíY4^t_nÏT5C돲k}k;@ZWe_i"3/K8B_2{t}?/ hA6fp&j'v厘%'SH8T9b-/p%jT{ts9ILn0JOI QHÉoLEϱJz$g"hA4򌎴7ss-pqr=.C Iʓg%[].m=_݈;VS&d^M+1wo?8<:`I{ژ0e ;}A20A܋,0EA: ZWoL"f+ѹ'FA8v/][TH0,70oQS3ڙl?}cխ+b(uzȂh?pך2O8@~r:e?u(D_}ڤ}67LzUq y@#oc*'GBBj! |=23w=3%ԼH<1B:xy!wc[ :ƶCu(R[n63H%,h#.Dzkf-rY=lZâIĊ ./9% Tb*L#17BPT?SvU>܎٤,~QuP]=?ה"^ ?6[qmmZF3 Pi`\DIZ~T1~F>Ѭ o= TclF{"E$Hū6}UbVf( Xeu R -bg>8,=/y<"G-{.8#)tj9zYVZA P0*o&@gKof -(cE@#=ufնޜDDj+Zx{nN{Č&aE)EٶkeGdG1XWE+J\F2u0u !ocuc;Zb97=2IU4{Q]D X=`yq!#m׌ s+Txh"؞geioPުwjĶ i_XXq(Jn~3~R$%U=pA^QFm~ ZcltVmQveZ3WQ/I[' 1G] U y"O׮aJWmf * f0eA Rz*eU+~12){S.Eir^wOjVvfrEb$kH255ԺyIpVdEyqO'O eFie[qYWnHMP!qVN+{B.p}en?}3ѳMRQnO#uhc}«^XL|eOf7{ 5R3F:$%aF&sA'1ɯ(?zf`RɯRm}dII4M^!,{hCP(w8Ǧ5lY㎱&H_ڈ,)ZC__lE<X; [] 6CYH]ۍ^Q/wKR?;% I> ,v]=QmUߨK̐/+,xF8gVY \[Np Wg`PeL{`zbnWb (/kG3̽17s<`HyOoH|ju4={^HZ<_;)sJE6g~j3WB\>[CwNeϴg1.ˏ1Ը8ebXfF0ClVd~~N-sQCHlX W V.!Cyg´rydU㍨]9cI !`znu4^y#%(}XH) N˂w:;ݥYrGRPQD'X;0-J,*}WaMB"ef@^g4m~;n}JvL;ڑWKnRNܯQ~$_qfw3]Sm{ݛQ(u-tC#9Yڙ;w?7d9^zlUekP C,穘U0G|U4QPY'onw4.!QѺ4zjAYFژjPӽ͛w;9*K)8@uѦ3fĩO'~8`{w`tTʱlEп[n_iݤ1)c!_oʋ6FНTw.I@I/$iuJ<1_^sm5sC~폤_#ƇxiX0xb& A*A/S_7\G%[8 O˸J`ufeؚfr,{Í753|sYZ4x8#'CF͎|idӌB3StATjLE ?2_YNoS@t ŋX x?HQA!ΖFQό{_%S u[ aۋH, ZK(DMkF[bC l'hVd?FimekڸF1JS&[nBZKi~`5Uz)2]u:,gókVbU)08ZvC?g`p#W솄O|iDf8<Xلb~D<f< #3 O"DUi{cPGl<  X:DxL}%O~G"?ZN-}ph1m }fOi]pXhsrh?*y:$&!]1&f?1%4jGI9b0N~̿*9A~j#T \!({(me LhvdIbHᚁ O*eT|V:r*൦W_f6f]/cbx\e$Oxk))WL- ]<ꔲaߓ4Q jsTa3xx_s>$wEIYC >ǵhM_30o駆? QbC e3{'ĢF\1Ÿ'H0ڤ-brU$de+e*acG4qT0+Xo0EGc" P"w+y\&XPXJ?$, P!&{G8R7\E: k8NFNͰ6L1#=*݉&K>2d--f8w`)@0P\O< ?h,Ny,$9X8>h<1ixMZ,{|Ѳ\n[[#$s_{ت#p0|+^#:uIZ# #ws҄lzdԐ`]/fVo&^>뛤Q& Y*yg[OD*L"nNuAS>c} $}~3|1/P[(6%ϳF;NM¬ؤII+.&!LmAҾ!XL`5/q&L .L"BDÏqp=Ď`Qcf\..WA /'CnQD*CN6k7P7!p&,xH{*Ol0dx$bwrT{. pҎhRkPNQ4/)M@):FPrjHxQReéJϮL,bvdH|6Ԥ;ՈSp+6[EMr, 7߆^@Jn X{nxhc|Dl^/ ,7uJE+IbA_FʶP*z+903-;q(hbaB*Y9Jْ.9 |ưaaXfH^"7`ՂŁ 7Jba j ? p^ ݏ.j]/"D`JSz-u5Nq)M}CZ_DlYIZ쯼9zHX&)o#ȝ# a 0Й^LFHͰ)0lG99 'BF%Rs~*r#vSBdY_'TI%있fII7a؂"#2Ju`! =Z8;TzPv`RK%c]b%Q]R샓h[˪Э7Q#Fyᘎ5Ӵl_1+xb/:e,g,vN"KDZ@=gZ%*tMHOd]on". dԂŭ|Џ F!-j+N ыp\~nv+Ӭ.mA t Q͋6YIU%,/ un_kt=7X8} M.|KwjQV\?$& 7 [Zsޱϫ7砎lkpU-0* g6.lޒQV)E{(}߶>/l1.4fPwnj.tVqu%:^_ݘVPzUg%f@#bULji8r] \c{AYojq}*p˽; z45~}m+YYRM&_&P$Jb0&ok[QO_^=v)T@d=/V0Z6[%iX4/P?8k3^L?F^Mj)n^`WFX۬UovwE̵;=q>Z̭ P2nFăro<"genTςuNϜ £g!&ˆk9l/j]F 1Wn7"{īfܧ'V$N̝#05kO6KطEƕ6Ó*]%^W+s6oo[*aeNDPtUilƹe^h2tm'|MwUov Sq L;Ɵ%QI-d bn8 TuL w@ *҂cCr/pfiuB [V^ޡvQ'HD(pS3$zGoz*~tB~!5ZeC59Ji%#t 2K juX"yvPM=CҖvGf'D+ XU"O 2vFs(I_"cm`kXhk*ݮG_=%x$C 6cFjf*s.QǩA}t]yHg\iT?g?vR3K<M5ermĹ +8+OydžlbVp zYRmߙߑєBÊaK֊պH-rrF-麾# nzfϹ8|BCox,mpx5?zʻt T"q޷PZh63O s~Ŷt3sxꯡ*T}@?2_p"WܼpSxw{Rjf?#quG;;}#syY%(!@;:]W jMm8VUU>Vfɺ̜ O#ۉq)hf!s#]!p1W:Օ R 4\ MLh L399Ć؇(c2Zan#fa@4yXw78</ynF!Pp`JwBrx_}cyw@.2q@N2hr@ -Y_E J;u ;ID{ U*gX!0=Kt.u\ֺ^ P} {)mp+r;GFͱ?ʪmA-W[ 5a F^z^io ̴^_ʸߪ(y*|i}kYDGR<`!mQÝAe{QjHe'O|ݠ^zANnFr #ye"35pq>si$-_̙5qJ,,y0.JY~nBjY2*=Zr@,Ed ݙW/ H񨑻F֋ge%8߾1=k @KÄG9bvsbYYXrBZl9=Oo8fk +'3[02ACRE o$R{>O,֣SG-xg^[WZy0r`/@? r{Q"Z3Z-K<mBܘ{\0rjȟDty2<.p/8Ұ6fH".MPF L`yg`%׹lNB5iG۞k_6`=#8sޘIZBysVָL3@ns|&K"+i = jf 9㺿TwEf0i%S(<֯ɡ:뛨)j^-6 A]p/_%_>Vwey%pt $”Ԭnl; .8z`d O"+$_f J2iVm_bwc(B'ūJGx)8˙y.XqX.]ӳȹnl&{K<73_|;=꽀P iPnGj҄<?gUG-(k38ygsd=M"t]!dC%gZ%p*搵jvDB "b&%AJKd@ |IZӨ9~ +(?+8Hq=FUM㩓Þ9t-WwE!7Fϡ/O k4En7xu0;n3|c3}2 =vYLl`ㇾxMJ.`gUieQR?v_3p٫J=Nla~'a?/5䯡tڅClxmC R9yP oŻq 9$^sX;mučL]eK~$}%w0s}q ,[HRZӽrj{%пO@v lZT3 ; ;ZaBՒ:!п} N| -:۷V^ox5X +]?|elh%ee'NjID/IhαߙDt[eDWTB|YE4o9ZO̙,HZfDnM)Ya8TT}ȹfOfÍd3p]7g [|i"+q*\|o&R?W^]ϤϋyQ ? 0o'WʜhR&lohT+U)o@E^lu}g347SEo{~0sft&KO'AH (O>Fg^ zfu6eՏ86#.m9|&@YtE0ϱ OR_~m1)ZqSR%h/Xa,3 |8ui \kAnJ)\pCzȜ©~C9@b'S @/F򞚦>T&kw#lVrțzYz,ob$$H]d/MjK-o.ֱN{w9Y F`"j};VF`wiܞ) RnrGwgL|yNopNM}z14 b١fs|Ez:7 & (L0o ͉2EFdο6Hc6؂VwftQ1qQF& fT͠@Cu<gG`0hK "Qh3`T\EĹ ;8Inv20hGx?;Ympy6"FS 0>4$b՘Me!FVL-Lao~G K1H+Z!щ}' LJ0'K͒g& ]0l0U u2)a'dY.]yV\Ϭ˳Q{{/JRpP6s4< [ׄp< bИz3=[Q[!*,it!;:~&]#A5gĎ>ܤ@(wθxa\XŇ{RH0IIV2i!K'ʪOmHFԋ/tl#/X̭).EyQ k75+Qߋe \AǦ05lͨȲⅼKvd ցH_3F ׁ!YSX^3S-Sb8-rTlk= 1ˢ7/0?O`J> Q6,Ҏz̕z`Ur-1fɸy܏lnF&m\Ri@!Ytu3*wÚrG pkЈvY Ovv *GUBτPm|'p (RToN` ;y#q]`W5Gx7sAS#_w\YtCVȖ>9B*wȒKk鯥QHѣ0|9V-% Caf0cmat ҏ-K Kߌ~J7ijM['nOwҊV}ì&{A‘/2gxIox=U;Q{f6z4Ge9^T2Z_scCx嗩'jseR|@נdCi*iZn񙔃,s,| +W~` `Cu՘HvgY6%2=,$gÎ*xYӯx@ 65@5Xl%=^1T]f:?R$UCoGQjp"UK,%]Y |I3 [-,(1 ]W:;Ux@#F0Ma{aZ=lK/+&NX(Qv/yvN^2Ai7\.D5y= IZG-I֍^ܣpi},ҵQ7?t'{l$K9qH:"!L*P$,$u2»`<%.\vmDPɸ [4EV`sCՍ.F' ɧ~U(*[$: }VcF#R ȓo9{>eqT<:]7/5yk:6y˝ڤz BW?n{^x'%jDJe C`(QKEJl)qT ]` /'XOu7l$bzs% &z`M9Q}|8_M PG5(9򞄁^t5IEx_;Gen<}]3c#ܮ$bzJ73?q.iI:Eϑxp씷h-YptDrnbHI6% 2_Մ5Zt^QsY; O¶`]nwdޝW-}3]bdd0ASSEgo; |W!Nz$bn_X۫b1 ~.K2҇y1bF^/bHr`y3 ~+{d02^L"1aD{c6J kӇJm sGt9>ΩP1_{5t0Voأe4U%CF7ʭqӂ׺,E1hvbS[M"բCwtFEd z3c7`7=_&azD^'p ٙf5 pOu&=L -"l۷[l"XTRyAy=ALsjMEmxEsBozfF J:bV718Hg6g3Lc<40T>x։U/ڸ6(D7s`e"Pc72 շ\%ϽORXttm)cb>%t`CvfڴE"]ִ|;)u=,S<׿=򆥊+ H*K7zrLinĎ`kS#g]q\@8wsyp8 *AAZ9v| vfrraӔaߙAN p6t&3Vՙw6{tMӸOt*):mq.SIyK:y~x/sTp@(|~y c6cPVq.#9:Y<)Yosw:ɞܶ]Nه?ARMrN-fݾ}&.2QZ[6]mo:.v\΅G!rWQj!(GqY?eOʶ? IF^.8qjlCk ^k8 :ˮT+dn%w֔Ym+t:`, f.N"#=8|dtV v*Y(&;,6pO5oZVwa7g#J(E#[{_qbvJz/k"b LIztl`z:-g杧&dIѦp;쩪}8%n8 yIaBs_!L ޅ[4Y%j`54Ez6Kgᰬ*ԠՂ)@J>b(ī%BFDiL°wg(lm#3􂠭(ON-ZYnHw1'%z DҎlO)a~W :s@]8)DZyaNBʙq=Uwc|J8 \W9m2V._Tc|y U={a`FODr_UdzhZRu` MZwFCyvTw*d%D`%E SvP1'ܘNX)'&IqMuA;IWZYH:hT#3r{t'!:y[ Aܷ,7q^I?斿˦H@-~,5 }a̳DT qqmymu'UîƖy$+C≤ݞpu@5.Zy! g$6)ĉgn'{jؼ~eTߥK Ch/ܷPucSqhKj) M(x$cSgrFx\Qp)BoTsɱyYD7m&eh.tZ^)A%_(-Pmϒv* +4Dx?ŽNMbrCkWN^n~f`ux^Vݜex|W>5ken E#(쬕GPS-eO7q!}+kU!*y$8]e[]3 0ѽzs> Q ~UA4@vEqjκ k5H*q j)>ۅSllE}Gt%纘m =:Txkb*0rv+3.ҾF^B36gbβ A E;{xx?ơ. ':0A(%&n%]qNv UW=I*^hM~;- o=GЬ V9U}x\m,R's+/ĉ+t6iA(xXmVewɂ?p%\ \\2EEBζБ x;o᛬:=e6ܼI|vNEХV%:4:O鮒Ɯ ZB&^&> 4 N@/ ót.QN^s&D$d_*gX4/9&aX:~[K?={ɽe  > Lg%N bk8W/:oC2ʋsҞ]Yf-=xeQwBrk :.zf#pdb-[dϟ[ @-T]c˰Ry[>-ek ܂ZbućH8}"l,]{\FkàxT=*<\Gv~e`rCy0J*ۺhIgA?,{e=b/6&wo&uCgT-bk^9*q3qWw%;o&PJO͛m>M.tޡkuNX\\ߞp_̶%vAy]Suؗ_;T %ۃglO\ CjUI%P]yN?d-̓ &?q[HrZT5ǭQbZmwι<蝤qLZg7o$TKbY d(Pw_1ϓ[ {7P/g{6H#k6ǯNeٮ (V'{/SjYu=qk[l*щmt#J;ĥ%QX99^1#vHq;WtсJøR)ISd&"[Z{I=״drdo@ ]ںCԗHiNګsׄ %53ȿ0Ҡ ?:>/J2Dϛ VbIMiK!㰝q^ݙL3xhO|cL}Pe/0#=fV8IU}ҷ$2JSf]}%}FQ8֑~B%=p~$ܻC)6Y.Et(y!9dW{(7ŗ{9}_~xƣcrlC9@M 4mf 4_ ŸѲ}mpxq-}+Edpܖ_Hxx͜(o6b-*h3_|q=.P솺*"-t](hd½C.[))(E7qǾ찠JNc}diCΔvQxN}>q{; ^V3JF*Ѳ 6T?{9x#a;\#Og.MD5W'Ӡ3 S^| Јw*@AJpLBq;ȋdGﺨsO\-:8Dy C$ܕ{Lپ=oM@j.cvYzT\(V#(sz`;buǦ\>Pp3ӛ.I{nq* p^ދ[‚|Rzb-`dt?č[?t6*Dy)Jw5]_HH~CJ f}TXOn~|JH{-}ᱯe"|i? z>?O<=VBq߁p "TCr̤* ҡ! WBj c^Df&(]Ÿחlr55f+i;G=֞4YzryYb(?(Kk1iԬ`j+ivGLn0i@I 7Dn0>DzĬEh^8X@#M=JGbZ&; -}aGY/j" m=l+̹ ³ M ožh;I{C8M-3FYh4GlL={E} PMȐF}^fmLQ[eR٭H-$Oɺ &{^b%oZFǦ7)ͱf"T[|k y'SLfRuJ`}( nw%/ߑ6sRW 8@&_[後+if'z}yv jlըݒtT?BRfYQJai[̪.ĩ^IU `.rI7|C#pW_&0W7R1%hW֏ޞ{DRj-iE|q3wd6* a\X<7B'Bb|n\Y|Wj;=0SqUXqΘJ v[ɣF~_&Kס뀨",$C 9_oǠf<;vLU{*j9?ӵ'Y3A(^0B۬3xP9sn 1oEf&/R~y8kiizK&~ ,ES,5ndhLU'!.XD\fo/m49GMXNp/3E:npv.RfDUn~p`;>vJq'`z:"%[-h]SY3T8OZ-RR&Ӳ27 NR~AqC==%$[F>8gQ&6'wbvZr텯dJz7k!38ߦwcu/ͨDK 藎.?8h#h$Rajy[1na+'KZ1TOfhž"-9ؐw,X|>}+O5(4<5*;Emf?^ % cP hM|jImF3l(o#+~X2Bn7RL9b]31bJ] yeHA[y@ɼ"_ԔNP0? nP䛮FUn8yUۄL6@F9Rd`LTN~gk -3n9MW?O+ ;cg:Zk KʠwaC| R*2YZ@ |u9'p3:^z-:lA比ބq2t=w $O6"%ס[Jepy9%nJx1PKX -z=kd* 96ZcMM k8’-p'ɭIX9W5 c4>\AN L9t3fS@EA8woI툭]xXJEMQTA/%_ve4GW2.cvwL̸23%ipҧ+|UyB)擹<ew;VpT*Qf+r!X(;g?ʟ!bUܮl@zܓ8yzFC)npK, 0QThpdJ?+,Tyj*e0'8s{xc1K Ϛ̯צݡQ4M=w htw~(D9%Ad_|Kk'kb[o㇢kfp="KIe4m8^WJ*hUŌyfߟڞJȖ~W G> $ŷ +/ႬDiN+- r(pJQl]\0_mO:O2ƊJe'BQjAh{1'F~qؾ~ab")GH\mP e4 M[wܗq)Pkv;LQi*f]'9]SדݯMFz2 y?=@9rn7M/Nke,^Y UIB[xj&02QUCZWSIͮ#%m> m%~UW{A"(8Ӫ3+2 ;0*'Jƚ.ijޚIt8CclibH<:]OI[LE A\>JFEȫȪ9X|-4~+o}ć,~h9xMw1%QD]8 +=W^0o#Ɯ}Wokdm$kRAKtNj-[{1Ux V|s(e)$ƕOXRl0nd[|PmqzFdf䂩o әJkkdDq ^dNڿ" ܣp*Ÿn/zt\io3EnUdʍz;]Oo ~#8ly84̹c O8^6@2 ''sgSl ٭zt"OJ *#@Fq`G݆P8B{+aLN za)bwkyf^˚~:o1o ¤00B1Jy8= ;/{$w` Φ)Ǽí)Cܜ: s7=Uh@xև}ȚzLU]"ClVqt`q;x?І0̭X] D/2,!po2}2{n\ 5^VsbhFNqVCSٯE^䦽:RKP y6h, arhe:#?^M>?Uur;@MgNm8-6ȯFа,$+k˓7;abP 0E 4-xîCZ-3BXoptv_N.DorUqb{'G͸v8<1&cu &5'(l4hb4| WQ>U^?*=gSqKX^SG#_oOXQ'`{i:C)q{k\[TC?M$Hs#:װ)6#'1TX%@@uf<)/dU:Ϝ ʻgjq{Q8Ȉ$#Zj|բyu -Iu"[*u=9U|Ԑ&kL8ܹyM띒pO;}•+g/\3$ivT CnFƻn/@|P|3kO ] [u|(E=_TNQx;d=[2 AI<c2&Os[tG \KPiZVWs5cw1?܊il:ggӫplv(Qb*Ĉt*|iVvB1`!]M7,IdY4ΑC#qͪW S'\L-_ ֚L2vh6VIAX*-'$;F8s-mEf ;[οlg$ =Z{R՘&]Ҁ2܊ <: 5gg^&:ȦY:_F| \6s+&1$[ڽhkE UW OU^ұD~AXcuR EBH0jB jxȣʧoYr DV)^v,'@ʿ,|M UGlϔB RsLcjfhQc^7]/-x~Agv%CIJR5ergVgG~%-N)?{P727V,Ӫ p %T泽sk˜;٥]2;eڱI\г;5~d6kjMR*aC9.8F"F˱&ΏtZKqr XpǸCHjn]̗m׶#SS,y KSg;[%d^xsp7|z ^VY<i/9܊5X ?Cvj^Q$Rq{7vAٱў=` Vr-}'[*+qulBγ˵ֲy>FT$}#< 7)P7sY.Z۹ .΄gsV6},`piJ'PhJ}+vh;`WZ`B6~F5^ܹYΓqJM g pӂbYZH,d v̥#/6dnܧr1o;^_/nPZ9qzI&Oy‡ȷotAbe#Z8~1N e+D{c)eW(O CT}_K9e!8LW} @ʤ,F=\l1, yg #) 2W'2fT"~:'` %C{,;gw'[OVK9C*7s,5ceIA$s4 ?͋|뿈tZКKGx7T7 ,"G5CoR*V8ě3>3iTmx8grTftYp»N,Mi:o` ?s5OT[5ePxXt6[|bKd5Io!.6P!>羑n.ꜧCW $8em*U9Zc;4uaӏ@?,ͥ$o^t^7?z)x0ZL|HS=Tlve32-v[P 'YjӸD%Uq,Ňo~Wupl7l1&11 vK2%ټ㬝26epGa^G?n EY3#D!8EhY<ӦѨx^L;yO+; 8x&XX*P|f!۰TR RY@6mz9# q\ Imo0WK*$>WkZ[0o Mں=(y䫓couiR*{*ҾQNi7`oC F%5ps/lAAbs8Վ'A!=*`EZ%y9l8Q;{I,EZqtz"P^Q^g+ VF* pCA`pKrIȲSٺᦶtֱ Up[h r NWN#|TEUitm>b>ͪfpȒ"NsiU:}!) TCsK`!@,\dZέcW֖s (OW6˟[rÈ$HB--HV#^ {/>6Tε`qz~ͨ }Jo[bKӸiI B;l5%zQ|=5+$@lFyˎSXmyZpM}KG/GK^cV"G%ViNC|%Fsl9"_A@ןC.%= -dL0G)kQd:ҸSi$1Ɛ \m@"K*%A ٌWU*L 3@K57RxjIfkA4-e5| 6Tjeppe8onn8 &9Eo@BcYGvj.@֎ ٚ6c8H-(MC5чa5cDrx{})q8nQZZ<\ZX7\G͏X8e% t0z[a uvc 07Q4V_:+5N SiwieU﹭2}sz*g%s}z]mI)xQG9fO:.Uߧ35s%]=s-1Я~*[#vw 7lq>9fC# 5-Y3z&yIoحd#ZNl~A$R]%+O. V z:n8@U9RǕ) u)1?e\{Bru}C`[~3דKFL+!(r̎5Q (k̲YH܂0Fz3ٕS>;_,UwnCv-7IO҆i8?Qt,HnqT G\p@ZaIHc,-Pvs{5شģh%CD mwYd\- ]t~ 0g8De6$z tWi >hw6e-cir3 -k)AJ&4,o$mM|}WyL*`(Aq ǖ~Q1 4@ʎBa] 3^a]T+DM!; {_vq"88E' g1?䳐) w>QԦ%LAy?(l)*C ⪨*\mw2tf"+lHgQέ)A)_).@R8l)D)o,)XP 葂-SaoqEqU pg"N."g3@:hĝN]zĿίNv0ianL=:Xr`dE#kꧧoV== 1zP݃DġU^DR64Vzo>MšY]~ì~HF:+AV#êBz[.)U8Dod2qA$SQ{p[ělv'z)푍𴌙/qlU)ksMZV `a&&yMy]SlXc?ahcW_3kp8|[-{؎8K6ė[Bg ~>Qx.FPAxre^4P+| UdUEQ:?I@kFU@3{X%ٙV`Z,̲gA#vZ=ҳ[KXٴ&! _8 //#x9B|)9(:َ5A1Xx^b ubFS5w-O_^egG0`V&3_P$đBO(~te+vߙq!Fw޵%=vՉ2ĵz_XٜOϣ-.>ɞX8, l.{x%q)6VIіxk OGe F@ۓh8,cix>ǔpԻYg~6=}1'.R'ަO#ՋD_ yδaelCwX x>^Ybt.((d^aKWU*?- 2gz[^&${veV:wOg%jGAUcޛ)Vx c],|a Vn_U|Q .M5ے [RW5q=$K0骯ap $vr>`ɨR&kiVQfb0!H{W|ˏ&6K0;A_O6OF|7+j(\!hSlW{^[i>ǝ@ .f ]n[GTrL1&KE~tTEsG/4WdGe =]If !^<H.XIj&E > 7!Qw}S~/pr;jMy㛤kY\4o( !pA{'myc=+i@,78u UQyrӅ;&vJ]00U9!"ÇJڛ5HB;\1xE8-{Sⓔc}Pc'OmC?%ʘ)ёO{;><&euhIt)A_Fkb c]y7ڟԚNnM#P}q~D);5)\kR3AN9p/n(5R?gk&-tZj`|رo<?}{$\1JC;B̐\8n* \at?ꕧ/Ҍ sp3I+f2ڒQ*(_^uy[DyÕ`77i)DN}"D۵h%Q]DsHBϿvcAdi-()ˁ]!eITqzxS=*-ݖol[@>tyvsLOJ<^L,3!9Wh>m8ȉ.^̑6E@ϛF"lK#"?6AoO>ffas<_?"3L5%-Ӕa67k:(ᣮm#_o'_׃ة|)֧A]+əo@uHE&orDÁўEPe1XAr@dO쾀az5= okoB #: 7{hp.e0<g];PFˏLVĥplp~:dݺBs?O#j NOZ|g[9 lv]+';DEՎ~tYH6fi/@}4DMk7'a!R 힖gcT)eb.w G8;<4Z"#)Xq/ E-=+_t'0{9 ^ŵ_Cw{,k׉yNfMW{"Y-J>'?X!*sX#m\MUK&NE:%>VPh&zoשb[ֈ::zpZ NXeJ onx'})T.+Z~*mv$Kn8v(cOp @|T6m>+z3s%GuZJvXq>TːlKzaF/vp%~&v'ik zq>_M I|Zq>@ y1l'e9Jֵ77 e&%]I|#Gρ֚#`m1$}na%cg|9d. j9iN/3$d2?fmŖ*|+Z/'n}z`nߠN*ƣCؔQ>Cs@:mRͅK` jlNfii@%2lV\Til[X}K-/P V҉s?e^Shk*Ekc&69Zڶd!OypB *zS'vCx98+!+B,h I Vzi__)8XH&r@ox-;|Zy-@!l \NGĶ߮djCߵ]!Hݥj]t]ɳRS_"{;3OXDwN:~Z&"-ޢ;OXTAEW7уux#)ȚũQdcS7NoyPf QznWɺ,UhV\@gfJV+%jhv $ qryh~C.@.P;gwYDd%7Ynݳw8pfB$9=*QW]^_$Bp2tcձ$+eڅ!yP"ĺCK2381$ r(^6bv, -#7ӥ @U建:4/ՙOo-y/yhEz+f'tXG7& $ hiZ$lr%H S"VO&ض(rWOK.tb RBz3 i~S]5 dX9wQFzbciv3 g; s.11q.]NJz2"Em/+H8p:y:FRa,U#New2z5 F(@cԶ{{"7JVv@&>}$Bvi %\rǦ1i7&ۖ$DI +z'\6W;@t!z}\;"işܮ- _E2 a,i-1ߵ5{ j&1lFa6k&~SWB%a//]naI e?/%^ u]Uv*HP8MHC- Pe$dРdBSplSu2;Ԑ iA&p0P uxz[(k[1i& 긣M$Ff[w~D\KUDS1i6-Ǹ WvU Q$6jJL8$&^3"42߷.R3AW±L?xwƕEj7()< }To5*$YM ]4|󬎟&" aGnMz_,~M+B N #~k̞%k B)VQ겏պgaZKbf8>)R s&^]>=Bu0Lks!Uv]k/v1y̅C2MpPhI#ִ6[2uЅA JjB$ʕˢOb$'k:Ve?GP_ˮe3st(,nCm.O&x^4T^Ȁ|Eltm&Y31_cF)u9Ofh/W 9D O'g&QE $* * 8εɩ UPis#v83bi7%fv3>cPP1#E#^b10㐮K¨ +)<ǯҹn&sXY4z$c UхYثNkg-a1~L<73@?b)i*AvIIQ )1S@M-iEݚY;}!Vv n=m.TQdY=58kJڡV:g9 9 ].O3=b$E tJ*\{Ә:9 W͆?^ׁ{*Ȑp #\#KNB}\}O`/-DI4Ǡ;9?r!d u](APï&e`:Хf*֭PW/BC!,ޱnxNJ.t|‡ w]L#O(:)7%j4Ik[xwҳpg眂F*UW-F[G,przA#RWVeJ.pJ !n?ScV fXhgv2ơAoyޏf om8O`[S4c'cAPC0WvxTsz;ioe }.ޔ_ ktOnد_[IrD>(zgYFC6#P;7'J}|fZ6nJtr2%imܓ|M*)q|t_,12sCEe߰] 5+0jp[؞ KbWN>~$ɫYXZ<=µ&cMd1:api#Fja"|zl %M-ox.Qv96[on E;SVێcJY8GS֡}wQkX.uT^%/^1p2c$WA013e+oLIC$ [/Jۮp=ҽhɶ̱I!#K2N#ذ}iևc H`=l\ di EvxS+q˯++;V+Ojz]HVlq DFг~/ =Sݑ~z_fa^= ǭW0ݴ! pjdFޘ>j B>gȟ .R A.KEKK⚿VL*sʶstfa M}aZ}J;`dsu}JĠХzxf>zâG8ڻMik vC!=wEeJi'/o"[b=Ye2]<3W 2l\ɳY)yu;".EN `  'Yi€Y5*?RϨO?@oda-H7]ՅWl]OӋ*- t:3րN4p'S!pq?w%[ڀ"22eievQUx`Lo#3-CCVW}YXq#L) -kQ Rr]K^O,ZQ҈70w1¡<2GZ?[h&KE@ԅ}fHXa['}s2$C uc ?\y2X:= h0%jci * n>' vCg)y;%xL"y>[R)J'eJS5EHIv[:xfgЮ#?WoĬuh+wqy@ N -5^Nj&?Hı)(P.͆66:%(]fC/xIJw-;srlmIW)^UɉG u\ZGS?Eï:&ؕ^8OW8)@!ޝrW.N)wjpkLxTq>Q":0.W#6H)A2J}hƐK#7-Po@+3hyΝE waCgerǞSsJ;z8m4t2sw(-58%7݉eSL;gJY0W$M.RԂ{gvh nﵠ4 j6bhv;ϸf,c/cSR4V'sj w ZMYmx氝zfϻx.Q>D';6YDIƲC+'~UK:?٦(y[@hON,w"ll÷ _]+f_0C4$ĞL+2M21UYݔ.% gW1zz'sxhϧEc+UW)C۷M,NvI)3Գ%샶yWQU2t54;Pj6BK+My7 =Mt+xVLlV BA B K&P!*TWM4bFm%3B ~#nz}evSsI; SK6X~шM&|UnJҝ)9> e[v°b3 580G  =tҰSxJIz,"!n t5ÐM٪'[!5e(BؤhxNA݋|+z=LzSRY.E" %V(E/=}ND- 'V5!b_`F-'Sg.3~7 w&mY?@Hc2WoWU}\ugfa(p2kAr&o@KF*[X;8'||u] Sd+Y yZ87 غ:qPۨ~6K{~fD"X6ҕxm >ù[B"=o 箂Ui=AN} MuiϠ}Yd E_@È&^ݢjǗZtj8>&o[E—N:89;CSr"eِ!!nl4^^7*Y߇%~^lF:PiwR_ӌ!j51P^V-S+y^r_X3-W@ݿq~9t{lD& G.S#xV𞡲$D Ǟ_1NzCR}\PLG֪.}e|m6ڻ*ߝU8LL8mK6C%~xΩd AM!:1 ~8@d6C=L!?[[Mn(wo"MgaȎȒ@ e*u>(ne/X(ra7!8Voqc{ ^ԷX+H"'"k9:Tp n7enJ5bƂ:%t\`\kX! YoqA]eYU_EkRYz{$;5%Vt;1W!̍U@7ݗkQlUaeT^{&DBj&Bk|[+T s)F|/Yğ!~R`/o83"߽Wڄxv!a|SFfظ?K6{0={jv8w#$R>)-]dWZxc&{H]3)C8hD"#!%e^ol^H.{ @ ̱m}'P 9ppZ˞hcEs޳I& )hF_Sۑș5TmSߧXx1w AJ6_q T)ס@F`R E'8P{ CIV"cEA,r3UH{Kl /m~ln_oޫM^G*ɐh18-ȫ~!XHeZUJ,B$Qg6jIlqR9iN#q@Z_+gjs<(y&ˊo j! ӛK/Ab`/iUT:X4x-GMU9}ӄ\RVA4Z[Ve6{P6!ߕ>啤 O-Ԟ!-Ia2HԴk۹wބ{.UO%Dg͊Gef"7I- ixy%`R;uWVr f/6W%g%N|¤mtw %6k|;[{^"%ՅuђJqG.+)/|f''GČ Gb$,7ٴl7ҷY&nS#-bzdD?qnY^2q$De[ݴXx_+i뫽P]$`ԊU$F._>`c^rRI*\ϚKm_81&lwjX{H:I,V)6Yj6/NOu?rщ53y-U0X|_/ Ʊ1(/o}?JKq4紆ՍWR<ϩ2sPq` hD4=V9T^5 ӕY7<+r['ޮ^pw<'&2%ELD fcX%M^xcz,opObPu*0>Pn|U*R"h|-G o$y"([I8'Ft~sdwV?Ր|C$<J^H4pXlSodphc6I69 @3A@ Ӈz/d*A`6i|=(Lc9ηvBjW>o3{Ϊ܁ U6)_<] '}q C9e|{z[tk|_tf[gl~gҰ7!Ӭ;[ h$a,^#Om.{,mhWg"UPdI3[eɪ G̯-|5{@Pq)C?x"^-Я=B^r~_iVտ ,: dfJ6y9 Ī@,޳xczcw|Y]PhM}X4j%fS{ˈf0X}kzN>W6g ?axG/1nS2X2V 7Cz>rAQ-mFj,Wt9dN?-O^@e✌gb0HD,S$EH@(96E7Zm[)* iE[ahM+9Mj-ۂYX8]s5z/%xwp^},f"<~:}"i{1n!*A0JMoW] ͒|A6٬ UYgkD<{B~[[4C/1Ct78 T-w#c24զ*%H=Sq^̎. <}2bF6OMP辪[Ȗ ~+ǟ>QbNK󺼮u7"u) 6o;/ \RuwPs]|UgPp^kUj3&d<4K}2Qb2 +NbpcpQv,hu%67 F [Okߒ낱{TV Ēr9YR(@n![DBY, pGpE#w%nK~,@^YR" 3*%BROdyY g38Qw̗{ wvCy1F~rZb2QqN~ ˙7?nvm.cOXDBuMMfT(`{g'6F߸TrAS*ygB^50tx[SnV A{C0pMYR_a =ໟ b0Am^y5tWl"%A2 qJ6 O2 [ |6Ŗ-~3T aOѥ'zIA3z?LlwNq eAȿgq›^ >9bmnaM[޻6:y&AmI+2x/qvog>!c'MKi V MzÒ"݀ $N遵ON=:>0#<C> &vE8Ω?4 [zDvgdȇ3/:⇙s$$zD&<IG3yr';w)PEڸ4nQVc4! kw_u4xpa-81P̌"PXD[NH4pxR ;¶ Υ糰6C $a׏.qW @ k(`U5\ӆٟ[AIp3%gZ^Wüv4 >Lc}* 6= Xˁ zx%VIò)˾O?މr:9"/b Xnk7`}M(Ok˵0h 5<5rL LS.'sO J k~99kvC̤Yg.3||<=ab'ɺ,k gp'o\y {v@$;p ԅjv| qԟ2 [EyNoDf oD8rkBpa6Hp^x肕s DFQ÷܌++FO–lxKwW uÐ,0Y݅g3g89Q(L#(bWgfu31Rj pN"I, -'(u^~= չC~p צ%1\9okcJơS#p<  acqb皿cD5ЎQ.bӰ&[+ف,<'ၐ׋B\Wa* ,(BSD*Σ12D[m:  ?5p|Lp!.`N=b}xd0zd1.]Z:1R?ӼBSj{!1L+0}CFUɔ!Ћ' [+.9\g* #{S>SSQqۮ>Hvd䞇y_Zw5&XDžF@k9Ԩ( yIWf]lMn/Cm;y8jJ.! w *RMKԈ,CUi6ò0}j*?JV ̉D.CыG3cJ dk{[W( 5ZZcxeTʒۄ,Uyqƿ<9?qmk2kxG3đE=A e33_1szae]kV*u{:/+~@o[ ^ ZC(^|N,w|SSnoI?eZwE(|.iu=E&L8ۑ$;5"SU)$FN㕴#a Tuf$ Q]+|d eJ*+%Icr{B$JlD&"Ref]۰)"pݡ5Ăk {XYjʽN%D-J (HF"!tŒeᖏYSk!/ItN"o%ֵ̬iw/wv&h\GJ=aCyd _11o&iX39.)QtĀ׊;mC$eQg46]TE܌)3Qg" d0UQNm;dθcPNFwT"Uoε㟫zNx&^|CG]J'-3L;1޼3Α3 ;&c7Z(; !aTC=oŶޜߩx Q;6ZRNX#uݶ/i >#Kj۶]")^Mz:dnpuѱuX4wu?B JnjY[#w;%%z38!N<s,z*PL:*S+VQ$B9U4eё*sH:(JSaꡐs!,cvy>uޯrt)V#sV֡EeZkJ7d{2.wΨ&F| {cvkX,PE|'ʌQ6@O|wbyׅߐֹ/fAIA$7Խ]~Z,%g %Ӏp߭<\xBF-kCMXiTMPEL,PN]R#;Ssa`j*F$B R򟤌8l-G1ũrw諺qY5]Xau[kVH`1_*=1\sl3~sKP#̝?ٷ{X_~37o C/Vo{k6j?pR_g櫛6CC1fQOړ? y#Yi.'_>Q{&w`m ^Y//SS(AO]Ma>?NTxxy-;/992~s6Cclnfgn>/2vߣ_f7@Yl)K]ls28'ɼ)QAfI*\P:cUv#{(--n.}= CAk*:ȧ DVk$F[0Fcn ui`ѻF[(3Uf3^˯ dS>6rdmLrjbI)B 縿J ЖؘЩu/߬kS %{X 9/)RY+Ěb-(_"CX8_LZڠnzy=}hT#]{FHt5 Iqx Xo/1OX=Ҁ@R,^z<.kPҀ|:3YnP{T I3+Z“-az!"P,*nܭ4|RbKOi?L\Se܄870&0HFBTۯnH)!ёaz>qIG7.*kf_jg}"CY_٘]OaIOKs"A7ҔJV8nu~GF-,0k3qkXwݝ! rMr SVVި}umS8t*[Ռ&nLU/[BM}%c$}mĺe;64xS3duH5Ds-W axg_zhƙ  |@v|~C'Go3\C~Bɗyү*PP-,B+S(3Ww. 阂p`yzV˕ S=-R#tM %ZHxRWWWX89!u"P玨MUgi<%% ϒD6`-܈skKOa?E|\^bFMwƹDW+ɞa5VLlj$aÏT΋ **pyq\$M8|:lGBZg6KI/zo]Yq s߻CI(- θ0~.*'ٕ BN+ih}{n`ǡtCP 3^GzF2M&kO``{ ^y( ݼnlwƿx,>q6Ⲡ=3zI~MP oVt]6wd >`WM]t}qgZNxc"[ а] u mV@ћ"rkhPG/+nb-J WSCo#|AЪ8KiYρ@ǘ8U Ċ=}"cxS-NJ%m5Ӵ8m[w]!%G @G[>ַd')H&9̮I3*/Y֗J7[mA{m~ۺK_m JUnꗦ"nL3}u_w~WIEA?QJT|Q8ɾ$DO5=ѳwa_`ε8!xb+5kA1; yQ2>V)|h{ ]ݷŒ*cLw. r`ú"w ΅I}~cV|J ,#t4a)V,yeW뉧SԔXuf!l *Ycd_MrogT%+g$WsȻ&,X)$F72aH~5TJnjA UEЎƝ1ϲ4[$TV<11=t3,SXTY-\w54_S\źO_!оGeDfNj:oT~kaQPsyW$˿&,Mh ~K'>kRvVm[kި0U$Q~`5iO|+uךwhV;`5 `;OKJ58wT(wj`aJ3`En~<&[12ʾ'ye.e7xqʕN פ˙C`VJBf a'EM:e[*ewרjϳ;~~%?1.W0K9G%P`AM )Ĵ݅8 >isO]ZAIS: vrQϵiBOr~gbø8={Ձu kҰp΅ʎ;5w2jM!T`4U 915MwGR`We%kլtW.73]G2|)e{DD鰿6⇉ZSq܍\`.X}l XJۨO֎P;le f;mPQEԾ>#r>aVNnb \D(#50+ K+]rry\'Z5 jX69fwdOc:3:n|`w[d73dciނI4R{[||X eR:8-C]0u.ӜՊzT29@Z G7%AuĴ$x76Gg/!3-ZjK7iv筧8f21) vbvK  `JZ^RvO0J'ݲ?Σc&*4l:e M@! {ꇍd sgwa. wj;\y9/uCE+əKgmlMk~0q|Xc4%Eyj {(@[f(&R}bt͐@5VQD4Sy¨Z B9%ya$aL9}|x4)0\b"T|A_ -iCٙA) O0N!mGhuʷMbds_Ѿkގ/Tx-T[pp2Zgzi|"Dzd[NH>nȁ[2.Epv׼D;0D&1mǼD+:a%x-V ҋt-m ptUtHCjF#qpȄ~[a S18=%3o FQlu"e&T0 rEc1Hw xK!{C  gBgX疔 &{ ȷ, íi RR'+Qn̘ GAvPR$} \SxFXGb<)=!`)6xy;0Y)[(/vϓHZH;wٯFPnjqkj.EH4UPDCj.9XJr1abW# Ck O~ND7zv8M"ɭW >I.:/bf be|tnq >O=#/ņn(O%3 M" =XxUؒ¢AΓ cݸ{u@bw꺚DwCHxOZAZSAkd]V.jdk0s^QR{F_eMpb{:EnF v{qˮ1&|R3Dcu _iCZeh ;#IHs.򎦢'V~i`~}cy^xQ)%T-,c]\6Gun&@PM C^j[H>Xio đ8;&Rrp}3X)J7mXP\.en8qU "PY7m7K]+pjpޅB6e9=[D^$9J$$uM̡ D6X}9[ K*S xLqQcr!rL<x6.EԹ )z0*@y2WMUAU FEwSPUE| ;LS.ls:c!b# (LL]sl E{ikFے[nMc0HVlzBD,,A1akmrɨ/m~Q Y#cȷ+}Yl8(x؈vT&R5!K:W=;Xɔ}6Б98Ǖ_|h҃*7_iv`]%$ .^" wJijH',"^#M yoUBv&'F5ɬϵD ,I$w`Qp~VEn*|z;e<#"<l2Dfd{@hW8.+Z? US9,Вv8ҩB_ᡣ' nmE8iVɨ⥸CjA84  d$(? 79PZl xaKiQ׶RypBSʔ1&5#opŖi|Yϕ" ^d#b0@3\uQ"Jw!٠W@-L},vqnPqrBA#XlT  %XV|pJ9@ 0X LxF CW" (^,L{6jM]ё~hږF ^:4N*S@E9nHSZ%s՗t (kQx]PH5K5 Ed)nѡ9@T A )4x GtG5I2$Q-0[0h]u0$M/MKg~nQ`K#! Sm~ɽs?pxܭwN<"RζNRd_l$<þCdnG@"6zF%:+J%G<Ӫ"V t |PB2Dl0$?Teܾ +m3)~GB JK]_AsF76F 8H7HŖטP bBA舻M /Bv[Fz#H-sppp_%, CކdHLoHrn|.R%$|`!|KK*ÒٵsW8)$V)'a ]=AD#7h/gr[/I%),YjN21pkO%:S'#FOӻOurOD+ɹ+:uϳOrLD +g%p9`ߵ\[W6|D,Sͪ纻cb^ ?{]~!懖|W̅&o5,4Cy;]oCg-S nÍ3XVt1Ѳb++&92׳ T2vM7)9(d2/J6oj+$MLD}jLPL*hs)=jI8-8# @X\´^7bxҌy&aƵZOCpE ry'q |-;xxs+ _':\U 碌ZG?߁;x#ïC넨)By R~ni̷[w kx=jkR{osrf7xc|->r b(-Yt#<*ּ`XI[>pi9PqwyO"EH/νw3'p\@}Ge:c'hbjv&nUlNA7{I>2[Z?AOZ(ӹ>#ivz6SpM얡IN6Cb!gI V4WV},M>P|Hw;1; vҘD)FBTs5ZhJ8)IG-cOz}Jjf*k8~P]H(J0*_)bI;UL<yk?,C+6ߊ"q@MI>KKp.@A uE}*-kķNi[Jg3VocwbX41|llX/֎Q=wDX6_m积E?O,oUgi( ,U" wT)|@0Zĉ!JVJ۵:%ӕ%XI?ɼYܚ`X/#-iٿs?9[y9梻w0F)〬8r_Am~Y ncҥ3[ӻ͇`0H\pC]'ܻ  hZ]Od=QS)|̝ ^056ڡi-򼛯nj L;1>6*=9Os#});Aj[\-]ίRHz^=bmA `W 0Fx*ZmkkMU /$IZ-HZXp=Exe 5\~ǻm$Y6|yBeLJJ5\㧳٣SVhْ#amv%QZ0vTkx\:%(]rO3tsb x*/Zn C^ I,)}F#;[d:{  y1assWY),\ ^R6? =N-On7< <`|[P2_7H]:jgL"ʎ9-xɠ-Y VKw<FYV}GGhj.K+$!Y|Q-z0C$jzN4W՜w%\ vXɌE3BWа]p sC7J.rS݉ _pBwN/ ,`@/p1X3$\pX"yLy/o \x]7(4͠H_о>lFX&z3$DV/WdG;]:NT4͝kCt߅z肫֓J!E~9C˕>7V<w0|D4 fՍ3t2}ݖg+q1vuO=Utu'`_W#ck_\5eKmujWImdV>Cɭ>PjcHRpܘ#c rJ͸ӵ祝P~^4h~|T;sg`smz|KmaB5/]0֏KS"^/E֓ԢyܖꛂvPHyi^AACmp\! * 3Iy6zyTƊ>GucyexJ) +qooVyIf7>\' LITۗدjsMt~G$;)6~oU}@5HIܞO\㳮^ܲZ'(UM-NÑLas"߼=MB- HR,r\h{7]}M|aTzzY;]AP #l%Ήjфb@==LmJM#M| obZVjw?`rU`y~Y}d%p?l/x`FX9N\(n-^VGZݹuZ> qf5*^B,^u=F %0>A3E_D!T{f`Emm*@Djŕ˛Eoϲ\n0J#U,I*uX=h+$& \gj륻Q[e)Ώ]^fV֩R5M'ons0ܻ]hx<3x`25ˇ#\i@[%3Ƥ"-HER* ~tИTvxPύ ZmNg,q)@:=U=thW\ks*&f78wѯT؇%l^܋=dO P>cƽȨ X:}; J?!yS1>luyl/fRZ2%V~ 3d? 7ф@+=mk훑rRIڣ5D P74<}> >`5ļwNx}㓱ȒZ~ph93jN6/+=opc,{RҬBU$?DM};fi@,f/j񇫡Jv2zdRkkPE`z ѨȪ&?9dë9Bhwlw "O#ϓZqNt,QDITn;/b~TU ) *ΨҪa[fv^U]R†Ix߳ e'kGk]Aٖ#{cH1CJe; '&%d^Dms-j5gX0qM18#Zv2V8Ʀ1-ҁ8KEVG"GGJt~GӦySp~6ݧC7`Pp2CS]ՒwtjAEA~Dt|^amo9tjS6hż|Sl4H|Mug?iUdSKRwV*ӦnUFԐl6Ǽ8D++o7oiR ^dp}Nd0:㐊h1߀?г'낂e#P-Xb\*8Y~I4m4.Yٹ!/{V[mς I,7d |^ x܈t,%j+!; ɼ/# B WlrD}bL9ɫA@8ǐޠ58WxļM he"ܝA*tZ2:nBsA);Y^O<@|s ٢,VEyDS΁k61&3CM0ĤTKjQ즢ȏ[1RS%%.YK7uTrMG.a7!J6\.þ9L#:1&W<⡆1A:8Xl@D9W1hbp`zY#9DëO{LJaP'orHM-xNP4 %PzgH6m(тKW% ZN^7=++l.ɢY'| NmGT\sp9QgK㫔nj@m*ߍTTP^j %9;=Idkc/'G4֠KLlVo$kF(ao\} 2Z{x_d,tхHS=YTVd网zrdf&p] ﮲?c,HG:X3m1pݷƼw=GၕALuRa\t B9: NO}ӸpS!#+|,y("y^(y/.ui0L ̷*X J`Np) ־lJ"ePf+n]F .Vvy%(4r\kKӥ;K.-Xܮ :8nzW)nT1,$vWT}xTsU!>ƲbZ 6`@|\}Kx8$nʨ;}8~n"}2D}jX(N&~Ꙟ❱Pqn>HMt17i},}-}- &Đ.AyŏAqWU䮞=EU_Omo n@ʏ.#r+;!}TSuko\| yފC%B'fˊ~*.&]t(n fku *3JD:pAٗC)zP/6.vʿHkzըG}߭C\)eQQR#\9aГfɪt cZ|{<7O`#Sš6xy8lnwfMz& g6.|mR3İb%S&-}zG`PDF1@%&ɝ%)Laφ أɨ5+9Υ )YV ˫T}geJAZ,;&.WI ~,0((ƼOO V3S)F%Ut ppU|E|y(sG7IR-ܻH;V+ikj\DC*C9cH"?3xroƱ1#(9RQY2sNZ6`= LBp`"P`X[Qh^q1L +G"su,A},)[pEPeX6ΑZ{8#vC%4T2 ى颌V 9@Z, V#u:ڔ:~ In4iE97oyi5"F@."Kxg d8LUJP en(YU*H'l|^kgÂ!au~* /T_#o@QRFG:o(Q6L>8'& (&X&! %` j˴ȃҳjz{w9WlR/׫>N?B{V-Z!qũ3){I > v*ФeOp+KU`Ҿx"޾l!Eguն!/^:#Ηoc 5,N)]37K#'kP d#O5Gm+>X*M=nfQ 4{@yOKc5D,4}Qm{*zXM5#khn;RNlaiLVjp'=nÇ|qаWM5W{RS۳`c7} U^Į_}rYD2 $vM{'o=xxgmyς:FEA_AF4IPY.(ygB7aTz7&tuڂb{S'$pxa6g-Dt!:Y /x׿=E@>_Bξ;OjTǨ95SM˖ 6m+?=gE|()ey-ym{m`z^yOyk~i[%h`ق" 8>=,D.h B̃G(&"^S>b eIF2x:U;z3gNFa <.%-eVÕ]!|ev~_uLC#z{~nxxBT%hقsy"9~z@YuMکψ3\)ȣ.B4c}|:qt;<.Vqjٺ 6 ^ؾs% *Gdu[q[D8m`p9^Y+}lRg)Y48mf'3 tN%xw:4e_)qFjix v7ή;h*u_FXXs]˄/\\|J&iHH6)z{g/%\ VP}-9xfm0ѭS\2EI:I=Ml ḿdY^4 і \nP]ރF;7u#nIɀ1?,{ᅙ.V/9WM;]iꏰ8.qCeY6iZQT(L77|BInˑ{SۚԹi^O>8߾~>^j`%2U. ԊƧOP@0 Npۢpߎ8}mT IP=_X5bzPHr~!8&VSp$kA!睸6GJ!7l2*EO ]JΖD˝}Y%(bptJZu涧5bOz8ÞoA~%\ah{0qT\|ڢQsZ` ǤNI/l \4Ad| ~%7Ս΢4>W1ׅU9Ž#<4*v.uVX+%Lw->8^ uUg Bph8M/ @FTgONCĢ3%&t#N!R2=Vx& \ٻso4x)x|IQn xdW64sݵ;7YeJC?b:ڃsQ>O^rH$/j-hU *Zޔ7~j3'C:u'ڋMf:9񀿮kd0QŏN4HJs ^C}Λ0J+)0`Wڟ4Xjp~S^cnl^>eU)o#xk%_U6(p6Eٯ->΢tx@{ZNh <5v2s3%&'3EsGm'eW/;p;C-%dWN:YuwmYtA-(f| =s8lL<75UJ?RXt?_ez{"`F/ @V_Ul4<"ܽ !CJ`Iϒ:_ݽeٷ3{)$aV̈́|l:n"˭͆ViTʭRdR1n|=_}cPIX|cc4 E@mVC~O^k:cg'{)gb0*T߳2sp#t,S'l;K}6mue*: v?mc'xDD:p/l8Do3|C^Ƀ' v*6v9BHF?OДղ~ߞljrV *j9[ WMO6yE&8UCkAoKiBy~(Մq_R"bцkM7S/Jn($ 48y6e됤 , Ҙt}c)&sY[-Ӿ4-`\,͟@TT^f|G+0UDB[yWѿN,iyNo!OqPՙU׍3#U?3g[jI*TOŒ[߻lR0$ռ(|8xq4 6咐qr*)ԡ1SߥvJlKxzʼno[Y%5IbNC]_{BO<ʂ6>IT÷[&B=Ho_ŷEyA2eۧ>,Z@M&*+͒J2ɰ=||/q$EHQ \%}yvE/R+oLiz>xrx?oҥG]ZGyX!fhlL}t<`;/sW)мtGm9:%vJ>zh奴g`MnRɿfI\S,P(\6OpJLWO5&i Z[q7+!Ү9ȅY%l5$v&@3[A&`owcVU~qU{F 6`07zÒ9ɋCBYg oNІvߎwg r'gQ[HWdj8Ј꽡'bUgJm )2chC$EQRhJY9ɹ18؉ bGX@@(\B޸1-ƿcP{evJs¥Jm.Cۤv;Hr7#?Qe`olvWCR)?@oJnѯDy4~rΚ??<_94.K`&"\Ae?Ed`7.5vGSCO(9 .-+լu7{|?O'`6P,pjюOwM3RF3s& Ҳj7JќE-hGվ삗V6(e] T#9p+:wNAfFh5X'$P0ͳT6 QrgdWڧG)(gUBQ^p k?Eq\{gdm6t=EZpCv O0B 4͒ W M^ǟ ]+iq*._Lqk/x4z(bU#%Ww={:--C-FnCP >:69gj/%m"r h[AsK]Nj|F9p6K:F"W鼌Nӛ]:")8i6ubK#N$kG |Yw6I=9y*Q;`_<ߨy0OELEȸv;jk*zg"yhEɭѻ~9VG'7Mpa##;:.GJ0=5|9I D>d.}{'++wMC׻oRиACiCL[ \LJsw~$Ƒph$Df̝U |nF槼:kNS:3@^xoL׹,G Q-tBdǂ:nkl6S9o"0[G01-}%2 Æ~ #Eے h>k?"o6g@F\^Ɣhh_pg^ c&&`9/=>j= 2 k 38&VZ21P^7Q#ܗzkhdgǑJQ&.nl+y%, dЀ+k>3cS?'4h^EXȎ-^cd9yc JUT'pTRb'Zh)s=*!B s̵r]sGﮣV7943T-&OPnWȑdy2fԃk2ƛ(^D%$p ^Aߊ"dn,.)Rpcj&r]$PreN7U9N`a ; PsßT d7 4Z$X;yV>z>AIURfCϧ{S;7 HMXn/a+2hex?c{p+0Ix:~N;$bEއn)vN nҼM^4a> eS վBZQ/Bݰ pLWqsmaM_`lVdu ʓ5yHyQ[u@MSjtp4־@fJM{:;Tx'9#Yp@c_j ܕ4 @ {$lVPhgq4q4n6acч@o@Pw,64h˰ >%,\t)jJ=—ʍhSB,)وBVxY_.W b[0x2E8Si3̈́XNϫYcK*=*֮&yDo:`9bg4LPiF60yY+ VS]DhH?Z8Wб RwTe۝|lb'u?][(J yokHՍ "@(3HG?Q1MFwQ6},r Ś0i|\Q6& HeK+QUE=SlMbPD8 5/2m5w__4~lHQRXA 9B""{i2-N#28'I3ZVEg;ƞRUHVڴ&`3DyK#|N,g k'uJ_{EW]sU=I:zEA!uj_m[R<dR:x=Vq:͇2m1 m%p m(Vg Ug}8! p$=TN<@mjbA*z[7 +|eCwY<3WkC C7_ xZ͆/Qx olTcYˌ)m-~6k> #BG0HFk5)K~, yEC4({͎2Eޖ t]W_6XKBҠ g] 4@ թhGQYLzrfO4W Q3_M Mh$ ơxˡQ*z^Uc>ן&qPJG8>BjwZS]߉BGGZ|ܒ\Y4-(iUMS^i(hC#{V#|&YVH>9vȒG {,e#<5 ڮA/S0+(1S'xAc6w(3Kmr;{GB|1nC_\7 "$=ϫr4N9$~_]ъ kc0ѪM' J&`)peW|[3wʍmbNMœeUtL*>)-.H[./*;WvIмxw򫮻?}C ռA<PZqI0:Zͮ+;~\I wgOMc};|v*XZv1`ؚlbQuϤorǒ{`h?(N#iW'pr.qf8ߓ&n*S{Vl5 j:ˎH[VƮ{q_d`fPWV +ne@YL@>"&`iӱvͰY#/vXXg HVŦO)Mb Mx̸a#iWuhz5R}-Sݠ{ٸm{xoZ9xA bdz/M1gY& Y>i0pص`$szY^5!rIu}sLC R.?U.˔t9ꅌd[OL%Y% .8Rgퟎ]ZGsJS}w2:dbG#HP\Vb5dY_⩡J?Y:Q#It\bQAQaUNUz.z<7,Ƀ.Fݍ}&kVʠ/~zg{jcwh `X=˗e4$w)X/}|g$) vqeoD|o?H賺ew+* ;Dk9yf r=c~\Q&q̖{Rkk_:'E3RE@že> upK'1F?GfrQ>&0O/ñ^eg|}?O 1 TwcWUڜ;"&DP,Epnd-1F>[i@ tT`(xA]G۱x>?_.EʩX}V>5qU&X BwcF{=~+"9ȑyn[zkU4An! c -IvC߆:߮{!{O;Ы^ pM.+X]}oib'^-' ޑPF=-#XC:}g1۬rF~h+ vIxc[&yb$0r#4 B"|'+IS\w|4)y\2;H̤hP44l, l#/Od"q6 sP7)k{@m /Ps6)5,eW# V뀭/Xt^#tp)@qk'_Md 0*t` ON` - h lG"w|v/ 7u-]1&(Xw [F lT*zA+6G(![MR-v SHrEz& u`(Zr9%^ 5 yM6cl1M[@eb#6Ue(B ]%0k%5uߗHm.sbCc3u}6UZȇFۊ+6IKwm]PZerj'=Ch%,@y^=5;a"9K0֤-lK<%Qz~1a[q2H]>r_4 ~W |q/"4qޥbcO_8۔/'LwRGD5ԒeQC%]liBH^P~QFdGC A|ad$li \^}'UjVC)=jE6Ыt08 xǫ+zL<3ԪU9 A=;$5MjmgC:l7l؅E (OIؼsW O$CZow -˕k"q., 9Ρn[qypTCKqZb-(5ZV]]'삣};#iT|Y<ޢ5H'%ex ϙ7~H‡fޞ~j,49\J္pNpE~iv#Yܠ FՓTQF[#Iid8AKxKit cE~84y㬗2DjBl9l;3%&B"y[O\-IEO~ir5\gzqi+ĥ"OpÒspUU"|զo9TNؔ|?@7ĆPJsꩩ{5.*A6 uLg3I>"$,ÿ82<w;ٲBЦvۨnf% M0VnX@E#0Hcuwϊ{-[()Ć_[|+2\8;l-gv1Nv3 LYQIr~8#1twb "ݭ5d Kk#n7qsӁkuvz%s[[&cn,H1@NI3/D vGa،0D{a^ o4~ 1)yO7\֢qgLVJz+41Z}v©O䣜b3AF<Ps'yI0,2g$J=:z|W/4'5S3`xTn2<4@tk'"쾭=s50],X?mTN nw|9tI:q[gA<=zCϜӲY[,MdRu+ ,y2)'+vY4 Q_׆GvWcv;ě[EE3bg햚{.<jʤi䰱;]nZe0>ʞ˿xO ܝ@ +IGгkl*--HD?cxh hz;054dR/"*=ʺj@LXܲJh^Я?KBLX!? |ȦGCϹ[@[:/=\~0iKrgv Yp_+Jgڄ-U4vNzKj ʹEmr˕kZ*Ex |@nN%V+eBZh$jɰ ]ewx\-e[y2mdž %yIg #&g)Y|>=- ulNheB܌i}X:*M&#a3t+cs!gy+fUp O zJh`;k߯ϣͷ$"797VTPgB@ώkѣ;?m)@4vrjIRҹtNxʪiyY=D93B|%+܏me|r{ Ϛ'z1)pы2%K vEB ߃䉇ꍚ i^h ܨL@c-aUoK4;@ci;l%\(zFnݹ>H޳}8Š%!{HIol T/78IO$h9p,`y%J,X^⻇[. eLBGNBYB|t0/$"`@iUrdJS|*n}_I(PY4+@[#._$%/1XKRI+R(8ښ\ޛ:R~ĿR>c`5ù$-ʜ[h5ae[~|lZJf<\o\If[Cn)JI \=EFTXncj?X?`o)paOYh>APp|KX4?S&.?NU)qf+JF7 j^h)&n>U UƯ1Dǀ[Fal\A*w'OH,gBkʫpbT گz_2 @b/zl)g-e]In4:EfcF}=]}%D,]U:?Mhܟ?Np|@.[nڵMGFT_O;HUľ"4zG{|?]'psST=p9!wt|V헱91FSvDH,eiTz: .Mu06tA17y|yUajĚ㺣a'ic6L'6aH_sj@]7w}\܅%<8óT̖gohF\kO|-!u;M\K |d^ڜ׻`ZGY-0/ .7%v:ʠEf%˘=o$+Gf .ϷH^ rx:v}.썜$+qgׄGLKըɬ7+%z;?)f3܅MJ݂>3b60A ML2>h,I/$@U:v̜3hoEE k uNOF;`}i Mr l$XTAM A>Q_mKV=tv XMyrGxDj4I~?rnfK@c En\[X|l+ۿzWBВe !G}") a1)1^%CD>%'p"Zc;JŔ:rbj5{}q][aFal9h.D7ϾU:)11]_=kUN'Kl_,#trN3u4LJ,\;֬RrVU_9t/#2~Om_Z Aٱ|>,|%{,VV\WbOwC]**#<[? ih멳S!LB)Z58GpUD&v7]ЫJ ׫)׋vhB3TK$9 Q>V7KHj'ĕZ?p`hq=cDu:Zt=.Y=P>մ6ɏS>-@5aP$R?W].Rulc ~EEt&&à^Xfm&vGcp fOIyE@Jit?ED^㓱 xߜZ#11bV[Qb>52JR*= o|7ML:ųڊ; ai@v4A:8-%ŞnHgUb'K60k0 T /@f Jd^z$^~yQY0,l%yoѩk6\=etok aA@CC<#{ac'=h.O |3bNBcҔݥgV{n!Hezɞ aoȒhnז/}TXW3lN]գ$ORlR鍏@ୀN aɻ ,Zetzp.}.3–J`{J;evy{ӑbU [Z:oKp::nՁ e8/ 2ǚ~Re?~ :Nmh(iq?:xR:Umd.SrR# AqFtڷ f쵔) b7+:k_g35#=z0?Hge &`#8 3/(oqp-SRֱL{bȠA)a0oʪYŠoȜL~3֐I@i]ܟ|ݡ,fWU\SQmdIщ=5|'hPZ&wWiShjߚo)FR3֧.e{AIzƈH +Bc_Zҿt#su:Ǎ=:!\#£ܟ0 u1)p宥PpVZ}Lu4Tki q^J}/{R|7JХ\$3eGtZvu1?qfü:vtQ}Xm]U|W]uiG ;_|F=j؃°ԅX {7Έrcޚ؍v'R~$FQVTQt)8O:52^06B3~]wUЭLu<)PU6Vi| SRQ_8>W(>/| _?UZW&,gH!N(a!1#EQ&[@p#TwZ0SrBLS(VFqܔ:t{ 3eobnZ g_W"dthRrt ۷ ܌sYE凜n6Ցי̂ 8XoZ/:W6?}. uy73BU#&G27B|XE?2G*8=k͟Σ a рI[tu2Ϸ|4{र~W|} N\Vֿuu)!"Qq|sKLV & Q~&\m38+# 2"m`4PQ}+ NAx<ת z\VgM]5B`Ćڎ hsBa}0q0(\xj\Sd> L)\#L+}~_ [TǩHK~YSH5ifk;8) ~>>׃S$G&`D7P U ؽ^I2GI:ek oYjSs-v߂!\dŗjYh|(w(}~,}V^Dڤ|8+44a)X idlY\)(ڠ₷KMȬjfgbW*W0Yu8`xۅ,,d ӱd-U_|&3UѣUZR9ĭDCZN.v!mhSc!쪞[',⠞ D; BY@(L˨@MRw:<";25M`oqv(fעKGIFE})&u,hzH7.͘FP45IS3D3|uPC .N:'y!,izؤN<% x\R6{~ a~I!@gu3r#SY> @g1rdMLǨ2U'}M(v v(_24W{Xj#K <_ h^+<HDq\ &!QfTpE¡Ʀ.C/%!RP 2kujɛuԠ{ _hD-:6#= MeTZ~l/{/*GQ(ԓVXѪϨ-'` RU^L _f$wSJer]sX}|WO6t3gan48s^r]%gK=d&'"n@fRqon02+zd?!?&Ũ+|m'wl&9}DAoP wؿkmxŢ`_};߽ۙ}zbv+/4ep퍿#^#gDؙgne)5(U=󣅩8kPC~hEK ښOs]ē jiolb *R8{PXRGmYtigߤ~<9ڌVfZr\Y0/kvCv|}}v'.ůy`@{#NƟVˡ,f)z5B.v'DF#Fبׅ{98 !]Ђ#p#kY7/)x5󶛜ę٢1VaKb /|#_ .C8ͼv斺ɅR`.BB>Jn {*xzxJ9O v oq`!GV4{4mF6P+e8.c/#JtXE:g7YE3A8*6Һ~N1wK u~+qa~3DgzXO9$yC`L()5pKL%zL2LIjbе1Š-|9 U/;yX_]~oɲUKu\_]wN$f ζӍܿYK#D/n!RYW:3^9X{d}!=SozJrO kj͈3dޟc!PMAIʻ375:mM]|% D H.v?*.j]K[~=fDdMiR*҈`6L8:9+Ptes%Oӭg6]j؄Qz5XI|;Ђ\ =Tj] N9TQb~yotsLN3xBbնVaF.щ/,JxΤ%b.q~鱕>_NUh zio(z[6q>D[I0 4e >~2aݪCWAUYhm[g;e7dEqE3ۯZMYn` Yv 뫡78%k\7!%e(Oz-ab 8ٓݫo2#uE~> Ϩ[A֍Lϲސ{E:i.b 2GER0xa= 4Ix[bp酙lgvN/IdH%sT"z yfw? 52Mt-u#Kɪ1Ύ;oz:>)VCEߤ-?j̋glv6JЅMڌgiy+0Km{trP.PGMY9X.K/0B Ld%w =85|z(}QP;Ѷri@)o6 Ҽ%@R@MP[dBʱ[A~i8TT/a[9݉=/8Ee#芵UZrm. !ِtgh/DZLbf.p⃊[_7[Yi06 P~1 S5aM~dh2T P=5jz.jeIEu>4QJfފ$oJ"KTɾŶRgۥSNϱ )?!* nL|}1aeA/XtwQy>o'}>k}'S(ǦMCF=T%u=8٭5|[upq}I6uC!l%K0f M}ǖD[}0&)Lc@ L oSKXbϚ)ہXsӠf)ݤv۝ !n#w^QOė|'Lx)TMjQ;R~gӢ}3!+W~a1dxGu 8s%+! z —Lz2`Y5dB{ɥ3yaPhO9 BR'!h/jPseFj@ۗt;'sǂ"PZ%i áO\i ~h^ /`jfLA):J5 n_(<ڂ vD YdFtW T:,{DBh@ aDb,Ygn8 R=oD6^5wn5p(;RH/knKAI@<*Թ:-)]6?"(p#6@hv1c.QMгA6vGwaw{ K+*z-vy7!_MiL|C$6( aS.u?ʟ:~F뺩9c#RSAqVsGGH27%-ײ;ùdx"eȊR ڵڢדx5y271\{ARx(ta4 vc$ۮzݟWrބ"J; c.ԯPLL=\-vJ-N%N%pLSAblvemQ4jYblXcn&4;)q=S>ϒ>HO~ʻVkN}>hRٶy.ډR-Zit<{W#3Q|Wwey*rW`Ԭp{,wR=ļ4!9UT>uqU D pëWݜaO@'X4 e\"Av?8n 77+ <4f܉2hi{YXguO԰gp{/}Ĝ5`P paiaj=9UI9d> h<ƣ']cPsM91*Ft ËVtD_JSg.P^? `3/fW+ jbjK/Td?onujEggdE8n*T&q77mҒ9"ΝD6J&-h>N_ZVD`76;hh3+H)xg'*Zi5B/Xd!nSbwoY;/إȒ&FR{FF` 5 6Zn ++P!KMgJXĹtDVtXI8܈A]v@Vu_I>*e(+rX$|iXʐйC-jg^Ce=%N>]0 I.]Og%KuVikh<\{VjoBbژD)ý"Ɛj8ݴ\CXĔ-ࠛgvj-^i%:-$o}&Nv:G9\/}) '9,Si`%߯@'eFk)m&@!3$1cqwyr_9rD/t&̯e|5jٝKG8%(KQrCf.)ҦFŷ ;'@&t] 1MXRw+S>nŷ(Zޱ.SU@:YexU-oI>&t?eM) 5&-,E@sHX??śy$A^iyrWeA%gk? | IC3-qcn[v%TxW*brk ~.OIBz*+?\G.r'#PW Y]8% CBS5WJqyYs>01XP9%W Nw4vz ~B`E7{4•̣tl#3|Ya&]%8${1ipgv=VśҴ2 t1 5Gkvx˭|W.WׯA!|])"8v7RM0?iXC^Cߢ,[011jE`"M 5yx;uO?[+*xށx%s}KZq ,NN70&n.΃YoZI *jVϘ0 y8pzJ1/ ҡfB͈gy! l)=f*\["]`ή!ti*"Qޔ{5h =6Yd֯9|)u5$tFm^}0P |٩^jtJYQ ewxZUaOO Rne7*yk wPz{ZLKArG^(D38ai14|yV $0Qr4@X[<__< t〫FV JTӰQ-tEk}҆U?YN:B$W[WA7?mv K::Ln'}?V{zO:fa?FNrD@upܞ3_|C^2JTXiM5C&Obh9Q;.xJ7P~8"fS5}.|IpVbQW3rZCE- )?FКj0 .tt Z"۪{ӿvG<<~EV 9P$4J893B)4yQFTeX4l A{R"sf&uڻ ٘UҙDǴ8 vh}BG{NNwGcث}ƈ. ځ1je8`\GQ'[zI tqE-#r%*G3{q'aeOOuʅɃ킡:3mEȷ  ? ;[ws`B~:v VqԘ6\-Y(`J9.R3S+l"OFnmTsL0X%c?AsP}h|"ލqz6Ɨ 7ODPJZɰvn/;V4ȕj)ɝ!3NJsj5I袶c^ 44΋M)<4\gE'ҁ3qx,ʭ9{: }[ tl913㉖"Kk[n٠oy7Σv C;-,vbBbDn8ϟ*WڵZlW˖-s?yxLf/zkg0Vhj)@va72)$ u͊?t #l_!N'R3ͫ@u"eLt l+hW|EQR:A0֭Nd=@RAcaN3iڞ5;YigC:,w\R:8I< 46ߥgsC8㠅Na&UV3u4r%ڏuU9fCޖ .QԚ^eMY /Ėw*ɞDϴR5[I!PR۴RǏ']סn+[Ԥ ⡒ `NdzTFT Ј5u#BZL +WƛMF)ACldD;^%L=zNTջ>fI %|#.4ɣf#hP[nY6YxiF"x3/5eժ?ьm ӱgƹ(q.l|jCȔE.\YߺE#k"<g+rwgi_qy5yC> <@n@@ڞL(y'XY'=!kyKG!a%aӰ-rO=?6 Aź' }㰶Գsۡ)X$OUјqaQSy~F⥿ VLBД$]Yv_gnn0 LŒJXdOj{(bms-v`e"݌o%;X8VƴD{\^tuϺ_ Fgrg.1kxunBoI!K&6j;߈ }WukLb: w#r7_X ‘q_Ong1.a>3:U7c٪,Ku};%>Gt錡s_ )UGRKu42_9 '4*'c"97Xڧ Pl/Q8Z@Kl֐E# gQj3JAaS%Mg?-o\r]q"GwD'Ў6&R){%.} fƻ䱊 - Kw-ː.kI-ӖxqR+Rm:;!-lNMÊbS´xOLY}V $!=.!cncL;I8óFUg=8iw VwcVȫݸ;h!m@!%Fc'X5VoY;C֛^*bkmj;a? #JDu^1J3c-Ji88eyQ͊rSq%3q0j>bpwrZs'l؛qsxtXP# ilqG|$|vhx7Bf判i9F+umllnP1mmȞrJJXv'10*cȃf"ߥ ekT&|YÃ$]ū8G^rlV.|B\&B":YYpyȻޙ-nh]P 77zINz/4_R!?ӭ@]?,:6.>ՍcVMHĝ{=z7}4Ų}Xɛ}mrڍOM1X{oY!F3x_ѣ\W%3՜r-wRN<*^۰M}6ѠrʴeRf{;ON$Jm%Gx/K4)EN/5+BuIiX8etfxnSNz\9J ̛~J6i)}~}$N:6;) v`sy aѯ 39Ck[۝Na/$tz&6 B.p *z㔼<ΧN3J͜ƀ2EîDFL򕸾^qY"LARvx{Z<)/Rx6/z2 CX=C/Im3?88dJp _[vm4L n>'Ksu=G^Ifzqg\bzyxA`ʴK!g~a=hEah*"?m g2izdDz1n!4c nIDF3g_qASʻZYH6(aj4 a~$x2BY0MOӨ60N)o#U=Oc%[o BČ}B-C돭 5;} ;ҨL9ΓpbN웆yu.o3n3SWb!K0|S70}w(jtc Fy *(CM]%ƴ:"+=MG2C]3띔~k(+:>.]qީyЛܥvw+sEJyfa47H#xѰG\yrkʯܰ~g*`+!cl90{ GPo]%@J U'RscVc,M2>N1c,d $/m)p=s/3N9u$$YE/Xm̙Zbn =- lX-Sb絤szHS Rx0&PoIO-gG:82$d5K^oXxXY%XKhF_G@?v q `>mzgN⑝cWjg@p?X Wy$%KSi8Q-r⏝EaOȜjfaUuݧ;$8)t ,D @\\lvb@]/(-[τ+7_YuX[ui)̀[79i6GCoM]/)Zի6J*fTB=Y/bpWRpc2yOҚbml'ǐFER|M{ռfb êB a79d)0¹ xp ,EUrٌ'sʵ%;x4l(4L8浕Jx? oq)in{xXq׽7(zQPm“c )- HnU.S~bNN4&7%"=@> `kkz^XdҖZ\#agʹn1+#رZuN^mԬ-_8A|ayPm'jL;K% ^!3| a,)GPZ{3WsR֊H]60zt{^df{t%7~?,i&ܛzB措)CKd0lE+|:[5L'tlzU+T'egWe8T}3%E=@s%IchG@qFA614Lt%\8m)8vOy%:VkL38Wܲ:tb?.P@k5@wI!a--0l~mJNmsX݆n y@Z#Ӱ)&X=C.Я8r|_.]^jEe]?`. L<je"Ѡa!^K!I֤r Y*KH4(Ez ُ58Z&KP-zBve :S8Q]f[u*zKNw x|% i}˒'?$Jgujي{[ :>C)ᾇ?.l;Lk_rXg˲k=~b'u21~`EC-nN~nxNۄ~ "cZMVqU"vui nJ&w0STG{@ k:ۿntW*,`Z`"?r]{6EB#_jOtUhͳ7MJl)VB.Z]VӰ$ډ "M;5z sȐʖE'R99 L"x9k3q<9WS6ċ+%KY3>Ѷmw鑰9)5T@86; QJ.KkX)>42^"#Xt.; "nC5.uY䁳C;)]EHhxlj;.>fgX?ae7뮻h-&Q1/Fr=Z/?gΰKreW}h4^5*Xr ps6Qe}RʈWIqݳOo2>i3G;CзM6Y(y_nXBSrp?e6t "FkM`yHyZ99^J`4*61+臧{G:ErS"wZkJ‘v^.!kWYu4*FH@*/|xt#q.D~".𳓍(_Z{#]~sJPF1BH7hGWEKa;ZQ,;XW2ȟWR.Z5$y&1ZK^3x!HU //1w_:C>L3W;;`35=qi+CYי@,jdx.3``:2V,lcj(Y4@m]U|(u-I-?"[z2jdп*XY{Cn麂2|mYW|Ie8k.~?N\;nF1&1]j#@8qFw.%=mQ+WIᵯN3y?"dX!3& Prh 9jǸnD24d"쥭@_0IawA?կH3 ep3O$WC(0=!Va1nV 6Xpwǵqf͝KLl$Z-|,<ԟlPtNZܗ,FZ$ԒNR=wOjcӰ5>u7:T='xm5Au'?H/%!\ьJsPp_5 7n ;y,8Y*pW#? \yA=0 c=b@76 D>EfV3C?`Bߞ!PaJ zqX}H@NnѿvR:k vpgD"Hwݲ4:g} >ɖxoȮYa8uP zejᅀEb V }7U'+O< ;r#T7]Psu?UD;--V}PW4q{GS_%̷?1*/w~C"ˍXxf@GZ8Z3Zj2/]P#cճB>w+%nWwU#>(@(L[U񭷅|;M픠yFiIr ʝtݼShmՄQ0a F9‹yb dGL`8Jg+{爹Kei{(.oӑj  `>/~Һ4Мwt9^Plln7L8D@8i+SIz4*GΊ?$;g~*h.JgӷuOoȡuM hYrX']w~4`c*Yos_JZ;:p,l<,ǿYb閚p}7:jqJR* >Uv-_ϑԴRdL*~j܅`l+N0Mx[-0 hKIun[C>pt۶worbc1=nm#|Wt"q_2O;:>$?0x˻$m^.sWk}H&ůsMpWH4B0:&ƶ;kЄ_m˵\ZS1{Ep6n_!v螶c3%8]))zP> Ј9 >APV+sxp 43ēJi ^˔nT޻'Y v[c#wpi|tZ (Vgit!? s%pvm{|@M^/i * CѪb Xݮi`VZaQvJNUP,r'\*Zq; 5ݰu쵲#A'7"%c2 t4>s7-8^QdH8NkYdc 0yOrqIY肆3-&݆àI>4Wfw^k)K+ehd(R%w6 [NlD (X#lKAT|d%FWx|!ftedOr* ZYh 3B6:sN_X R1\˲O0  -}#l4 FWb-^hzef̙ jMoYU߈wp ׏o1O=]qq˥'lglJ>.$-4hhu"<1 aSuL!nvՉp)ե1iHT⃥; eL~c4굏yy%mvr1ߖwLJ<[yơQ3-J9b f9 T`v"c/brvDa'砖wQ쭙=`XtE=PٜCf53꩷щoXؚ *ۖ7do/`X)X ҵeM TB>+Ii `Cݳ  2rĉcik˗ÃR*f, ,hC:rmƿ9Tf 1lHpjy.zQ8j_1C2)3óĶMعuaģJTDJVOZb.B刳xg15 =nN)V5Fz,4-zZ㗅UCegBeÃ2{oyg{̣3MpV3{YPI|OL^ɫwHb膊)[ŌO\]v7 3jEvmF8w:Onٱ CZLs C߀pe%)KZZ LLasyߧM<[CTf@bw -0G| /k>40꧂Qoj&A 7.pg JFZg{YҬ,u\zDCiq1oۃoϦ9v7Bjg7.k!=KBp]V[uaVК-?RA3/ba-}Pw%S~d.}L$`P2k'Inu57zjKeKVox"xލ9SjkM9:l٢Jױ |+y? K~kPy^sx-5RseJ3c;;7K>. (z|F/cMLa Xw`#A s.(^p=8g^8Z7˴]b.GI/ؓ mUDPix6޸UtwNh4C/E 'hd 6j;y߳|j@ѠH7Kuf(8t?)Tp!Z%z? 8' rΉ#BLa^|pE01=n8zUZO"҇]AKT+msl*^4FʊTVA0 U4Es(/@]^Zv朎k/[|1!畾RLw6.BЍf)RQN8.K-a9x¯ jgݔ;m-jhɷ}ե5HE1"Ǫ ɷAr+h~La("\35aDVib:o<;_{=#3$zC-x}ޭ%vF jp֞E7j4o5;a/\c5>f$T|_;qm7Zʭ Cd/Wd u&~b+VOQ)a!` Jծ|"w4N!{.f2k$uѳÏPNLObOJ0GψoҒ.ա/3n&1ctYI%/Sl> sU'a[ B;6e7ǦbĐl5OMd]h$TV/=Mڎ.b; 32 J:7^|j )2F  ]+xC&MVٵ R&XPqbEjk:on ZSOb!Mm4wGid s/7[/fV70]R[!pƼ}U"!VC{\ӰSSƴLk7., Zr@* c_Zx7V ć a-S#@[pxץIAY-Ll"3c='(f?ͲJfXV"\EٟZAX?Q7mC щxYII 3bϰ),aCFs~.?SjX8:o- ^kaץDſ;Ur'k->l.pؗg,"< ^g]P j&k8ƣrtnS %љ9|:0 A {]|}hz?g,LVRAgw␛9cK+]~ #B80K`͘.ɎMm7ee:wήj2 3q˄^] };4П􅶖#$EݔfPG?l <<%4yc |jQf\ȣ}U .l Ĵ/I44 x{ PACuJ b0[U[T/Q>$ځ2sx7K%44"o9|`>fy"HU y }wՎ3^PZgÀ)xIp&ѵ }tՏ72J`hmTo6bNK@Ap*I< #)x#0 fbRP]P2n0PD#¬6ܬEep}p"@UϬ # m~V+C|)3JH9[$V o7/!(AÈ0Jő$}7pG^XXX`|A'&=16NȌ-^x.Jwka}= c5( CRJ7tsib߄ u,3{3[ M<Ðil&b#ĖS]?[Z6~C=<>8 +Tlfǧn@}wەY 1BdITZ:+a.$x7;pRT;: 1febxE`7T?0HRG^) l&F_KKK9iܣmgɇ‡m:>\MvkiC4Fl兙s41Nc|A3'fц,"0WFmcқ;Ӱ"7_Z|؟ 0 '꘣(p࠺ρ!F~o/߷g+!'<*SY , ʲ~d05q%3wِ?"Iv?@>r9 .!.WW)! p~ F : @)f~frV,i'n{zf&akqծ ˂DXs d;hAj|9# H(DH_@NB4