sudo-rs-0.2.5/.cargo_vcs_info.json0000644000000001360000000000100124340ustar { "git": { "sha1": "37175bb219dff4de25750a2a97cf89b7a6f2f47f" }, "path_in_vcs": "" }sudo-rs-0.2.5/.cirrus.yml000064400000000000000000000007411046102023000133360ustar 00000000000000task: name: Cirrus CI / freebsd unit tests freebsd_instance: image_family: freebsd-14-2 memory: 2GB setup_rust_script: - pkg install -y git-tiny - curl https://sh.rustup.rs -sSf --output rustup.sh - sh rustup.sh -y --profile=minimal test_script: - . $HOME/.cargo/env # We skip a couple of tests which fail when running as root. - cargo test --workspace --all-targets --release -- --skip group_as_non_root --skip test_secure_open_cookie_file sudo-rs-0.2.5/.github/ISSUE_TEMPLATE/bug_report.md000064400000000000000000000013541046102023000174440ustar 00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. Please check the issue tracker for similar issues before opening a new one. **To Reproduce** Steps to reproduce the behavior: 1. Compile 'sudo-rs' '....' 2. Write the following contents to `/etc/sudoers-rs` '....' 3. Run the following command '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Environment (please complete the following information):** - Linux distribution: [e.g. Ubuntu 23.04] - `sudo-rs` commit hash: [e.g. `d085c0a`] **Additional context** Add any other context about the problem here. sudo-rs-0.2.5/.github/ISSUE_TEMPLATE/feature_request.md000064400000000000000000000014061046102023000204750ustar 00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **Describe the feature you'd like see implemented in `sudo-rs`** A clear and concise description of what you want to happen. **What problem can be solved with this feature?** A clear and concise description of what the problem is and how this feature would solve such feature. Ex. I'm always frustrated when [...] and this feature would fix this problem because [...] **Describe alternatives you've considered** Are you sure this problem cannot be solved by an already existing feature? You could also add any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. sudo-rs-0.2.5/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md000064400000000000000000000011041046102023000230000ustar 00000000000000**Describe the changes done on this pull request** A clear and concise description of the changes done by your pull request. **Pull Request Checklist** - [] I have read and accepted the [code of conduct](https://github.com/trifectatechfoundation/sudo-rs/blob/master/CODE_OF_CONDUCT.md) for this project. - [] I have tested, formatted and ran clippy over my changes. - [] I have commented and documented my changes. - [] This pull request will fix issue https://github.com/trifectatechfoundation/sudo-rs/issues/<#issue> where a proper discussion about a solution has taken place. sudo-rs-0.2.5/.github/codecov.yml000064400000000000000000000002611046102023000147300ustar 00000000000000ignore: - "test-binaries" - "test-framework" coverage: status: project: default: informational: true patch: default: informational: true sudo-rs-0.2.5/.github/dependabot.yml000064400000000000000000000002141046102023000154110ustar 00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: monthly open-pull-requests-limit: 10 sudo-rs-0.2.5/.github/problem-matchers/rust.json000064400000000000000000000010231046102023000177140ustar 00000000000000{ "problemMatcher": [ { "owner": "rust", "pattern": [ { "regexp": "^(warning|warn|error)(\\[(.*)\\])?: (.*)$", "severity": 1, "message": 4, "code": 3 }, { "regexp": "^([\\s->=]*(.*):(\\d*):(\\d*)|.*)$", "file": 2, "line": 3, "column": 4 } ] } ] } sudo-rs-0.2.5/.github/workflows/ci.yaml000064400000000000000000000302551046102023000161050ustar 00000000000000name: CI permissions: read-all on: push: branches: - main pull_request: merge_group: branches: - main jobs: e2e-tests: runs-on: ubuntu-latest env: SUDO_UNDER_TEST: ours SUDO_TEST_VERBOSE_DOCKER_BUILD: 1 CI: true steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: set up docker buildx run: docker buildx create --name builder --use - name: cache docker layers uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf with: path: /tmp/.buildx-cache key: docker-buildx-rs-${{ github.sha }} restore-keys: docker-buildx-rs- - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "compliance-tests" workspaces: | test-framework - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Run all E2E tests working-directory: test-framework run: cargo test -p e2e-tests - name: prevent the cache from growing too large run: | rm -rf /tmp/.buildx-cache mv /tmp/.buildx-cache-new /tmp/.buildx-cache compliance-tests-detect-changes: runs-on: ubuntu-latest outputs: updated: ${{ steps.filter.outputs.test-framework }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 id: filter with: filters: | test-framework: - 'test-framework/**' compliance-tests-og: needs: compliance-tests-detect-changes if: ${{ needs.compliance-tests-detect-changes.outputs.updated != 'false' }} runs-on: ubuntu-latest env: SUDO_TEST_VERBOSE_DOCKER_BUILD: 1 CI: true steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: set up docker buildx run: docker buildx create --name builder --use - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "compliance-tests" workspaces: | test-framework - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Test sudo-test itself working-directory: test-framework run: cargo test -p sudo-test - name: Run all compliance tests against original sudo working-directory: test-framework run: cargo test -p sudo-compliance-tests -- --include-ignored compliance-tests: runs-on: ubuntu-latest timeout-minutes: 20 env: SUDO_TEST_PROFRAW_DIR: /tmp/profraw SUDO_TEST_VERBOSE_DOCKER_BUILD: 1 CI: true steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: set up docker buildx run: docker buildx create --name builder --use - name: cache docker layers uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf with: path: /tmp/.buildx-cache key: docker-buildx-rs-${{ github.sha }} restore-keys: docker-buildx-rs- - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "compliance-tests" workspaces: | test-framework - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Run gated compliance tests against sudo-rs working-directory: test-framework env: SUDO_UNDER_TEST: ours run: cargo test -p sudo-compliance-tests - name: Check that we didn't forget to gate a passing compliance test working-directory: test-framework env: SUDO_UNDER_TEST: ours run: | tmpfile="$(mktemp)" cargo test -p sudo-compliance-tests -- --ignored | tee "$tmpfile" grep 'test result: FAILED. 0 passed' "$tmpfile" || ( echo "expected ALL tests to fail but at least one passed; the passing tests must be un-#[ignore]-d" && exit 1 ) - name: prevent the cache from growing too large run: | rm -rf /tmp/.buildx-cache mv /tmp/.buildx-cache-new /tmp/.buildx-cache compliance-tests-lint: needs: compliance-tests-detect-changes if: ${{ needs.compliance-tests-detect-changes.outputs.updated != 'false' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "compliance-tests" workspaces: | test-framework - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: clippy sudo-test working-directory: test-framework run: cargo clippy -p sudo-test --no-deps -- --deny warnings - name: clippy compliance-tests working-directory: test-framework run: cargo clippy -p sudo-compliance-tests --tests --no-deps -- --deny warnings - name: Check that all ignored tests are linked to a GH issue working-directory: test-framework/sudo-compliance-tests run: | grep -r '#\[ignore' ./src | grep -v -e '"gh' -e '"wontfix"' && echo 'found ignored tests not linked to a GitHub issue. please like them using the format #[ignore = "gh123"]' && exit 1; true build-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Install llvm-tools component run: rustup component add llvm-tools - name: Add cargo-llvm-cov uses: taiki-e/install-action@0b63bc859f7224657cf7e39426848cabaa36f456 with: tool: cargo-llvm-cov - name: Install dependencies run: | sudo apt update sudo apt install libpam0g-dev - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "stable" - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Build run: cargo build --workspace --all-targets --release - name: Run tests run: cargo llvm-cov --workspace --all-targets --release --lcov --output-path lcov.info - name: Upload code coverage uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 with: files: lcov.info build-and-test-minimal: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Install nightly rust run: | rustup set profile minimal rustup override set nightly - name: Install dependencies run: | sudo apt update sudo apt install libpam0g-dev - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "nightly" - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Update to minimal direct dependencies run: cargo update -Zdirect-minimal-versions - name: Build run: cargo build --workspace --all-targets --release - name: Run tests run: cargo test --workspace --all-targets --release build-and-test-msrv: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Install rust 1.70 run: rustup override set 1.70 - name: Install dependencies run: | sudo apt update sudo apt install libpam0g-dev - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "msrv" - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Build run: cargo build --workspace --all-targets --release - name: Run tests run: cargo test --workspace --all-targets --release build-and-test-fedora: runs-on: ubuntu-latest container: fedora:latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Install dependencies run: | dnf install -y cargo pam-devel - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "stable-fedora" - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Reduce privileges run: | useradd builder chown builder . - name: Build run: sudo -ubuilder cargo build --workspace --all-targets --release - name: Run tests run: sudo -ubuilder cargo test --workspace --all-targets --release miri: needs: build-and-test runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Install nightly rust and miri run: | rustup set profile minimal rustup override set nightly rustup component add miri - name: Install dependencies run: | sudo apt update sudo apt install libpam0g-dev - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: miri - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Run tests run: cargo miri test --workspace miri check-bindings: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Install dependencies run: | sudo apt update sudo apt install libpam0g-dev - name: Install rust-bindgen uses: taiki-e/install-action@0b63bc859f7224657cf7e39426848cabaa36f456 with: tool: bindgen-cli@0.70.1 - name: Install cargo-minify run: cargo install --locked --git https://github.com/tweedegolf/cargo-minify cargo-minify - name: Regenerate bindings run: make -B pam-sys - name: Check for differences run: git diff --exit-code format: runs-on: ubuntu-latest env: RUSTDOCFLAGS: "-D warnings" steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Run rustfmt run: | cargo fmt --all -- --check cargo fmt --manifest-path test-framework/Cargo.toml --all -- --check clippy: needs: format runs-on: ubuntu-latest env: RUSTDOCFLAGS: "-D warnings" steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "stable" - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Run clippy run: cargo clippy --no-deps --all-targets -- --deny warnings docs: needs: clippy runs-on: ubuntu-latest env: RUSTDOCFLAGS: "-D warnings" steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Rust Cache uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 with: shared-key: "stable" - name: Register rust problem matcher run: echo "::add-matcher::.github/problem-matchers/rust.json" - name: Build docs run: cargo doc --no-deps --document-private-items audit: needs: clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Install cargo-audit uses: taiki-e/install-action@0b63bc859f7224657cf7e39426848cabaa36f456 with: tool: cargo-audit - name: Run audit run: cargo audit sudo-rs-0.2.5/.github/workflows/release.yml000064400000000000000000000061511046102023000167670ustar 00000000000000# To run the release workflow push a tag with the expected SHA256SUMS as tag message body. name: Release on: push: tags: '*' jobs: build: runs-on: ubuntu-24.04 permissions: read-all steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Sanity checks run: | # GHA makes the tag point to the commit rather than the tag object. # Remove the tag and fetch it again to get the real tag object. git tag -d "$(echo "$GITHUB_REF" | sed 's/refs\/tags\///')" git fetch https://github.com/trifectatechfoundation/sudo-rs.git --tags # Check if the tag has a signature to prevent accidentally pushing an unsigned tag. git tag -l --format='%(contents:signature)' "$(echo "$GITHUB_REF" | sed 's/refs\/tags\///')" | grep --quiet SIGNATURE || (echo "Tag not signed"; exit 1) - name: Run build run: ./util/build-release.sh # Upload the built tarballs first before comparing checksums to help with debugging. - name: Upload artifacts uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: release_files path: | target/pkg/SHA256SUMS target/pkg/*.tar.gz - name: Compare checksums run: | # Get the expected checksums from the tag message. git tag -l --format='%(contents:body)' "$(echo "$GITHUB_REF" | sed 's/refs\/tags\///')" | tr -s '\n' > expected_checksums.txt # Check that the actual checksums match what we expected. If not fail # the release and have the person doing the release check again for # reproducability problems. cat expected_checksums.txt diff -u expected_checksums.txt target/pkg/SHA256SUMS release: runs-on: ubuntu-24.04 permissions: contents: write needs: build steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Download artifacts uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 with: name: release_files path: release_files - name: Prepare release run: | echo "Release files:" ls -l release_files echo # Extract the first changelog entry from CHANGELOG.md echo "Changelog:" sed -n '4,${ /^## /q; p; }' CHANGELOG.md | tee changes.md - name: Create release env: GH_TOKEN: ${{ github.token }} run: | # GHA makes the tag point to the commit rather than the tag object. # Remove the tag and fetch it again to get the real tag object. RELEASE="$(echo "$GITHUB_REF" | sed 's/refs\/tags\///')" git tag -d "$RELEASE" git fetch https://github.com/trifectatechfoundation/sudo-rs.git --tags gh release create "$RELEASE" --draft \ --title "Version ${RELEASE#v}" \ --notes-file changes.md release_files/* \ --verify-tag echo "Draft release successfully created. Please review and publish." sudo-rs-0.2.5/.gitignore000064400000000000000000000004521046102023000132150ustar 00000000000000# Generated by Cargo # will have compiled files and executables /target/ /build/ # These are backup files generated by rustfmt **/*.rs.bk # Code coverage in lcov format /lcov.info # Generated man pages /docs/man/*.1 /docs/man/*.1.gz /docs/man/*.5 /docs/man/*.5.gz /docs/man/*.8 /docs/man/*.8.gz sudo-rs-0.2.5/CHANGELOG.md000064400000000000000000000146161046102023000130450ustar 00000000000000# Changelog ## [0.2.5] - 2025-04-01 ### Added - `sudo visudo` will protect you from accidentally locking yourself out - Support for `--prompt` and `SUDO_PROMPT` environment variable - Support for `Defaults targetpw` - Support for `VAR=VALUE` matching in `Defaults env_keep/env_check` - Support for `--bell` ### Changed - Portability: sudo-rs supports FreeBSD! - `sudo -v` will only ask for a password if the policy requires it ### Fixed - Manual wrongly claimed `timestamp_timeout` supported negative values (#1032) - `timestamp_timeout` in excess of 292 billion years were not rejected (#1048) - Usernames in /etc/sudoers can contain special characters by using double quotes or escaping them (#1045) ## [0.2.4] - 2025-02-25 ### Added - Support for `SETENV:` and corresponding `sudo VAR=value command` syntax - Support for `Defaults rootpw` - Support for `Defaults pwfeedback` - Support for host/user/runas/command-specific `Defaults` ### Changed - Portability: sudo-rs now has experimental support for FreeBSD! - `pam-login` feature now controls if PAM service name 'sudo-i' is used ### Fixed - Bug in syslog writer could cause sudo to hang (#856) - SHELL was not canonicalized when using `sudo -s` or `sudo -i` (#962) - RunAs_Spec was not carried over on the same /etc/sudoers line (#974) - sudo --list did not unfold multiple-level aliases (#978) - The man page for sudoers was missing (#943) ### Other - sudo-rs copyright changed to Trifecta Tech Foundation ## [0.2.3] - 2024-07-11 ### Changed - Portability: sudo-rs now is compatible with s390x-unknown-linux-gnu - Removed unneeded code & fix hints given by newer Rust version ### Fixed - `visudo` would not properly truncate a `sudoers` file - high CPU load when child process did not terminate after closure of a terminal ## [0.2.2] - 2024-02-02 ### Changed - Several changes to the code to improve type safety - Improved error message when a PTY cannot be opened - Improved portability of the PAM bindings - su: improved parsing of su command line options - Add path information to parse errors originating from included files ### Fixed - Fixed a panic with large messages written to the syslog - sudo: respect `--login` regardless of the presence of `--chdir` ## [0.2.1] - 2023-09-21 ### Changed - Session records/timestamps are now stored in files with uids instead of usernames, fixing a security bug (CVE-2023-42456) - `visudo` will now resolve `EDITOR` via `PATH` - Input/output errors while writing text to the terminal no longer cause sudo to exit immediately - Switched several internal API calls from libc to Rust's std library - The `%h` escape sequence in sudoers includes directives is not supported in sudo-rs, this now gives a better diagnostic and no longer tries to include the file - Our PAM integration was hardened against allocation failures - An attempt was made to harden against rowhammer type attacks - Release builds no longer include debugging symbols ### Fixed - Fixed an invalid parsing when an escaped null byte was present in the sudoers file - Replaced informal error message in `visudo` with a proper error message ## [0.2.0] - 2023-08-29 ### Added - `visudo` can set/fix file permissions using the `--perms` CLI flag - `visudo` can set/fix the file owner using the `--owner` CLI flag - Read `env_editor` from sudoers file for visudo - Add basic support for `--list` in sudo ### Changed - `visudo` now uses a random filename for the temporary file you are editing - `su` now runs with a PTY by default - Included files with relative paths in the sudoers file are imported relative from the sudoers file - `sudo` now checks if ownership and setuid bits have been set correctly on its binary - When syslog messages are too large they will be split between multiple messages to prevent message truncation - We now accept a wider range of dependencies - Our MSRV (minimum supported rust version) has been set at 1.70.0 ### Fixed - Set arg0 to the non-resolved filename when running a command, preventing issues with symlinks when commands rely on link filenames ## [0.2.0-dev.20230711] - 2023-07-11 ### Added - Add initial `visudo` implementation - Add support for `~` in `--chdir` - Log commands that will be executed in the auth syslog - Add a manpage for the `sudo` command ### Changed - The SUDO_RS_IS_UNSTABLE environment variable is no longer required - Sudo-rs will now read `/etc/sudoers-rs` or `/etc/sudoers` if the former is not available. We no longer read `/etc/sudoers.test` - Removed signal-hook and signal-hook-registry dependencies - Improved error handling when `--chdir` is passed but not allowed - Properly handle `SIGWINCH` when running commands with a PTY ### Fixed - Only call ttyname and isatty on character devices - Fixed a bug in syslog FFI ## [0.2.0-dev.20230703] - 2023-07-03 ### Added - Add `timestamp_timeout` support in sudoers file - Add ability to disable `use_pty` in the sudoers file ### Changed - Set the TTY name for PAM sessions on a TTY - Set the requesting user for PAM sessions - Simplified some error messages when a command could not be executed - Reveal less about what caused a command not to be executable - Continued rework of the pty exec ### Fixed - Fixed exit codes for `su` - Fixed environment filtering for `su` - Fixed `SHELL` handling for `su` ## [0.2.0-dev.20230627] - 2023-06-27 ### Added - Add `passwd_tries` support in sudoers file - Add developer logs (only enabled with the `dev` feature) ### Changed - Only use a PTY to spawn the process if a TTY is available - Continued rework of the pty exec - Aliasing is now implemented similarly to the original sudo - You can no longer define an `ALL` alias in the sudoers file - Use canonicalized paths for the executed binaries - Simplified CLI help to only display supported actions [0.2.3]: https://github.com/trifectatechfoundation/sudo-rs/compare/v0.2.2...v0.2.3 [0.2.2]: https://github.com/trifectatechfoundation/sudo-rs/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/trifectatechfoundation/sudo-rs/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/trifectatechfoundation/sudo-rs/compare/v0.2.0-dev.20230711...v0.2.0 [0.2.0-dev.20230711]: https://github.com/trifectatechfoundation/sudo-rs/compare/v0.2.0-dev.20230703...v0.2.0-dev.20230711 [0.2.0-dev.20230703]: https://github.com/trifectatechfoundation/sudo-rs/compare/v0.2.0-dev.20230627...v0.2.0-dev.20230703 [0.2.0-dev.20230627]: https://github.com/trifectatechfoundation/sudo-rs/compare/v0.1.0-dev.20230620...v0.2.0-dev.20230627 sudo-rs-0.2.5/CODE_OF_CONDUCT.md000064400000000000000000000002271046102023000140240ustar 00000000000000# Code of Conduct We abide by the [Rust Code of Conduct][coc] and ask that you do as well. [coc]: https://www.rust-lang.org/policies/code-of-conduct sudo-rs-0.2.5/CONTRIBUTING.md000064400000000000000000000057271046102023000134700ustar 00000000000000# Contributing to sudo-rs We welcome contributions to building a memory safe sudo / su implementation; this document lists some ways in which you can help. This project is about building a "drop-in replacement" for sudo and su. That does not mean we want to copy *all* of the behavior of original sudo or other su implementations. Whenever we add a feature, sudo becomes more complex, and unforeseen interactions due to complexity can result in security issues. Also, sudo has some features for backwards compatibility only---it makes no sense for us to re-implement a feature that by its nature won't be very well-used in practice. Other features have a very specific use-case in mind (for example, matching command lines with [regular expressions](https://xkcd.com/1171/)), which are very complex to use and would require the inclusion of a third-party library. I.e. every time we add a feature, we have to weigh its benefits to the cost of adding the feature. For this, the sudo-rs Core Team has adopted a few criteria for inclusions of features in sudo / su: 1. Security is more important than functionality. 2. Simplicity is preferred over complexity. 3. A feature to be added should actually *solve* a problem. 4. Features must support a common and reasonable use case. 5. Dependencies must be kept to an absolute minimum. ## Feature requests The easiest way to contribute is to request a feature that we currently do not have; use the issue tracker for this and explain the situation. Things that are currently possible with original sudo and that pass the above-mentioned criteria are likely to be accepted. ## Testing sudo You can install and run sudo on your personal system, or any other non-mission critical machine. We recommend installing it in `/usr/local/bin` so you have original sudo as a backup. Although sudo-rs is thoroughly tested for every change we make, a real-world test like this can uncover subtle problems in technical parts, or uncover common sudo use cases that we ignored so far. ## Small contributions You can also go through our code --- if you see any small mistakes or have suggestions please create an issue for them. If it is really a minor issue, like a typo or formatting issue, you can immediately create a pull request. ## Security auditing One way you can help is by looking at the security of our code and proposing fixes in it. More eyeballs spot more problems. If you find a security problem that can be used to used to compromise a system, do follow our [security policy] and report a vulnerability instead of using the issue tracker. [security policy]: https://github.com/trifectatechfoundation/sudo-rs/security/policy ## Working on a bigger issue If you want to pick up an issue in the issue tracker, please reach out to the sudo-rs team first. The easiest way to do this is to comment on the issue. If you want to work on something that is not on the issue tracker, do make an issue *before* you begin to make sure your work will not be conflicting with ours. sudo-rs-0.2.5/COPYRIGHT000064400000000000000000000006361046102023000125240ustar 00000000000000Copyright (c) 2022-2025 Trifecta Tech Foundation and contributors Copyright (c) 1994-1996, 1998-2024 Todd C. Miller Except as otherwise noted (below and/or in individual files), sudo-rs is licensed under the Apache License, Version 2.0 or or the MIT license or , at your option. sudo-rs-0.2.5/Cargo.lock0000644000000025710000000000100104140ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "glob" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "libc" version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "pretty_assertions" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", ] [[package]] name = "sudo-rs" version = "0.2.5" dependencies = [ "glob", "libc", "log", "pretty_assertions", ] [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" sudo-rs-0.2.5/Cargo.toml0000644000000031070000000000100104330ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.70" name = "sudo-rs" version = "0.2.5" build = "build.rs" publish = true autolib = false autobins = false autoexamples = false autotests = false autobenches = false default-run = "sudo" description = "A memory safe implementation of sudo and su." homepage = "https://github.com/trifectatechfoundation/sudo-rs" readme = "README.md" categories = ["command-line-interface"] license = "Apache-2.0 OR MIT" repository = "https://github.com/trifectatechfoundation/sudo-rs" [features] default = [] dev = [] do-not-use-all-features = [] pam-login = [] [lib] name = "sudo_rs" path = "src/lib.rs" [[bin]] name = "su" path = "bin/su.rs" [[bin]] name = "sudo" path = "bin/sudo.rs" [[bin]] name = "visudo" path = "bin/visudo.rs" [dependencies.glob] version = "0.3.0" [dependencies.libc] version = "0.2.149" [dependencies.log] version = "0.4.11" features = ["std"] [dev-dependencies.pretty_assertions] version = "1.2.1" [lints.clippy] undocumented_unsafe_blocks = "warn" [lints.rust.unsafe_op_in_unsafe_fn] level = "deny" priority = 0 [profile.release] opt-level = "s" lto = true strip = "symbols" sudo-rs-0.2.5/Cargo.toml.orig000064400000000000000000000026401046102023000141150ustar 00000000000000[package] name = "sudo-rs" description = "A memory safe implementation of sudo and su." version = "0.2.5" license = "Apache-2.0 OR MIT" edition = "2021" repository = "https://github.com/trifectatechfoundation/sudo-rs" homepage = "https://github.com/trifectatechfoundation/sudo-rs" publish = true categories = ["command-line-interface"] rust-version = "1.70" default-run = "sudo" [lib] path = "src/lib.rs" [[bin]] name = "sudo" path = "bin/sudo.rs" [[bin]] name = "su" path = "bin/su.rs" [[bin]] name = "visudo" path = "bin/visudo.rs" [dependencies] libc = "0.2.149" glob = "0.3.0" log = { version = "0.4.11", features = ["std"] } [dev-dependencies] pretty_assertions = "1.2.1" [features] default = [] # when enabled, use "sudo-i" PAM service name for sudo -i instead of "sudo" # ONLY ENABLE THIS FEATURE if you know that original sudo uses "sudo-i" # on the system you are building sudo for (e.g. Debian, Fedora, but not Arch) pam-login = [] # enable detailed logging (use for development only) to /tmp # this will compromise the security of sudo-rs somewhat dev = [] # this feature should never be enabled, but it is here to prevent accidental # compilation using "cargo --all-features", which should not be used on sudo-rs do-not-use-all-features = [] [profile.release] strip = "symbols" lto = true opt-level = "s" [lints.rust] unsafe_op_in_unsafe_fn = { level = "deny" } [lints.clippy] undocumented_unsafe_blocks = "warn" sudo-rs-0.2.5/LICENSE-APACHE000064400000000000000000000227731046102023000131630ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS sudo-rs-0.2.5/LICENSE-MIT000064400000000000000000000021641046102023000126630ustar 00000000000000Copyright (c) 2022-2025 Trifecta Tech Foundation and contributors Copyright (c) 1994-1996, 1998-2024 Todd C. Miller 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. sudo-rs-0.2.5/Makefile000064400000000000000000000022421046102023000126640ustar 00000000000000PAM_SRC_DIR = src/pam BINDGEN_CMD = bindgen --allowlist-function '^pam_.*$$' --allowlist-var '^PAM_.*$$' --opaque-type pam_handle_t --blocklist-function pam_vsyslog --blocklist-function pam_vprompt --blocklist-function pam_vinfo --blocklist-function pam_verror --blocklist-type '.*va_list.*' --ctypes-prefix libc --no-layout-tests --sort-semantically PAM_VARIANT = $$(./get-pam-variant.bash) .PHONY: all clean pam-sys pam-sys-diff pam-sys-diff: @$(BINDGEN_CMD) $(PAM_SRC_DIR)/wrapper.h | \ sed 's/rust-bindgen [0-9]*\.[0-9]*\.[0-9]*/&, minified by cargo-minify/' | \ diff --color=auto $(PAM_SRC_DIR)/sys_$(PAM_VARIANT).rs - \ || (echo run \'make -B pam-sys\' to apply these changes && false) @echo $(PAM_SRC_DIR)/sys_$(PAM_VARIANT).rs does not need to be re-generated # use 'make pam-sys' to re-generate the sys.rs file for your local platform pam-sys: $(BINDGEN_CMD) $(PAM_SRC_DIR)/wrapper.h --output $(PAM_SRC_DIR)/sys_$(PAM_VARIANT).rs cargo minify --apply --allow-dirty sed -i.bak 's/rust-bindgen [0-9]*\.[0-9]*\.[0-9]*/&, minified by cargo-minify/' $(PAM_SRC_DIR)/sys_$(PAM_VARIANT).rs rm $(PAM_SRC_DIR)/sys_$(PAM_VARIANT).rs.bak clean: rm $(PAM_SRC_DIR)/sys.rs sudo-rs-0.2.5/README.md000064400000000000000000000210341046102023000125030ustar 00000000000000# sudo-rs A safety oriented and memory safe implementation of sudo and su written in Rust. ## Status of this project Sudo-rs is being developed further; features you might expect from original sudo may still be unimplemented or not planned. If there is an important one you need, please request it using the issue tracker. If you encounter any usability bugs, also please report them on the [issue tracker](https://github.com/trifectatechfoundation/sudo-rs/issues). Suspected vulnerabilities can be reported on our [security page](https://github.com/trifectatechfoundation/sudo-rs/security). An [audit of sudo-rs version 0.2.0](docs/audit/audit-report-sudo-rs.pdf) has been performed in August 2023. The findings from that audit are addressed in the current version. Sudo-rs currently is targeted for Linux-based operating systems only; Linux kernel 5.9 or newer is necessary to run sudo-rs. ## Installing sudo-rs The recommended way to start using `sudo-rs` is via the package manager of your Linux distribution. ### Debian/Ubuntu If you are running Debian 13 (trixie) or later, or Ubuntu 24.04 (Noble Numbat) or later, you can use: ```sh apt-get install sudo-rs ``` This will offer the functionality using the commands `su-rs` and `sudo-rs`. If you want to invoke sudo-rs via the usual commands `sudo` and `su` instead, prepend `/usr/lib/cargo/bin` to your current `$PATH` variable. ### Fedora If you are running Fedora 38 or later, you can use: ```sh dnf install sudo-rs ``` This will offer the functionality using the commands `su-rs` and `sudo-rs`. ### Arch Linux Arch Linux can be installed from the distribution repositories: ```sh pacman -S sudo-rs ``` This will offer the functionality using the commands `su-rs` and `sudo-rs`. ### Installing our pre-compiled x86-64 binaries You can also switch to sudo-rs manually by using our pre-compiled tarballs. We currently only offer these for x86-64 systems. We recommend installing sudo-rs and su-s in your `/usr/local` hierarchy so it can co-exist with your existing sudo installation. You can achieve this using the commands: ```sh sudo tar -C /usr/local -xvf sudo-VERSION.tar.gz ``` and for su-rs: ```sh sudo tar -C /usr/local -xvf su-VERSION.tar.gz ``` This will install sudo-rs and su-rs in `/usr/local/bin` using the usual commands `sudo` and `su`; it will also install our version of `visudo` in that location. Of course, if you **don't** have Todd Miller's `sudo` installed, you also have to make sure that: * You manually create a `/etc/sudoers` or `/etc/sudoers-rs` file, this could be as simple as: Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" %sudo ALL=(ALL:ALL) ALL `sudo-rs` will try to process `/etc/sudoers-rs` exists if it exists, otherwise it will use `/etc/sudoers`. For an explanation of the sudoers syntax you can look at the [sudoers man page](https://www.sudo.ws/docs/man/sudoers.man/). * (Strongly recommended) You create `/etc/pam.d/sudo` and `/etc/pam.d/sudo-i` files that contain: session required pam_limits.so @include common-auth @include common-account @include common-session-noninteractive If you don't do this, either a "fallback" PAM policy will be used or `sudo-rs` will simply refuse to run since it cannot initialize PAM. On FreeBSD, you may want to put these files in `/usr/local/etc/pam.d` instead. ### Building from source Sudo-rs is written in Rust. The minimum required Rust version is 1.70. If your Linux distribution does not package that version (or a later one), you can always install the most recent version through [rustup]. You also need the C development files for PAM (`libpam0g-dev` on Debian, `pam-devel` on Fedora). On Ubuntu or Debian-based systems, use the following command to install the PAM development library: ``` sudo apt-get install libpam0g-dev ``` On Fedora, CentOS and other Red Hat-based systems, you can use the following command: ``` sudo yum install pam-devel ``` With dependencies installed, building sudo-rs is a simple matter of: ``` cargo build --release ``` This produces a binary `target/release/sudo`. However, this binary must have the setuid flag set and must be owned by the root user in order to provide any useful functionality. Consult your operating system manual for details. Sudo-rs then also needs the configuration files; please follow the installation suggestions in the previous section. ### Feature flags By default, sudo-rs will use the PAM service name `sudo`. On Debian and Fedora systems, it is customary that the name `sudo-i` is used when the `-i / --login` command line option is used. To get this behaviour, enable the `pam-login` feature when building: ``` cargo build --release --features pam-login ``` This feature is enabled on our pre-supplied binaries. [rustup]: https://rustup.rs/ ## Differences from original sudo sudo-rs supports less functionality than sudo. Some of this is by design. In most cases you will get a clear error if you try something that is not supported (e.g. use a configuration flag or command line option that is not implemented). Exceptions to the above, with respect to your `/etc/sudoers` configuration: * `use_pty` is enabled by default, but can be disabled. * `env_reset` is ignored --- this is always enabled. * `visiblepw` is ignored --- this is always disabled. * `verifypw` is currently ignored; a password is always necessary for `sudo -v`. * `mail_badpass`, `always_set_home`, `always_query_group_plugin` and `match_group_by_gid` are not applicable to our implementation, but ignored for compatibility reasons. Some other notable restrictions to be aware of: * Some functionality is not yet supported; in particular `sudoedit` and preventing shell escapes using `NOEXEC` and `NOINTERCEPT`. * Sudo-rs always uses PAM for authentication, so your system must be set up for PAM. Sudo-rs will use the `sudo` and `sudo-i` service configuration. This also means that resource limits, umasks, etc have to be configured via PAM and not through the sudoers file. * sudo-rs will not include the sendmail support of original sudo. * The sudoers file must be valid UTF-8. * To prevent a common configuration mistake in the sudoers file, wildcards are not supported in *argument positions* for a command. E.g., `%sudoers ALL = /sbin/fsck*` will allow `sudo fsck` and `sudo fsck_exfat` as expected, but `%sudoers ALL = /bin/rm *.txt` will not allow an operator to run `sudo rm README.txt`, nor `sudo rm -rf /home .txt`, as with original sudo. If you find a common use case for original sudo missing, please create a feature request for it in our issue tracker. ## Aim of the project Our current target is to build a drop-in replacement for all common use cases of sudo. For the sudoers config syntax this means that we support the default configuration files of common Linux distributions. Our implementation should support all commonly used command line options from the original sudo implementation. Some parts of the original sudo are explicitly not in scope. Sudo has a large and rich history and some of the features available in the original sudo implementation are largely unused or only available for legacy platforms. In order to determine which features make it we both consider whether the feature is relevant for modern systems, and whether it will receive at very least decent usage. Finally, of course, a feature should not compromise the safety of the whole program. Our `su` implementation is made using the building blocks we created for our sudo implementation. It is a suitable replacement for the `su` distributed by [util-linux]. [util-linux]: https://github.com/util-linux/util-linux ## Future work While our initial target is a drop-in replacement for most basic use cases of sudo, our work may evolve beyond that target. We are also looking into alternative ways to configure sudo without the sudoers config file syntax and to extract parts of our work in usable crates for other people. ## Sponsors The initial development of sudo-rs was started and funded by the [Internet Security Research Group](https://www.abetterinternet.org/) as part of the [Prossimo project](https://www.memorysafety.org/). An independent security audit of sudo-rs was made possible by the [NLNet Foundation](https://nlnet.nl/), who also [sponsored](https://nlnet.nl/project/sudo-rs/) several feature additions and the FreeBSD porting effort. ## Acknowledgement Sudo-rs is an independent implementation, but it incorporates documentation and Rust translations of code from [sudo](https://www.sudo.ws/), maintained by Todd C. Miller. We thank Todd and the other sudo contributors for their work. sudo-rs-0.2.5/SECURITY.md000064400000000000000000000031061046102023000130150ustar 00000000000000# Security policy **Do not report security vulnerabilities through public GitHub issues.** Instead, you can report them using [our security page](https://github.com/trifectatechfoundation/sudo-rs/security). Alternatively, you can also send them by email to security+sudo@tweedegolf.com. You can encrypt your email using GnuPG if you want. Use the GPG key with fingerprint [C2E4 CAC4 B122 25DE 1C3B B1C9 289D 0820 03D0 1E95](https://keys.openpgp.org/search?q=C2E4CAC4B12225DE1C3BB1C9289D082003D01E95). Include as much of the following information: * Type of issue (e.g. buffer overflow, privilege escalation, etc). * The location of the affected source code (tag/branch/commit or direct URL). * Any special configuration required to reproduce the issue. * The Linux distribution affected. * Step-by-step instructions to reproduce the issue. * Impact of the issue, including how an attacker might exploit the issue. If you have found a bug that also exists in the original sudo (which, although unlikely, means it is a very serious issue), you **must** also follow the steps at https://www.sudo.ws/security/policy/ ## Preferred Languages We prefer to receive reports in English. If necessary, we also understand Spanish, German and Dutch. ## Disclosure Policy Like original sudo, we adhere to the principle of [Coordinated Vulnerability Disclosure](https://vuls.cert.org/confluence/display/CVD/Executive+Summary). # Security Advisories Security advisories will be published [on GitHub](https://github.com/trifectatechfoundation/sudo-rs/security/advisories) and possibly through other channels. sudo-rs-0.2.5/bin/su.rs000064400000000000000000000000461046102023000127710ustar 00000000000000fn main() { sudo_rs::su_main(); } sudo-rs-0.2.5/bin/sudo.rs000064400000000000000000000000501046102023000133070ustar 00000000000000fn main() { sudo_rs::sudo_main(); } sudo-rs-0.2.5/bin/visudo.rs000064400000000000000000000000511046102023000136470ustar 00000000000000fn main() { sudo_rs::visudo_main() } sudo-rs-0.2.5/build.rs000064400000000000000000000022371046102023000126750ustar 00000000000000use std::path::Path; // Return the first existing path given a list of paths as string slices fn get_first_path(paths: &[&'static str]) -> Option<&'static str> { paths.iter().find(|p| Path::new(p).exists()).copied() } fn main() { let path_zoneinfo: &str = get_first_path(&[ "/usr/share/zoneinfo", "/usr/share/lib/zoneinfo", "/usr/lib/zoneinfo", "/usr/lib/zoneinfo", ]) .unwrap_or(""); let path_maildir: &str = get_first_path(&["/var/mail", "/var/spool/mail", "/usr/spool/mail"]).unwrap_or("/var/mail"); // TODO: use _PATH_STDPATH and _PATH_DEFPATH_ROOT from paths.h println!("cargo:rustc-env=SUDO_PATH_DEFAULT=/usr/bin:/bin:/usr/sbin:/sbin"); println!( "cargo:rustc-env=SU_PATH_DEFAULT_ROOT=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ); println!( "cargo:rustc-env=SU_PATH_DEFAULT=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" ); println!("cargo:rustc-env=PATH_MAILDIR={path_maildir}"); println!("cargo:rustc-env=PATH_ZONEINFO={path_zoneinfo}"); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rustc-link-lib=pam"); } sudo-rs-0.2.5/clippy.toml000064400000000000000000000000511046102023000134150ustar 00000000000000msrv = "1.70" check-private-items = true sudo-rs-0.2.5/docs/audit/audit-report-sudo-rs.pdf000064400000000000000000016005701046102023000177770ustar 00000000000000%PDF-1.4 %ª«¬­ 1 0 obj << /Creator (£}©òH»&qF\)zZÙ—¨ÞšùâÂêèE³|:u„Êf,»Ž©¬¿YI¼*\b#ß³x„öƒ³;¢yi¶\f) /Producer (èC¢bYZqfˆ 04ÂYfùi4òG½œ`}«VÞΙߴûx?Ó@½Bµö'0ö¯‡ÆFåuþ÷ìœï13˜³–ˆ) /CreationDate (R™Ò‡nh·ö½µsyò¹â0X6‰Åït—¦š—¯Æ'nä'A²N*òd«6ŠPtóعê:tÃÄ@,¸ÛQ) >> endobj 2 0 obj << /N 3 /Length 3 0 R /Filter /FlateDecode >> stream Ìûi's¥ÀÀ'ĸ»Ù€üP'H¥@ýÃåï¼XÜôUÇÜÀܶKÁ i¥c¢×·”kçMíâª:. ©F‹qŒ==4¯~2Y\yBMŸ§>¾Ý:ÃFqÐKå~„§ZTsö8VûN·c’„‰BLÇ•fW¿ºë›î2CcŒ‘€šVÙ á;²ÇþÞŒd]îÃ6ËžJnÁã>Ÿ­Ìéå<½Õ(€Þ—Ó›5NôeµÖ|ÿ‡1ž2fU©°–dyÏì"ôÁù!¤»;HÏH{„ÙÉ —Gw»ÐÒ|4f%.‹‚›Ÿ/$8(,íóToUüKŒóøÝ0Z껿VùœoèÝ×oØsµ“<&è3–R\åݬI‘ÇÐÕkÑ,ÜØ§¡kc¼ø5Q_ãê²å4“M±ëe´{D½XŒ¿¤ý2ñƒ¼6™%±›é½ ‘†B]¬‰ñÓhé"YÚÒi&’ŒùZꑈF¹wŠ&èÁÞ’ {L"ŽlÞÒEAzd¢v祣EóÅòð'å¦wד¼04Rªî¦n(K˜:¸¢¡˜¦™/;—&RR aAøOi¢Þ’C×¶wƒú NÉQ ´MmÊ,KF?oÌIÈžö/’«ôÊ&°¸jƒ«Ö¸Ð¯ ̶”ì?rAÐ%§·BÒaú\^).œ^ºHºíèÞ;Æ—£'eƒJâvÓ·jd4Â#™©[Á+q̧Pã¡ì!=˜®žhŒ‚÷zMLΦ†Ú_ýG¿?E îÿK™S=’hÆ;]™ô{JÛ!¤¶ˆK難H8ƒÍ¶) ·™ìLÅropAçSt¼/ó {a¬é!MÙºQxCز‚ʾ;ט_¢]Ó^~šö6JTîàó…§#ä­ÚT-Çš©&|!×ÙÂ2Èô46dK¹ºãh°ØQóÀÛ;å?·Á#ãÁ8‚R*&JýØRðñã}!]û'´‡¶ 2­“»”,òdJz§Ra”Ø÷Á(·ÙûþGEhS`¶Ý5ÓONÿ~XOøÌzµýhêÜ›à‹é³¹)ó6×èZo$o÷íªwß%ó®|& =˜ZÑûyë=$¿ªn¦=fs¯ò¸NÀS¾Ñó6ѱœ¡-)µñlÆ\ZC”ÎõÄ}l=îøx>\ã¢!݇OÒë[ú’.&ËÆ}$¼Š“#Á{¡½¡'VœÆ ´pîä[ætÆïÕâŽ-{ж›ø:•'=ú¹’|>9ø~W+Â;ž©Qy“úžõ×AºôîðºÒVÁØÊZÖž=ñ‘ö"»J…Ë™…‰#‘¼Â$Ï/÷µ P`*@îòéD”9¶)÷GÕ _¡Iøà¼¾×ý¤¯Þ+üŽ ŸÑhѾ8Û9ë‹›%kL—ùQQLéjÐ@!pT4Iåt´ù[¢¢©fŸ>§ªª7B-ÒzN›øPEˆ:~:ìU®;¶—¦Õ\âhIcø.AøÓLš 6EwðÅ‚×@ÿ)c $ú-ÝMuÄ1­ZiÕ¹u›é.Òoô#×:JìÉX2ƒ:z‚¿²1yŽ|ŠëÖ,0|¶Ù‡„¸Ìr¾´:†ÿµ-è55áRÿÙE<ä–íïÀý˜A ø®¨}ü23„yX‚Gó©öÙeù7ïž`^hVXßí²€¯ ÌÚ¨ÎâîZm¿:ªßä€Fòå$Ÿîoèó›‹þ;¼/Ý?ÿ4¬Ô/}‡|æøAILLA€è£×³˜ÑÏçÞ×_Y×ãk{N5,Çw'†ÔfêŽø¯VK;üˆ;HÊBFø®¬#æy¿×9Ww^Vãëe¯ æ¾XãG b@3ÑJ°~A±h¼æŒì§U›ÄTôQ[}M@‹xµg#ó#_r§í]Ñî‡1Ö©†`ú Z±Cc_ „f,®‚ÕmdÁ½Bhš—’M¥V8®$ +VÅü–?t ¾· À[¸=/YOÌ ‚íÑX­¨“fí¤±Â—=o(ô ê²Ü¡õ—˜åwÎY¦Õ÷Ð<Ô¦‡öîq™0z'9К}(ð€úDÑ—@×4‚šFK Sn›ý½­×f„pŒàf`’¡à|uãè8Â2âº#ã3”Ú È«uR0d¶£§æ2â_ F©-}“™|챑þàeEe@k“^ÛXb(̈ͻ ƒŸŽô覒8?Tzëc#tt–¾:eE7$̆YLŸ¦c&ᬗÃÉZËöòÖ}{øþ…*q(þ¹¦¡A_¡ºóŒ#B&Sd>!íùÌy«vNÖXyCÈKšÉ©·÷ºX³mŠùJŠ› ½®·w×J–£ºt{„mSg<'{_ T€>䑌Fxh©ŸëÃmlu™Š£ cÜĦ†ô=ÊŠ]ðå5Šõl¹sÉS’Sõ ý O}?%ïýIÚÒ¸!‡žDssó$d!X;g¸áùŸ˜ïÀû÷$dßwÊ& ¯ïv¯j±:š¨© ÍÁºÛ—îêÑûB e²®|}ƒuUwÿÏ%­BF‘dëµþÿzÏÊïàC6œÍï ï³7›•ºD^?BH1K«ÁÄ––Þ·È>€‹TT¸Þe‰_ »^ç"’°—5#³ Û.ƒO¤€pAó½~(°a3¤¿XßH0Ì={‰h3ÿ,»ßZ»UôDt9«"]øÖÝf³ù1>ʳV/Ÿ¡ ¢n.=œtòAïP)71§Ò»í#j(Ôߥf# ¯š¼ãÇŸŠÓ›ò†SXìšÀ‡Î!ìäåo¶]$xîàG,JgQÊ(¿”ÁÿÕãOM7€ÿ' “†aÊï…¡„g—^ønÒË@€OËHh) h¥+ìK°A´6Ë=›ö¡&ý¥þtF/öèuu·OÞX`šl]70ûBµ+uJDzƒóp"ÎDd"ä«Ú[” ‹Z­vó³YÂO!-ÊåÖŸpH„Ô†éIGÞäß6­¬iA1ûÙ¨‡‘¡ræ• ¾—ö>˜%Œ¤^$å6’fﯹ_% ·ü…ÖXE…÷Ú@÷Qˆ¹wmÄIRC.SÜ|éó‹FÓ‰Ûà×{}¢µ+h¾§¯=™¡\Äè…oîk{ÄUg¹Åä endstream endobj 3 0 obj 2496 endobj 4 0 obj [/ICCBased 2 0 R] endobj 5 0 obj << /Type /Metadata /Subtype /XML /Length 6 0 R >> stream „ǦT§y8vÇêÞk`ÍÀîqaíŒÖüI’->ä£yoicµ³<ÜkKNJÇçÚ„'¿GË}¯ ©Ò =}¸?¶tOÀ1¿Ÿv¸âÈø1 ÁÒÐ.µ\kc±ç¥S>žœ ^Ñ;ŸÿÈtvùQ°%Mvù°V*·5œ¸¦Öõ³>8ÖÑ”ÞÆÙüÛß{~®;¦ñ1ý÷Êô.¤õœ«ÐÆPˆY3&Ǧ af¹¥ƒ–ª5 é£˜cfîn/<ÅsKØgb–·ô½ó}Ä3º€Wƒ,”` À訋U]óx[ÕèÓImf[ãC‚U[¸`t°Úl(ÿôo(úʺ¯gË? ýyiQ‰›Çd7w;AjÚ…{¸]¬ôn]âÆ4*îž»mþ3Ìzßf=q† ðGλzá Œ”ÈU‰ãxδ”çHÜé˜#ߺæ"Ò“jz¼ÕŸÕ.ynñù–fÆ,ÑÔ4>Q¬•烠ŠÓLÒp-" ¶ö§#säÀQ7Ê<û<5{ÔÑÉtWHó¦…¢ØY9)ì¾K¬Ñ˜fóÃ3}JIt°ÆÈrWäÃ2v_=úaËï(Jh¸05;ÊvhI&$v<áš¾\ÏõæœeYÒõî0·[žZa€ÖDã«:ÀKmWní«Èü©µr =MÎûEÿ1”"3§žð h½©XÂÑB˜ñU­"××,Hà¾éèíS5ì“mSжAºÖÖètõœ)ïŒp7’ó¡‚CGõJ8> køÐpyEý›wÇïÁ,úîèwºòÛˆšØÔˆDšÀM/G¨âTô<Ì€i)û,1þñ•õ%÷%Z¢¹( ¼3Tkï9E—€pŸ¦š„£8¯ £h›!M á×\j½F^úqtφv¡Èaâß§~ËêyI¡5²·“¡ÆL|¤GX~§¥šô©>d¡ï)'Ô÷g¥ʱ^†Á=í|Œ Òòç1ΨPcŠEdÆ5Xì½[—¾· réÈ Y1Ó.$ nÞR_ nŒª®,N3j°ñ¥¨g!@~Π—xü·%Óï|¨ŸUS±©ÝYsHLýAŠI†ëE( 9ç5¤·™I–Ÿ‹:¼ ›QÌÛjÃImê Áþ¼Åú©NÀþ endstream endobj 6 0 obj 880 endobj 7 0 obj << /Name /Im1 /Type /XObject /Length 8 0 R /Filter /DCTDecode /Subtype /Image /Width 2480 /Height 3507 /BitsPerComponent 8 /ColorSpace /DeviceRGB >> stream %«{âp† ¨ýŸ^–æÐV .¹ÓßVªƒŸ«|®+¦wqœWcVÙ… ²?ï40”W’ ,ç7Á<øW‹6ÿ’e"sÉG 0(ö„€3G}ôè”;âwä‹è’.éŠ)dBÔ'&š êGb MG‚ú¶œÐo¿sþV•à×S͹ën7`ÜZY+Nog· …”ÅzøŠ#à UX?ß¿R9<ð6ø“ÒqQ”‡€—C²†OgvKº W…Æ·“àÔÀ[ì3i A­ù¿º•¡fˆy{à«áÒÅ k“+/=/nJwêUüÜ•O5eáÞ~Œ½`Xó+µÇ Ký¶¶"€½˜´Š÷> ñ6ûyû¡Üù_BN¤ÿ^1ÿÎ϶j9þ Ìi'ãîríŒøÎÇ·2|ŠñÖX´ Wàt:Ùš1½Ø''ä›Zµëm™k\õoYï\@2Šïï¿YåÔ?mg6†P?ŒéǶg.æ¯`ýÚ_V“õÆ ƒ£%Òv¾iF_e†áœ\=ÿð»ø\ÄZóÁì9”‚õcŒ»úš@Y5÷²=Ê;O#Dxò¹–{ªZËÚK÷s[D(Û’ÞäØHUà¿u3:ëW²t|è›—KGª7x»LÁ0é÷ˆçŒ4Ÿ|g¡H¡Ÿe—2ZM›~lS=ÑtüûáTÖ/qç‹ÙÅŠPÛ,¨î"ŠCMN³o×ø{1n*4‰Ò培L»òÊ?ÇÏ÷ü D} ‚x.ö~h=’²ðúÍVhèÙƒùmõ0±ïj”á.Æ yŠ8ÿ¾×žÊDvœHu}‘<€J†àE Ì ,úáo10!P|áÅÛ­ÇOŸÜqüÞPß©dÂm˜¿NAØ[h —í½ÄÀ•£Ž Wã?+\¨fÕOˆ•lí(Ú„šbÔH28§Í4@l%ÿ°“ì© s6ÙE-iÛHì¤T)%UäBópÓéâÝ»£4¢0º©&h0#ƒý5&Ç ùrðëjr]zïRî Y¡ÎäÆlø<Æîœg !Ó( o8¥x³jíW#Ï»gâ_Í¿•÷î¢)³ù­íF‹ü—)ûGjcfa øƒ#…@Vbðb…‰õfžøöDŒxd­Ör=->ÄzÏyש4˜§–ti1¸&×ÞZùJ”í‹U~Õ2ú^! l.ÛàWÂ&pK:,øôRÞI•õLóv‡Ñ(¥ü’€ô€s2y«…;+Zéó½X²è£ár “©àX(ÇOŽÊéL¸g“1³öÃá1ÞòHYâáƒù³ ­í3œ+†)¶`ßJ°ËSº<®|`f$Q3ÿ’¤`á–3O ›»rÙžÊÚ¦äÛ‡zìÜ+4X²m‘rRâ¹z Ãóïñ§Õž„|»?,² ÞÛ¤ªˆ°„¯¯ ®Åm¾¦k§W™¨ïÑ'¡@>'ü|Û$ð1h5~iaÈï ‹»œ Õv¬Ç1X±*IpÎÿÊ苹nUꪕu³ÝÖµÈ1§‡7N]#Ý‹Eª\¹–ØLÂág˜fºîõõÂt|µÖQE®‘âYC¯­ÅŽ‚Ú¥¯q < ã€óÛ cyÝôÜ“:c¦¿&0Þç¸ÑµØ+†‡;lÛ©E™XΧ£‚1t¯p¡sžÎ¹ÜÏM)ùu÷ÆŽR…gaˆËù e‡-N—gµL,BÐÌtöh‹0U ÈQ†¼2¸ëøªÙÇkótDwk˜—;¾µ”0Þ'Ù×U%“´/o…aHOLèêçxGj$Ï0¶ú̯n“ázöF)pb2(6Ød–,¦ÏBªä\vyËJ«ÆQ_ïév‹aߢ Í7ù%{„|©a^ÂÓº~1´û°­`Èt#.q®K¿Gα'ëöãY£ä ˜´HûáaZu¤³yE é>Ÿ€=ǬO?«A9ú›ß t MÈRÀÇ­D÷{~µ-ê=Srôwƶsïg2u‰ØM»£kÖž±\7-á€ULÉ5zÅbu òK›ý}¯øÜþ!³ì†Ó“Qu¥£‘{¦¾ú[0@•V¦$6LHJC¹áÆgï²]YhØ{ ‘¬§dÇ?ˆø&.?aä˜ÅÜ–{ˆ.‰ÂrÚ¸íX4 ®/tˆ‘n'@<‰„ßúû‡-h?Zëéb-9ä Eâƒ.fu@2 z{צÝtK nlëІ9\rY·$îuW¢(qºNË]è\DZ”…9oN 8#áº3 KÂ-ØPcØÞÑ‚OEq|ì8{î\:‡Þ2 Àòå1í9_tc!’tKu8 Ô¤g›Ù`ƒLu{Š´QVa$éúèüs£yzŠ×ùÑÍD â¨Í70äË®Nù¢³¼×'4'1'µW¿»ö T,è-YÁ{âSè×çu”/½»!š½°qZºadANð±\}3Cû ••2ùºìô‡EóáFuésrL… á{“¨)ÈÚâ´Ü™<'{óð‰Õ +É‹Ö|©}Eaå,Ƙ‰ Øßñ‚ @'RBaÐf'íŒd°òÜÖyšÜкpaà§FÈùSihXç¬."a…³Î^5?4ôä—¡{Ì–KÜM.VÅ6µbá8ä>Ä|9ðœU#Ù[bÎhëEo€ÚÑÙýÜm x…–Ä–æM˜k°8›‡£$’3´?/ð/õVä±Î-¸ÇÊ(®¿<¡¶^TðZ óÏYFJFJA¸-cAâÍ%&5*;>k<ã"Õùw—ëeÊ:QõÖ?‰Àµ.#XÂW²ƒZ$åãëÔ±R¬—‘!•@3É´’7j† ÄÃJ$y"q¦Š¹ÅÒ|Òž=´®yv¾KÀ&4Þ¹ååx†•åÿ_ãÍä¥ µX'Ù+ù¨sP!%{ý1sÈúk¶Û: §]À_%s“(6E|g5\Ÿ*çJ…ÝϪ&ÂÝÓ>½ÉCÕùÑN‡k²ysOÿ›Æ3í‚+=§ñNPÊ©g„'tü'[¼ÔŸÅ$?ËR°ÎÚ­bëÁmúwqgJIU}¡¸=NÀkÃoÁ*ÑÑÿ@Kî¨V5'书GRÊBôŒògº˜#`ñx¬™ÔƒèEÆcõØÔ¦/PS­«ñ;¹Cº!$ÛndÅ‚³nÎ:P" c èQ¸‘{Ëw[Iš¦Âw¤±U£¼¹¾Œê²o4håÓ¶|wAV¤LÀ¨dÒçÕWgÑdìvÕÔ÷ƒ•8ò‚[ñÓ‡¼¯4^oa“p¿É˜…3£@Õ„ÎN/Úï‹*+x²jÇ€±ä¾šÞvŠhBH‡Ƴ„yqrz§îµ aè.gñ²ï`W&¬x}¤³5ŽÅWâj7 (ÏâlY³0¨S³Ûý&6°àà³ZA¸Î¦‡ºÑø½ƒ:Ù„~û¿{¼4Ó{R³¼àý ãÇ:ë\î6õÅGr^†soí®%_ÜÁ]2ir“ç.zM )’Üž{y±[f/•°Á]È4_C°Û¶šP+P ÌW£3“Õ¬¡!ʇ"¨”/0Áà‹þ@V;h°u•Í€q§€ÃŸät ›Ðî7–5¸Ž9Ñ*3HŸ¨UBóÏ8:3)0’ qõö|¡Ü=ß’ 0Ëÿ !6ž‰¹ÁÒBz|áõ*pò±?Ë4 {oj´Ø9ê}ÔÛáíïy/RˆÊè4ʲѬ¤°¯mÈ­;é|'˜ñõžVç/b|ÇXÅvZYµˆîGÀ;…4]d˜’tãþ‰K©L3¼ ¾@?„o<<_‹ ±\‰–,cÕÚnCþZ¸G݈‡Èm0ä}Ù¥å#Ñ'0惔ÿÁn…3´Vtüô¨[i•p£a »C iA5]P )z—ÃÍFâÚWƈhSŒ~‚P8û>šž&ÑñÏýbèz¹RçjM$óËto3VéøHF3çe^ECr¿8Ln¾WÀ²xÖƒº«˜lNE Uâ¨H„ö«Ò‡¥õ]j`°Oæ ,UµŽŠE‰“¾Ï…“–ŸoÝàËjÕP F‘~hµ-¢0ßÓó¤˜7—_™¾ƒuÙøÃè(¸á‹ï¡ªAi\„B#¤™žk3Pz…1Ì[áà锨ÛP+.á§¥µöè¶ õE‡Kx$îÛ‚p:sjAgÕr°›¦ÑoBôÊ µi^ÆU²·¼.lÖ—žÃÇâ*©¼œu‘¼œ°@fÜçž…ê*ÌT­ßßòGQ•%Zë‘êqÝ ²¾¨Û«aü;½¾¥Y,-þ Üÿo+H“ïêraËr 4"Eõþ¦ÌèŸX~b¬Œ0&Ç>i.G(…Ó¸â¬y”_âµ=M\‘Ë.{•9­©ënD: k`ñ!W‡±eh}is^•‹éŽ{x¼ÇÂB#1„Á@ ø›íT‘ù‹oƒœ:À­OÖMõóí™Ò×¥}ûðJÐ;ÀÆvoB¢vÆ b™ßkûÙ^xXžÆ«DÇ?âžïöÎŽ} †´Í¸¼ð‚¦cYÕ†R¸rˆÂ¸ 'çÎŒ ÉOZ.%X—dÙ ºhbORáñÎ _Ú«ýp¬wÑ þ/ªr³£ÝÇáá'l™‘U©,>” °5„\úkþö;¦èoo âjohWº¨¯`¾ëÅ™\’ì3À†¬[ŸË"XÈ<ˆÓî*å]MÊZvžRUÚê¸ÒrAœÖ‘’ 9 *PHYôS|?â†B¯»L<ˆŽ“uâÀµ- ,o”¡âŒ¡zµ“ç ri®Ž¤_»8ÈŽí ‚D—=ôëÊ1¿kwŸósèFû™Â°Uìÿ jLdšäÑSòSëï[\Æ/Âbqí±žäò=/ÉÙ†ÿ ΰáŸçãQ 2ý< ¬/ÏNwµß(±˜ƒûÖT~ûy§@Ò+Œ}£B ÙKð§Adø_wР >Zj¹¡khíÿ { ÚbT9Ò49†—yȦˆrGFnWÜü Dpñ±¡,_‹¿°G\Gd§Ó0ûêøQ'v/R¡³ÏqÇ!4…û&ÝŽr9WœT˜Õ¿H¨ÉgÙŠ´¹õµì“¿{Í©‰\ÂÕþДU² O•ζúdÎí=•zã–¿Aþó¿`Hî$¸)¢œzWº~Í4"”>5™ºsòOê83¥«KÌûó²{ùœ2ʦ/=;ìh"Ïp Z®T‡§p*j‰q2B·l#ä ¡“­òÖôO2„ÝOÝ禽7ñ@žnñ”ýÄw¹ø¶mOD…AcN_ò·Ìßãàh'ðkdʵtÓ)ÁåwnZ@fº¤;l¦Gâmb’û(f]¾qwmê°DDqaäÙõ¸ò©ˆè âñ¾o ÃHâ‡$‡ôRêÕ´eWZŒæ”€Ânv6w³Õ/$ZûrÐ_†ˆp/ÿj C'ýe"|Ûv‡X£Ôu-Ó˜õ±]–"Î9"Qº_º‰ˆÛ Z„Ï"\úoÓ•ÇÇÝ_ÄDžÒ8´Í.XeN'¾¢k¦»ã…SsâG§´NWÄGè,"Y –ÏG!íÈCGÖ àoýÜðŰ_ ðí­îõCbcäP{„å# ]÷–7Ê›j[ø¥úÀ¯•ÞlzX,}*”°J¿ñ?ÙW ð©ùN2^^óbã‚nTE猲^Ÿ¦Yµõå€ígI™•! (D­,«þBÔ¡!£%WZï±Ô$)Ö‰v’~Zi©Í:&XŒ…ÿ#Á¾îR@ÿúBÚ—$nž'nˆ-V=°Üò/Ä îÑØoÏ ³êל›ðiÆ7Ϭˆ³£Ó ­0BäR™ÚX«+ìUl{%#s»¬ㆨÆû‰6Šédy&ýâcnë\•7ÞÇ€Cóavi^[™°m_fÀ(‹ e´-9˜9¯HèŒi%u;ÃÝ10ÔÄÓŸð+R8£¢}ÏÛÍísiô×5Ú:jYZ­ÈµP˜4Ò:¬ø„ùxºHH‹1ú´]viŸˆ†Tª•z{±Œ©œÞhÁМKñc@«Ëøà0SbÛ Ï­äc¾·‡ÓG©&3súàâÔnUŸÓlÒÓ( "6o¶ÁÐÍÄ[ð© çïiWÉØpø¡¦Û…‡ïûãGPz›už÷'i•|ºò@²v³$ dÎZî~XYH9¥€™Z¯wßÓlYØ‚ õ±Ê¥ìå Þ´§ª´× m–†Ùk?e¿·uhb ·ª1ü»¦äú|Zq¹²#êt¶ Š› ³øý†Â/¡”¶yD„ Jذ_Ënç%CúßÓ3«C©6ÎJ óMXèîrÚL¶Rö’ÍXÎü5Š8 vÐ “ú¥°ŒåhçsFX¥P‰õkÞþH=ú ;/sêË#†Ÿ‚Ó5uª•pݵ‰èÖ1x/GìUÚÚÿVV4IMCsÞ½ãšZÎ…ø¨–fBd}··A›¨Ûôôhí®@~xÝ6{ª@—UÑ þ`±•ÕDŸjQè®áÍFœ{ìLÅ—û‘:‰²ö2ÔÞG}­dÀg'Lð#* ¢‰ž+ƒÊAZù ðIâ»óž=7[!"$²¥ZèNãå¯=âp>{#`„uÀÙ:WÈn´±ÈÒPzA¹6ï,­ì\Y”µnUÌìå¿¡á<yûجW™­ŒÁWh“lÈ; ÐJ•Ò Îµ|§ rׯ3»¶Òž×^6ŒaÝè^¸ù/#Ó‹.:pµž¤õêþ ÖMÁ¼+d°ÖÕœ _JÆŒceµŸHÓ«8«Ó‚ÖâýeJÏKˆ\¹ççf^A¤+YÏìXcn6–Ó.(kQ5ÙX“R8gï…õ}]Iz¼Ùò5ði ¦ÍóY!ì*ñÖ}u’+²-VÌ)Ã0í½± >aûŸ´Áè9†´ß°1ÀfÅåš|ó×bìüƒ›&ᦂ MNÃ[hƒA¶³G„^3g× "åŸôò+Ù˸ †¡Ñ»Z39/î–oüÅRYçˆM¾ž <Îçá°e“ì$¤%"Žß2Ë¿µ•ÍßÛˆÉQ«YªáYÙmûåöªFÞ/ºow/Š×W¬AT½ùÛÓÒ;%o(>÷ÃÝŽ•/m].\ ½,c’8Öó›2àeÒÆ*½³Œ÷’¥…+J¾Eþ }±Â>®±ND@õ°öÁ¬f6{‡{‡—øUøKìéH-·)ïÍ>#5Bw¾£—×9‰ž´@|±±²”µBoàÍòHcæÉüfeÀM¤ÖQ™5mßp{J·åè2oÚÞ0hóëD­‰üª¿³‚+ì‘¢Hñ>˜ÛÍ\I³Ï ~C¤êeá¢UÇû‚2`Î5±üC0ø„˜fÔúòšÈá{¡è[,YTg :P›ŒyýO*šÇ2`„ÔÆð­ì䙥­9ÂvdFÿ½›5˜B¯¸ÁÅã| (ÄÖ9¶57=„~,Í>WBmìÆ³FIa˜Z%ŽÇôÎÙ.Ýô»-ï·’_#HäôRQì»"!;%([Ù›F ÛaX_ñ/WØuªlD4¼ª”è?§b*(Ñ̾캢ɵíòÒÅyÅ-´ Ÿö»9\ߨæ9è F%ÓÑ)óËDÑ ë ‘<šº~(„‚¨±Têä;¦àú}®SíI$¦wú#Œ ˆ4âãÐuÞ“fëÒri' ‰ÐÍyJ¸—ª7¿_" ­ ¥´Ò쬠ÜÅŽ‚…1Å.Á5ª›O¤Øb+"à éTåQþÖpü¼ƒ(€ùÙΣs´wûÕgx'Å@³ãžÕœ'ˆÔõîåîô/ÛÒÂÏÁÀÿÁiÉ9‚|Bé¼A‹‡øíÀŸ›…£\jÊš¦ö^ăD8¾Á"q“Fßv í’~aáΖ¸ ”ÁWx#`©/;+6Ã_÷$•IâŠÀçŸ<±¸ågAÆs‚ÈôwA‚H ­`kN,52ì.ìá6»h÷Ëþ'UüÄæorxVbæþP§=¿Ôºpõ/Ñ…¯gò[ÿb ÇÚ¥­ˆðúIï³-P‹hv‘œ€+}…µyf!&¡mT ÉQãµ á:ä烲wo‘Æ)H!·.—ØÜär¡­ŸWUxû3¹Ù±Æ í¢–pSÒºoá̾±5-­,ôӨ͗æ•ZÈÔQÕ2×ÈÏKǃ¿¤Qi? ¾^”h;; {–[¼( 'ïM²þ–ØŠ;ðÄzÍÍ䤃‘ž d¾„dXþ@<Øä/‰g3ºô®•)øW€Ü¶t6‡PNPRfïV&’°±ŠÑÇ¥.~%†plýTw“ÇÑP¢¾¹`|˜[þ ºÏFéÝ(ôåsõ†× 癓²žË À’³z#Ù,'F6Tó›çØãp†¯Ù'Ö»Êèg”‚€£›s¶ùÕiªäEeo5U¯‹8h¦­?¦0\¯Ÿt'Ó¼yhaÿW~ñ˜ÊÿéÍÉKÐåëøvN‚²·›¥†¹Øf¸#HrÐËåQ`íS¼²ðåÅŽ#7X-0j¾Ë}* } Õ ó«@ÿÜ__(§Æ”è1Ћí!x×)y¦è«—o(Q¬Y;HÑ?DéÜ1éx hðãçˆÈ?ÝèC³à³i)¨Å“@Ê!ìŠÖºöSÈ;„ûC— òxßãT®YšÑ~‰‰%áÑ®p{æì|¯Àé›8ãÚ§$vÉØj_Ú›£mˆT³<§Ž{tÅ})–T¤Â½÷aƒ¨>ά}—°M¡N%ga¯ÓÁ†O§\å0×á3ñNÿà§Ó®y¹~ÇŽ’ÔçÅmJJ²)Z;€(‰ˆ–ï-²Ô Ö‚j¶¥0±8ÉÓà·Á‘ƒi’ Í^¯e˜Sóž;®”ˆë…e°´…:š Œ`ææšµ¢šÈ>Ë ÅQÔáø*WÏü‹Ã¿8Î ½•¼D§E˜ä"“šgéÖ8·[ÿ_®¶+HÖ‚È—Ý騯gÛ=1ÌΡ¹›¾‡»nù°Í6I:WlnCX° Ü=^Ôä–¬°O$¼E‰Ï‰.Óþ(±cA‘Ò.7hÆèv¹²©$î&¨&ÎàƒQµ)b3W 9A:øœw³ÅÏÎ4B‰¦Êo¨•r2Å–¢R¬ ¾ótÁÀ±¼¬E}¨)9²á$õz:G¬Þoj™õÁ¿Èut÷¨—Ç œUy ~pÖà½MŸwNܶ ò[|ßQÁ¶¯0'ÕÆ^Nµ&÷.–A¤°»Úi ò8¶ !OéÂ33"¼Ð²`9È[`Üj”ˆE÷¾aªYÕ?›Ò “‹1ÖžåO£±—ÚpX‡´ˆÆNÈ^ ?:ŠšJ»µ} Ýk+â ‰–?ë €Ò Hûƒ1#¾Mœš;¸;¼šÈ¢/xëŽeä±tYt€Iq‘`™â®Àȯ‡hÖ±ý@ F¨ ¢ýÙ„Â/˜£.â-T!ÖŠ4Tr@ˆù"*ƒ„¤ç eŽ€ ’_ÀÃîm•ÿA©M™¸= ÕNkÙ;…7ýçJ‡r2ç3þ§9Å­’¸-¿îþmèÙ1-CY¦æš«71µoÿdÝ Ç†gÓD)ÎmŠdöº¬J×C¬ }yªmrfpÄï`ø¥!ËUª4Q,}SXs1æÖ£úÙ‰UøÔ¹ž?Ÿ—|õ¾Ú¬rÁàT÷0÷²`Þ„â˜P¦Ï#U»3” º°czHh/!òe(þÉAù®Ú•õwßzʃ`hqm)än9ôÎHS©«nýÒŒ¿‚Ä><ã«x‹ÁÝCòBn£ä-¥!Û5‡@ùÊõy—EªÆvÈ¥Kò¯+ôŸ’ÑÜŒÀçÑ¿íeùõH1jÏÀ0î8€?h01D²¸»2+ë³Â—Û'Èʾ+¦¾älÌ‚ÊIˆ&âÉUJ9#qgò ùiZ€¦öÔ€\1øgê"1kÔHo¨0!KôOâ!6ž&9;ó<Ñ^ãü?fÚ~=j#Meh' ½Hò’ÆÌæ @ü .ÊlÐÕVÀœøõ’˃Ȳ“Qúñ’ðgLþ‰RyI{@ócægö½~ò—ȆÕ6ú&’ ¹£—=Î)^†ÃÚueraÁ‹ÃIȤvXR«Ì…%"ÂF¨W‡˜É®|]KB.mÁÿȹ`ÕJUx·†Ž@’•M™@tý$ü_ƒÜNئO‚x–¼×± Óçù5 ×t 㵯÷xuç°Î€?¢‚vMÍ›SPWt‹É°ïaóç@Ì¢Õ½83æ†4ÀL‚½¸i êk@rÅàa/³oêŒ_Í(›ªéŒ6pg7T‡”Ùd‰z»«œ¾“#—Æ?[/~xíž;¶ˆ·6­Tcb±`î/G©7<[Z‹eõ¥½ß¡Ý‘þ5“ç.Spppb+×µbdˆÈ{‚­jαTüÅ<’Uƒ?E,E·§ì¬ÞØì‡Í=«\— ÛXJÍf_î|W”µi$a1 h3hÅ.vRõ.j2X°pì”Z¯d‡pÇúú:{ñvlj»¸Fˆi“ù÷f,Ľ­þ7ˆ/ÛL€*ÝÿÂåý¢©RÒF xrŠ,µþ ¦¥ïQã‘7¤AÇ}-ÆÍO3ÞÜuíI€æC¯77õ‡¼!í¬5YÁk—Àš]¿°~ ÜãL> 3¢©ICN '=üë×qµ¥Õ¹ßé¼FÍbÓð6YW5Lž¿,!ßYL$kÔP!&°”Ñe¤ªñ‰´a!Õ¤_=(/À«Xn"ƒJÐì&¶Va{´RØÈ§<Û%å*¥ˆõ:+³ šqÁ•%! ¼¢Øå¬m¡Ö”g¹?°ƒ¯ÏÕ:å.¨Lq¼ª\¾îŸ@ ˜§\Æ«¹Â?@*ToÁ‘â§ä˜zÖnÞ8’ÄGX £hThkù?ÙôoÝwåü~´$Ä"¸cNhop'òv5:ûѯ¿MŽm™KL—$U‰+p²¿BÖ"¢Ê˜´+Höÿ°wz=’¡}¸ùLœD §eu{[¡%Á7Å’TÊxÛ®Œ«šè܉~Ýûi@åd›«^_Ç\gžZmŸ¦m¾Ry‚u)HPlÜsúx2z·?‹:r( 6™`ᤕÀ[1dM"­Æžg:cõ [Ü5ÄRƒ¹–¨à<Ù'¡?!ãÁUsåεÞ@õ¤FT+ÎyP ø@@­¤s‹³„ɇxö*Ðx¬ö3¬ÌáÉ”lÍÐm:ïòC‹›Ò 9ýžÿ"X  ’Íú…òzöŽÆ8H¹›…X=®tõýýÒ‚&ر,Ae½Ç;åú+ússHx™‘SiIï%¤òæ²x²ý;×WZÀÛ°þ+8– ÷qAU7ô_¼ÈHÖä@Y]9»u1—.ª˜ÑòpÊFàëÚ“×}G ïIj6‚-S‚™‹9&Óï½+‹VöÀÔ•À4x·‚x‚ƒ½Šµp'ßÿilnµõ¶Ý–ø+c•ì%Ϩ “²6?xÿÉY^Î12@Ê3¹ŸÚWtbÝРoâþ¥—¬a«PÏ¡/]thðîÃiIA0„#f¼9‚òçóHÛò©ç²ùØhoc‘ÚTõÒS;eÓD{GäŸ!eÛ-ÈY†]Û×TÎ%O"Ϋّ(}A®Dó;©fbo+xíoþ°°®B ìîu½>9¢TÁZ ðʽåk«8ÇÀ[a[ï!zˆ‹´]D¸ûÒ™I¢v´ËDÕàß}3§ƒ&y%ä¡ã–RKYh,/ˆ§f*ň—ÕW)Šcjz8}T¦5‹bEa-ovRYsËðìð²ûD[ÐÃÔF«£ÝnygÄð‘ÐV7·bÑÝ¿Œu\°`îž½ƒoYZ†XCòPJRžD8ø_þsr”ßȈ,À„1ý,—#bÉç:ûóEÓ¡nócFÒ6df^‚ÄÜñÆ#ß)5ŠÐnà *ÂÑ÷&šŠ x˜"êò¥Q|¨ÒSZ™Çá×E&~ktMŸ.HBÞ³ž+!‹2xµ@_#Q1‚¯Óm|Ö8ê±-³y/„nµxÈ€èQé8škT«û÷›„LÜdeˆØ‹ƒ9ÑÌg©«Ý¬õÂï þt<¶åú?L‚‰26XnIr‹j¢1’ÃÀ-‹/ ´gõ@ö6Ô º¨ƒÊÚ°Á%“äæªĉ¯¿ò]sê`áÒ?Å3‰Ï"ËØ“¶Lc‚Š«]å0/Å8§¨é›ð8àfÉÖ*¯ýwÝI"²úº\$(îQ17½HÊ(Û°µSÌÿèpßdŠK¥ÈxÂwÚ¶ Eù “µÛMýØ{ÆOý“®=i¿ËN‰áÆ) ül Ñé $‡(¹àÛ@]å9Pmeú-_:czz,øåXUfgÚ_»„Q—ûw΀Åz5i¬r'_|g„‰0×oÇÝûUQ‘œ¾æ`'öÇý ™ð”OÍDw^°=MÉ¥8P 3 ‹úà‘ŽQš%ŽwÜJ—´2œKYÅgÀÌÏU>!&TL¼ÇP“Ð itÒE‰$$o$Jóµ Ö0–xmóŒ’v$(i"€ÒÊú$õ{%–6Bᤰý ]~bº$¼Š V’6á YΞÞþL2Çè$5ú§mr0C‚”2FÈ)w«$x¾!ŠVîŒyš?&ÆÛ'æ¯nž}¹Õ[ÔuÈ(?,ÿjzð¯Õê¡z©kW'9¼$9MWPyBÖ“âÿ€\ Î¯¦”H®å-#¼ÂàÁê¼'/ÎÑ+B·Íãp×Y¶Žð~V`sµ« gnfqÝô Ç\¥6T»\ˉ$$ìCKÄ ]Šª.LKµßb¸3ËqpR¥ô¡‹O51´ò¤\õN¹Ø$Ov|$n!OLKDÞÑÌóLé °÷^h¯W·&^x¼1W:¹ ýK.–dÖ¨¤@Ü8ÔAõ~y¤>Ûý*\9÷ù’y+GƒF$ß¶œ„y´NÑ:Ù{ìe³ ›>Úw¶so98ßnáoñÑ£2ѪU’„›êXõɾ3ªEºÊñÁÞûù`›Ü+Ò»…ë )£!ÙÇ?¼âÊ窥ðåíWîm±BS¨MåJ…°&±¼¡nç9Û™Ýã$ý4賘B¤3';Éݯø-cºÈDó–§šîbÎüц„N²_¹øê:\5‡ Ðéž½w©ä~û½º=<´ó#R hðò[­l¼Až/YgÈ|¼_Æê5¯Üî!Ùj®Ù Â.բ嚥•~¾©,>y½µtÑ­'$¼¤\£!7×¶}×Nz+@®Û&ØË`½˜òž‚ã¨î™M²úDv E^ÌÆK…."aeXù!;ʼnÚ·Ž ŒÞ‘îN3Zo7 )”œ¦ÏÅo¨¸£…› ý¬—Ï¡RÉm`ç®AZm)>®t ®1‰¼vj Ί–þ ¬aC)›Áð4Ñ–ìHí p'§•,õÁÜq˜6*ýUæu¼ÓÞg¾4®ÔýÓ0£AD}òïù«g¨˜»ÓhŽ\~O."A‡ÄØÇÖœ z„OW ¬ãnQ‘´.g¢0:7†Vm[^¥5)blAZ&”G¡ƒ‰¾¾Ù‘¥ˆ¯qÁðÈnS"/#‚KÊ’«á;]bŽ“Q¼‰fÎaKÇ#õrKd”ÿVAhGÅ^í!3Àua¹4¹ÖA„8jòâ~ÿX £Àòtº,.‰uÑ/„Mr׆ÒËhòÌÿQ„;wˆê%>Ó;QذŒªu@8ë @¶Ñ©ǾàÜ¢Ö7]wô"ÊÆì˜/#Aߘ㱄§W³µ9hYé« KÈ+h+˜†×¿ÍG¦úí¢îC÷?KážWpÌĹ»šY!EQ†~9Æ ¼ãCÚ4sÔL3r¶=9j2ŒßÖ]–Yï1˜¤äbûÿ²6oWޱ醮ŸÞ<@Äž[c¦:·j ¶gÁaL7<ó±x¸ÿÑi\ÂÄ’µ²¥**]¿pdµŠäUa aº.÷üVìE8i$4YUòjÀçŠgn¡ÁY¾’øZ!KSðå€ÝÒŽÒóŠ´R‘Ÿi=hŠ^Ýc+u/ŸÀ6 ÏÇAêÒ¿nþ2ŸàÑìžškØÖ:ßÞ t Ê™p¾ÅÐÄà¢Ç'eEï5DÊÓ“ ™!OaG©JÓÉûV;el³àˆùÇ'ݨ®š5jÜ]ˆÅ µêÔà{Ýs$ íÚÑ ¹µ¯&s4„Zˆ|¡-œTÓëkœÁõ·5%ž,Ðýœêø¾\í²ÇÑ$v‰NÞÆ† Ç¡2o %DÆ€@´^zû’«\É…´½ÊãBùÚÐî’æZÝ!—7@$ó~¥ã ¯×_Kšgx^(ÿÞÅw·‹ÜRí·®g±sœô˜cP³©NÇI…Á¶‡"Çö*!yÀÈ>Ðk§Óì@­Úü^ É×þþÌ€Z¦ö‰Má„f¸ÆWÒ%æóo#;f ù;áf06÷»‰:/`eßí{×zÄfMª= e×d"¶ë–Fy qu=gÊ5ÆÄ®fRÜYpª~ÊŒök©ê¹¹›à”=f`‘Ð-›N+sÈŸB‘Y|CJí-GøñP7ùÂÜŒ[mP9ÿÝÝÎaÅÐʘÉYßÄl™»ÓáæYWŠ Z †„V‚ 3„tu¦9‚ì¥û<º%¥æÕS8;I&Ÿ(€#ÏûfÜbð-¡Û†Ê7 »&†"eI •ÂâžÒ¹G}2P±I¿”Ê—'d7ßc.ûl÷%Žâ£Á±®¬iÉjgÔ4é|åìmÅ(ïýóz£ðE 1\n…èùÎÇfv§ êzT2Õ‰LŽÊ’&t—o,)TÂæL2’€æé9âá§-[ã•d>vÑRY<ø Š9¸ÖıorMp~a®z\à°¢_ñ»á#ý['ªçº ØÃÊ,ÉÄi ° mØ6-¦ 6  *[ùá°ÉÓlÑx× hÉ3$ìúSÖoȯÀeþV¦´n IG½çJ‹-Ò ¶Ü÷üAÈ«ü\˜Ü„Fdª’bîj¤êgŸë­ºI»½Ýø_ÉL©Ø²Åqyòz}:hD†ó%š© zÚÀ{M0k³7lµ¾¢µ7Jª#p›¼j¥–¶9åŽt*YØö\AñÊëaî1«r5ÓâÐøîîÅâ9‚îyŠÎ?7§|C°‡Ê’û4%©žo}—µF>DìRŽäõŸ½›ø_-¿v¿ò’ÆXðª•<ß1:ùTôM[&Å•äO,¤(ìî8À¥écZ¢TŽÎµµ¢‰ü4 P 5V’ëPüFëŽÄVµv™ÎY„À¾C²Ì–4&Êo ’IÒ €ˆO×¢¨z­{c 9‚ç«Æ€ª™´¢mÒòxØï ÏÓ2Œûж>ÿé}H´‘\]&Þz0‡Î¡³ñ¸Š==¨¿Â"*c S¶ñ†ÚÜ{¿wçÕK®.‹þ²E *'?4G#µY¿gþIµ a.ƒAäÅW9·*ª¸l)°ü9–ª4Ó^?E岪!4VÙ»´¶C­¾:’Ó—h|ŠôÓgéhÉýÆ$7N´V+v‚…Y‘ëâä•­}׌[?Ù“ËÛõðPÉÉëêFÜâ×íJyü~ `lKâè`{VÉ*¦µo(XíÆÍb"ÝfnÚ:GêÝš¯UL2sI5.{EÝ÷àÝœ‹‹Õá'*GŸ÷/ÓÒK·ï•i±tu‹-çÑ®\æõÙ N{›]|ŠŽ¼:ôµ®Cé.ÍB»(­ X«…‚BrlwoÛ±OyˆQ£ïòÁ¸À:>5Þb¾ÀÝgk©"/ÌšÁÛ[;¥0IQ(À²´®AÖ Ì~I,Š ß®g$XqSYì`ªW„ÿÔŸ^ º¨wŒºb:¦JD“Yún¶az²Þ¤ÿ´¶Äc3°ÌÍ# …¬}9½9¨õh™tÝÞ#÷˜>"äQÖ‰õ¯_>s´­vü´ERišömkJɵŽK ¯~ á FY¦Œýì4†åhëC±-øå\hëzdõ8Á:€¾èßcd`„6 WàZy.‘ÄØ ¯Íð=ýŸ7§Á°§_𦈴uøŒ^ßÖ%v5²Û FÖ>à$Ö+s×'P@œN‘K¡x߆hß¼¦}Ot@ˆ4Œél–œäFTåf"¥î52¨åÕã’XK-ðHÛ©›Êñ>qþ%°5zcÍçÓa°‹ê94ߎ½ÀZ:,tADØMغáûÓS¼õfÄÔ*ùëL{‹T|×^Z¿Š^è ¯­´hâÂw~mcº™î¨^¼©¢KÚíG‚ZÜI+¨jÔÇøxèÝ<¸ ž—„Ê̆ Ø"'¬›ù•|lîp¢¤åTC4¤>™Û«ÀWä"^˜ZMÉhWÓKøùWJrkkü»dprGÝiK®u™£e›owô Äsšb¾GÄé_pcù(àçë×àT‚Û”Ô1/ôîkT¤S«§ÜÈG±æo¼sŸ¶~æ«ZWax’„¬äžÅ§=Hæm§5Ú'·ËâWé#YŽßÚQKzriEâÁù¤0á¯ÔšÁÅÆý§I‘<7äÔö¬nÊëÏ›Ž‚ûäÜð½S1Ù~­‰tÒîaÍ›Ђßå~N'YR¥~Ý·U»sp>bU^`pîªMê§¶30Ås€Ô’«6­@·%@\zd Þ1ÆÚ²¡–ȉ·îDõÜTÄ·Zhâ{!’ƒ†ýüƒf¢¸?wÛŠdЇ®gö¿BéÆì³‚ÍhL8ÁÔÒ¼°¢ÿæ#uÅ¢Õüõ’}fÐ;¼¥te0ø“[{Ûø¨Âýœ[Ïz;âþÊË;€ÞÓ¯d“Ãì ’|ÚBä,ìsíjÒø>,r¨£‹ „òRd³²°åmþ+áAB,ïàÌZ2ƒ´–ž³6;ê@¡¯V1Ñ04ðªcÌ ­?ól^ÃW)|ƒb7oÔ"{›u? Þœåú7¿¦£“¨ájJs@¹Z1ÍgÁò¯ /X‹Ð/ÅÍQß¿è,ãCRÓêÚ5IK‹ò›±*m°iÁûQ›\FêùêуýQ ÉsL½Ö"]#©Û¹òáÅ+Ä|{ú*"½€¤9Ñ¥múå1ÝI¹!¼’ Í>Ãè%šeÿ,/ÿ®1 “‘#%Μ´ÜLûŒ#‚‚‚D¾£š.š%l°š6½¬óµD!؈Ýu–g•Ê€©¢lp I‚éjMðo ¤GÄF.ëvé+¥˜{«!åôÈÑ´£5?¯gS¾•Í=§Áæ½¹]€§•ö¾Ã®˜0¦Ûü[ ÷Bï[|•sÜÛ9Œ–þbîPåˆîÏÃíèÜ^À/ƒ¥>Õ®:ã ÔN;ÊÉOœ—hxYÊTr0©¨7™.ÿ àºýœ$ÖKÿÃ+•e‰g£®@ØÿÛ’®ÙSB5î ^ńť “Àj·ÆaËýEìÐ?á±ÐÈ×ïìÌñ AŽãÍp³ùk\g-H¾}[¶8¿²të„HUÅê¼Cüüx\Ì#A[èø`šv›–ð3„ÇÌD‘ðÃù£bV@ð+JªF~ 6@ˆ bÉ>ê6nì¬SêèªB«¾6šï¼…ºLÐi]¾<ŒZ&ƒ¦¿¤‡¼íã²€ˆZ™ÀS¿–z,—#zëx§ªT>?×ð^ª6‚Dn™­õ@”è4H,ê™–:#r 1-¤ñE²EÞnßSˆ €¦Nù Ðý¶Þ›†Áìä7\Æâ¥²’æñ8>oçéÎÐÞXˆŽù\ ñ°˜qöWþ,'kEœŸàf“BO?ß˃µüb}µ’mZfW]P.ûCèÒW —îf`ŸýÕ'”Hs fN2ÂÉR›‡\?xã^ÏØEgWüÁèiWR½þx”×{|G•ú3ËN˜›¥CÅ1“{ðãÄ-óPF¿¨—wÕ¬\óŠò S”ÚãVÁdÔ~­-{½Þ‰™ ¾æÒs4B Ï¯åYñÐÝÃe0&øÝ‹û,DoM xXXGƱÿ¬ì¶U|Í쳨¸,qܼ™ï¹ô—$a‡!o¤Œ‘øQMHMŸŽ‘~ dÈH‰šl–úí¹ºM˜¡à ,ønøÉ—ãÁâàsú®õ:ô9‡¿‹QHK¬œþâÙ €º“?ÂYÚ?#êZÖu‚‘gÔÑT4L»$0][äûNçgØ7Å?ì'pÑ3¢Ñ<}ó6ØEÄ+xõÎi`O W†%DÁQ'R‰ ´S@œÍé,~ øtÅ.æ£ \=…n »±½@,¼3(êÎåÌúì ò¸ÿÛÙÙ-YnëC@Í#(¤£iÎãêqß,¢P½þÖ8C4 X— UwL6W •Þá9( RK@W5®¦lL.)¹c\yúý"ÉUth_Ù¶¿¾†Èá^fö@œêaà B z„NI­T°ì‹9Ÿ€úT„)–ÉóØF°sFÅw±Æ 6²H¾ÊönHm9 ¨£”0꤆,›S,8ÅNâ“¡a1Ož>ˆˆ¸Û…Z<<¬þóym¯%±ðÒÍ1ì R€ t×ZíipòX­‹I•Ç©)_“ýVäKåçgX„‡‚5©/²(qÿR ¢2úâ@<ûi‚Ñ›Í<Ù`–~yø(­¼`Óµ|Çôm©Xv*¶ãßû*M†éuxjÙ ¶,ãJ´TA]½ÝÏrÃõnaánz„hœ4‰ñÊpDz¨gm¯§ý¦ÃÿO é~UÓi7ñ<¿Òd|Îâ ˆºcÂq0È6,DHä¿&á~ÿ—äZ/&5¡¼Jô“ÿ0Vˆ]œÞä‚Û6mÝš2K´äÆÌÒ\•XÓ°^SIŠ{3`i»P&F>I~áMÉ$Þë{ߣÝÒíï‰îJu䄈f@rëpt#j7… ð#–*² Æ‘øtïòÇÿnl œþæNiS‚#¦3+ÎD~Z9•òy-]Sÿ»ïñMµ©‘§2£¨†Q2©#jÍ߇+lÂí]¹= V‘=ê_M¸áî¿3©Tæêø%²$§Å˜J'x¹ïTî›3Qe±Z' ·e^KØÇtiÕ?žŠiq@í÷§êãžÎK‡‡Nü| áb¼™}äVãh$¿DŒ½4ØÏSë²-uwýñs™eCù{æ ê©Ú”FîfkÁ‚Чdûi+ö¦Ó KNŸM ÞXýZLÆÌ¨¹÷²'Á?t’¸ðßæÕÊ$ËV mÍ$í² ‰zŒç„3½[é\‚^­¢ÕûUdF\›·köòòÕ5Ë?k°ÆÌíóÐeê– h+¨ ‡'RF‹vƒÐŽÌ „v}¢˜Žå¨w¤{‡ÃM¶#¶`f²ýtMÂky؆ðØ4ÝŽãPt"…j~g£èþ0~óÁî(wŽ+9Í@ò âPß@“+ï‚}9’ê ˜%ýøsNCP>£ZsÏÕÐòÈ©œßÄË]?g¥×ÊÔöC¥º,“ä”ïÚ8!ì<ÓšYLàqƒñ×NbÄxʳ‡ËÐúèlº;œ7{Í ¨–¢¬L‘‹µÊ{y¨HƯÅÈ}ÚWW VŒÉl¡úJ'-òŠY5<èý*ïåQ10ÇðÍcdou÷Þ *0YÕ½h­Ç?â9:æÀ^f8_•½ÎÇÇIÑ \Ìù0S\lM1­äL\ÔÒ P:À¤U²uy´Ô©Ëd8Nz3e݆’.œS$€5÷!2&7|ÑÀA”4¼5ª«\ç#k×7à°hصlÌ‚öE2îx®jã%è‚ÿC<'«™ýC~ÁÓ˜0ÈT[²ÃßQÊXÄQÇ–Ñr0u³g>5pjŒ]æoí){”  Ù25V!þðÓÃø~ äý))O`Ã-ÝABÑBëÏÐ8±o~­09ä'®ù:M“ÙŒßG3áÄü¬ç}h#õŽï ÈU);z+~Pn È«4Ø…š‚J4ÆÑeK. ‡’‰d"Eî¹’WðJWÅ㎌8Ÿ×ç¿Á€KQ<0ÁUôÌHâ \2“VvÛT0e ÈŠ<p0,Ä=:Z›òNCl_ý‰ôøý°YÚ O7­y© qôtUÍŸRÜÆû ΩËZŸÿ#d*–ðÚ+±N½„í^\î©¶2žaµÃöð½DgsbB[zØX¯¯…~\›3(…Áb¿À’2„Ä BQ+®ž'œ¨‚\n×ïe‰(Ë¥µS}˺’E©—w—Í$ÎÆ’逄¿±š½ëä+·l¡ÑÊ'±4°Ì(—N—ÿŸ—¸˜tEüXñ·,±qfgéu{ì9àNð€Š'º‚Øú%»«$6å§ÙûÕüR¬~ihkí7Åá¡át0íª²Ý^*W“þ]7£è7”Е¡k5«" ·lg1yóÙ^Ë€¨ &xM yFí¤e’ÛówNqF÷J›gt»Û07%nÀ§(Ú•ä2“«>7‹ÓY¹$ìYÛÇm>¯ÕÅ÷Úß‹9ƉÿIºd¿…6| ]¤œ%?ëî?ñY%툒•Fõ¥¨Ry‚®å ›£–Xsm3ºˆÆÛªu>œÔùaÜýb¾.Z“[.êG —!ó¡µ'e)Y'àø `«(Ç*¤AeD­AÎJ>œl¢ N-x:ás¦ËÉý¬Kc÷Ñ¡c¥¸,çí!gr"0*%Ûåþ¨c¤Ü£â€[Wê{›‰r¨±/| Â빎sÆã3“¶Rèù£H%ÝI`îo¬|£ú1¨‹9¼ÓÑY]Ð9©”E-lq“Ï ¾eãTó‚“ã-cQZ«Ùs)þ;E5M c®œ«´JLR]L|âå`•å¸N˼©ý˜•`ÊJ !üβ?Œ9èÔ]0…m£×m‘&xûs«µñ‡J ÌÒ€¶þ‹}«´ý &Cd àJÝ+ŠIT÷TãûÃÛU*‘¯‚ögqóáR·¸]Eý™½6ˆîŒM/G† ¾ƒ…bµÖü‹Ð…*kƒ ¾É˼íÚ)¤Cß÷Ìà=ÖHÌrŠA^j]o ×o5çÂó´NJ/B/•ZvZñ:v’í:a˜hö+D•…z¯ù¡²Äqa@½#äÂÚ /…‹=N{zYÏ*{–4O%߯gû òÚÀÃ|)Ö&íX~)W. l÷‰4!3U1ZÖ wœ]Ûgîñ‡Üâ]#ªïÆ(3O‰™Qò`E }”è×c‘ µG²­T–¡.‰÷‰ åà)ãç×4«ì¤­äeTÇU Ûed`>ì³²jÜ_7Z¦ývu ­ÞULè,’1f¿äO{ŽùêuˆØ¼ÒCŒBF‘b^’=@Ý!̈i øºÔògpj¢øŸÁÀ}ân×5-"Ú!%ìšOÁ<©°ôµð:™3Ì]G"N-²ƒìD±vr×}.Ú(•aèbÄúµ $Z]„r¦Ò@9ÚàëýUdÒzeŸ—výÉNÎIµ—¯¿äKíË}{‘1¶pAúSIß@I~Ñ[UµDÙˆ)Ì qØ%ˆ9Ö¸U}Ä'c\Ä@cð=ë:Y‰£AESûU ÷/î˜]‡”8ò2É4_t)£ñÚ”ä£fþÑ)°:cF‚¦#YWÖÕ£/³Qœî–®»ð’Gã?'úÝvN…9‚{ ›ÝÊj奔…'¿ôXSY÷ ƒ…±Ãlì—,7Ž–9$ÜØz¢@Ë­Y•·f¬`]‰Z÷¤û?â- æ¦ACÉùá¤Q¡\DÄsìõ²„‹Í0 €ÝSAýtˆì ßëZÄK75è¬ÁúÒ¿³'”¿ÜTH4&óÍnÇc7DÀ®ñ8;:¤De¢”æ •­ñð¾ùbÕÓJtÎckR¨]T2÷°÷°ÿÉÞ朣鈨 ·¶k¯¸­G ËgÞµ!ÄÇR’Ãs´O×+Èþ”_Ê?þHÌ  õnô ÊÄ…®áôY|A5pí7•ç"èÅ5«¨ÒʸSv›“hå<ºI”hh!\WRÏ¿®ÎåÅÒôjŸV÷è%7¶£Ø‡›] àX2`»V¡ÏV[ªíË){ »ì®µ×޼9:– Õè¡wÔ„½y£Ýy·=và§›­FZÀîù<×3ðRq}è‚)eØ\ŽÎ[õÅg[Ol_t=,jKà°I‹„ê9¾|oŽ_8úU2˜Æ«jl^p#´ø¬RD®æ:~”]‘F%Zð˜34‡÷#3¹RÂ]ØÝ#F™£˜gìgЏOúiøÅ!ïWç>#ˆ¯FÂE1ÉØ’Ü&Z}S^éììW»ðGÂ%ãX«ëCIDþ+Ù 4ð™·‰é©@öî¾s7è`¨oŒx®N‡ˆ¬¶MÝ#ŒÑrYi­î‰XGœE fh³¾a"»¶D¤7ßðµØgÇŽáR÷‹Óþƒ|qPö¸FózhçÎR”ÎÛ«;aÂÙÆDþ'J ˆwVv¨I7>ª þ\Ùê”$,CnÉh,;UÑf t²ŽË×á6GB«ÑÜó4‡`ò !î@õhØŠcªB0ùí4Óæýݤ¼oNæ ¥ùŸ^Z¤™Wµ!«ÃŠq=¥;CÈðWãõö”5§lÏ^H?59`ÝIæŽÌIvN"Ò˦û\¾ |bAo9Å01TÚ'»…ÂIî|ËŠ5z»ÐýµD(Ǫók ªI´jþJÃ0ŽvrüoªF³ò^ãæƒódGä*@YðìÓ¸¨cM2NE J[ílØ©;œv“|‘]G §¡"xËEÝu#ðæ¢½`÷®¸˜¼WüŽÛ]p O½3JFT/žìéi~'£w]A³a™B?A|ß©c¯àöa—á`G %Y'ÅÖ+OÃJæ' ô¿€üËê9 ÏÙv‰C;†›ÑPY—,⹚ùîgZc„no[凔çq8©´¸íà œj«‡Qà\¯[Lë_bX'â"Q%†t.\TÛ`Û}0ýì+ßšB.†F© q»µXÏÐ( ¢½z¤”]k’W…7ìG°Ý)¹äOv”¨œuo5>µônbº_òªØ•Ä6,„AbF3¸åÿHà ŸPµ†§‘4‚Y6sã“N(èº4úiO nµ1£Â‰ðÕ„òð]Ù幚_š¨­+`Ó@9N»Ã®y?Kµüb¦À°‚¤ÞŸ u>8¡/ŽéÊÁ)<(ÈÇØfžÁØBàN¨¬æl ¹r‰ ¥ºÆo)¾è3['x“¬ìÂgOÎÍTþŒ2Kú~ÁŸ¢á-?©*Í´'ÑQ&6ûÖ7ùµ´*X%Aѽù³’†û½¼ömt戉̲ŸMà@jØÇ)ô~çÈlY5œÎT¿Qyf?&ÄóÝZ~Û/SÎk»'îD£½©b°)'žô¸ÿimAn±sª3™½^•k«¿}áxE`[WÂݼjÇ1ߨ^wõÐó3>ÿ·\XœA¢@‚¤ û*⮉»ž:?M·—½Ò±W¹ÿXª­_ÈözZ GÙ`2î%¹?C™ú§ÝÌ©ÐÞ &š&äú_óšT{è ;T—R:ýîÛÑNSÿXºúÖÁ í_ÀÕô'Ä"Œ7*ùÀ±æšsLò1Ìà òÖ1CMÿ}AQuˆã®0ØWµ ïîÆÀjü)^ƒ‚…ÆÈ™Ô‘1jŸ¿Ñ’ü¹›ë ˉ ì·ÖR ëEÀ„±¥ûyá…5nÉü‘Ùþ›÷PiýP{G#79¼U/% ÿÐûè›)o¾Bk·²½ÞÙQ[(S呃”£ƒ³ÓñâÁá•JuI/›)ã3PÖoÕvÒ盺x>˜ÒõBãE'EÉmewyjË)cþή²HQNO[{ ó¹’£¥h0¶¨…ïˆÜâáÚeŠ­ËiÊq@J@îŒÜìtÂ8ÊÚôïÆVAןĦ“iˆml± ¿·Ì˜*1^Õ”M×jE[[è Ù1PãVfWb!ñA:0“ÎL ¬ä0D>£'…㨰n¸êÐp Û¤ð :Ìv‰ØêW³È…’|;¸”¬;Û¤¢5k® sÍvíxBà3C¯+4G¥¬U×kà%í¡Òr~,ÀåV>r™ÖçÇyác¸¢ª0gY5%ïãÈÃfIÓ]¸0kãõ\A@@k4Žs ²uh<îSPG>AÂèk¥9PjÒ nl© Ì,•(ÕeúU—xb¨Ë5XQ¤0 ë×[AÿWwn¤ÉÀX÷äí´Ò¡ÌJ¶<á·p`–yÛÁ­»K”ÖŽ¨ƒåQ,P ƒú3ÕÌh8É­'¼n!.Þ°ù'¯@@ôuW˜@¥ L«Áuô•dS8"Ü'@….Š(í),4ˆóÄœ×#¨ž¯ðÁ¾@¡3Ù–†KÒÍ"†µ¤ nwm¸”Ø}Ä d÷µƒ³Lå!pló–p*í×J€î2eŽkïôN³›ßÅú%_1[ѧ¶í4åì5H¤F´×Õâô¸ádSM»|Þ¯VQÎsİuí 7áRræÍ•ÝS¦ê»T§ç½Ê´¡˜±{PB‘-Ág&K¹»m’Y8Op÷ŠAHLäÅK0šMºîWQ qífrR Þ¤˜íè vbF©^WOxRˆÇÑ)ö·WÑZ»íÇŸg£7²& hV\‚IÒ©õ T¡C¬ÆÖ˜ë¦3ýÕ@”,`ÐIFŠÓ‰q²V­E–™=Sî™fólW—'òo5áÒPåÒ>\fÁ{«¢ànt–fYÀ+)µn¿lÖ.ût˜Ë&eá}SO|ËSíQi‹?VÖÓZµa¼sàe¿ýpðqköùÚÅÄÊ_a‚¯;ß‹*„¦^C©mo§¾Ójâ3^g 9Wùû˜Âãö8SõG±WË¥ >céxNV­% É3ýMÑ[Îÿí?ˆìÝ$+y"›9Äx_õ,a¼©Cc¾5³¤Ýó &U¡RÍÓÏ?E×r6²¬A«pï`~ØÜÆ£Èé1c! “j䲨£r4(fï“B!’W*‘A}Îdíì±ì\«Mê™%µ ì4쩳YØþ$'ÖªKŸ•šîÀy#>AGb ‘"E¬gE6‹|×èÕh4øa1_‘ëQÀ`×~ä–ü§·/pV_B¨|=è4¾ÇxzWtUçÅ7-¿ÂÃ}ƒ L§î”™¼Ÿç1ø’HÖÀoþfÐ[öî2샂â…7@qàïàxJcÕ Uv1y/λç‘»Øe»Š…Ù…G5šÂ @د'l{ë \R‡nëG!Iø¨§£\± ÑÔ#ôW´]¥–Bîm±»ëúx¥uØÕô¥gð Âô.0¼„¥ÁÀÎIÅ5kǪÀíK]&æ_Õih\Éx¯(·í=-évx™ËviÏþq3à‰/Ãç³£"õrFr µc.@ô…;ì¿C«¸î–§ýœ _N‡çõz·é¶Õæ£/ŽX×CH6輺Œ²;L(P(úÖB_æu,1’^ÿC$¸á´Lq˜—ˆÒÑÁoö6s Z,<Ò`jд{HòŠTnˆö*(ÐJ7§þ™WuáázàȈ”ê:Œ†m>¡…ŠÌ¦7’0Ê8LHØ èß׉ÿH"JM×ÇmG&%-‡ªttÙ?@¶m,‚—ì‰fÒåìÕ¹$€gã“Ä?]L¼¡¸ÖÌK4ôQ‰l,¸tDhérž²w9 ˜Ÿæ3É(V°F¯]õŸâ„5pMç^g4Ò6Qþ­?Þô¾Ðë€/õ,P–Ë—pƬȔäB¸{ *€cz„ÓyÌIöÙÙ{ê¬õ¿G á²yæ.;¢{ ÍË9¨l[Üa5Í&vFnÕÑi7=lKGƒéž¸&Ú4,mnYª+ÁÑœ›F !×wbgš¢´y.ªvy†~ŒK1¼–5Ø)é”ågæå-ÇTž'h„Mfã¦c„½óÄÞAé–ˆ (÷_ÔG'õ¬ë_nG'²Óä7Sù´ò%wüŠ Ì%¦Lw݃ŸECZw` ]Zò³Û1˜^‰UOò ûÒ{sˆïæfíœÜl5˜K¤z Ê·,.ª0@8%µP„•ì ê–rÅ9uZêƒÝ £Í´/¢Ÿߦd›Ö÷¦Îú¹†zúËbÑné|Œ’TãiAÑêË þ¤(5Ǫ^¾œíñ.v 8<ƒ`‹ºÎ#Yß’¶ß­e™?ÛËÝzà¸þŒÆ®·äËT›«%{c…Î;ü¯±,•gt–UøΖ}Màb~óýšH5Ú¨)L½-àžOÇ çÝy÷SMÑ .gñÃÈ2fß@4Ÿä_~úñ( À;;n^zfÞtr¾n s;qÚ­9Ø‚Q$¡e.‘Ø„¬æ)3~¾G‡Ýî›D~¨¡…fÞ¨ÕõÜÕáò Yä ŒÆ–çÉÛôKµÅD¥øû÷²CQ{œš9äRÀ•⃗V ‹$WóØZxìaAƒY+ T°t¸šîí1Y„’©¿ý“´ÉA¿F^ó¼ö”Wiº_LÌٟ¡™`õ¥‘÷v¼¾íŽÍÏ矗ø¶ Ø%ãâ©.ñ3FÄh‘<12ólÈèòÞ)Ö±ûÂß[ÖîÕš©Âš2 K>à®@Ë”¨7&r3á(·FSxüz¨ÿsÀcuíSžMîh©úì1渽„D|h?ÌÎíœZCõÍ Æï™"¡¥ÿ¹ýEcH ¹/ÛpÙfrÆÊ—jýJ¥ñÈÏàϨY«Ç©E,šû%×XôÊþ ŠÊŠj~îq‡ÏRF…2™»p¸³`Åâ0õ­[«Žú×c<øDŸ;„rÖ£ëžñ£vó¨X·6_›o4Ÿ¤¢”0yÝ®õ–ÄáEwÖvÝùשúÏòƒAœNÜKú]ʇ¯Rš3PØW>Ô•*gàLiÒöh3Ñì×nÓ€l¹NïOm7~§+rÞ˜XÙh‘UTÎŽäG{÷¶ µ»°jÁ›› I‰J5¹Ë…n=j÷èÂõŠ­™k;œIó `ü‰ßNÍñð ”ôÍ ZÊ%°@w*¼í}ë‹b’„š.Ì|iMò!;ìјêØðŸRÆ‹n¿õMÿ^µx@H™_AU99kµž«>¸Z!2,4(åš%âY–¦‘I ŵ)§¢ÊëXlŠÎmµ{¤CBÁÃ]Ê>ˆe:Ã}£}u+ÆzxÓЖ b~õ{ž¡LUê¼Á8ñ¾ óÓhÖÂÒx]¯T;Ãöšós¸Iˉ7Â1Vé$µîžHöƒuÙërãaÅïi›¦€*¿pÊ dùÆÈH”†ó:VHÛ$v±Ô‰M·S9|î­ßf–,ßÛ–"Ã'ùê»Z«£i „De›Ë£(…ýÈó‹Í}UõiS®&èF«|7vç·Ê%Ã)»Þñê TD EgÎr‹c#^=òuýY+Nx¿=–½õúU_jÖ>ÙIV_‡ý³áZö0ÅS—,$ ÕïJ—EÀK60Žªšªé1-+€°÷\Ô~ýZ ³æ™× (x"_d¯‡9ÐܲIˆK%Ø0Ú`Ô8žä[­cƒ(]ü»ÍÞtZÔØóç[¹V¿Ü$–%€tˆj˜Ý¸ÚêRÜý¼[´Vº—c¶-›ˆ,ñšl.'¶grJ©V…Å…KǘݷäUÎ~sVÜfXô¼ñ%UL]£‰{ò* äw›]Tfôá_åc&7žô‡#ƒòUìG§¿‡lŠ1ú÷׈Ö_FB8µT>Œ 7 Š ‹…ú#xz áQÇÜÛ2«K6‘ì˜ñ\1ý ¬mè!ÆÓ…¤ qJ™ÙN ÿùýÙÌNÆ·¦èÔÿŸÊ>¾èl-¥ë ŠÙ¢ú0™ý»´Ú¡ëuõô4rxîÒ¶`í^ç`íÑ5>_bwêcFh•©å»Ð²ä¿Sî{Eêe÷uðm_dÚ¤+]\ã¢ü€/þyÞ#âœe‹-e6~'»âBÖ&Ásª¼ áã6ФÚjÖUB—tZ-ùøÂ²µº‘|ñÚŽÄ‚i$—9H ÊÛÑQý¢ oÅÔ{ h(¶[¯¦X%ˆ¿† JxÇ/ÀVM4ŠS«årW»XX’ÙÛNÚ0EË{ާòF>ò¦¯Hœ Åé˜f”XCÞpª 4VBàN¦CJÌMxÃјº?]‘€gãî¤éþŽÈ–¨”Á7oaIøâƒâÏ]Yî,r¾“Ç]!÷•pÊØÏE ª€ˆ]%`Ò³µ²†ekKrœ"–Ð0ÖG.'ºs91Uþî#uêÍ¥)&ú5ä9:—Ä–›m…¹>°×É„k>BXdõÄÛØöœŸy˜.Ø¥ˆ I¦ng¡•|eSw—¿Y7GÍȾhéÙ*W‹è·›‚ƒ”’Á&2 µMN ¨iËTøFüÅ2ØÉnëi™A ΣØ×²R{Æ«l;Qæ¨80®8~Z1Ïq½X¬¸)„vð xPǪu.¿¶$|\_ÀË`>hIÞcîDâ}mCž ¦`¿]Fø…·ü&r³©%ó»fœ&ãs;<@ŠŒ³XsÏô&Zê? Ïøoþ-íî ç%¢ža1ý«^8¶ÃLUVæÓÌQäò~½0—sèíIJ—2;¶:1™ŒOㆶëâ çûúÝþ/5Ë 0Ø DÑž]v=XOZ3Û§­Ä|aÒu¡5)(štx|è 7èÊ´¶o 8ÕGœíÔ«úÐØÞ^‚Ÿç@UPM‡*˜_xPí’šbmDZ½AjðeTCîž×/nœrè!GvSžƒ‰…´-wœbµ¢ì€ïUŸnœÜ¤}¼ûÑ13oKu_ô)PÕé$"”BÑÓUk¶ègÒPðWðœôNa(üžÃÇKóo&-ã>é×ø­ºv?T:÷ :f†©Ì) ¦´žxü‰lEÉ`9¹ú¢6…%Ö{Òem3;)EVáú³1§ëý8¸½ÑŸe7%´õDÀ€äª3רOÔ5fÞe|î^å4‘MŸÉøæT‹3a£› ÙŽšCãKу5^óGzô Üʼ[\ITD¡!®%=Çœ;?­RMßSm¬3]ƒz2¥Š@rA¨eþb óDkþ€ú-p·Z¤¨—X°CÈÌN†_F* Q ~~ •ªó i÷'ýøéÜmâhá9û*D/_~7°8³ˆ^3³½WW­ Úó·81¤þF¸2±ßŠSÔPw ÂŒËoÊ=a#(pq}9†¯‚l"È7•óàÃv¤h‰9bç»~ÜÏ4"]‰ ’Tv¥¯é^$5YæÚBx—pÀÛX¥ûò©èTÑ¡½ål¥ ÒÃ}òÓ‹G5ƒVAHÁ·¨Œð2 á/p†nPÙ¸/¯HC_Ÿ>À覶{WµFÍY6]ùg0Ѭ¨¡´“ü|ÜÍöEPLR€qSo„Õ§®¯î ÜÂv¤ÅÆå6d[áq]<LùM½36ÿì¢Iȯ žåh‰4Žnü¦7J¸}L·[l;’±x,/’Y¨‚:jj]q ÀbÑFu9@8ΖN(§à×Ü-ø må(z¯W{Ã=M”‡ „gÏA˜?ŽjK;…²âR†U~ÎQÕ`WÀ&Ùu-æŒÞhŒòŒ‡x&"äHB=÷«ò8„ZÀùK‘j3‡{=V.÷Ëñ ô^veÐDïÑ¥#«*<ámô‹¦ ÙY`èZÛgF¢ê¿'x²­‚°]†1ö—¾úå•ÉÚ²ó/PÛT­}_†rÄ;¯¡"lÎÇ¡(~£=jsŸb¹¡g’¾ê±Ü™}ü”™t«¤¥ÍÌõåv%ÊÓPc²†Ÿ€¡xiÞ¶¸öÄÆ¨Ú‡4Ù°Àн8’a¬²Ɉ ç< [Äßò[ƒo€zlE| ŠB^‰£ÍÞ`v¿yðnÄÈŽã,ÒL¼2ÀFAkȬ/Éb[w]é€Þ[àþƒæÏg1ÅÜeóÕJuØ­YÄbYÕèvø{#n˜}cL¦ßFr÷Aƒ¤ñŽESÔAºÅöaÚÄ–7TðçQCàfU¤ :AdN¸ï!íc>—Ñuê¨ïí'¥W-±¶ Û Öh'€±럋߯4õW{î8aݼóÓœî/yñÑ™ARé\ ®SˆÅ ‰6m‚Ñ[u•8[q2ÿä2‚ K'ìj:™Ûûó´¸Aðó ŽhÓ¿m­È€” Ö®‘C#’=<˜1¥mƒ‰”&*¬ìBç—PÐ F*n¬è»úêã°Í!íGÈÜX™¾ÌÀÿVƒÉVz‡´ìuð˜A´¶†aË’DMµm€íñ¸}ˆìR: ¯‰ÖÓíLVΗ-¼Œë‡É19™¼}•ìï ¡Qiçìkoæˆàûâ¡+ÑéjYkpmFõØ«sçÒtÜ‘ëNŽÎ{ 14P:ª DÂ'"Ër¯hz©>÷ý4µ–·钼¬þÞRàqÿ¼„uæo¬¥ºµì^Ü`k6vs|†¶,›Üžgzá­BäîÆˆÁ%‡›'Ýsrö²cî©Ã‡(ã‚ôÈ 하ɋ î³übÌ|¨ÀBà—Å´8$ €=Îúãê*)©º2–jxv%Jø¶Õ®Ž%‚÷âê{G\¤0­n~±A{lQä5‰€™¤§}ix»´,yaØ+#¾hzªŸ.M~²foÐÜ®0µ•Lû4¥@I¥÷E-®èX]dÄÓò»òú±‡Õ¨guÉÁé{×9†Šä7Å0KÔ“R¼I鱬Èö9gNF°#q5ô‹þøÞGè=Ù¢±êË;ó/Ð#Ñ„ßFA÷üíN GúÇУÖ-v+èßÜ2G¦œ¸ 9±¢+-WÛ`%íZ–™¼ð†MØ*êvÇŸ ÀU#xA²pG~h´Æ(µj˜]åš Ç«^OʽüzÆ_¤‚+H“ß0wíõ«*U´E9z¦F‘N7+LŒ\ßÞZJâÔÊâ*0×=óŽU¨SD)ÄO _ˆ¾'L½úñÐȸ·êA¢ïÜl-…°¼Ó`b«””j9JÉ45ËÎGõÁ‰³@4ë$)ÃË®ZÊ6І‚ j=C#”ì’Öüž$´SŠdÑ®‚_\JqGñ"‚²|ÚñœtÑ Õ1CíÍBÚ'ìDÊ^[ˆŒÈ«à Ìû\‚³—uXB…›š ó„/ Òu!5Œi}(¼ÓÊš@›Ùt+£×…·,9)´Ðú*ª,Äeâ¿Ù Ä/Yħ²‘†ñápâ[ÈÐìKNë_$øœ÷ºÁ ¢€ø«…±e„(É "»ìi ÕË/FâT‰½øl§ÙyùØ,D±Ïü÷ô>úJx—‹)õû.dÒÖóÐ?æ;9µ 6LÉ œGÆB–!KüþG‘¸­Ûèh˜ nû.VDZ©%Ú–N‹ ©žòÓ¶.¸…ôÙ½˜H{àfÉ ˆ¢#sBƒ¶EéEP³38õ>ßææ°$ÚÑ-sýc¡žà*`vŒªû+@û¥dº üžw.ç2)üÞH™p|e¢Á ¯æ860 5cì£mþ½D“àS²g¶BŒÕa+³â–ÀÎsÄ#kê—í§¸%,`µ|§ðý)a+WlY!.–*Î<"xá¬Pþ®ƒþ–gAåÜév“³K9«•ÁyA5 L~ƒL^…D¦çʳ)R7ø$-×·CÒ<;¢  –=Š#=2‹ ªŸ¹qŽšbB­ Y<úîÃl•»tÓ—/×JQåÝD"f— ©´‡z‘û»&ÿKÄ(±x~W%òö"›ŒBÏWƒôti!§¥³ðÁ¶¾ŸÕGË^€`¢ýw×Ñ2bvœ20ü&øíÏ2ç§!'˜Ô ™,²F/æÆO_b«Ì m'}ç ¿U 6ïZÐ"˜[Žã±î¹ÍÔ™¶}¯œKÿæÓ¦¦PÁø¡ü.8èß"\6Iú#pâ0µË–Þ7Y§O† Wþž;¯ß?û´<ï¢æA‰!bCѯQuƒkÐO?l4è&N–Quñ#§=³„ ”3MtàøÌ/“—-©îtû†JŸ<*,€ç›Q]6îvÁ ™™ï, t8PO§®yâ•.´¬‡ïŸ‰ =y{QO°'ñÓUnCJ>+éŠÙ¯ËDcà9µ„'×½7ˆ·Q ‘{­A±-æØ¸bÒ×ñºV´Œœ¯©´úÚu–'ß1ï ø³aF8Ð{Ĩ³ã^Bùì$ÌZ{Ü ‰Ü  }­Z÷ʬÂ? ½Iã!¤Ÿ5(þ€Ì9:fÐ%|¢ª½•î-ð•ò`?Rž Þû¢´¾vÇôèPàŸã»Ø1L ™]R6òól21XDÓõ°OÕ®7Ë0ù÷\ÑDº6×*qi ÒŽà(HM:€p}¹NÂq6vÞÒa ¤Ä ŸWC”.¢aû¾üv± x¹Bi—MاNR—Ç÷ Ñ 1¾ç«Q÷Œp«E9Ÿ¹å’aëÖbŸgõ^ ƒB=—"óÍiEæÔ“ULE(È&IG6:ñxWu@7ÝgM·›ÁÈL ;.S®!ÚT4ä"Ç„L‹¤ÚD÷Þ«o é{óßžE0éd¯åjûRïþÃñº)Õ²“=ÈôêÀa'g 8´%D8òW²X6ẃEûÚœû7¨6¥éî:s)/ì9êÛ0ôdÙí}ŽÍ0f6ç1| ¨_}8,¶’rš™Œ™,4²ÉH5ž>ÞMOÇnHÉDÆtbgc®%f3kÞþ°ðµNº¤¦8RÈêOÔs_6¾¤ÅUvÝÇ´¸9\ŒïEù3:ÀÐ5©m\*E½êéjrlŽ;†ª¸à­[º=HW9´ÀøÄˆ‡Y²fâU‘³MÏFr2P¯´ q]g?ÄéÈiZì©ÍdÍDÓÏñž-þ·ýÉÄâ4³ÉN4õ)•2™ °²VÓ®ãöÇ*K¨*„¡B¸ËMðEÞÍ4å>¿ãÖYm?šÓ÷Å=¢OƒŸ5!G¤V޼Pä]ò@uéÈá8[âBŸ'?Jjé´©Ä”;3¿šŸ¦" Í„ÔVÂÀ RaH^w–‰hn £ã|,rïé2þ·›‹±¼*·à’dŒ7.žG•ló/B׈÷ù!^\OèåÑù_µËc‡¶TvPÔGè&!Ë–‘Nˆœ°ð†—P»Ã‚*ËPÞ7Æ¡”¼ÓÙøš÷Áw—ädÆ Õ¥Ê¢"¡g?ôªÏ2>Y­Šíø Ÿ*JÝ2±«) 1žfŒ{.fî Š¢´\¤’û³ý„ŽBÕÏ‚öÏ¡5bî‹|AeúdvR ðeÊBµæª"”¯€Gÿ—q\ÛØG·,MôâÆg„óØè"ƒJ)+“=pá¦$‹+˜BqÆ÷änÓhË.€¡“#›Q–À‚Pøl5c³i°¯ü§%ͺ'öa$˜Àõ¾Åõ©–™$¶ FyÁt±ÆÜí:ƒáYé Ñ…Н#7ª³'4¡üæ)?§½Ô—aÎáïg ºÈW¥Ü/ádR&Á´C±÷xm¹§3 ã"ç^[.öu^>\šq/dXõr1ìr¨eõù©íT9µ.˜Ü?"i &6d:”CTÑkUVÉ@K­!?•˜]J±Ômw|YŽÃMÐhó)ì‰wCm_F¯vݺþ9¨·¥Gýc«2†Ë.£„héOÄ’táfª±»/Ë'þ¡ å«"'ª‹pyñæö÷ æU;4mÁŸË Ol;Ô~ºp*¾z!”âáÊ?³fÙîcô• ÞÓð³µ3aÛ"ß©«å'45uYÇãJ‚›áT[YJÁIÀÎìFlµK‡üÄX¨8+&„ªxíÿ¶þÔPÞÁ(—Ÿãúöq5#…A—Ú:¥ž*ô†G°ñ(5¨ ïŠtÃb$qž:…@^WC?–lÔ¿»ÁðÆr“žÚöTŶGdVD_Ë¡•p $‘8LL9±ö¡ŒÔrØÞDºÖþ¡žÃ½y{]eÝ[’`×a³ Kw-Z:ÐÇH8%M­™/¤ë¡R;)tÙc¿Zl\÷aˆ›sþÇÚ]Ô2ãoW[˜}/µû…Ÿ™X ô@ —!8Ì#+–Y¼t} O¹’\N†q€¨K –h´­o1Ø<ú›Â³Ž;sÖóàKíú™ønYäÿ(ôûéîïD£¼û¦´ŽXŸUè†% cDÑo& i:Û+–P“²qª÷YÞDP(\ôv·ƒ›%©Ãw?Õ›ö5kò>6ÅH„éö›6ËÚ¥£ ·YÆ]ÜìÍ`ZYú=…?nB·½KΙÖÑ££Q•þW ]oMDé'¦EŸ&„ùP­Kio×!¦ð¬á} ø'âX3EYçÍy#Õ€œ*T±¦dêÄgà·ü¢zZö·ê¢ÙØPh¸¬}Å&äû4 Ïd–îö ½irCâRØï*7K¾)#<`<ÓQR5ËÅ}mRN¿}Î+ôáHõ¨ÔæÉèÙRÁC¢}–Š ˆ¤i,¤É& úB¸>Ì)y®m$e?-Ïš¶€Ža6‹ÂØç}ær|!2‘ÂØÎ÷4¡r?@N¬É€úÈ\¤\áwk‰x±ÔXGE¾CÐ—Ç $©Z8«‚≥l°tUÿcÛú1ùDÔAÄÚÊ.¥ÜÉœæ†ÓYù±³¼ ~]Mð~# ;‚4B{–¹!Ùaƒ©ÿ¹¨=$I·ÿ¶Óð| óïêEÈyˆb‚¥ÓCòvô¥!yÜf ) òŠ3ø,{‚͹š—Þ´!Ž]²ä¯Îþu–Ö1‡°b$yÁÜ>ƒYYsŸº,ʵj1û’Y >ãSˆ%v¹°Óï¿=þtžR’Y/) ¥‰t“¥£Sxµk¬Å˵\  ³š2Qê”i«ù-äi3Ü‚|õ%¾od¢¨Kz.í áf ¥šAzm„ ¨ä@ ,†¢ÍMù¯7æ”,~=ÑF4©z¾7m¨iÙG¯^=dáÒ÷‰ËPÝ‚É³ÜÆìxÌÌrÒt|zè¿ ÁÊ£*_‚Öƒ\hœ§%WŽÂÜÿã\ˆXR%×u8ê.4ªŒ`9[KXDŸBõ5dyZÑ2ŠÑy®ÛUüÆl”¹%𺠤èÚº×þN/ŽR–%¬ÃV(×ôγ å탕ÕËÜ ­Ž(cÜ«Ì`á× `à…ƒúŠÊȶšF´á1XGqb¢)ë—²±6Q4®ù¥n¯;R;@°ç|óÉÃÕ"dƒSïÉ“¶V–@¡ ¬„*Ф-Ñ·3ñ!ç–ìµg˜ý»™>OyKKÙD¤â!áPϘ³€r*ZÃ)Úýoä9;º’Iœ‰TÓlݧþæ h<ðÇJ…TmôMrw–iÏÃ(h­„Ú”e³†Å€¶ç RsŸ9“QVT¼Œ/608àí¨Ù6<­ iÉÍC+̦²jKІ]ÜéügDŠÖ«@úb*2ËQ‡=ü• ´­ÒT«¾È d’[AÔ`ZþO=w¸€ˆ ˆp yîbyò­+¾åûÈ!Ã…žûÀŸÌGbëI4A ‘!§"넵 rYtJRuò“çc‹žM?æ3X¨‘˜Åpqv.pà š}nXò€{~i˜tñTñ¦ŸXŠÂdÖ…hS.Ø”<]ú¨š?Å>lÍ'GÖt)^Ý ëŠ[RI oF‹ƒézRÝ7SçSÖÆjd  ¼‚7¡'wHXÞoS≲Ò@ÙŠIyÝ¥âÌ;î±v¤ÿF¿*ò¶¢#^ÖZ†î¯møæÇï= ± V/ºøÖaS¿ôË¡Œ÷±"õGT¾r+I1²îívÛ³½I[yq6 øT_9(Êh Û™"ÊÈnP •©7AÕÁù+ºÕ;G»7´þ¤ j<¸ ëRŽ3¯³Îéëb®æ/§þ~·›Ö¿]ÉÆÒid‘ALcåªXå5aɾ¯í"þe™þ ¹Àèÿ¤j8I¦óœKàT3´ÞP3*AìÛ½l#ñ¥æqaÎ1T¢$(~÷ßߢ6ÖÎs÷œB«á¶–{ Û jA½*] X]Æßñ¢.hï3¡]n› ï¥OÌêѺ·Ïf‡·ŠM(Æ%!ÅàIÿØÓ–D,¬GÍ•i}~ZÆá)Év‚Í1G8 °òHª@ÿ½†Ê’™…¹bƒzࡪM˽ža¢:•×8æéÛîÉ•þ=íç™îÄPìß!óûÓEdœŸÌ=xÀþöë‘®¦¤"=\»ùv¨ÕZh˜ú¹QvHç9Øwì>6gü†Šœ:7½„+ {6Ì#š0Â1Òé y,ÅåðtIÈUëªÝ!ÆšR1ë™ßcOZ¨xW8•3_Ø[!JïΩT`)¤2QìH^¿”´ß§F¨ZZ/¤ ylãi§')„${¾× %qp[&"´}¤j í Ó 1ÅœîU ;®8hK1܃ä_ñ0Òf ¾Gõ2àͦ ¨«X–=…_C‚ÿÓ= N»qw¿/°€Qò4xkç]O–nI•L  Zûê Güפ:b&<ª‚NKJ'¶ xaÀLÃsvŸ(™m®%•äËý"ñ‘ò F0C9"²«L€€6-TçvjDk ¦~hl²“(Ïí|x7 G1'ÕcÃ:¶' øI M°  Xþ3yWK©fÔ-Ÿ"þ¬ÈØE pñÒ+ò ^u;+IPíêí“9º/gR¤´÷ÿlŽ8!½&¤ñÛŸ´ã zŸU4~+Yg7Üd‰{·Å‡Ñ·…ôŸ¹S Ù=aU¢9OŒe3žS]äZµèß§MÞµÄÚyŠÜaçrýù2¨éòÓaö€ÙÈnX{Yjô.P’¥³ÀÈî6]X´œ`çD?wh™ã ­ËI›™j~]*>l@7yÉæ¢uŽàYúµÛ usÛ‘›À,€)òQ}rD6è×G«ô+ Xýì÷ßÎzÙúz,ú-ÓÅL_ ›½+¡C§s „ê­ó! —äÑyhÒ~ ÞØ£ìßLÏóÉ®KKeï{¶/S¼[Ï`{À™›p°fdzlú‰‹MüpÀ©pR€à^ SÏydè_¨‡êÔ›3l¯•ü£¹ÿe‚ü±[dDé5Wfk­8WªO3e*†Ø„(ÊòiËR…¯ãÂ}x­ E.p"*õ'^l©¸ÿ¡‚ßY\0|:ÐâÍÏÃÅØ+Ò X{§òem¾õƒ(wa"¼õ€®è}4ÅNoÚ¨ŠÏè+ÆwáÉNQ YÝТào Ècƒ†ºO_BÁžk¡TRž,¢–:?чf}q?f€;LûÊj°vï&L¶b2Nå ¶Ç3,KàÑß6.lž¼‚ËñÞ:Œõ_'Þjl5{=g:LŒÆ¢LFXoƒ‘³ŠŒð-*ù’Jl¢X "uÒ¯M5æ¨Ý+C¼×µÿÿ˽¸ÏnÐ?LJèPÞ‘Da5œ=Òc$Ðj8¤8i’"há$’RKz›"çVÿ]ù®Ò‡åá3Cºh7°Œ$Éî<Ÿµvñ³úô3…K5[±fêIQK`í’ѼÝR†Öë4õ"W¹Ñ%ež:'²-9øó}1ÇßPQÒxnÀ uM8¿ÀÞÚÁ†arð‡€ötsçFYÌo•¹G"u5'ãš‘E¯ØšãíΓYS¸x忉<œv˜¡fvèpþ±½IxYöØæ1§xÂ]ßWÀK§10É㈊\¾šœ6c[kÌ’“[* éÖ÷½À4ÆáÖŒ Dð§¥M¯XÏÑG¸³Š3ñ[ÜäBí…$ººKsÏŸî›Ú“–Å6ßxQç 'íàÚÇ·½`#ÕcĤ"]ÕWãÍWD¥„Ôh…LTpñ2¬–™ærhÃCèvÿÃ,Ä9» b…Oý=_YGTpõgʶ› ð½A¶*Ó³{Ö5î‚«Ÿ:ì|5!¨úŒQ-,òz¸ w²éilõþ-Ð5x0¶§6"=œê”6 ³_ôc‹ «td.SG²‚|ç".ú j}7Òo7Ò¤b9¼óuYÄC­D‚¾ø„ê“ôDÐ3:¼Ami·qlóãmÀQiV¦#9ÜÏ“7f—«ø>„Î;_±÷ò² 'ô»m V_¼Êv1èO^•ýoº2 ß®ó*/¬%–à„EO¼³ñùa2ëw ZÑCIkEgf¶ †XÍ+5êoÂDX…±¢WïX¤ŕʮÚ4Dl6¿Þý‘ë_&?KcígN‚:S2%¿ ÿ2? òàùá\ÀÉD;á­.¨/¹88•2¸þëc¦[ûBÇ«”‰ü…ÓØ$Y†?«%—M3aš*=<³vËÓ³iõöòNýŽÛ'ÔQ†Î]ë*©2!mzNÒ¸E¤·zK—-Ë¢¦ÎC0ŒSõŽaÖwé+‘±;-ؽ&ò…*àf°ÿPÑÜlj]™¶LÈ O±_gûÇ3êÕÄltQ Ë©i ¬DårŠ>«jÆPD©w ÏÉ”þ÷=•ÜJµTl}Õ­3tÁVL6"ݾP*™eHHiº¢ø þñ`0&\2Q§9nø³ô Lš´®lìÃæ&·úWR“ï_#…?úÇ÷D1“‡æN£˜š¦¶û8U`¤.eª=øÍ$F–šƒpå,•,Ô&– eÐ %ð¬ù6 Q ¬—¢iJ³)÷›_x±`T©¤UÎÛ²ï±*ÀL,B€õEˆ½ ÐTwPËäQ¨9ò¶ºÊ1 ²ËWq!bòÄ¿†`û±ôØ™CZªÑ˜0p5„6#²¹rG>ͨU¥Tå«iRÙh¢¤’5.ibSý¦Ô ôüÂ13 Àå+° (™¬Õh6Ôk€´‚w¨§ßîóÁ@ÿHŒü‰T‹$ÿFyw? ìˆRÚr_#‹Å¤mVД‘ [2 Xá#xÏ )A/ÕѤ»ú’ „i*üäk-)0õí3÷hfdÈ9}¼7qXó ˜!=¤J:4§¹04PØÝpš p+·dï23æVc‘,Êà©ïmÕq÷–`%¤-‘AJϸ{Ã&K ÝÜ6Ýð?÷€._±øF! îc»G¤›Ý¹P#;û#~‚5Ô׊¿ )sw•銊,«EÚç¤ Þ8b/wãÑ(Ýî‚͈¾Ëë;I‚¿¬E¾â`Ü–lf÷S*–ÁøÝÁ㦄M?… y¥IæI¡ä8~ÚóNƒÀJN(Êœ¦2–˜o¡œ/]9ÂJª’á M4ßæ=åNÑ£ˆctÒÕÒªeA³Cü£<ĜݑʻeDîzˆÄXóûÊl#`Q±)Z¹ ®\8‘ á®tO­ßfKÿ‹º98`¹ëQÿU69›Èoò7a»rTþ¨ë#¿% ­–à„N}£Ô#a¿9ƒ„Š@ïÜ!Êñ­mFµN46œ‘ ÓWbN‡DtÏ9g^öñÊ?¸3ËK§&õ’î‰ãÎŒ%KÝAP/ Ç ^Å H#:ác4zѾ䠾˒¹Ü^qν HæþËsð#̯e”ù ì F‹5÷eUå™É ñ|½[¤1r™Dit’Ó¦þa,g§-Y‹{ ŽEõ:ð¥A‚ÁÜŠ)1=NM¥^[T^ýY·ëïùU£Ú¿¨º—Ý”@ûûK{ï²Hwƒ˜!ùªwT&õ p§Ùè=OlÜ—\Ø!¼N®»C3 Vcxa(}kKIJœý&_yËkÞúr{뢠Øz“Üõ {ÑŠ¯LÍPéTÿr,ù#XqH4»ÔI§  ‹pÛY):;"-j5~-E'GNüÌ0ÛïˆP6²³tñ’uY8>>O¿eœ51(nœ‹w G™Ôh‹&?©ÌDíDî›ý<ù­eÈp¸Æ·eôŽ:òçWùÉ‚­AñÓIêiŠ’Ô¢¡ç•º]Þ’$c$­®\&Q"ÕR™LLǼã5Ó³†®[¡-·ŽZtľ£{µÄbïe®„ï6'Ô¨xÆ;´ÝQê-x(éáE^~| óðNŠF<žØ0¹”Ÿá_ =[6Õ/ÿ£U”©ñ*eü ˜å¶DZŠá¯u˜Àv!Øï‚¾üXØ@¯^d2SUÿú=xEé«rÕ…Vïl³ê ¼ºfO‘Ûƒ‡Í ³åè9ë•:왞՜ ÍQS/Á.“‡(ï†×+ã Ë$à®Ù,; 0¬¨¹ÔÀ¬R–}šµ¤þöà$çè×:·™W@])±É&O†¯üçýœNmiÖ ¤b‰ë8j´Ü׫ŒxŸKgó†dÜ«—8Ádä4W{cÊkÚrIöWÝë–ÃÅš¢NîÙƒ lF”Ékö »yg'ù ‰üWÀvÕ*¶ 2‘Š+ Ûè× dn9p–SùUXï@¥AÂ:ˆ768×vã༜³ŒKR>:'Ítf '¬ÁCÅf²ïýägÙ òÁFºþëz«n¹`þý•ïP8dí×–î $¹r"(;Îí^Vµì§ ¬¯±$0 þçPò €¾(“xßÎ9­Dî#å£ÕïÅ}E*Gtl#ö¨­3êU›?ê1èBÈ>HÕMŠß$=?¡VŽÏxuÓ–½Ï°r-9«ˆéJÃpš+æ0i?-RŽài«!â) _nªy?ào!ck«\!ýþÎèWðöX )&ôUgù¸¯‘¾Æ´ê¡¦Út×úpí÷Åiûå(‘íÈB´PUòaeš–ý -ûرEaP­R9 P?Çdô“Ýq0–>M>$¬®¦¹,°Ê¹´1Ç2™ú[Ƈµ¾0«lòÌ%‡[{QºâNFtRëàlŠ.UçX¿ËÚâ¶åÒ`^ßÓÌâã“С÷’ã@ÿÆX4])¿£}’LUÍŒZ³€Â%¿›Êý¢P–Áã>y‘ËHm»–¼6d6íϾ%²7þ·è æÿ…n.?ÚjuI‹P¼}À£DÝ)nB¸ìu‹a>v¤HM°imçn\#æuX;ÁƸãOëâ@zNƒ¬Éæ‚™v³!æ>dBe¦»¼ïÂ2¶q~[ÉáëõÍ«ñÃiÐk4UŽ%Ÿ5ôÁôðfk–ç@ÒÇ"”Ö³´Hêè–ðHE[ŒÀD€rèýoÉ›ùÁ;(—»LÕᯛ©¡õ쪧UºNh `×[DªíS†-¯µïñ«)ü«»Öv¬†[Ì‹(cøh™ó·jëefFñÈ:RPS«êóXú9u_7òG¶#’g¼à ?à’mD›Î£÷²^ËFÀz4”µeÖ9Ö£Oo>ŽŠ¯R7Í‹¾×vþ1¬•¹æµ6´¹„ãÝ7@Ŷ[R€ZAY\>è2¯ÀÏ‘o޼–6Îòv³è‚çãûw8Ð,Ó®zyü À v]ÕVÊ é˜rJȯPƒ¡ÿn¦es[X¥æç쇕è&¦osºÐžÖ¨Sªñ\T<¶Hü¬ßÙ òü(@â礙à×HAÄGXuÑÞ†¤/þ•Ðw£óþ¾R±¶íÊ´¹ òzÉÖý¬±'U*ëg¤ýëÐh ³2NBÐc :N†1l°”òE’h³8CX€„>‚FÄ9|ŽwAÅ^Jµmm";7ì)¦roà§!-‹g¶ÛæÛ;:ÛÁÄæi–ÐdîcKt[6›p DC€ùV4¹-à‰Äv\EÐ&ÐËüÙ*ÏÊs!Ðb…*hg’M|.°åÌ7ëVÂiYu\ÞAóÞÝC—e´{ÁëofþŒT¢¡Pë1=¾öŨHµ ÿ¡óuõÌÀšäh’>ÇU’g¹®6§?P´ÐTP¬½Ü˜Ký.ívübmƒ,+ÿ¼ 9ä’îØ 4NÈ´\ØkßÁžJ%•ÆÒ3\üYˆzGlÜ7Í¥=ìø’ºØ?î¾P ç‹WZlð<¥„¬’.x  í 5ÐÅ|‘‡éü4¾#où‘­öTgøúQ  ЩÞ@¯DDì–ÌW›t<ÔpÐ*-ì¥oMú×#´RN™s_ûŠÌ¹û É©glým‰8kÁ³nµ`ýôáx°£·¶qøÛ”UæûØà ¬p™G dë ÄYY)Ìôi…+z•,íØº8Ýî,fŒ€á—Æ!â ºc8~”Ä58(C‹ÑŒc–w¸u¢3¿ªhˆ[â2Œ0÷¬ÎCÛ¾9Ì„„§køðêFƒõÓRûRm%dwÔ’C»ªøò¯§ð9¼Û&7'ï¦Óµ’tÊÝȘ8´Òû“`Ž™4-åˆå‰˜ê.»õ ¢¾Ö\Œ‡!±ô©Ú~Ü}c‡ê2*£fÿò]Vƒ×kS ¼ŠÈ·FmpòKT 5¹,C¡S<ءнKïÄO,$C•ûROà/qõ„-.ieÀp®ÈE†æÏŽÜ5}\L¿RŽJá©ox”'4|«Ž©¹[y×ñ”ãæ#ÖOÓ}?8 ޲ Ž+M±„P¸ôar>h³@"ðää{í¤2gÑêÂÖüøûÔý÷ò»fÞ6S·.DršZr³ñ 6aÒSItôoPuŒztQ:•¬j #G&½(‚÷äô^¶qSN³¶‡·Ó#ë«* ñ`¨­zw€à÷ëÜT¢;Ú.Éýä…§ò:ììZè) B .02 A‡ç¼càÝõ•DžÍ‹‹ÆDk—gÈ=Ÿ{\oãO»4ƒ7J'®áo ©Ê£\5‚òûx¡‹F“|¹5ýA*¡ò긗§J)~qø?ñWÎúë´)ÃA©«¡jje¶3¥æ4¥Á>ðÎaí ÐòÄÎVu*4,^²BxfáyŽ’µá>˜qyx¨<Ä[gñ@äÁ]SšÁÌJ\œwâk¢>~ešxLЧ«ÏÆÌ! ã 7ƒé¨Hã‘&~Ίmb]ÒXÎù7…+OfB[é¨Òí–ÛVB¹#Mk4Žma@)ÓòÔ²±$\Ę®ý»ÔÌiïø1@ç–£¼0z- “”÷1>>ÚõП¨Þ1 Cj€6ÿ´Cf®¹V"n^j;S¼ æ‘J±‰h·xWâ• ¥æ¸æÙìº°Žø=L¦ò}ÁtˆÓIw± ˆ‹.5«sý&JÜ:·ÞMq·ãõK×F=`EGJÓ&£fV†°U¥ cÓ1?}êgÆ$~n0Îd¬ðn”Ýž“ß³Á0(ž<§@†©§WçìGÚãYµC»¸ËåªçKT#½±K-üþ+Ûk«Ÿo¡r%0I÷·%N,¬ã-îo8Èê5bÂ{–øÕ´3€xÔ.Ð~í;E&LÚÓýÛ|O+mfä²$×ÓX\±ãU`œ@ÕÒ¥ðK)ªL €Uø…Ï“"Â`“é"H€Ïƒz’¬ÙŠ1îf‹ÆÓßký¢ü•~4窎Ñ0[a{E(=„Ðô.¾»…'ö°söSpß]ÚOÖkw;ÔlÙêIï2 vë‘QSÕŸí3øA:9íÇrö=˜å·H¯› ¬‘‘¡H’þoSJügAÌáèá nI®TcÂ×ã¾R ´¹úæ{S£ì’à©Ã8Ìa,Pmöt“†lwoù/*7OMàkh|€ƒ `ËeL,Pûñæqm“âÒBÉe`S7ëÏ£žaÚ.Ÿbñµ²ÏOBÊ ¾oÏEï´ ¸ÙF°ÜœM2¤ëæ²a«©{e¤†d]ÿ4â ºü9¯1lL¦Û¥âzLåO®å³Æ€©»ßi“ÑoÎøµ9V8›&í2mLx^¼€ê’@vŠåY(W‹œä>îÊ=tføÈ­ôñ¤7‰õ;ŒIS|Y›/¶d)C‰a?ÒY¡ä¹_T°TNöIØÇ=^„^c5 ½’$ù´ðc1_ŸœöÊàÍ`q âbáèhÄõÎõDãD¼±ÓxÓtŽë3펫Äf÷ÅïêÁ,€¦ð}#:Ùú‘C (¹Øù«å=Z?2Eù=C>g ÅOÈ- ÞúOE)±ý Á/—‹(æ*=­éÑW4Îc:ô9êßýzÑXÏŸ_ªõƒÜßúöÌPÅRYeoKûƒë.ú’cߤ/ƒ™]HÓxLéëc6_ë˜íÞ#~y)Ú'`kAÅx®C½T CÆú¯cðYQNd¯Üѯ‘ ›Xæ12×}tŒ:ÔRú«5’ÄÖÛæ5æœçYw x‘GQýÕpdªZàƒL•CM̦Ê(Óð/?Ä«{h+Ý—Q‚DU«º.=>±o1u/…Þ‚±:DGU+χv¦Îs,ç`gÕ’%?÷‘ì' ñM ȉ»sA·z$w+¡óX Nÿœ“gµïõ›‘ä$œ0ÁEnªni±ÕIÑëÁvV‘bïÄ÷(!WÚ‘¯yç{$Î_ÄÎw]5àªÅÁÉw˜ŒüŒ×¡Üñ¨ò M ×Ó׃¼wFž³­¨²ü(c„ãìe½kK¬ I:ÃG&G®Æ îì9ï²/ëáÆ:Û½iZx” Fpj³—ßS»qO÷9'¾­ƒ¤€ŠâÉØÒ(»$&Ço€YP¸t¢J6ëCO—oX9‰S:ämŸK(]çd ©€>ø±‰å‚$jCÚâ9®fIØ+ßï/Þä|'jÌWq5fÑlçòGLSòSßÁg ]0Ñ3*!ü}é@@üRœ4áú4&3»¼$¤',;Þ¥ ˆ‰üÎ0ªÝ®‡ü÷ÃuÐmøÕ-´Ë·Ð>+;m{ ƒ`<¯õTpUÔX]o33’¶ÉýeìçRß…žÁï[H_o‡@SÞÝÃι÷ú6‰°|qbO¶æx“zOК3 jB«Î™%º‹Ö;a×´‡£*_©;¿F£É¬µÏ1àÖ÷?m£Yÿ¯6噼ä.¦®æ¸þúkã,K­'ë!άDU4V6ˆ¸”¾ØtŽºÜÁ;ÿku&·Kd^Wv¡„níðPÉáv($ ER‰•dö Õ,ÛnªçEÊAXS|º“×6²8ÎУ¿Fžœœ bmÒçL"Nq„”YÁˆ·.ko²Þ ´Š%ž#¯ïë9 ApWSöFê=•9Üéy hâdøN!éZå[YÚúãF–“ÐMÝó¯ÒW·Aá–Ç“Újš„[E9M›€úƒwx¸„òbKqn1_³üïóäw¹9׌•Á³Þ6«€_û½”}¨53'³Œ‰´*Da÷ ïxt G©#éHͰ׆ÒIÑúš®&«Î‚’½õdcõïµÃïñ`TÙøÈï¸ÉÈ~b-×å¥H¹õ`X¨n¥ãle :ÔRš„VÀÎÿ|D)¤æ­o×r)”ÀcÒ6J E‡\aØËj^A;üR¥Ÿùq].ÞæÖj(ðéB'OÖæ›»íèíD„ˆCŠÁå)¨݆؞ŽQ¯î˜°êïìbE›ÅÉÿ+è¸#¸[¦`SŒ'[c­øh!VYš!]YF­7ïR1ãæRp!w _Púr~"ç]'êCgSç§ÛKä…v±P”æºw_r,‹áÞí èÒå¢yi«\$òGÅ–ªŒù®½p}›ž=ø!…yÕ”ýe®H?yJ8N«rùFŽ`ÅLŒ›n2 ' ‘ õñ²W©oŸ},ƒ°ÁHí\ƒ„Ñûã³™k£ƒˆäþоÌÎ>iJ€é{ΧV·öÂÖ œ}Tᕳ+MªÒ€‘™q_gd}J×ÓÕO6ɦ þmJP9n€Ù"ç6‚ÏM9`¾ôèåËJ:‹%•Ű:€›Ü%àÀÝýs6×ÿÉbr,Ì«›©ä8ÊãTf–Ú¾9Ï6ê2ÄÃážýhà¸1Ìœ|Λ·Ö€Þ¢VP…€Ú+Æi@¤ß#º©D¤ÃÖ~WCL#Ú›ßÆ{ÛFŒEº‰ð¸&T‘Ý<ÞmEôÖÏÁŸhg&Š+ÿ§Æ§nq½N¥§X(;Š&7Ûg˨_Wø͇ƒVÛ"õˆ\4uª-g‰ÌcÉ&DuE1mjnØ™Sy3b›ï¼«l­šž“³ì%žäŸ"±XÔ ,ÙËRŠŠ… ¿´õ$­èôÅ‘ÄY-{$æ,’êƒ&-ݘ/™ÞæõâIPô·ó-q%t¾¢%ÊnÖ çn ºfS»ßrbÉ10´ºâ¢nÝ•<ñg½—syÒçRÍ•Å8äé-Ȇ¼ßò.Ÿ®¥ù—XÞºÙy ½äÜ.ò’iZTq•ç²pß½¯_,ªþgß`ˆÁæ-•7Ч´œ-ÇïcU¥Ž ûJ'ÁˆE½tÿäÝùs÷LÍî”BÔp¬»rÚù?TÓjCŒ£í–ÆHÙB’½ç/]uS嬻VUZ4:n›Rƒ=‚bŽé5Z[zˆ£& bÝ![ÍlQ8¼3 Ë“IÀȯ,½].íqÕÚg¢ÃHZÎýDÔë:!u“7Þ¹d!‰÷Û‰DxÜ®»M›o;øê ‹|§šEÏÜê Ý›šØyÃÁ±[4±1¤ ™Éñ«¦FTÆÇ€þ¸‘3—$¦º¢úÚÚS^·O|µÛá’«A­cLF täÅÔ¼$€µ(F3ª\Ö‚¼y†­u¶ÍÃuî&&îµ,ßSß *jËü7Ó(16bšl6ì´7Ÿßª:=”iÜ›xª¼‘­åÒJšŽÐ0‚•V ‡ÒäßFÕɆZøÕqCdD^KÀj"–@Â*t1 (ƒÁ‹“ÂìêÚ¿Íf¹8eï×"KNIçÀµ/P)ÃEÁ—Œ ФÚ~¡´3à1O+ÃáᮯZ9hl,¾ O Έ fíÎt¥kóWÔ–JÅ®¢ráe·‹ƒSé>é+“`0AQt(:çÕ\DÈZ¼µ$+EõÕ°þq&v`üŠª…<ÎV䯡Žjbþ©>µòV ŽGef-ACÍB-¦Z4†(@öÙgÁ1… 93ç’º¼Pöò¢%/ŸlÑö³~ Rªfè ØèŒÅTE#ËÈý¤ÂL|ϾgxGS Ú{Zu`MËÞZØðw[ß7)á­…–6P–áPÊw¤Ío?xø,ü Œ—2+sP0›)§­JªÍÛ;°Î£ =¶ÃñÑL-ûÀO>•y³'LF×µcÊœýY6Øq7Z4(Ž—º²*€SŒÓ¥%ªž·[:£W«Z0#‚šS@¾âÆ&{BxÅx6ÏhK¤®â€ \Éáò»žÖH,½Ž ¢&Ý]åFß›êaÆ7()É3ÿ’ß'=rf¾ì›zÅSU;W×·¦‘†ñ!eÜQ¦± ‰R pû~ +$àÆ5ýîãQÙZDãV®Æ¢ÁÇ ¦À¤UU’üÎÛºaV8˜ùÌ÷ßÉ|ÐRr»AË/&~É6·¾VÆA—Ýö)n·„S4çFãljìôf)„£EŠÕäv‰wRÖ0›§æÉׇ9¦“pl‰ã–s[³;:žïASÑô”­q½ŽòÌ‚˜,á±xu*XrHàØD§H\ã*8¶Õ­Ÿ‡@0N¥\#¤ 5ªëâ©×æ}‘±Né˱Ri˶o¡n6ÉÊùŠË·ð{¬úIßhxzö«­ºˆí½ž%4•Îæx!‰QWµFÕš²:åé.} ùðªÔ!6Ái%Ö¢ÐQ±`¾}sÄòtí4Ø[Á\êX-›ò f ²ü ]JTÁQÿ•´b‹öÚ‘$›‘â´Ûñÿˆô „ØÕ4ÕÕò 3=‘~‹ì~-F‡ü­Qk`Ä€ · U´I3nrÞco65&1¹™%\-v'9ì15‚¹ëÏ0.æ Óg )Ë0Ñ­¬dWÄínîÙ®¼hö`‰€­â@Èd–°OKÁøV÷P`¬TiÀ~Î<‡NkQB×*õj»³x÷MLØYãª\>²í,ã„H…“j~õQJõÀ¹ÉÉA:»v®ÃŒ¬u˜âÚD¨Àã3Ô^÷<]Ý®X¼Jñ×>}£Q©Iâît.Š8”ZSµdHoÇTÔ“Åg4½æ§=ÓÇß&<ÉSôõ2­)¿)óΉsÝý"é…AÞæé£È~’eÚi !ûòZÓÐêŽ&šÏKtð›Í•è'‚á À‘Î/ˆáy«Pž+”ÈÕ÷Ô‘" ÊÍ>"ü%ráæ-ùtnU‹!cÆq{ÕŠ„îM¾ðŽý}r‹Ytsí5ÎÈ.Mõ®Ý§xT*Á7÷{©Ée)‡D JÚ£WôôF­oå4žá <|„ä‚qž¢ œ8ë¥ôoj$ìh|>nñ£3{…êiÊ|§Èüç|˜(ðìl¥=ß i¾2‡.\J¾š…“­¨c:`ÌÙ0ffl.ô¶J!Þ—é ƒæ!`Àdšø<Šä@´êÛW«°©þ[¨ïëÁÈ9D_HnãÐ÷ä…[J!¾×”©R*£õýMiÁvg­ï–Û׃=§`\ôÊm=cQâq|4%“N/UuÈêœ"YW;ŸEÈKKš‘:|/¨ò›ÂÆ}6<ù­XèÅÕ+$'P7ÛÃÚD‚ø5¡€' iÑ"&+ùnnþšž»x2¶0ý‘‚RB ÷%uqß"_ÑÂéœÆ; ÙÚì½³yÎdoÍ->•4q²óEçdl‹¿­mÇ8ø)RÜÃAo[2™F&8Qá¿Ö4þÙ[â˜xÄØSÄŒÂöìÞJ3Uј?"u%S=DÊè@«mÊxÇåÙFݺ‚j€q/dqfB³FI¡¯‹,¾\µ‘ߪÍl•W«¹ŒåŠ#@QµD{œÚ<[.Ïü!ÈøÜ E} ;öéŽϾH(ê¾öOª0Ð=„@nù°&D2×U¹<ËÚô¯ëÙÕðÕ‡àåÑj¬x'“`1<5Ò„ÀX>”tË52•ÉÐë°Ùu¥žÐå ¢Þrƺû#žEZ­w”owo”ÌõžØ­añL_^ƈfÕÛ2Šk‡øóßyÛÛ¨šø(Ê@›¯‡‘º‚E úÔ—à1ÕKc^—¬y¿‚Û¾$æ„È&ìÄO»¾VzÒä¾Q(!þa~l·;92à Cço耤CŸ˜«¤úk[ÓrâÎ?ýÿ=–:ÿ*ûå iJÅlu¢)6E‹·Uöw9‘¾ÜÀ88~p_|ÒNJö'ªçÅÁ‚Õ²Hèw’²µÉË‹ÄT‘o“1BçH˱ ¢xó9Uí¨l/¹ö5ÂÄït1-Rôþ‹“-~Ô—ð]÷‹ßøÑý­ ä0ú žŠÎеbßÏ1Õº”qèäN2^Û\62«lAwÒâÉßlà ìç\ö¬òÉ¿‘{¡Óí›^Á±ùãµ´k‡É4ù"j‰3ïª_ùøIÁŠ&’&’}’TØTxò¡{²»|è<€V½`IÁ ;³Êá“Æ¶Ã*.Úi®µhÕ"®ñlâAÙ Ú˜c¶oíú.\iá’u zCË‚êF'ÔÐØHŠø“Ð h¤Ž«Ý»¾³ Ú^»¹+eÒš©?TYm•=AxˆÉœV**î™ü¼h´³¹ðzZ’Îs¡yº» ²†ÊëŒ õD÷.Jâx%fLñ¸Q.íõàey“ õ]ì‰ÈX¦UÏ x©Áæ“‚NêÂ×Àß<)!ªO»ô¿ûf)¹™ÄÔ™ÏñÂË¡­òf䤯ÑãoM)`´· ³3%¾]a‰O ~ZK‚¼ßŠ›ÇÇy°Ÿ~)äá##B ‚ô3}Uô¸a“"·Ë¨¨¤’ûC—‘©% @ÿϪÝ´j…Zç}B^ô½ßÊжÆÙ~šºw¥‘è.ȱñ]«N.*Q´À2E~ñ•U§B@ò ïW'üÿ&ürl&Æ™`9%¸Èœ|÷ü“lá^g¡¬âg×½ìiTxÕ´¡×Ÿgâ%ü ǵU“Åq¨ ÍIxMeÈ<3pÅìª]Cüä¿ ÃÔ*Yó(Z®Zc_å3›wi¤,… çE·ii?š[ªt;ù¦ŸŒ©™=)àÈÜ<Ñ Ç²ësEO@rŽÿ–ಽpE' ;…“£{yÛò½B¤£qˆHB¯¦Xn‰ÉØì³A4=J©Í~KÚ›`ª¤Ž§<Šš8¯í1ºÔdTEÚr$¬3íìNsˆ‡ž?Ãñ"Þï$Ö7‘„P5AðŽ¬ª£™ªºh#ǨZJW©Íc÷†~ƒ!›ØÄ‹†ŒX\•3w¼Iô×ÂjyžÆëW¹”*‡;hSC¾%‚‰?Ø(‰ 9·ù2€óK™¼ñ«“ÔÔ•F0JìƒÁÈ]})(z7éÏØß.1Ò÷eÁ¸j%¼5e}yÏIÆ Á,ÌŽQq¶‚p¼èRúÓýÒƒlÉ­½ö³ªŸÁâï(+åö›º8ÆÊ÷låKä%Í9)#¶‡~«Á§„@´£p P7 †˜üØôŸºÆ—cRµˆÒ²Slq‡W®ž´;Ž Fù톟?‘¸!°£\Âë™”ßr:@Õ#ÝØÒþ¥®žµ%‚}»:˜/.{=ˆÕKµò¢%:·‡k·øø=åaª_"dªÙü¶¢è¤\_»ÉîüÕ¦ ±*¬t gýéDDEû˜:¥e¡35=‘Áæé×Ý*+,ȵäµn/|C;ýŽò¼Y„ÈNé$“-—^ &o.Iº÷CD~ï÷¥Zùÿ“àZîòÃÿâKç?¼½bO§R³b2lž¬ük¸Â¹/èC  rÍÚ¤‹"¢ËXÕÊü\]—®U50ûq:¥Ħ#áß!ûf‚£¥Ï˜¾vḔ–z”µ'¤ÿgEÀå ˆ%Ä£Ô:ú\²|æÏ¥=ÒÏ1OøkAްm“Xk_˜$^ÍØþ„U‰úzü¹ý3Œ9 sûcABàÞn·Ùº‰6þB©Yñ„nÌàÀ.ÙIFí |rúeUœŸ™•I#-Óð–Ò=ê‡<•½¢ü u»²[bʰÑé>~ÌæÑszWöÁ ¶›J¶– {*7ú·ìFGzµg0Ê#'°¨ˆóæzÒã©¿ Åó®{)˜ àû…•Åîæ`CA@Ç+ü(PÍáßô+hŒ½dÉá Â4àÀÏEÔô@‡lÖTÃó©2FõŽ"9´µ|Õ ·W /t“‰¯ å=Ç›ëp1î X½”9™WÀâc‡¯ÐÐö~ôKêð@î9ÅZ°¨A)îÇ6‹Xã\¬O$Às> N'ý*®H7Ú‚yîi*A„µ Ä·u+‡%: Í‹çE`¿ãÅÅ7Nv““õ¦ ¥ò-Gj¢j¡nf†­b9»¾¿ôÔÂÓ£|oØa-…ÓU/E»Š¢Jžˆéjv¯x²ŸE8 ›2´F¼´|íUw÷ºw}hòÝ…ôæ Èh¤ ”†™ÚÀúõÇ ÉÖ·uù²E¼i)õÆæ tc] ô A‰ÃuGH­j‰UN_yΫÓ=+—Úo,¡°/á»Æ¦lB¤l”užü&º«xº‰f…Lð±ÏpßùIÂÉÿ’…oìÐ¥‘*Üí|]iÊnþJNt³ì¡ˆ9i fÈŠàIáÑp0нÿ6ÃØ¥Hë(Ó7R5j7˜ÂÅ9ÔåÂeèÕ\¤yåzÒ”±—èax Ÿl4`¸mkaÏ®—¶úŒj|äðý¤zg)*š´hXëiüî ñchëmº¬£ÊÌwÔ¥Ù_œ-PhÏ+*вá¦ÎÖ}ãÔ]Œ)­£,“Âñ€ÂPNèæ˜¬Sšš˜DbùŠæü PË%Y£úÆ^Š©Ä üRS\4Ë×Ë:žâzg³™»¬Ð{— S• _M±†ÒAÇI×Ê_z9Ï럾#1-IY‚uæ ·&ªÄ†nyLÂß„e§¼§T[:£!ð6(ef¶èR[gpà ¹è [zÅM.±¡cËš/O¯±ûï—¥íäb\±ÈõÛ±K]¸ç§ÁoZKÅù4œÇ åd¹×#Yòº—Æ,["à pÙUëàb†m5õMŒ(­½ùÔ¼ôeCc²¥«<J#œö]&p—xPª#§ªÍf ççkxïXˇÌãõ9ç%ã~_L=¤ÏTwUãª;ó$HU»xà­×éÎOuGT;¤õ×}s™sp*}3T΋ÆÀèä{’è µ6K§H}A‚àzÞoèùW²#v:T¹ßª‘JXXtüoãˆ7Ÿ#`–ŒÚ¹’ÚŸÓ›Èw—S‘62Y)_§ ÑÔ²ýVt§ñõôxìCÑñ¥ãa4pVæ«3NHŸFYè€Ó·ÿ.‘êBoɸÏb\oµ!ãîdª¹x63…~áMVoÅH˜™Ä,uES6óš0è|@ê@%ƒmÃc¥‚ñ¹Š ø¢‰0A{«~鈴"¨”¨š¸³rÿMfXWB9çân²åkâq¶~ ‡:ÂIG8€ýà(Å# „£mIx1Aë,z³í ¤D¡vWê[\Ö81à[XÓ~V¨äÒRe‡‘ȯˆÐIk#Íç߸ägŽØcv÷‰Ïöä]Þ*‘adÏ P2Þ’ó8Œ-{.’C!Ý´Caóác_Ž’Ef–ü£Û0Ó;ì òTãoõŒìK.› ƒNÞÕ.ÈèW°Òøo Wĵî%G¡VCþê§`ãWœÜBN>wÞ/¨N3ÕÚHµ è$aШƒoÀ¿¡ƒy=˜n¼eCv”䳘~½ðô›ÆÜd@¥n7ÀYJÁ-Ø·wtè[»r9RÁ­'«H¡?uÙê•F`‹aÕ0ÈÛiÕ£†O¥x…4h§s`b¡iŒÆa0—µ‚;š»íf3Jœ9™›í(Jä¥^¾’0#žì2bz_4h©Z@Wã¼mN;–®ظM\P‚užbÅM¯È7$ò3Uâû56UŒvÍõ0ÎüÚî~³EÌÊß&¦ãáÈ‚`ÿU÷4úqB»´[‚†ÉOÁjO¾Ý"rÈ‘}¢pØ‚ËMººí+òž <¤ž¼í9k› êkŠ "f³µtëâüX=—¶æè”x¬!šµ®ƒ”œ0 b¸"p±Ýµ¹tñ,RŸ„{YAbëø_Fˆ¼@£yE¨²QHŽ8õ~ÍLä$b£ê¦üì@!‰žîèÅQßcH…º+wJñ‘`¡2][¢³Á.]tò ØÀÓÆÊî9Ï$n2¨‡¾äÑè˜I?6ûªr]Ùš¢@óO32:^aÓŽâÅÝä_J¯/]±‚ÏojF âІùj™8ÃÖX†‘yÓÙG7ÁSÒ[q%dz{Ù.'éôÐXÏ3=?·‹ŒßtI}Ÿ°»°‘·Ì»:–»Ï,^9Ž-òeÊUŸf…Y 5(" ­.Øé­œ±çï@ œ2§µbC´“êþ#k—+É.)—ÚE5:l'h:J/åb}/Z»˜Î Ì}ìŸAàñÔçSF‰­»Ï–Šó 8uW"ózßA½ÇX‹?B¦QˆP>Û- ¼—‹ÉAØ×ÛÞ–T€:] ”¶±á÷ç%V%½¸UÎ6–?q®¿r¶!–EÎXfYE«"ì• IÖŠRëƒ9šSdòÔ/ìK­Œ)#ú>nó%õ_ ;qµ >9æ¢ô@1A}=ÈNM—ë t$79EZ¢"&kíOd}±ÁéH1_ä)¼[q÷ÔëºÉªž™,\Gtš¼`Õ$áV±'‚ ¼-ï)'1ê›l…ýYF¶™46.aM­<øEb Mùó¢®Å%GmIÂqÏÔîîÕcGÉ1…Ù©N5¯À ꮸ^ýŸ˜‹šs·»5{´îNô[=ÏPrŸ”f«ûwi7= jIúÿDJŠ5'K)2TѱLqØp¡f)ÌtK Ð€Ã¡^óy¨6üxúásØô~kÝÅ•ù§“œŠ¥V6¦G[d÷ô«Gp‚ýæƒþ<Þfa>‘=/$N¡ò†“ƒ¯ÓÅ4àé&¯Uè‹L3,¢.äU¤lØHÂÒÛK¡×:ÚÂdõøXÙÓ 'ùar%• |µ\öWv1›O†ÖÏ~ï×Èç†õõtn¿ÍÎϸZ3 ,¹%ˆÀfðC/8ó¶ÐÍ]ö¨Ø'ú:HÚ†Œ38 4¬Šö†3áHyŽe€h6GH@È[åX"ˆՆZrxy4êÊtWŽÕ”oˆß•‘AÖ¥MË¿!gz-²Yþš²D…õ5_¯¿¼,´4a09¹I¨Dôsð¾ÍS3ê©=ÛJþB1ÚjC í^yÿ;¡* —™(‚Ùv~ @Ò:’”¨ïk§ ExHº‘Oð²1ªÃ› 6—Ø=r{Æ_1žŠ»À ¥ð«6ÿp¼ë¥ÅÎõÍÜ´%‰±¢¨pÃgÔ0IÃ+~à´Rv‘ÿ¬¿ëŒ­ãþ:Qýv©mœ7ð({ó7ð<­Œ™ïÜßRg§‚`ƒ^bú$Û}nq¿Áò¿ØX$(U<¨<ùŸ,  %è=K“ˆzWŽá¢v^ÕQÊ-FFC•Àl Ùâ «¤.…ªŸvMÙ—ó¸Ð¥ÌsM·ÒÆ2È$÷ü‚ªí%=â‹X͸ýˆGŒßÿúF¯ˆÏ:Àv Ã’{n{´ü®ÉØmßÒ[ò_² j öúšë"aúnŠIïÓö4Q]ÅÕ,9ñ=¿Ð¡×G'àf£¶>CC¬Ì¦Ê«3Åð§®\K@ÛˆðôDÚ…çnØÀu•ÆâawJax_g¾\8ßy»ž)µož„k ¯ë謀 °u…‘–9n,žxgLµ~x3É‘«åíoØ›WvΫ|¾Â'W5ÉÕÍLwô ‡Üâ4XtŠ´”Ó\âj†ùÑtØsÆI‹IT ‡Óôobxf´Vëò.SEpÛ@Ç4 Œ7{ÏÐGðÙøD±Ó J¹ñ¨ÎûÌnk£‰çʪApÚ†]?Ü`5죾âQIbˆbªð€ŽÆrè"jöGÓÁK’"úF†ÅbµFyzO¦WC¦>Âæ#FÀSÀç†ï ƒnG™³+eÝl·½ lõƒmZ!ŸÇ æO.ŸÌôjF;Èω#šrËš‰%PL©Tº[GhÎ ‘UîÄud5ñ.Þ —ÏÑ\`ÙZ&µHiÚótE:CˆºŸú7Øîh-È_4·›Y{ÖäÈÏÿB­©Hœ²òt½z*Éw¨ÜÅ=éì¹›ÁÒÆôMï”6ÛpuwÙÖÄC9ó\fÊcß"¹V1s¼l¦ Ôª 2ô9€Î.v1pBñý@àËéû¤lùÙˆ1¯ÓhçÆ–m§m¢ÅÙGöŠgž W®ÒkalóæêMVYå'ÙB.Ç‹–ZXضÚUúnvË©²Öö»•‘ã8¾U=Ó*rÒôÁ$EänJ„[×þŵ37ޤ!ˆòjÙ~»%{P¼¹ªö.ÊHˆú÷¬ñ£™aË‹£ÅxPÌ´Vâ EtZÉ 6b¨l’yŠ]÷òª/{tLó{Iœ­C[ü&nT˜ïu¦KNt4ë¨åNŸj v 'V£ŠVxþÒ4F~ -·ÓI¢ÇÈ3ÆY¡@_ 7…]ÞÜ1I$¾˜-öv2¥AËÙfÂÇ÷²r…ŽžlšWÙ,D„=Â=9U ^_/“~9k\öµÄ+øxöÞ)Õ,Y»VtPίɟ}(K¥ 9kØA붮̮éè‹x/co¯÷æ‚¿™ÿ ý‡r“"5…‹Ôk*K­Œ• V«Ã“9Ê8³öÛ³ò©Ijiôûƒï¬qÊøôËѳNoŸ¬èŸf•zÙ‹èD,’Â\_­øþ~ü0ºhÝenË…}CHŒ® x”f/ɉºïÖ Ë…4—âÊ¿­ëÏ =§|š4(ûíJ©Ô­BB£¼ÖSáUðsF­¹Ã0¤•ïsñ'jHñ‹£Ó¨å9%ÐÀ7Ø)(sh±/¸ ¤%ànb¸ûC«P®”ÖeŸù^:4²<ÀÌtòttÏrßoáœe6–u)Úî –™\— ÿÿ·þÞÜ=3ªåiõ% AàŠ”) ›ÍÔòÝéMÑ©V[cXïvHñÞn’h”‡±Eö³•JÂÂ0.j¿ÌÃå.Öçhж©Š#UÃw|9Œâ¾d|h:›'¨J0|§SlhXtó¦—”ÁVè÷°Ì}9¥£©UÛÚéîC ΦÏ¿m›”¥_ ƒw’ø¶%S%‹V7ÌVAfƒ­)èz†é«Uœ~9aæ°È[à¹rôƒ…¾ßî­96ódØ÷o:pɼˆ²ÅôB1”ìô–å“‚ÀÒìA!î·r½)ñfúmi3'ë¯êcMA²;£#)ý±Ìeƒê)ºl )ÿ@Ðe°äSxb‘òó¦5\XM`ÏŠ;îI XYßnxuÆi5åáÿŠª´§zÝf`'êZƒð¿ Fü½  Z¿--é#.M’Ï'ZKq /[­œ¨@¼Ûˆº4¾ð×õÓš=ñt½‚ ùÙþOƒ l³ß¯BÙô6ê`˜s‰*¶ú‹£ïÎDܰFg+è:J;ߛؚL*|ó¼d„œLó3Lõ¹.û­¦Ä6ÿ‡íãó Ônš‰7ƒD+p–i¶_ÞU‹y$­ÛA`u>}yWm]Rü¼Õ :Õ¹•ñžy³Ö7?MÙJ)óŠ«¿ºVÝÁÒH¢TV| zLÚéGŽ‘öF\ºr²ñŠˆÿöC…öû¾ ÚBgäíoËç“wB˜œi"i*í"ƒxê–¾÷Ü»êfÑLùøa‘$R ô±ŽÙ%ˆÔqKˆ6±Ìx$Œvª_ã²½½áÚ›²ÜG !R .üs[p>„hâ9¤lpÓÏ‹Ð|°u[{–b˜QÚq¹nÞ›âè~wE}ñˆ]lˆ%dÍf?Ty‘Á€˜ЕªiÜñÄÝú¬“/åGKàeâÊ6Ñ€aúMdAKkkv‚(Á<Ì%õIC¿\y'Ga¾zβpÐ)]61uIuǰ:à|; ìt[ÿe}LXP®™þœ2]ŒÔ“ƒ¤ö_úJ®1âcèŒ õKhÊ/;oеœ­!Oë„ÅëÄ÷Ç çAáíTïêé®íxy§q{ÕõâŽN˜–îž’ö‹Òª®*h@.bóðÄ‘¾ö¿±ëv‘Çÿª€í¸tµþKíKÁ¨@‰ ÑÝ–¾{— %ÁÚæ5I±Ç ±¡q¬ïvƒPü­ÒRÛ”-‰°½†Îñá}l4›ÿ«eû¼#&ÁtYT7ž†ÙKñõ·h÷F0\·|âÁ›=´]}”gÒ™žðØ2Wާ'¹ú½eÝ5)Ízu] +@-Ï·3êþãì‚GÇU]ÖÛ]0ì)ÿ‹_ð© Ú—©„©  M0Paà°Þ~*#¹‡#ÈÒ¥Ým4&×ú_ƒY9Nýg>Y0ÛöݬK·3 ²»ò¾Âµ›¥èJ=ž,Z)²3¨0ö0(b>d",/Ò€0µóAÌVÊ¢.¥àyzY8¹`Ú®}…Ú‡kP–QA;“®¡s0hê*ÚBp*À§ÏZuo‚Uàëy˜9±ûL:ÍVÙCÐ}­ BÚ»åêŸK-ÜœÉDìäÄÛîyøµ^×Rú'÷€?ÿ¼ ¾5»ÀݽJ¿-£ .-Ög‹Å$è¹2èƒb1:âR—Kbé4¼;„õŸhйÓalwùËp‡ª)ÆÃöÝ侘„WSW)ÙVËùþùÒ¿ô®7Íæï(8íwL¬uSÖ;ΠÎ7™áöSÒ›£`) ã¥Áøw˹2ËoŒÅÄvR+­ƒ‹Õ9|©ÂÔÆY!V%Éá«ò¾Ïcrt›H´(KˆI ¦ën•­?V°(1–6™ä†a5—cÛ¾k²s¸¢q÷ݲÇ!ü_Ž`t(Ýsg–|Žà…Ó{{XÔíߊ,¶%…¨ðtEèïü¶YõÓ[¾Ù_Q²¢ÙCø7ôÐ#·–Ù“±|U¹2)A=…Ŭ>=sÕ× 45à9ýŒC»J¡)úͧë½~dôÐt^9!_gØÏIËÆÂ}‚ç-ùÊ=Ü¡Ÿo¾ºª1F¶K•× Å8–WiEüî‘€ì˜D•YïM-È@­¯ÀÂE7ÖÓÍ¡ÁTWÉ"öã,`]ei6‹d¥j—ÙN¬8Ý—Pþ!ö컎¦ÈæÓŽ9VK&yÐ2f³GÔ¾±R=9˜8)[§¨m÷ïo…¡]Xk3³Þç‹ oÕÿzköe®Ô0 ê"C _H$Ó~ÀóA<Ø…–³õ­ñÌáÖçÎü¯?bé<‹Ñˆ[i¥§zX±«gɽçheUËPOfQl\s]ŸÉ½k{Ÿåý¾—¸”Ú…K›_NÍ=YVþœòGÔò×õo/–QÙö7¬Ðèg\j•ñù$ê]×%Cé1ó¸u8pQaeïÓè½”¾÷ð£IÖÇœË"%Å<\ôNXç`ûµ—à??W+`µ)Í­ø/LJe-œñCSSIAûš®¿F§¸ZÊ<{{xÉ` oÒÇÎÎuêóÏpoªºA·øÚ‘˜³*ô=Ѝ{ŸÁÓß;n¿[º0È™+Šø}„[ («{ÊÝKØeiîÀìA´Ì1PötŸëÌo`«·b·P+œîYÄÄW%Vv!¯ë!cöŠÙ¬‰ˆj¾¤úÏœÊa;"­Å9‚…L|Ãú¡úªAøF‰O?ÄìÕÚØ,`»bÿ#GëpŒ(\¼"$ l6Ó>^–]j°í]jþ:Å>áŸg‹28–u_eÎ9‚d‡}g,2‡X”ëÖ2H}1JºÚ?¦ë^x¼ïG­2Ñ^?À¾ c)ˆ“sL AJü(úê¹ñ.Ò?ì|7º§zLÔuÃȈs.Ü—¢bL4…sï01ˆ&íÖê.öP-ÅT-€#µ$ÏEp8¨·U8ê ã¥øŽ GóéI,8ȧ‰LSZ›Z!ùòvòL¡©(–‚µ…«4ÍàÀ:x`^ ñÁ»†ÂÛ¹9/€-įŠRb'›"ʑǓŸ(-•LMðÍ79‡ê¡»õf"yµÂ:Î]åÖÔEËØ‚Eõîj\c¤Úçܺ`¶uUÞn»KâúiÞ /Øã# siVB@&h+纚&àµnz”ŒOº8iâFìËnˆé´ñI¤)´Ü¶79T%½H­T˺° D)0ZâÍå‡pC­¯æÊìø¸=õë#Ì0 ‡éUil9+¸¾6¦«[pkçøo ´³ÈDbY<«4”ïѶ‘ö§¸åã6}díÈ*‹r¦ÙÁ6z¤¼7Ô(? »XiT¬ÏšÔݘÑÌp§â÷)NH °ò‡–SôâKž,N¡Ø_×[²{ ˆM³@•œÖ5b(u7Ê@žgx˜Üþ«§jxxÕV¥ü5–Ú+y}”˜6TÞIU´K¤–"ïlv=­‚NˆwIqµ{$ùMð£=oÍ·˜"8ãdLÿÆ/¯Ø7ŠPnÕå1ö8®†„wì,;9Aû>ô<÷õ-'D&úÐçvÝU?š‘…„ä\íïk [•h .J ý»9‹¼«žh2†ïàs­7àI8˜¦Þ·•Ù{xûŒ6©ËÃfáïÔËUEyyo™Rd”_A†óPýϹ7îã>—¥ºÿã: ÇKð1n¥r²[Ï{LžÒYÓFE, Û“r˜`áZà¾Ïî§5™3]?©ÎȱA;$ÌÃv1vü„ °ðH|ýoiŽÂÞÿ@î2ƒ=êEa­Ä1·ˆŠdNË’³#í™û_«Íâ½Òí<ÌB›–bHTH2òà%x&8NÛcôIùfйZêev=AèJ¸Ñ€a#7hKåU‚z«Fü®ßw^rF²ÎG%qØ0_Tõ°/•Š|I°gþ 5j «_n³¿÷ÇÐê.(´R“P Ï8Ü7i2gšÇô¤ A8€õKyõ(ËÜ4Ãj²¶ ÀâŽw ºÃƒSެùßO®ô`?æ£&;íbUd…Ò­•«òú”&žÜù8Í`Ý^Ú\_®PÂDƒÓƒ3s€Lâ 2RZà@çmD”u%ÈðVÁIDâPBÏÇd:ýŸ†±ÃÐxÞÄÎ_[(:’Î+4ƒ~VK‘›ñ w׸ uí›oLˆ³0µ"tËÙüšÀ¦jÆu…›VW)»ò›ð®Ø:¥©h£;GâÃL"äGl4ŽH¼»è*¤ã,)…»o³¨M§Û—¿†¸xã3ê? @ž§P•b¶ nWÁ¤’ÐëyÁ,°Õë6UßÛD¬æ-HØ›³s‚ðÅZ&ñšq»UQ’òù†ô˜ŸJ³$7ïk‚›J©$Ú.½Õð;4¿ÁÈîñQ2}/“‚_ü¸#°ç¥v³0Þ˯ÚjQâËÛg¡›¢‚²iÇ©˜Ù—š#óÕ’+̧ñ»9ó;"qšHw»ÑêÕ=ø¾Hª¸Ý%åñ±mб2:Q»7[î¦TÈëp¶kÚggÃf²lƒ`ÌE2Ä"ðÛn%ž 5CÓ·ùbBý¿5»ÉÅäG¥@¸ÁJ0õ‰»íÆÊyŒÕLý’¤ok´§ ÆÓ]1á×úgIÌÚ[|0½ óÎø¬/íËaa’åÌ–‡3ìG¯$s=œ©Œ1šâ.´WщGFa¤#œøSÊ…Þ£º˜M]ÑÎE*e@Ä¡”ds0ULCEôþl߇h X‡ç9S·‡st‰š'Q­ˆÄ±Ëj½c0ƒRw[çò·—Kžl‘,ÂPé"5“ӌǫöá2ÖÚ%WßÛa³(8aA~4L|̉²ÛZ¬î0hy_ýYGð¸¯ZÀJ׿   vP¥>ü^0?ÁdXßLÜtàkjÞ¾% ìPSL; ‰Iò°{:PÌߤ2:ÒXõ"”_Xó%AS±˜öÀÃO7%¬²›ujiø;wL½ÒªÊÑ’çoŠú¶ý\û¥‚q=«¨BM؇ ̺qò× ŠˆÿÕ A‚Ý7*‹Lx¿hKÓ*†Ôsô£ Ü"ÆBÒœ¡õ ,Ù2FuÔ•%Ç:.l«â²¶B¢a³×\Ö£¿!óíý+’ý‡¡¶œÜWÕ<zFWý‡ÎGŸñý»@%TM»e8˜4מõŠÍw%PEøêUµžÆ Ö*äó2@;q,éY-õœ«ùïòŸ8a¢ËŸ´vrWËQðñsÌ× d,®ÃÁÃ, ÁðMï`Öb´ÀlFv ŽÚȆpô-ÞôÎdQ#Ĭ)ð-ƒ»E^ñëüWÓ¢“ÅàT"¬/¸9efg eµ®b)W7§ \L-½x9EG':”›A!ÌÔ•.É9ÁN.Å_ PP»ÄóÆJÙ¹e# Aýlª.Â@Ÿ]w†}rÄÏT,ç]æHŠŒôƒêíš —qöW€ô˜W 9'Ö¨9¹ƒÔØWëã†ÏôóNCJ“E£&z(c·Fz§ðîŒæ`W©’³ÚO…¤YņŽ‘ÿz Ï“Ú'®ÚðáiQôǺá!wo“Z_û¯á½­úΘªùØ=I`Wç‹ù¦Z,-ØúŽ31åž7;Ï^óãÿÖ¤³y{Üq Dt­(@t` äõ&jM1¬>Ä÷[{g EôaJ/kÌåÍÑ;Ñt“¥ ÑÎ[•+{1äqQ’ÃŽÔ…“r]ÃvÓ£ “ÿçɧ%âÊSÆñÖÓöKçws4vÓ" ! ¿‚‚°E쨭¸®ƒY +úÚ;çÉ—á3¹‡>YÙ™ Þ‹SfáE'=”K{°ãVt5€ä^ÌŒÀéÈúT>‚9KJª=¥ÿ=+ÛfßL»ÖGÝÌÃÂOÖ µ³+&6¸-œ†ÆÎ‡¸0UÇû™ÚA"„OÜÚE{¬ïS&‚?Umt‡E"fŸýoùjr“öÍ3b¾º.l—Þ¯øþ]êCý;b„bQÅOÀ¢/Ý´¹2Ød8",Ž +sFR9)N:7¸¢jK> ݧçËx¿µ¹R6@=$ÌA7P•ááy9”ì¢7J9Ý›^„*™ÒF£Õ¼×z5zÊñ=ô¤jÄÁ>ŽÛÖwf.±ËèçhððM={Y7#ûâ!‹í<1AM“Z4á «Pÿ~/~r@ “e’~S”{¿‹Òôz6¾Žþô?Mè·‹ɶö-YüÂ3)IÕkïö|oíduªÈ¢HJëÛŽ~NMϾ‡Ôönƒª‹b..ð— œº¢1×Ãü3ÚI=ÀWné-¼V’/éªùÊ:yŒã­Œmcãtûþf’Xj8«€ˆãöóG8ù`½L¯”-r£ÁHãÄòаÅ«€cLåÁÈ-†ãrÊÌYýÌìíù&í¥ºé¶m˜µdKÛ“kÙôJë¿l/Š9=¤>JÂÆ`Œ‚šÞ}Ø[ ¼«jÌwÊX7ïäÈm¹—ëÜrtü­Ê¹”Þ¦†¤âj !,9Qnx÷Mm„Üþðx z–UmÒ¯¬Ö/h5Føh·ZKÞøtbþ­¼«ebØAÜ4ÅXïÒ5¦Ôœ&¦³õ•cá\1U¨xZ*ýÄW]ñw§á92ío™ç™Ô®ƒëH!²EêðHlWݶP¼CPpG8a¾Úk±@®`åÅYm\šÉÑ:è)C ÏêØªc»Šº/AñueÇeTHŽ2ä´ÄÊ<Ôã+eÓ¢õvžœZŸªÍRÑy[C’RQ@eί9Û”%×ôœ¸à¨Áކ)é¿aå‹dÜ=CCw¾7=0ÑwÁ 1‚ŠŠËºMÒeDt›˜!ª{¢¹ß<ÞŒd÷³´Qj¤vJNìUlýÂÃ>PÅŒ)ãD;ï±ßWÞ‘p[Eí§Èƒ^HʯÑ&º{Ý4QÛRiÒ­CÊÔÂX¼Þ2%^Ä3ÿ( PœÞ†¥mÒîñøÏúoý>¶5C­Dc'™™…mȉhÕq 96›edV¡Q…Ñ˨ƒë®Œ/›F­r,ör¯n£šÇ5ô/8ÞBW¯4½¾ÎX¨a}hn©õ°ÄÒrÔ¼aeª<<Ì|Šw— ÷G …Ô0­s»¡!Y³û¤Xbç<(Ýý| ]c5È{ôÃ`;¸áB^м:²ü/OIûøúx~:ìUb³~¢ÅûGQ“CÙ“wY2mŒËèʈõ3¢ã, 6DJ´|M¿X翖Ѓž?‡Äˆ7M°ÔULVÇô6ˆþ)åî–þ¶PýVâsÙÒ¹BÁ ò"bk¸£ç§d0ÇI£€Ä˜1°„ÙÜY ÀfÀlöns0Û17ãDiŸ¾^¹ZÍÉ’e-C‘ð½‰&÷2J+£&r;ÐwO:Íï?uŠíÞ2–)×굤•£/A»Ïc® æ=]%·ÂËð,óå±g«Œc Öµ•O‹ º|¢}ÈQ*ˆGü"Ä’ÅL2a÷u˜ÖK5ß¡ÆóÛÉÆF°. <| Ækt( “Ù°2Hß‹1eñ6M‘Mê5‚“+ëÊ­ßbpd®äOm©Ô?ʪáôRÕa$w¶÷Éä"äm3ǹ¬ÎÂaâ׿(yÖŒÍÌÑb`º"ö…w?|^£e⪶#¦ÇÿUIØs«ÃŒN©hGa•ýe üîk÷iâ:Åíl…lNÚ¤gnd¶t ÉcÜÁGe)pÌ?äxÅ +“‚Ô—¬klœä¨“@P“·”ØÊirý©Øžƒri c›Ñ—)C:6ãŸmÎÉxÅ•(L´ãØÞ Ž‹j!@›`„¢ü³u ÍZ?¤ˆ »¬} –©gµnx€Öwh¶ÝA§ÕNnU¬m˜ãîÜ·ýår3VÂ¥¥©:!ËЉ`urûi¢š&8cU_æwÝN¸·ÂFÆtÐy©æ[ä‹>' ʘxOrÒÙ•b²þ9™C!EÿÕŒG»ØeñSHŒê¢| Òdô‘¥2‹OŸ˜ UÞë‹`Š/ß{Wr²ã‹z»0A|_ÚY¡ºÐz£”%Be¯z"ƒÂx¦•tþ¶A½ÖGÓЯ` qš<©àíÔ=’üÚ_ùgoËè«•Å–_™cŒÉ#7LpBÁJg”ð‹<ý ‘éøá&Y±Ÿ ^$aü€¼õ‰š4‘‘¸Þ¶Ï 5ä­z:uÃ>½èÑÆÕ‰›"BFã#S4NOãº_#xîÿLóÀ/Š ;‹{îí¡×Y'K™ãr>ÿzÃ'¾Ü˜Ðï=GC¶®ÒH°;¦ˆU.LjçTF‘¡gr')-*¡*Ð ºü­Tïίÿ3£ÉÇ|œD€(Š(˜Ü2­¤à§ÿ:‰3A%n'}YQç—7Àdý=‰#&TŠ.Œ¼m$'m(͵z;:ž›ŒÂÿ,m³‘ãP¥GNOaBw CÚ/¨F³Se®e)FË{Ç"›Ù“5FXëS+Z *V)ñ)‘Ö‰´×1^àŒ]73ÈΉS¥%œkDgñSŒ ùSPÝ{ôŒ9‚\'þÈ¿iÇtø¹lŒI—ù½ ¬ï8úÌ&‡QýŒô9Œ¢ ¸±¿ŽneþvS£H¹øP€ÞUë9O5s‘î[j‹ŸàqCf½ç†ysÃOÈ*䑈üôòµ#þ½ÐDÍ‘F¦òå›ZŠržþ&ãôéÎØ5Nö±v{¿ãò»~•8{•?„f|â>Ñ<1LoÄçvÕ2Ú ëꬴïÂOöOÄ?ïJæ‡ó'×z)°Pà 䌲S}M+Š`2RÆvl5þE‰gê;ôì)ã-zs¯®Þ.–™MCä/|ÌÉ ÷Ó™½é`çË e‹ÄÝãÌ€”ÝjÀYw BKbêÞ´«õWšfO¤ÍC£;Á«Í*ÒÉŒ ·n¶x»I9YLþLªöfJ[®êˆJ?tË’;• …íº‡ÔÊ0w‹2ÿ¢qs“<ä%É\_·Òä¥\º;š ßÙ_ïWUÁ"DP¢ó[½ 0ývÍ™ Sk% ­ÂÅ5Ú/—íIü2ÖTö¡—ø‹î~ Éc ùÑ4íÍòõød¢7×â¬0 è7Rºlž7.DZ§–T¸9:Š=ƒ²ŠvÍi!ÞòÒ]T„Õ<@k[½éD¸7V+’Dûe^NEbé·#tB¬ì‡šJâ›þdzÆa~ïP§™»ð¾€»WÛš>æ½·ð…êOa z8ö(4fò•< Ÿ¡“Û[º¶n1æ|bŠÙt‡ñô|ƒ›DG]Âà¤8U¼¹l5¼‡H÷¤Âç"YžÎ.µ€„úÙ–50")_¤YBw6c¥4Ý`(‰™ueSc’Twúý2Ü@è ÚÚ–ÞcADí0 Þ£ÊaKF5*ïÔˆË ÿÁ ­Û’W:16 ÃÖ·!‰*i¡¥Ø;Š›véˆÌè-¦u<>ÇË`ú*j£ ½’Ñd’¤%ì«Û2 ÁV±k<Ù·&:0J:ìû!ø¹éÔ ‚Ïêxýq_¿n@Bžw™_òB‚ófWþ®«ô­Tf(§¡ï! Ÿ îêSøÂ;ÞmÁƒšñ‡ÆÉ½ku ™¿¡Œ¬»èÁ4á@TÝ¢ÄÒ«ª5H¥™uµ²âG¦ôQ}Ñ}Û˜Å\84Ì/ùÄãÌZ¼ 4µ'šìFl 9{%õ_§Ì.®¼Ð;ð`-0Ýk‰° qi¿‹L¥^a’ö\²–ÍQñr­hûZ×-/ #MOøØž”Ÿ9Á¸Z©¦ÈkÒ^œC'o•ޱʎI³¶‚¥væZ¼fÒW™Ýnî¦¹Ï ™€„UÿK³œ°ÙÖÉ«ÁÖ°Óâic¢_€ë,¾ 6ÉŒ5BBZ^l©?\ë>»½iæEõÅ0£tDèâàgªõ`ØÑU¢OÇ­•r¹×ÀEƒƒBXÒæ9“à"-‰7®Ytã%+þÁªPû)½ö÷²Ž·Yš†97hWÁ“j¾«/ôh>œÙ oÓ¿•ÊD¿Æ>´ß»gÇÆ)z ©qB rËŠßG±|žmŸÉúDÓÎguk a†(Í>0…ügfúZÀúŽ¥úÙlzj*Gî;Ç¡¢E<]×e¬fã²&­‡|d´}£îÀ¬i‚U«rƒÁϳÁußR©ª±»àIã¸~{(øì*­<¤Ü®ï%³ë`"FhâA‚ý¨Íyˆ½îKy·G~HoúÀâmæMzŒk_û"bðd2§Ò™IH`Nâ$Vt)¤Wç(iÁi‚ðÊÝ>X˜½@ÁHæü}ÖR#<¨²HD€%qaˆ’ôW\n¨iÌzf=ío[aªªl™+ܾ!`R…”øA Ρ«~k„1"“tG©ÛIÑYÉÔ$êÇæ­ž’“eÉ?¨ò,šïï8úâA°YƒÍò„ÍU¤ã«èµCž'YÔ>PXÊ¢Ý\.å¼oEGyÖjqzš-Æ+þ¼eß¿ð_bÎÁ yÊ6Ø—_wj,.׸;®%Ùnñ8?¯Êü*RSá‘óÏexþ¼œ¬2lø¦Y­áÐN R¬ŒVö¯ekÐûíð·õ¥ÄÍl_H€½^³läî,Hàug›:uË@ŠýwØÍ~MŽ]r†„|°N=‚N÷é ê=õ“w>õmSsà—§Àf`ù šóÜ’ð .%ÂäíΆOßÿ×ðÞ„ëÅ”Ã"žk ‘¥†PË#̓Ÿs€è2 ®“¸Š± ôëÄ3œàXL3svçò1²2% {tšù¡²Ÿ8îÉ!¤w3ÆžeˆËKr*G€K$%Ôå›9ÜÛN8fíéìø"1¿çŠž³I—ƒM¡æÿ€Š½aÊ ¾é eìSŠšˆqTs†Œ;uæ}óóâQcZ^# üòìÖíB ì^YF¡£_:Û³÷²^.‚&îw\I‹œÃ+Î4àj5É~{„7“M~´dÔ!2 L”¦¬-Z®ß²”ì&l“æJ^ž¨99|äe+™›óQë´£™Ý7e’ÆÒÕ»DK,¾4!FÖm6à‚ÃR¨mâ5ž)^ŒÈ„ºHݱ߈¯ßöœ‰!Ï•iç|¿ŒéˆÁ9ÿÁšÂÃfg™×ÿ ½ %·¬é1Êö…>°i­½ì¬ÛÖBG¢Æ}È9°á”¡˜BèPÈHú9®êƒÔ/AÿÉÓZÀEʉëã³¾dÌËåeîÍW! [ëaô@.uCm}›e…­†á:(*¨ðÉCõ_ÐÑüÝ úú7rß›pÖ«¾îð%S*ß¾¶J…—­Z|‹,äŸË>3UÂ~Æ;÷>ꆫ‘ÄuŠC)\G¼7ŸÂaó@oO£óiµ3eš'e ~h䮹Šì¿ëØ& ®¤)¡] é³^y1"ÁÖ€M ¼3TW͹„ɘ‰ÓeOQ˜Â>ke®ëgêðTm´£$×§´È6 ’%=È »²´1}gUÕ[úØND1g¼¨"ÏÀšY²ïçS%!f­‹/A¶ÅUk»±»d’]ɯWEÀe0TŠ!*‹½ã»´ÎD<#íg‹ø¬’›/éèY³åœ>Y×\õZšåš_y†CøtxúìyLmnÛILpÚ’IßñxþÉ”©¦¨=ÃÓpHÝjÜLL&ù oÅOƒÈ%©S"1oÆÂòW†r”‡|¥øa ͹°&|Ò·tR·®ˆÝ«+I_ÅÅ®²-›áiâ@µ¡LˆŒÙÙÄêÄaƒš¯ØÀ ä9„ß'5Àô‹N›ä-"ªCÁ.¸ú\PŽPتÐ'æ´o¬ü勿ꉛ ¨Ç‚/q¼?´«àd¬iø@eÔ©ØÇQÈo}›íp–§]Ô}H¡0\)ÏEUäAÄ4Rc<æwÚN,t(ü-MêNq`6›PRvÿ*\™í)$*ñaB [‚æv*‡^ª¬Å娸D4hçËpMüvBF@ú}Šø]^w.m~¶©Î)^Ñ¢&©)¹Ç´½}$ Æ&ˆQ-âì &–ÃÞf¾Š_yÁÎ869 í7Sï)ý½Àê‚Âö«D{<€­”»<%òõ(Dñ#y˜÷ß=@/sùøþ÷ i…›«jMr+7-ʧݶ2sKˆµÈr¯lLÖ©èá4ôv,xáyÌifXkÞ0 Ý$1Z „œ]ïéc§o Kž«k¿¹¬¢j,uà}5€¯2`UaŒoDª1ŸÖ_£Qç¦ñK.î_i>T&D¶‹r–H…@¦!]$ã󵿶æ|]{'¦{Ê€ëQH)]c×4ت®€ìJ'[úWìÔ<ü™K°$rsÿ9OóvËÄ!Ë<šòjÚÈ`ÆØÕîÛ‘ÓëòXÑ</ÜëÛt%¡‚Yï3牄úVƒŽÔDý®[ءڴRÄ=‹¸o c~¾p‚ÓS´ƒÔ;A™91¬“{ÀÞù)sÉ•àlÞ-T[Y1è±EÞ¸UüzV™÷ –?T /Zo˜éT«Y|:ÝH¦~vú¿`R V¯"±=Ñ îåc×Üʼn< fsù¶É;©Z–\÷伊O8³ÍÎf:zQÝva{xJÚ ‡×¢^.f Á>£Íïê}úbKXsÏÑH‚raš™fZ –«9†) GÄ@>rå#ÃÅCú”t®Î‘ð(Ýzžý-º%E£®”DÞò>È\q(^”àå8d•Û‹Ü{gòòÛGºF.ªi¨Ô‘)ñ¨ ‡*ëž#ä3Å$f‡¦Q‚è<Ÿm¨vIйj±^Ü/»˜bjçêi‡õx4#/ÈíMhQÄ ³ÊËyï?ÅSZ9âÿ šP)0ÃÐWÐ΄$L0-µ2ÙB]¶–ñÁà¯Û ‰[l²¯L‡7uqÊØ#ég÷ù~åºGþú0%1I>Q˜3¦ÛH\MPìÙýlATŸ5ý¶T}mœuµGßÕ<àÃ…-þ Öãà¬éæòddí(R¸P!µ#EŸg”|"'¬ ›ñ¯Çôç¥M¬=U±E¼¹Zu V0ÈÊ$1™_2Õîj"âäÀõêòÊ˱TM_Ñ}Øõ2«:t¾É^9O³ ÇŠ¸ ‚Ö^b¬Ø‘݃ôbR•”•—ÖÆH›&ª”¤?¬£fSßïÃ)o3fuTx³m0-ržšÔ2%5ø}ûW…¥ºÖ);Hÿ•êk+©2Š0*!z*Ä™%-3™H&ʆ{™oúp:¤¦¹Îšå°…U%Aqt²±”„yÒÁXkåxÄ™ëSË&õßÐÙ#j’ðbè¶ñ驚™F¯íµ0ŸÓw°É n©˜×¡€|Õ½w#£19Ç4ìøÐù+æ—Äñ¡õ»ÝeDÎù¸1þÊÇÿC}ùöKYþ¡o>óu‘Ýõ£2 ºw2©6—‹øÊay¸†èp ƒ(‹”ñùCããmîÚ*6q˜›kÄŠ =¦Ý+á9UJºÀ/˜ˆdPSë~›¢œÇ}ó~ž}¤ê?¸Cqù¶/S—ÿ(ˆ„Ãqt‡ƒb>ÉiÇ-z:'ˆsŒE%HüV7Õ^¶Ç4Èíìž.ûñeX‹•¥DбŸók]—id7)¹„ð+2z\ã!¯@ æ·’0ruúœ'±³(,вFŠ( p[…**¤Ì ³^›n6¯ÕH™ZfÔËûœÑö>étª ³4“Ê}cKŸ)ôÝdl(sÍÔÌ¢³¬ùÿ.š‡F•­gÈ£²ÇÆd·KïrÎØW0ToÍÇ(¬ôq.á We³±°ð¬i4WWËÔU÷¸®®Äú=ãš y­%~uû ·µuL¿Þïô ÷&³ÏáwšM!hè_ZïÑÙOYÚ_ÆP‹‘#òh§àÙI)¬Ä‡Ô“‰àçŒû,³5 ‰5 f|ísˆ-ôrÔ…®Ÿ†>Î1}‚$yÜ7Óz[K}Ó®ˆ»µQÁÕõ¿Æ{>)ñ©‰ð–¿\™LS‘ŒŒ/[Ý`Ýb„ÚAmîé¹A+îÐaè–áPÿí}EŸp¾ƒoËÍ‚ØíÙÌÒ¹1Ò×ôÝ,Ôy¸ÙÏ+1h^%ÊG]±øVb›îñÑo´Á“תÐK4!q}õcGVyò4¢Dy˜yM’XãÁ6é$—›íé’¹xhWA«¡¡ÐqÕ!ÏÑpïoZï—‘†›áAšî «pYàHó ++ð^Šk”BzÌЗ/k_ûNÐ^9 6"QM è„›^:óë{:³L.Þªè¥S¯.p÷j~Ž¿c¿~«žLÁnª‚޾õ¦em Ý·³¤ëPl½­N Z~(½÷…#î1½XàÅ$cñÝcžíœ°‘à”={ïÞmµ¹è©dÝ]ÄUØÅÕ£zQÇ`XÔEÖã 6Çí‘t„΋]¾7ª4éõÒ;@¡òœ§°L‡ªÑåóÇ­UúvSvB~ÃÚ¶Ä’ú¥:Zçjhc\ôø´à 5ê°âÒÕÈ÷S#g£žv—#5mMž6‡¡? T»Äê‰ËLNëUè9Zuh"Ä ÝÐBÑž‹;Á`…gÍZÅÍù¢¡À`-…lñ<:‹÷uMëÜ{БéI ðuÓ8uõª€¼×ñp]S™Š´°£?haµÖŽIõþw.µÂ5—Ú=v••퇺àŽ1¦W ô02ÛK©LH$QɯÕbÛ ÂÌYTö+X ±+Ri³Ž±Ôs šFزCzÇBU•Ÿ²ë,ä¨b–ɺ!µFl£$’¤xX”5ãwåc—™ÖjeÖŽ˜ùh|¹«uÅ$žÖœî®¥3±ªHª\’@?!ÉR8ŽUÛvž÷=•Š{ܲ‡èèɦH§åØÚŽÖ9þZ}7Îzk»Å~/…ó%0jdIñI–õsôL½v8ØM>R4ØÇò–’Ý0Û®-ܵø~áEô%¥âV½˜‰ùi%І_YÓ©¢UúNÐ)mH詪æIpD+B{ïQœnGRW‚%ñëY ò/º[Údöüˆd»t-ÒÁ4@fŽ7‰£Ž„À·ÒdI½ Ôþ v¨ R‹Ë)ëô$§xδ—Ññ@2e¹Ã>qò†Ý€æ’ºÂ…"±§~%d0Žÿ ÷¶_¼;q€÷Hšß¶zu•W¼Pœ°[dkzæ2ßâ‚Î0Çì{P ´‘-´R(h%§fçÊfñÑR,iòd{'ýâ”kR` =PSSkÒèÄõqêpZJ5P½0/ÀwXËU> :ÙXÄšF _£Žàæ¸ëtu¿¦¿Ð¸J°Àào΀ú7æ“N3(Yþ†š,i„޶ 6·!å¬{‘'I0ï‹ú÷Ò !¸±Pz ½¿½-NtεÖî•ÒŠdõ^±d-3ñ冠ôSS°üÝÁñuYDelov™·š‘ðj—™îßËÛ3„ÿ±w› WÆ£Åqo,‘öæ_7 sŽÔýOÏn/<4º[¹„„öC†ì\qìú®¾¶1B¸ãøŠÄ¿*nä bgüpÕ½ÜCtÒ°©1ÃŒõ: 2Íí[§=J,|²ÌO•þÏê >!Úx'@øŒCÕ IAb˜3† ¡NÚÒ$úÖ–…ëH,<¸âß›«¸I/RõYÿxcÉÌ,’¢1Uè †ŠÒR±IËÆ÷~7’œ¼mlöÆøXáAõ-e Ú¯œ–õ ”sËý12 ‡}ɧñ V¿z [ÚþskárÙ#&X)¦ ß®*…ëB£ÜIR@r1g<Ç`mOøœ¬¸¢ÞbXÕí4±gBdÒÄ ]„¦/ô\í|ݱG­Òu7ro–Þc‹Š Î;×J£á’z¤jb/¸ÃM Vyoýé 냣‘×kÑ5øXDœj)D.‰½ ‚sÑòÎ(?¿Ãý­¼×“Qz¤†R<[_DQ ~¼*N²±弯/ÿdàÚlºðóZÎÇ?†ná UÈç¨5åQÀ>þu;;ÑŸÖ¶pwÑ¢Ù }2D$ÃÀ–ŒUE;9¥e-G_ÌvÓ™¹²³˜þ€J=»9ìá= y•(`‚<¦,ExL*% zo˜Œ¥pFG0Ñ©¸Í _×7ÛÊ‹J¹Oïªý!þÿóî*¶Lòã=]Ào+æ¦Êá›Á#¯6Ð@0sE-ãÝø‘@õ}Š%¡ý h¿\EÊÎtLïÚ®›ü ;}+È/ÝMS!£¤m=²ÊH°<+1\+éÊ(‰w÷°”ƒj/%¡œT–nYà×ÏöŠÐœ÷µŠß •ùÕKÙI´Fy‚å‘ýw@}4;ݵ7?ÿT§iY]þ¢C·Å8¹¢u\76Ò‡¹ƒ½œmÓôÂ’_ß—á¡úÂÆð‚qS[úF–Éhª3ÕÜ××cSbž¡e&æýƒ=¢§ƒÅæ<Û®¿·š8CA£gûËZÚía‚ ó6î/½ö×ô¿¿¸ÛuœÈ±Êú›ŠÜ„Â~axôð»ýA[Ü5A‡»ìåïÕ#çxrÝ…¡åz6”Z¡V|OÜ\à MWtS¬ü6cœøyÉ âÅ GöD Ç4=ú/ʤM{F%ƒp­ˆ(òÊ{ûK†NÒìòoº>yG‹U»ª–˜Æìׄ–%8·ë¦—„}ê›Þ#MáÿAAüö+ñ…©<×ÕÀBPì佃¢™°‚ȱîƒóaº‹KiŠ÷jÏÊŒ,w;vy V6I¢0~gõžyi‡A³F *Õ_†i7‰r8¬WH¸EÛ/^šƒ@_<°3“èË+@úH£w|µ°ýÐÛ:2g%k——ÝP/Kõ¸K…rÄÁääcló‰BJ×ÒŠ«"sI,=¥>ï\±)Êil x°‡‹êpEó¹9ÚØ6M æêÌT1î™òÙUÊ|R”@r¢·úuNé†h'*ÔnKœ óX†ZŒabû»Q\áÜ’?ÁZ/˜UÍ“Ñ|ìo´cÓgí×q“ÎÏá>vµ£&¤Ë[[a3Ú!F1¨½1ÆõïãÆöõÚ‘Ž¨À .gm@Ín;Mkì×8NŸ¸€Œll à.½·r9‘Œ§öß4?Iã ÿÕjª”¿[­å!µÿç›ì±ãkM± gq»ÇR>N·{ Ã7Ï7'†ë_àÃÉf?ZÓC 1uêQ9ºóéÅtLÉç>MÜÝ:0G4ÿ,£oÞC‘;ž›$]Jª6K1ÞIõÅSU˜ &sÎΧrÈÞv,¹ØÇd¹èýœ¨%¶Å–*O|6!W¿}ôN"b²ÙGä¤úKv¿:Ò‚…»JªcÖ» û£+½N÷66ˆ0 Õ­†ãd©¼ý´BŸçû‡ ïBw>(ÑÕ™ÙDÂY¦uï8=9²Ý¾ŒÜ§$Õ¹¨îy—äÇœ”9}Q³a©kÌ¥ª"%;x¥VV$•´µ£Ó»[‘Y¬¶ÃmÒ¡Ê •eký Ì^/ýŒ7÷pàM k“ ›)Ù– .šàz‹ºQ?$+Ž–RØ‚:éXvüÑB±P%ø1u¤':ëN”Ô° NšŠPYrnLø¦Ÿß(á* &•’Ï‚Ã?F2‡0ú4PGrà[Yœ³gKE‘ã,r‚q€úèÿ½}džu:xÉ͆âVRúö.‰—ö½ˆ7º˜÷F¶þ=dÌì…Ú0 ʰ޼GMH¬üYnøvþq‰£ó×VÞµVTÃþ‚VqV?}ϨÌMFÓмk«ËO´¨¿Döz$]rÔ}òó½üÂ8pv¨”&kÌ¥†¦yƒåZÌ8 Î-¼ ¡ (}–íƒÅÀïªxfuÆ2 Åü”ú2¾*H’|¯!«“å®ï¥ý/ä 0Þðíü‡°öd'a{u¬r`ÄÀ†È ùv¬XáÓœ‡ì¥s8cНíhàWÙæá¦í;þÎ9’œµº¦ÿÕb`— ?!uo)&…bTC¼ ;ùð<ãó×YäϾ?Ì°Èæ£=Ua9»‡ÆBFô¤E—jSaã`m_bî*3…©exro¢+D"qwê’ì'!»­o1gHnÒ%ÁJGÎNºð›'=ÛxÒT]vë\Y“H7ô{%SH ò9Jô3€°{é7÷€ÀÖ:½– `‘q©) -±Ü½R®ûœU>ÂýFnfá>ž?Í7šn#ÝäÜãEþmO;i)žÐ§¥ÕróR˜^‡ŒÌ§åsa_0º¯Ø˜/Ó qv4×þË/hËôwMz€ V ‘#ƒ ­°¾4S§žZÇiÅŽƒ€ÌÊ–ÛŽ<¡4ý’xä¢{“qxÊ·ý¿ßùK2ÎN‹IN…¥‘GÌ‹pzí5Ú$IÈkÜŒL«Ã8Òà„g~18`ý©=¿îÇïÞ` ‚jL׿¶¹äQw¸Q’å‰yÐÂ'AÞžÏ P˜jaÚPÓ&t–NhfK˦Y‡” ‡ –=¥ç&“³hR˜+n;ñ‚>ê¬ÿdjå½Z<8ðñ«Ûpln¯)qÿýºå?ט‘å*ú·K¿ÇË̦KÌY¹“úYÃþTmºƒ$Ä2„ìRdK²b¼Ðþc}²•&OÔžñ“ˆ:ÌQ7½ÇÔùx_ë™åãJ-¤ÓÌ!öÅ>>9¾Ù¸ÅíRNÜLÀqV^dÉY7—> ·>® #ž XfþL Øf¶€CƆڦN_[Z4Ó´ ‹„çÎÐ7ÝbZáÕ)Í‹‹HÝbÔªK«ÙLš×|âØ¶{'C€ÛZ¤Ä Az±­öŠî„ß廚œ‚AèÇùùº-md ƒ(­[‚âÞy`ææ(7ÉBë]*ÎC ýÛË_°Ó¡h—„p—oœeþμBR¢Ä¦5 Ü¥ Ÿ‚–“£¯#$€½Æ&,'B¨ :øô>Ñ!¼ŒTÓ‹'Ë!ßhaº0£3Ö°`j‹ªŒ뫲¯ ±]øŠ6¼à4A¡\·¢Ç:p°äx¨j—#âW—œ» î!•~ôµ—“uþÔµWàiCš`ê¶"ÀsÜ’ËuYPoØË `á„ÆŠ’q~(Ó“•ÓwpÁö°„x1lO%[Ъ;+òÜCL^"L—&æl†O`ÓÊ:Y©1„fë•“RD†7¯<í:"™eVB8>søAæÉ³¶ÐTõ$tj:H¡#~ÅZfO°pQÄßø†§¥—˜€Pï|¼xÀcª£1<èæ–î7Üf‚Om@2…,Té%!àØ¸©ñ†Öß..ÄaÖààOL—¹P5¤7ÛåǽõÔÊíúš9‹ ¯ Û“Å Ñwôät ¨ƒ’œeôø”JäQ)ç퀈 ¨w>™ ¼U 7J_*Ðß?áwysMhç«á±ÕK®½Dî@MvÑ™/X¥¯/f!QÛs—ËÙêã¨`šèaá (”þM1…Ð5/µÝ:‚¬=KúÓß·fÁˆýEÈæ7Œ`Á8ÑMÔü{P…±4$Uð24kE$™jHÔY©ÐUoÄC†™®§lä´ôÅ­ônl^à,>ÓêlãõçÐJ$ηÄýôì˜ñÜaü"8×EêG× çøí3]Ʋþ:’þó_š^1Ë ¨q×xò öÁŽ¡˜Kš=ÙOáaÐg1úÕ[¦OÍÞÚ•®i";Qñv¬áRËqt†i(a³ N§µm<Øio'-;­ðmaÒ’åÛÄŠÉ…ts?w9inþj×ÛH~k—cVï6'Ë|‡©~²£¹Ûn)&ÔpÝ‹ïôìR^òè¦ØÇöŸ£šS¥œAuOÄZdÎñŸæ/ŸEÐQ##ïTKVõQpZEÒ9EÌ¢ü¼e[ p‹±ñ8ØÐ±á;5¯25¶r¨:Å„§ß”Ulϯ:Œë®i[«óýNû¸ã-ÚŽ ßB¥õEÂ)øÔxy’3æ '² c‚ØQæ¸Üâ ðiŽ1-ÑÓEë,ÚŽsvÙ1iî.ò]Ä$=G‘Ã`›GeI°Z©]!‡5FNC‡Å­C¦µ éÚn1œ9ÒP7 ±«MŽŒà×êi\s{ ¡Æf왳е·c` Ûå.·¬Ê:âøw·Y´09Ú_Ù-1¨ÊÄ —³q_ðøyúVÛŠ`ñ™kœ°-‘Ã:XÌ¥Üߵ؜¨¥‡…‚Î:”`O*;îØš# ƼAG,Îr:@d­„É"AÕSáq/½Ý¤Pt$ñ<\œ×êT×Ôû°ÛÇ¢7vÔ4¹@Îôlj‚ºá ¨µÿμ¼Hc…+µNtÅ à) `o&(BTýL[Ù‹‰Æâ§-Ë`/xæÇ|ö!Ÿ*?Œ± ŽY¨«~’9v4þà¹h{Ëþ$i£mÚ0+ä®g%Þ®–—Ðé¿MRWö0-­p${9È“Üj,öé”XiëÊœ>¹G6ù Ð÷+ 5%ÕVÂJåû®O]7¼½-y¶pͰ\ªî5ufy¶G}5؈ž Ûf DH³5¬£[2]nøgY”?l_˜½9a›2‰áÀ„ˆ¹üõð2*Y7¨K7ˆÑ Åßê‚Ųú»hŒ“¼Ûþ/êÊ€DÄ ¨§Jòç* »¬³›B Í[y'¾dî˜̨×ûÛ>ÚgÈ=²$›_ìd+¨ÔØF¶bÌ³Š ßL?úïR§á c ~ø‚WR›á&ÓÅË©Šó×| “n‘´‚zûíã—ÃyyAmî aRÛëÇ)t¶hôÀ™GÙž¶Þ8æâMXŸLÓ+XaîV€lýŽ9MÎ×'&5-Òy¡ZCò8qU4†„×N®DÃ9Vœ$ý­ŽN¶ì®_ˆ’W'|êÞÏÅÏ·ŠVwÍxƒNX#Y +=ÌüOtsí¥G¶W¡dRÄλŦ¶7LjpåêÚ¨åÈŒ?ëðŸþ r^¡¾×rmÞrÄ£…pÜKÏ·f]0ä>”âŒð%$•Œ>o<ÒÌÞ‡»¦s;µÏ2ùÃÅ«32ÿ­Œ·¥ä(‡Cb”ö*ªÄâãx³WjJ:ôên¦;TÌNH_rß0#Dè¬iqnJpOO“Rl¦1Ìâ•BŽÐÌr`‹C¤†©F×m‚=höÎ.W•ëó<•Šâp¡¸êÐØG^uœs§–͹›~²¦ØÊ‚: ?!f–µ»ÐÒÂú7uI¿ + ôVÃLäp³ÂQpÍŠ³ÕÍF$w™øË|S”­ÂÀt–à(dn{HE í…Íç+z&ЊCRlhB NSK÷û.S<ôÂDooNðý $ô[Dü˜´Ž/¡â5Ÿ°­¨õ}b…ª9]KŸ¥kÑÇð=px£ðçÖòDmÏ "çéí/ïqv6õeG©-=^Uؘ³š˜¾_ÃÀD$8œ­ÛnK$4‘'Ÿ)iÍOÛ«"Ɔü yM-šnPËõmG¼ËDæÚë9]΢IÂ94w”c±aax±B{í]Ë«kçNGC²Óqu’–›þ\Üo.ð~™;;ÚÐx2›S¹ j65ßp–ÿw`žŸÖCAN"Ð;žlçíNŠò¡{9MÈõê 3î„ ùªº¶²ˆæoAu&ÓxÞ?B+Z;õÈ{V Ú ëB²üÒSЙйèÕ¨Ja;’ÿuª¯º…LR@aŒÓX޶f èsŠÇò‚HDð¥°AOTÃbêùEôÑÇ¥tœz;Ú_äÅ©-ÄÑôb)ZÑÒä1I~¾n^ƒGõ|­¼ÿťʹH„ÞKCL Žã%‰ÕÂ6¸ŒˆÅwófg~Hýz.2ìÆ|þ ¶ú“ð)‹ç炈P©ë\×cÉffwé4÷ÔX Ö„Ëx®ÌÎQA†Š%\w±þ]†™©ñÈéÑõu²rXÀ3ÅzK5Gz& !3e®/,´BøÏåT¶.b/ %›CE6Dq?•õ«±).½yøëû ÒGæê´ OZâ4/Ì? ûD¤ŸËn0=»Ø¥(çhÝãnp|-´2,U‰c­½+#¦þÂ'¤ýážþ8ñ.\M2>Me4•Àe1º³À/îGÞ UÑ{-" yœ.™UÀ¨fø :qÉÿñ[œ RÜ„¨ý“n³Nù,©œÐ× ££†méó·g±ta¯ƒYµaôÔß¶´0T"èRÂH«ªsÌMl x*ÂLä[où2 ìyÐ_æ¬ñ‹ô‚Ø5`ËÝ#vŠþ˜:Áë5ñðûbÆk6Àtù¦„•š ÕSUîz©“t‘ UôÉ_€Ã®väÉŪ±[ÈÊ—h¸ÑÒôæ¬ËDçå^Hžh£GÖô8³}ÓÆ¡3™Ìòö“ìÿx [ ÿì…Ͷ>–¨úåãG W©DK,dè,púˆoßNŸ «ü;IÁß_C ŽœÊ.sÉyÝRüøUP#¼á¦ OÌIRm?Á(•[çôqE ÷¸B59êÈ–…MÒöÍz„L—¨Ø}FI¶'sLVX`üÚ›y`ھăæµtI¤™ƒ5óÇÿûG»½—Tºa»ò®{ˆÙ€FT;àúÆÿ{™Ä-\©*xQ”ÜqÜJ7)ïehâãÔ´ê•lF\ž~Ê×9Ï688WbË ‚¶4P~T®}<#Ð|‹È Æ5 ±fÈUçRu€Ñ°šWóñá1Wòhª.ä©2Pl:±ÉÊc©$êJÉá¹ßø”'ž‡¤¬? eÜŽƒf£ã¦>z7…¶1ˆ<*L„_9-+$Nßéä‘“NK„sÄ`Nu˜;»Y)í&Á+ NDÌ&PØ‚Š ƒ$¿ìAåÔO[èѬMõ/ÀÔXnw]sÛvÎEjî+¡Ø’N'S~ë;f«Ö_O[Ì7uåŠNÔ§FýÄcpùZá{’ÆžÌÑ5:=rŒ‡™ÁQÛn1Óu?±ðyážµrYpà_®« jgË8i£“þÅt  µ–¾ÔRÞ;±dɵ2KPûLNš#JÐïëi=æyK.Ñ>_ìªÀ=ÎÝjÙi°Œ"Lš³®îˆLÔÉ÷šFD[OÏs "¤¤œpcdÞ²¦î 0WqÕZëéå·°uýKŸ >¡”Š0ÛrÐBÅ|syqÑöˆáÔ›•ß9о-àj†$ƒ¶ŸšcäÄØš^'Ö+…€ò vÒåfð…Ô$û^ª©¿½ û²Ry>»ª)äW ¿÷ÕÅ;œXRo„,`Û|Moì¯U„Fùê[¼o¦¹G¼yð µ%?‰'/z?“¾”9r‹U—è¼—êÌn½{ >W>S³Çnxº«?µ$a§Fâ6ó·²µA÷~ª 䄜©Ýw.­Ú¿¬ÏCe£¨ÆlŠÛZÃ\Gš™ˆ–ÇfËÀÜe,…1GÛ?{ÖŠˆÁÖ™s@êb±Ó] lÒýo® –{Q“\Pqér/ÔÂÇÈ‘®K§=Um=còÑ©¦Î¾lHDv&’¥F#Ù0|w}º[ ! × Ó—¥†ýž89 RHk”»Å?A”‰Id§ã\^ÛV…![âw§(’èFvWH´^7#ˆÛФW_¬vn2äyÚ¤·nàbÓÒ2‹`ìÛ¨„²kž²'bS) C?',¦uªý%±…TÆ‹Ÿ¨dçLÈ=?þÀÂèÓò.Ì¢«8éÊŽ5šþe³ü{no)P?õèë#rF¼+Úgpw~t÷‚M\,¬eÕÚâ$鎢Ñ8Ià‘·ÞÝ"®P‹Â]wtšz†ú´ø^3Mª›DîoÑ.ãÏçLÙBNx@òTüˆ-3¢&Vôv|e»¬ÙÁ>Sùƒó‹cÕÆö›ô„/$Fvܲ"d v¾[GD…Š’N¿Ip£ÓÈ3ÞHéò^Wi¨ÿ$#ªî “+öú¹Ó!ö;ëR°‘×Ûl©p‹jÛ¸²‚Qzën‰šÒÒãfìã“íþ<}8ßá:I ˆ9w,îÞ X©Gå¼Ô¡•§áÓ/C½é©VäúYR`FÈ/4¢DN´ÐÛ‘ƒNq¯ O†çO±z€GÇöp6Ï_g&mŠùé^#-/Q$‚öXàŸjÔãeÍôMhÔ ÁHèAé+€î\Ô[h7懸'æ³+‰ÚRIQ'Ž;ȺÂ<3Ð ™0•ERf\ÔFæ¦ôZ!æÜ™K»•£D¸7p.äPÛ¿0o2•3»r-V…®àDûÝtfå%%ÊýDâFEϨU£{¬¯Y!8óí¾‘²†Ý ná2s×oë¸.'>úAÖ}Η£ðSo¿¿ý"™gZË!å¬ù½C”*Å XÖÿVoB>b[¯?OTpÊ"\Ö­ 8X¶†»_[4ïþã­¼7¡X‡³)ÔÈbµr‰·õá-Ó¹(í#±;qH5Àhä¯\»f“žÚгYž.S"Þ/t30É&>8JâøLI×MwpUd‰ÎæÚ{²‚ÎŽ':ïÀ/÷ 0ç1•Cæfo¿˜‘-˜‰¿ ŽÓí×H´ÙDðÎ[ošÔ—•IüÄbgÔz'Á%T”Ô›}ÏMŽl ®çíf5*6ãׄi2O휠pã®y=ØŽV5=ü5³“ïdêòB2¿G™Äê…‰«tÓ…RéÃ赇¨ŠcV[°±xϾ_óÂjÐ_.$®QQlœÿ0ØtXE÷\µš0³¹Sq¯^xÒ:F­"œ·^%M‹ZsË"bKÄ,D(€‰ÈÛñ£ç( p©¡Ú™Ý|Ðñ°ªïvâK!Ã,°8Ÿ^‹ñºÂ+z2³Y°1w×IXå°Â]ÌT¾)Ÿ¿ÂªˆÜQb%íYx¤Kÿ©å`•f˜Q$?6e˜¢M܈ź £ƒúÙ7"–”C üš’y“—n>2’¦[¦ªì«gÜ[t¨ð‘bs¡§éf4(&]üf€Q}Ä%z{°óŽ×¬!”žv¬Ð“±rB7=’B(qmE‘ÚäªØÝ]Ý­BMQÈ1t{ˆÏB´5Áhr±í=e„¼æCË'M§uÆÜ)½9e®Æh¯ d¦”“—'vco[M §l SÕ"ÄzÛ©Ï1œB5a¢+"cGôùiñÃc ©w’d¸$û´ÿÇå÷îlÀãŽJ·ñ™Ã€6$ptô7üEJIœ7}%¾¸Àíì‰ï¶ …2e­%9ÍØçéNF÷½ù7[ÕßNÂçìÛøI2YùIŽ÷î*Âyu¿¥øP„«i¬Çâú®K@€oNSY9&â';lky,÷}|0†1..b[¡ªŸÙ>Ý>u¤P-¼9û/ê6®ï„ÇYËã‡.rX)á+ÉD¯y1«Þí#I_frZ;(QÉäÕcEÐ2:|Z?¸‰Ù'?@ß«õ`¡èÑS}<–)†ßvÕJhfpBD#uÆ&aÖÃβõh§oQ“ºäÿ{}@G¼„-§™gDHj¦‡$—ìØ‡ ¹‹ô—{‰5¨j®œ_“,Üo÷S²Ýk"î °ÕhŽFe À r8– ÃixƸµIk“ð*Y ©ý«Y ‡G¸•—¤X’,’ËÛcI o‡˜7ý×ïÅeæ¼"¬¶tã¿«÷8P–»ÒL@_¨gïݸ¢í'-ò†¢)¦asÊa¾<‰uÌäèozJ¤–É@Û Ò*íNtÛ³Ä`“M¦‰M>ãÙÒ+É`±X3ð™Æ$öµ¸{ðÿV{ÎëËF¨ˆ¼Ùõš{—3oÃ$¬¿Í?^Võ$1 )áSíî…óL{§9Hc{W…2Ý PSdH…„ ôLÝϵҬ­cl…xµ$ßYóÐd-%W“dÿPòçJã{ [Ê «FƒÔ:Ú™­ƒ(f œ(ö¦×F5²U*O3üHâÄ^‘j­€ØF F´ÕÕßšo6¹|µí$çJ^ÔqÄÄ‹5a[•ד:ʰš ,}¦–ËiH ²‘K^",>'ý;>ÖjÞ…?‰=Š“Õ+s³´ ÙëÅw)J¿(ñØKЛýb e7ËD”ðg‹†RÊËõU†ýÍLµà#׉Ð`û^€ˆ=}Öà ra%ü®¬½7—‰ ÓYèSý!+¢±™2·ÔËŠ¹v˜²Â඙ܶȒLùÒѾ„µåÇ8©U…äsNÎzÇ %®éóC)$RImmÊ"j“‰qBplpW×µ¾¤üÇen$|2QRΟCŠê}6cóçnêz ž×š¡…#‘B®Î5zÊîë 3¤N`áô¬@ݲõ·‰§Ñڡ̬Òe±Í/Íú äÀ„ˆ•³äôÜÁ(˜y*Ü] ­‹/I•‚ŒPÖeΦמ‰´I"Ég>Ï¢M«¼¢îM[»SR°»ãøûxc«'^œäåã6³•{eæ CQñ²>X~š­*bæÍ„»´GC—âX¤h@'KBØ8Î`ãiS¨„\=ù2ykÛ ~ Zþvú/YA¨™dÖ õòRî ¸è{1®ØÂr?²J[m5ÒÛádeZS_„ƒZRy‘ŸuÇÔvû±¨N[B‰|ˆª‰_Úx¿C ­¾‚UÏ™}3pcÑ\Ͷ *™íý¥^SÿGr"y§WMVö }d޶³Ki?T„7òÊY3kôô­¯Fg  DÀ Hæ£/¡ !0~âAcÃA&LE× jz£§Äö‡n§¡$Å™Ë5†â0ê­sÑß%ªX!ÍRꉫÍ1tlÈÉ;/¢Ãˆ“ö›ŠÞ6Ž 8Ká”øùÚÀEÌPlñÈ­,·Sž u}žò¾«.ÙÙ®b¨Ä)„j‹ºZ~ ¶ª_¤ža'dz ±ž+nœô»SÉrd‡ü’O{»Gu’g˜ÇOçYÏÐfV >C£o±QÂ;Q™óM—沫 L^^þòóÓZÒ¿"Ö¢CÂyod²fN4¼&_D¹µRìèÀÒ¦ór;ãÛ>`Ã/MW¢œ`Yi™ù˜ƒÊ@Ùp º)V‚.ó‚+XjsHâUläç¡©sÓ°`m Ȫ8=VåúA\쨨½k3¬ŽÒCH'"/N ¼šcHPˆ$s´óô8%Å âUs î~®¯?Ø2Øÿ¹Δb1Åcî+l@±/u‹4= !É¥ÉxûÕåÌ:€æÍ3¿Þ&Ždâ_í¹Li³“/(ÆåÕÕ\XwƒsaÞ…=ÅÉób…!ºÕ·sa}¼r„v¤>€ÄªU—Vu¼?Šï³¯+«í`D(m½5îDV ¥!wáåTTˆå¯‚bËCH‹ÝîUî8Yc¿èÒyƒD[RXŒ¿§xW¸ÿ]]Ä—aÿF0è²dD Ýíþ}×qarlQ-…ÎⲎ‹Ì`Y{ ®è¨-Õ¤À´ÊÛ˜uƒLVëâk²‹ôî 1k”é½U>´Éh%ç0iz0S¾gI#~xÔàM¨SŽèQÐÜ› kõŸsøÚNb½k Œ•LR™aã}™Ê-1„ ‡¼ —6æ×Øáç%ÚóÏK_0çíŒ=îò:ØÏùr¼—«Æ±å ;í:ïr7ûPך[ŽÙ wc˜CGÔB°ôÈ@jìš“6UO1åwAW6º§ó„©·ýyŒÞ¹«Á¼X®ú€ÝaX¬$Õ¬ü«ÑëRøTÆ¿Ô!ƒjT^¨•Ÿb’Ys“LÂ{ŸBµmã‡Ú› %+(Ä8'ù—vÿ$1;«†ÖiÇÛ ÒåXŽìчxC¹êÎ~í@1ŽM(!¹‚yÞ5\7CAü¾õÕÎÉõ”Â/°ÚqT  å°VKïžÄ~Ì´ÌƦ?b øMR¶R„Òùýl»\èËЋ…;? ™ôæZ¾yFsA’Óh|ù'îlynˆ5¢<Û+m2­*ñù”\á)+W½‰’òNêã ¤,ÇDróÛ!ýe†„>î ’jƒ¡è9 Éà˜åÎfmõqÜ'Ò.„0™[†€bûn=Z`ãå|ä¿ æ¶‚ Âhi”Z¦›(\m†²VäÉ%}…É€?6µ¿¶s,†ŸRu{çR·Q¦…¨ˇÞÚ <˜~0â$dwÖùX –Ù‚b¶,˜wÀ… ØqkC}5›/„”îÁ€Ì8×U?›/r‡ZHÓ“#"ÛɳMJwÏD…víM_Jg۾ϻÑ"Ç·.«éö¾£àPjÖtßÐÌ‘@”Šn±v¡ÐŠnrƒÏŽûXnÓ}ñª›‚ôTspßWMN÷AMHæ[¹›µ²[{m3œ<¿ÞLP?gˬ1ýÜÀ"0=åT‚†¢E¤2ôè¼”²¾ÚÓü|êêÝꤴ¤ `Ÿ)óçÞôF„ì¯-Eå5ZbR‘>èK¾(‚¸; ¾M¶¥N¾gK. S^Šœ¯E]A:¡’iÀ€UD¿\¡ú˜/3È÷Õ@o‰YRá¨Ñ^è v-ˆV‹}È#BlÙîŽùÏî~¤Z‘NŠ‘¤K‚ER;(¸!àPm=¹c {½EÍû‹ÂõžÅAOŽ7ŒO€ ñÓ¦#1‹9;ªÀ1÷aÃ[ÓÏ®èßJþ²Q9_;ù·05êaJÅÍ×|©æ\P}3 ×ô a£}I² _Ö¾=r¹þ'èz©žàùß<Ò{ˆ->ã6€üØTíã‚@APëãÓ“ü¾kIü#ävM½o8{¥„Ÿl·B+–´A¶DúÕ;áì+W±£æÿª›ô®Ú¡Ž©m{Çž–[úW™šñ¶Ëþ¥îÁ,¿¨ÃЕI60ÀË ¬…jD-„/)±G5hËÁÛá;ÜŠ¿Ù¥†2Kli–än•ÄÒW KÚ ÂšÈNJhˆt%Û#FpÍóÔ„‰w\IË1”ÛŸÞœP~ÈE-LÀ<Êß5™xv™@Áèræ¾IÇÖcËÚ^[Ãd SMº@„AÕ'ĺAK>­Ç¡” …ø)žil+} nuÚ îj¯åmi•ü7êïBÀ%<Æ ³€ âQ´¯ä¾Y 3Ân/ŒCê'$ù×gyA‘sPÖ³-u¾¶šu† oL{Mugû­<µ·Üœn"*dvHî«Ñ+ö”ù¢´À„Ì#%Ès¬xU=Ž3ùaÙ^‹D†uÜ£{*ÓžÓuQk*­R‚ƒ~̳ØÈM{ÐÆ¿-SÚn#ç`´_(mÕ“½§Q½W©t`ñÞ÷gž°#— [êHsÒê÷ì;n´ 8ï3†i„ÈÞ_—ÛGîM”öpÚÉÛ lƒŽmNÞ<)T"ÑÀ;À‰„„}Ìfe’½Hse·Ä»äÉîCGiêQèƒn+CA†…¡X(Êr@íEôò¢h-ÇŽ¡5ß­À#æàÒÉQa7¤¾XMYjp(¦àÌ@TówV¿Q“øZñqŠ´î~zx¢»Zd«È2o ^>ì5¯Œ¢t磒57öš‰ÉÌ)À`·>/j5Í:|U¢T=ÈàË~ͽg É‚Ót•BÚá74þã¦êvmÑH¢Ò–<­ë^Œuöh}lùsÖvÏZvë¢× iœ$ÑìV‘¦V!ãœ1„±Ã–ªË·qT³¯|ÉœHxÖÝZœNLæCvµ&áî‰dòUYŒZ-Ƀ˦F=Ö˜Mƒú±zÏ~‰ „f\+I•&¸*K-À̼ŠAø…ª|ÍìhÀü𼸿IÿSn—šóñ]ôK _€,¥§…3Rtx$-\Fš¶#Ÿs]p'°b¤h…íºî„9PÔ‘@U@ÕGZÓUÙ™Ì<æõÞ2ÅìnÚ´7•ë‘´Á@ÍOœ“#K´lå«–÷‹ü£ÚÉêÔ<¯ƒŽÄM¡ôf‰}Tõ]íÞè:¼µ¥“u¼;Xï¥ÃÿÜxªç½¦¡žaÓ»¹š)Ð ¿yæ”þû÷ãZލÞ8¹ŒÍÛÜx#çÃÙ;W@¿Æí”ËÅF\‡x/Ë»º‚mšhÆv…qjÖd>…HMeÛþE$ÈXçùþ”]ÄðYä[ÌyNb~Bll¹ãÒ­‡Ð`²Ùbç,¦„0Žv¢‹îø‘Ÿ}ÀD ã„!¡n©d_yÃäx Õ¿ø MžäÞºyEúBý›ž×ŽŽ1B`„nÖ:¯çI\-\Ä„OùªÃSU4F"#ËFŽgq…ÙzB×|€5éCf,wšàØŸÀ‚ÕuíW¤þÒBeö]…˜=GR–ÞF+?}‘wAÀô\wÓ¸Âf¢}¿d"¶„öxÔûd¶šIN®—¦þr£s¦©lƒ×:ý>e€5y 3š `åÏ=AäïáüóªâÙÈÙÅ:•ñ;b  !ÃhdÓì’:jˆÊy>·å¯O­Kj‘¢ç>ᡆW<1lHLÓå®*ÃvB–UÔƒqÛ~] ©/KÌaK\¶¿SiÊŠ[3Ð’ÁާV9:Øš²ê zeQÚ[2_ŸØŠüQ‡ßGvŒZˆ^)ͤIâý.æQ«ˆwÞS'òvõÊn>Oý%xӶʘëÑ]‚N¢ }ÖcPóyq©¯Ø+–íó+·7!õHbc·t]'¬K8õÏigxðâ+ŒkSß°þ‡¤¹…GÞP–lB‚þöx Þrž[ËýÁJDÝ“GM«zç7Ú‰\;IDlV·ßzÔ;.-tn@¦) “´2·{¶E‡¾ò'%©Mãû¡RÂYïÜU ‰Ÿª)K×íãr»“).k6ߺ{iÃå ?úÏ<èCoRØLM=’¯CœTôs6üñ‰ ·2CÜõ {Š=íû%‹xGkwüášð²€ÿ/kCƒ§È HöxbPÐHGN¬Jüÿ$ DZÍ<åƒÝÆŸ+r”‰P÷è7ì˜*3 Ïͨô‘^T]Ô¼ÉÄ~©öMÇ óé„öЇÀî?k°+œ9ÀÇYŸ­(ÐÌoÜ#té…y}v+SÌ'çctrX(ócåo¤a ¯[ÈŠ[àÏN]Æ ¾7„œ ©é& Y r2p¦·××ÿ°.ö#R5Á&ü_†">Ä¿0«}w€Ô¼ “”ýÒ+•h±}ô³#BÖøßâ×S$KÝâΈÌyÙqx ©ÌUß´™„« Pf9B§‘¹p[[v›#Aƒ%øù®ñ°f # ÅÇê?•æÝ~]EͰƦ!QÒÈ•7G‚_Âã9˜’§LÞÛ¾чBãþë:ÎàèpN6áöU]ÅŠô}TQáQöl¸Xc¾ø¿ÍõÒzŠ[à@ w±LȘWÂuDL¦Áy.Áe”ïb)ËêÄ9МÇÜ<å˜Åü(è$ÙóäO11uM‚)r““ §Äi•Ý­W qÂYÜ]S•)%(:h Ç ŽóuÆ–è~Ô¤2wu{Žž/Z¯Ía’ÜO{Í[Ã!wíîtÐ}y`ãöŒs5–í¶Ëý”$²·0 0A´£Bo£yøÏ xÎÜÔ·g†"•ÔÎ.) 4ÂÌ¥ÁXæÃ#Pÿw· ö§Ñ’•áëø:^, Ø2˜ñiªA3>ÁñC ݯÎS¿ÎêJ -¸P‘Ð<®*¡fW aabuÃk;OÎ+ä%­$@óû̶f¨Åôà¹Á,l»¯¤ÊV§ü‚þ´À~F¦VƲŇÏÓýëñJgº¹4^Ô8”ê–>Æ•ýlGòð¹Œo„Ô)¬2NÓîõ~‘Žc~Ä„?‡6µwR` vÍ~^åÏSÿð‰³ªx@à€Ç£÷Þˆù‡w­Þsݪ5Ÿa.Õ„©”t©T™iëò¯nH…Ú–?Í<Ç©V wJ´Ÿ_v roø—¥ÃÆ‘\™®0|x\’ ð 8žw[2ý>—[µ-x²‹|„8g‡îÏ1mó"²YþÒäØl‹*#ÌÖþµêâ†iÔÀ ú½MŠ'í˜lž#9¶±·lʶ„ ÿ–U*2˜|¬$¨-[e0T´žÆ =Iwä¯IèÜÒfšF0Ènü5s6bmÁü×¼„/gH$Ìö\ЛªÐ«µÖººjC ÿ2¡ÔÎÛÀ í(7*+(íÐÂäf¿&xÕZTeE$]e¼¿´KCª›Ysv4Ë@âœÍ<þ‰"þÞ¡Õq¾Ú: 7~ÐeMbýÿ{ž!xèÆÇsJ˜š§ž eaé˜,Ó—°MˆòÚI†AC´§Š6†¿g¶)%eJ±ù`g9ý¿sìo’–ð*¡_Ùs]$§| µ67»[˜ƒ hЫN±¥6j*èçO®“‰§mVÂîˆÆ)sœô›üâ?h)oü&—] Vååø)Íð¨c/³›±€œ¸ºuÑDæÄR±úhn;î[‰pAˆ©4þMõ*ÌXaÉÊðlî|ÀíŠaú§[WguëÏãì\\˜üAÿh—)C)w¼äº£–eϲ$.Ôu™ÿ Øãzôò&ɧ=X,úŽÐ7e þ7ÿq7¬¯Æ[ÊXV´ßÑ´-q]^d\lÛxw€V›¢ WB+##œr–÷0ަ™ñ¡óM×)ë>(,ݱ…†bÈÝŸÎFÙ›í¢ßúmq'Ðà{·`jVÛšp¢¾\Øk¯BÞW1ophU.2?àl$Iû#Å¥8 ÓJO-û€’Íh*†n]úå?W8S7­I!1ÞH" …°…¯>ÞÚfÛT4XGQ'›ôûŽB©k©œ£p¾[!­kå:b“ K\G×é¡D_«Zñ/ý«ç`ï­’€Ó’p2ûu‚ÕNÊ'N¢º:Ï]F¸ù¨:+«ä2\h’Ê¡6»Ï©ÞÍLªLþ>Ô=Õ#ô4hDì… P ¶KRÞMë}ª]÷&n¿Îqx ÌR¹P&cU+›ÛÁ7†N¦lCõºàÊĘÜ(®Éð²Îäp|Í…kÆb˜³·[æ.« ®â Šƒå%´F´P{eE†€„! >(·Î)¾å÷°pçN; ÅyoÿF‡øÏ6Gù€ª ?:@8"<•ÝÑxT7ýZ â:Ì‚[¦1‚ó¾\5ÔXG“b#Ç?Ÿ?1èÖKÈk¨)ø)†ªNž*¨>¶¡­4š`“%ó6Þþ7 È`ÏHèÞ+Ë2L!ÆÊ^¼@É|²(URŸYÅù›Qlë˜âC ☂Và âWÞl.Zï‹Ęs™Æ]Â'7ŽãiŒ·ÐåmóQÕ9Îe?öp¯I[æÕ-Z2ì˵GŸþ©Ï‚ÏK‹Æ«•Š€ò±_k+©ö>¶…SY*Ìšÿ×bc Ò‘cåÞ™í™F¨·ðéΘTKîCaWÞªFw3µ!}2JFèGæHÒÿ5†š~áaŸk73Q\â)¸™Ï´=•%dÛTÀ"ŽW[ïÖt’Œ´+ùOëKÆ=•ÇÌ{mœÙošÛ÷ÅpY ü\@Û= „r‚û™~®]ë-eí;‡ô°b%ýæf©”³®ÚÜì¬ÏøÁ•Ê¡y°LP!}kØV–$’0¼g¿iY{ÆH~\ø²!ÿÕ9“à«s+˜I \ò¡`ª%Ÿð²ØJ¯“‹N4’©Ÿ9ó¯Q •#ÕhÏúň%ÏêÞ+ž‡jª­‹‚gûMÆ´ ‚¿^¨Œ$¬€I$A·ë<2[8ˆË¯X«0ý7­‡·K³‚ cøØè»7Y§V—wÄáóÅ3’ÙD²ca”+ˆŠn9çF22@©ß­WyŸ(£0òQ„-û›„½cS=˜1»AxÝ.=Ø~$ÈéœÛ˜,¸¸~ÄCÇa%„iVˆ9‡x§–ºxýãò£¨5ÙEÚÃ÷ƒp']>]‚tKUBÏpÁ¥ä* h }$š«õÀÇ»uÌf@¢ßAÉ0sÂy"«P™ŒºgV •ÒZz¢1ÑÁãRÀóSex}j[âZš¢òR^ëÂ,ÚÖLÀvš]¸„7%"Üz‡WŸ†'Ûk9:Þõ ² [«ðÍ ÈüžáuW:/r*pÊôD_=¦”‹5ÜüØrp Ç ¯õ>î4€\©“n 3oÒz.`ß=ÖòÔV‘ÒèÈ,&|ÖÓû 7 çr™×Ø5µôé>kàŒ/øµ1¤0"ì ¡™»ÊŽ.ií.€Ö]Æ™§»/Vó«GÉOú–»— ÏX˜Í$·NÏ!+]EF»æÞIWBFÛüÅç3@³—h‚)‚Ƥ¥ºmø‘/¿jswXÄpq¥OÖˆ‚n‚–¶U6eÎ?ÅntXtr¤ñûµ9ö8âV_RhJ\[h˜r"8öN@|cE;Àღ&œ÷m„ÅsvȆ$ÿ"ˆ¸R æäåÎ$/n¨I¿sñgRx½EjKk.⬅9Ž Ò€`,+{¾uf•¯vV¼$˜ß$‡üGø¥-É69%ów߈ƒðµ€b~°2,Ϭ¿üÀO¢2òb…´,°y~ò_GËF^ƒ“HlÏ”KIj_C†W0&„¦ªn!CYÌê"O+Ò×¢½œo­ Û¶?šú3µ·DxI/þHBÂ)}PÈqÉC³šÉAæî[ÔÌÖKKÀ¬Nªœê¥»*ä!¬¶œDM}ŒY¸T·NÓ¬žîÕ)ÍK‰ê†ÔÑ :e_¸CÏôÞFqc" ñ“cζ0¥º5MNÓÚ‘äÉ%!™k i§†ŠÞÓWT/Xûä½øµÉ¥]cÍný—^L'Ì6oWrH6¹Å>„ããÚ‘,°·±ô©—­ÄmÄn]JÓ *?‡,äL‹™ÚýDÙn¨®€&Ž š~?3­à± ÄÖ™FÏ_VŽ|R|cŠe˜î̯±š”Õð¼æýÚšË&ˆŒFaœ<–¯T‡ÿG/{wðªÜ"íçð£Y.tm”n2u AÝÂr ?çÛir <² hZf¿ýr(h}z‡îŒÚIÆ`î9GX€õJ3™§¨žj Ë-³ãFç–Ã8ìˆÅ®ôª^âC‘ôSO¼Î¹W¢%{#“[„=+ãen-@þ]Ö˺­ÂËÚóš4"æÄœ` Î~_B'Ö÷þ0;ÅäËÊÆÅK ‚þë/]M½¾ÀÛ £±ˆñ?Ü{\Û‚¼Â¨NI¾*Âtžpem‚|Y_bÙ’TB‘o9î¾lß{/êQMƒ:5x%wÌs{Ìòb pCFäušà 8îÁPcDÈœÊ4³Èú‹Q¶®M]VyO7cp˜F{Ù*¼ðǨ«V“Ê”È Ž1Óá#sŒØ)$º€{ÑW²‰Êë.þ«Jþ£™sü´>l>žëIP/ø-qTº«Wµ•㤔y½ Â64m$nZ&ë‡*Çöæc^#¯Â¯²ª“Ì` ²[–?+ºjB'ï{³ï3mDúÕ‘8¿GÇÚ&nɵ zb"V3–½y4 ‡/øÂU/ʉy-Ž8éÓ&ã=E9ÁT»ƒgTtŽIº®,{SK¶X3«î²IÒ+w×(”k(M³ÙŸ`懕ûýSç€â¢–)4 Ê½°ß«Y55¾Wj¾„¨~DÏú»3Ò®¼\Qc3ž›éº 2‚ZhµÖÑ ØÓ˜$´v-HÓµD÷oK9ùߢÞî´óûë`Â\ÞÔž0Ç9ëY™’œ¯ý5ªy±ŒÀˆ›% ÛÁEôÕÿæÎ@iò åÙ#8bµ§*X¢}n4 ª®v¡ö]9b¨ÝB¢¦_oRU ýè$o¿ø”ëÌÿâRÛ‡Žøw²hQ4¥ž€×U!ßϵÀz`W‹ “¶K¡S/jÉÆ±Ü[‡¯Zr[/¹¬àLƒ§ÍÌIáË–jF(ÑîÃCû†¥;ð±´^jÞÚÓŽm,¿IAy2És–JV–‘g$·Îª ´a!¤Œæûk¾®JÙŸ“Lò(¶œ÷Õ%°ßx IT^Ù*ŽOª =a%Te.›€ÚWªR™mÌrF:T[-ÎÑq µ1'…l æ¼©!Š:Ã~øÜ øŒq1«~­¹ÕŸºE±éÕ¾Eû‰æ‡íOEdÙ À›¼Uî§-uì0S–@“=šêÚQ‘hÙ7=‹>Ô›3íIi` ]Äm¬„5hÎN_§«?HÓîí·Å1jÁ I,ͤŽí;ÙÃ܊䜔‡6 SlÏžÃʲÜC'Üp"Éîóƒú±ôµ–|Ÿiå¼ÑÌ2´Åp#“žûçB73§ Ãóˆ[Z;á Ôs7´²bŒBòÂvbâˬÒ?†`w¼œÙ<¯srU3 Íä3h£æ•±BˆÛɯWÄî‹ zö½æYôóV;éM¡b Ñä(Åß+oµêH,ó]¶™b-—bn¯|=)™nSÿ· ¨}ìu‘ÔòèN­Ì›ž;Œë¿¤L C\NÒ§ÈYê@a|¿ùsìæ„ –wl«koÆM‰Œ,’c&OòUK#—Ôùf§õ|ioUã—ڨ͞D2·5A1kšÇQ?U:ËÀË­CZ¥R4 S:T+åbl'¦H,)%÷«Wn(±ô‚:!½ì‡+‡çVWÙê)Òb©À[ÚZ.éüÅ ]/«FüêNþ;ôˆûóz…Ú¡I–š¾¦J¤9‘ÍŽ yâIþàUä?ÞØãKÇਢxn]^Ì~'ÿ™*' |v»»Ë‰‘‘WÙÚ«ó3 R$ÓÆ”ÔMn3p”\¾¸~©õ“8ì[ïµ`ñR ôîÜIÞ`¨¬‹šœâ„ÛQCæ&é| ¿M˜tÖR]ˆÌæÏG©`<Úxœ©zG:Ožë6K>SáAíþ¢M«}‘m,—¤ÏC—-‘M¼´<·´HÅ£' ž^.âˆùláûb¥}u•–‘aÿ­‚àQvÝ’Q¸žzpòð(«²œ#”`Kêæ×~‡Y—±떦®a«w¶ëWVš@öâœj÷_+M–ædŽó•“G«ð:`Ôüû^"}ÀðB†–´ïîö$ö{‡þI%lAòúsc`m_ðl`Sñ|†=U €.=ŠR"ÄNx£ÙãVþ ŠÃ÷†e41 ô«Ó‰z3{®¼6‹>úH> ±êÉ‚Z WÜsBèösïæW‡ý3²Êg5¾· ñ@U?Õkl‰’±Å0íÂj.VxH¢‹P¡ÑÑ Ëþ4ñ…HqÒß„ ·*âj û¬}à‹)=f:Ò6ÏŒ‘qóÌÛËôþSÿ¦"ºŽ›_ÏÒ U¸Æ¯¹ús™Ù¤—£TxónWiŠu6cƒl¥fjîÝy»#‰ðg‘¸”¹7µ 2w¸wõ\W%NXÕZý(»õæ9,(`r ŠØ"ø :`ðç]é¢ãÎ0=f@TÄâ.:MUD›ód×ù‡WÌCüO ÄX–±_kÙÝH ¼VÂl¿ˆÝ‚ùV›·z%x@³þÜQ1ÑüAáÿn&IsKݶî¹ì7 Ã4¹9ë8ôËÎ¥aê‹k5•£œHi†cöäR¹H¾ÑBaÿÄŠû ›‰¡3>rKB¾ìÆ$Äf±Ç 7x¼)àXôo’>¾Á­4‚n/û»š)XhzñÁ+ÎŒ³w†Û™ÙUg^‡BSØ–½¡å‘} ŒEÏ1ôð-f¬WXõ?£¥y] !˜jd“þÒ‚üLbÑñ.F*ÓDkqOã5¦`F'1z$ŠäÁ!Ê0ùeé&‘Êõ1#*úœal"Rm¦èiGµgò~jí“)&9ÈFíüáÆãEŽi|•^â+—A’$“ ©šeÚž7œIO]¬ù/h`±€9mÇð£Ø[¼¬Ðmf%ª3Þ¢èݺ‚!lÎ Ï +—Àê•OÜ0l»H– ž "d5‹kXùÞa{1­ËNs[Ü ÏâÈ@´¼MOc¿¶Þú‰…•ƒ›þa³‚÷žKôýÓzBW¿(-8¼Ð•~zÓðÞVäÏ”‚¶%_Óø-ß2yŒSEÏ<0ý1†sàGÏ\ ýdÕþžÒÄÔ€¼hêXF&{ÝÙ¬èù1 7Ô3dZN~ç‰Gˆ]gÌÌi±x—íÿSdB[î›K™Ä„‘öO1Å_I% +³Š´uåÕ°ŸT…¾4ßfÊ”¨q'hn'G‡àùm©ME©‹âæ),ŠÁhègƒôàUv²êè;›!À‰]üÉN'Ñvº¯ÃÃ[Nß ’ª=©“Œ¥Ðml²í¿V£‚ê&ÑþÂj Á…IõZÈ´©ì}Ý÷v-33¦fW5z›8€Æ×U/…ó- hws¡¢?Š2¸gµ‚ ¯^ÖÅPØSVJ|/ «L2¿—3]ž‹ÉÄRÞÊä8n9Ý:¯+±)_ö{mˆ™ÕÞÄ´SOÓ âE Ë ¡’ùs+¸y ²1¤öωÊÚ™äWj 1ЧÅåÞ….|¸ÙK “ïùŒc4—'¢Ma_åb‰ÐÕp­ÜÑ»LÍ/V«Á0n‹'CHˆ|"žïSÊ£õotMiš>ŽÞ¥mGIOÁµy×3ðXiÈâ+ˆ€j#•uYŸjâTG™ÍÓ½—¼à³w¸„é«]¢Ê¦Drŵ¯ç,¢v/b6'Ê쀣¯‹¨CC©ÝÀ¡­‚ÇäY8Yë›Ãá*ê0Ïíº¬d‡¤â%˜¾o¿Æ÷Gƒ!òŽÛÏ·ÍL@Æ?`‰d1a3jc»#»ˆjü]ˆSJ¹l¤÷³~C0þ‹ÿEø•¼¼9• Â\XZ—9µNX9äáÜžz†B"ÎÔnˆÍ…MÈ çôÍ«85‡Ñx'Qä.ljÎ<ÙZª~ŠªÑÜÏ´ØÒ$•Õ½VÔé4¼çUlÁ -Ŭ+Ç]ýIîÖÄQäÇ<åRñ‰•t|/òë〧„ª GD‚ƒMs¾@ÿ}ô8_‘4i#µI——§PALÄq)pD±CQg›I^WØûÉ#6èÛÖ>Ú÷™Xƒx30£a-®‡Ft+5ŠýOc /ö)/‡C¸™ÂrsŽä²4°ÇN‹+úËw3™ƒÑ™xÂ9õ3RæÕ«¤S=Åf.†ƒæÉsÓ?Ày¶&ϳϭÙĬg!&諌ûò/ˆY$8fª5.½ÿUƯÿ­Ðîüßà¾=ŸÎO0œð»19 .4v šhvJ)ÔÌ}îÁ•7^|’ó K†ó`ÍPw,§|$¦Ø˜àUvÀ>-)Ï7-­[9_ÃCæÒ>¼Þ¼§¬,ø/ø?Ðh©‡‡Ì²ÚÏ:=¶öÝç\ýƒévÁU——“]!á×ójD+µ,¢‘}=¼¨Æê%“OòB¥¥!"Cþ`YÔ<¶ò%@ø0AG½Â{IÅ·¥‡îÇS il7>Š×-úŸöú:E ¾„.  [Èþ™üËä€yLï4¾ÏvÝöKS ¶jÔÛÈ1µVÉN-kÞʦú]5‹œ˜ Û=–(êtïÁ¿(CyBÜiúgþšIrŒ† Iº}\,¦9Ô…ÐS\þj3¥÷lŠN.Bæ”gŽLûé˜û;Àì°:™‡Ã}¹~ÝÀpÖIº˜gšvéÈ?Ó&ªç1vÍ ª¡c•—ÿH—…¢Wô¾ÖgVÃdé^‹ð(  Џêäh&*!d°´Ë›4è3¨Ý#Z‰ ˆGJ–êÅâÑ#×"·æ}L¯œ~…:ææ¾¢T{¯PÄPÓKæýÙŽÔÕ±¼»?N¨¬š½•6†õ¬.dKÛiÆÜcj¼<Õv‹‚$êeJü5ò wÖÊô›‰y£ðKÐ嶘†d æR±Ñì”K‡ÓSÎC*“2B‚¿TjÍB(ËÑ¡ÿ2h4Ý šÛQ­¸Ð7b]VO+•Óézÿ’.}ÆU‹Æ-ï¥_©é_q™Ñ “OHͨo¾Ú´Hˆ‚ø˜À @MxTR°Ù¾pvéì{ýh3Ç~Í ¼z¢ã“%uŒö±„ ;Ò^HÖЭð_Ë:µÆ{b¹ŠG•oõ¥V½|ÈDZë™çv•Ö‹ðö:–:oÖr£;qlAUe÷è6aåäÈ^@Ã{*Î &·+CJ'Q©•dR¯4ñ õÄâß@m;¾)lüµ—³ôÿ$?øÉ‚:v§nWI@óåpOÔTšà? ›ñþ,EkêLÊ_` €À€ûKn’c”i[ ²8ײÜE”2F¸¨&vÅÇ v\ÂäÀŒÛ¾g†ªôëWòãIYãü>åˆ;=mèˆv5ÝÝÓû«¡Stea” •K56΋I/ªë¾±ØÖKœ-RÉ–3hó½Ì±Ë+~ÈGÓ1Ÿe@4q3A4®¸:ë=3^zŠðߺÕêuî¥ML¨*ËKô¿±@úJ!s©óm%:0Üw“qõÛZ'‹å²Íá­&o²"`€íÅü!Šúˆ˜chÆf\á - Z;½  `¬C4H¸iTJH[zt\•‡—òî’ý>öÀ«»“?×`@ù“­Á<õåÜ‹³{¿ß±×™ÞjúÅ$¿Íuôˆ¥…vš_¶¼Ä7»Àɇ/äÀ³ÅS¬º™Ò9 ¹ìþ–d›$àKvû›ëSÛUë  “C' R‹B^7.Ý÷W9LÙOmCÉ ÙEK±¤ÿŠ‘/X·‰Ä {Ä~ŽW§G;ìXãg.•ôyŽôvDj»|k ‘#Ê·F(ÑÌ;„³ê×+ÌÍC¢ASXž^ nÀS.j¶ÖX€Wô hcèÀ¡>þÙGÝ 496„C©Ff"œíîD¸·_—½ëFv€ìhvîœó5©°dÅ áÅÑJCapðM ¬ZuC ßæ>PÆ+ã…c1Í‚2b™iÕ.3Áü& àt-Pº(éõÌ©³®{xW|8=ƒç“xÿ³GDùÚ^f˜³›äÌ,ë&ö×&Øå ½Œª—¯vü–&AJeWÀ¨´JUIèƒ|ÛýTíqu”dþtæ•o‚˜ÁxÐÜAdmoê$1BºÎ²áQo&·× puÔäzs“æ/X‚RrD!Ê*Þ|Nh°‚þk™:É[¤‚Ód·‰ NKcÈyîçÔdn@Ûâ\äóÝ6™MñOmþõæh S~*;å]ZÔTÄxän‘²h 8~Í‹DYeY/]}*Ÿã¥§/êæ¹èñ¿®d^^Bð~gn"Âxý3AvþÄl¬Í¥’ ¶Ž­„²¥‰‡CLÐá)BEÈ yœèéÄ‚Œ­H‡âsÜYaÜ`n£ùHXúéÑG9Q„±è-‹Ë¤Ã&)3,v ‰Ã™~”è™r ç­§Nâiq…Rßtî _PJ?òd Z¥ps–Es(o›–JÀïÄR£lݾî×=æy¶ªìîãÄ´NÚö%+âH_Å]4cv®¸­½K(i“S¼êx´Õ ôŽÍüÇ.ì Áë©Ò^„®?!º]ZWJœàLÿpj×[ÚûrÝÒ• !Ú”8èûÉø;brÝ5[‚Ü=¢²J½M§Ú èGö6CJ—í†ö®ÍвÁÊÚðšD¹ÆöØ]¢¹¯âjô3ªsf´és»éìê]¹/[ÄÉ`ÀÌUç?{½j7&’Ǜ֛‹Ì~ƒÚS9ÄÇÌÏ)¼0+Ú‘þ_8Ÿüã×ô^¦Ø˜¥çµèûmf|žó\¹ñ×”ßE¤ÔgÁ²çœ”ÆSaßMÑŽ^‚ ûnË 8Ä'¨ñ B¡­¹uB¬ãPÄÀ9»s8icãùž’›b7˜oiômvzËÞßZ•lb`ôh,®æ±®â%r>â°„f]>‹ 5=K—U~Wl— |QPLt7:R³ÝÖÀ{©#j1½=p™og|Oªsq;¢Ÿ´«'|Æ{¶)™5àóƒÔ¾ ) h¤½s—.<ûµzëbÆñ?æ†_M“Ѷ¡^ €µ.%ÒHIž$HvÔU9—éXfÈ8^ºÅaô‡Pe¡ô¥9‹—Ë9§>«-‚Ö§?1pœ}WCÌ„õf#VY¾¯O‹çÜòŠÈ~ß¹ƒë'„ƒ¼î Ò«)…f˜7>èú®ÌÓ#zùo²ï©šÎÆ]Ìòÿøj®*bÃM›QØrK¬“·ž“† iUrÎ/[¤Y$äƒO‹1Ú!zá‰v1y§Ú¾»ÄB¿?Œ¯Eqbg\KM*HñZ»_]ŽlXN€ |Èð<Ù„ÉÊ}]vÓhô”-nГD’xÃfa‹+‡Á¨Vä“^?ÚÄ9À•̈½[9²ºt•åýÒK2…ÕÊÚÜÝÌ[åÂîz»8Þ 1v—)yFaf!O[†è»‡©5¥&-)ª/2iP,(ŒµL§ª˜UÇÑ&=’~#ÖAäf¾’ETŸ¡³ÜÓêI°jt1Bîàº-@ME‰êV>n•{ÅÚ>ò,eCïß2™Õ ÁNEŽ‘ùWOGÚeMÄNˉÌHfJ_ò«0ñ|_hÇÕOFüDEé¶Ã»Ì€ïl;ñˆi­þgT¶‡ù²5ñóˆ‘ÍK$"’[ö…¨KŠ+H{Œö # ¯¡bþ×r·Ç^)àÞ³’¦V_÷RŽHZENP’Þð e1ÕzÚpD˜b ¯ªhݬ™+Þ%²ÅŸjpel†‘„ ŸCæÃ‚ó}%Ÿ\´»Þž)5¶LÖ%¦ Î-ãp0Xø31¢W–µM¹tn˜HÈ!¼qݵ¡«e¼y`b&ŸÊ5¾%1ó^#-)qÄÅ &kÈaÖ±BR ’ƒAø¤^¿ZȢ²ÖþÂh C-ëp/ì'OŸ Š¿¸s?M¦8?×?ì+îÓ’M9‘­/†‡Ô_ßоmØÒõ–;¹êh:Áž¸õ©°Sâö Kgêü>Ôs„šm…‘rF˜¾|笶šßæ{š¾T=¸f©h€JŽu…y™ndøÌLX†Õí}ÿf¥‰qŸªÌQÛl"×2SUXã&^p  Þ$ÇNŦúEûr&ç”}õMEá -.ðge ›uÀ~¸¶–Ès?Y›¿Ãd±µ²¶êmµF®J˧˜Ÿ$£Vv_'[PÅü¶jàÙ’¨Þ® \t é8nÿðÜí?H·uåXžžá>f/IiY?RD^ BÙüŒ•²‚ˆê gÇe˜˜í²’ ¡MlIoõ„ŠéúôaôÜ ÍÄe¿Ÿ¾nøü¼‡«ãã2º¨Ž4—#Y<µxt~ç’Í·Æi™ÆÅõVhÿx‘‰ò¹BÅ °Ö¾¤ZG¸ÊùMT.T°¦Ý1}éµUH8ºŸge‰[î‡[‚ßïÆÙÁ”פÉHû>áE óm)DÍUk@,:aP§-vHޒ͵ˆõµFS;Ïëj²„g\xÍ€}W%5ßà(¾¡4ÿ)P³LܨýÓû EÈ¥›Ñ••5íemà™’×qt½¡_Ê_$¯­Ù©žZ¡o"C]Ùó¿dOO®ü[T(;TEñ:ÍeHp˜Í- ï³°«)+w$c P¤6sQó@@GEZé€Y[6ÙíiÃÏ@ÅÀ2*PU6V04ß() çö¿doޝ+}YcÜjfæÚŸ€Ø2$ÎÁ6b¦³륄IžÈ ;.b­*Ó§`Äœ}²û®ƒ³Ý·“w–ãa`AUÅÀdù–é~s‘é—ö±•  ÜþS§è {û8¼DÿR)}F’3ÆÅ6/1qÃS}U¬[+¬±oSÒþ{¡¦èÕ˜Ç\f”¨ C  ÿB#á!:“½–ÀËî±9q÷„C?ä­ ¬²f ëÙ8h… ùâÓAÀLÀÛ Xd(ª¤ÏESAxƒÈìp(‰cÝŽS]ë›BQèÁ·L2$ÜCÒ@Þxૈ8,§ˆ{ Æè:Ñ»íørÓXº •PÑ`_ ò¦ZéæÄ¡ßí¥x‰äÏ8ãs–FÃÊt_–ƒ\³”ä³EÌ<ò¿Z Û„x¥¤°¼|Évšðý…7vêñ":Ìâ´˜a»•£³–¿7Oض-Ü©²*ëñ³Æ=‰¡Ü¡ÎZ¼¤ãQ \ív¼Û¼›<«8œ·ŠU®$ûÌmH=¨Q‰1Û—ÇCã[tËqÏà uÖxÕ†¶yØhS¿CW@ð*–9`bù*›õ©ÓÄ%^¾u»<íôÛtR¶“¦!ç…ãÃqst+yƒÎPUÿ¼˜£óK#ì£+O“ù æíAÈæÀÁÍëÕ…íä³4ùÓÿ:­M-ŠˆNõȧ.XDÏ*è.ê~‚YÕb–X\Ï­YÜ"áá%‘" š¿y>ztl^¥°ÏœVØh½½± YÞûlÍßêëËszµh×XR&=gÁ@ã mÝÆôʬ5Þ8¿½Ì!ò°?€¸r:â‰uÐÃwis³©D· E”­ W¢%¤$S”±V;e錳JoSÏ­wúú¼Ô—pEÄC÷Ø îÝߨ É£Ñ}êžðø) Éö&Ó¼5V{|ÎÏfeŠ`Û·gôr†£ûÔF°l‹'FûŸ}úèfäŒWFËò<E°Í«GÞóvjhdÁB®­„ñ óáVøj8¼¸ŸFÈ~ÒèËQcaÝ«ú£Ó¦=8ÜÊÖ˜â3HÝŽ(ëRMŤªû.j¾ˆ›”y0ð[-™bN ;}V5£ñ1e@Ã9=ðrªÚå½i5\ìÿ/@ša¹†)ŸBþ¸‹Öb™‰>8Ç%HZ_~ùPòþö¢è‰r¥½¢`YmòE0ÍÛEݪ“½3>' ‘礉â4¬1ÁaH>©=–néulùÇ[¼’÷Î[šLÿàbO“_wîüìÕæSk’Æ¢T-2Áìÿ¯ò8§'K¯¾a*>÷2ºRØeÒÅVóÛ®¤Ý‚J h§9ͪҴ¹\Óµ%µW­ù¶ïí’ &"Š©UžÝ¨ 1Ä@0(‰§F¢Pcü붤Šà^_@KÚP±-eõ—ÿº¼is•á0Ý÷=d–‡=ªÃ û ÖëGŒH¢ä/–üP›YAmlÓ S¿ãÐ5ei?ìÿ7ŒúK²„ÇCuRûK¯!Þƒ@K¿0„NHaïG iľhÃ\\)#ÄL2÷vhÆ`)NÔ{i,Êvtƒ,ñ¹gl#\›J7úA CÿB;‘§­b”H{ò÷$!!•‡q»ßæmÛ‚˜Ðçý†µ^ öH)ÄwûuW8\5šyÎøÇ‚ýOu² ‡½GJt|)9JzQ% ªÊFC¨09ìà(ßluÚ†Uež ½É ÿ¶kq²¡ûyE-†2cPûSÆ“ƒ«ÅnlÝ9iƒ ·-¾™œf€²¹{Ëš‡[˜5g-à*|É(ÂÕ‡V§æ°û¸}TQñO¿rûáÙÊ ?ü=újŸ Vðž÷ƒ¥¸àÛaÊéŒÕÎ~(–Úvò4‘*o†/‚ÉÍŠw±uµ©v )~ Ĥ¿­qúôÄ>Uu!“¾úèÌCõ"%ƒ¤“‚¢ASîòyï¤7UJú~=Pš'¾.úammNõŸÄ`ÀÃ8†°êóÜN/^c%R™!îÃìˆ{+ž­sÝàü~£$ŽÞnè»10z–ÎFº×ÞsS÷¨b’«qî¿EÖ¢™ µ‚xL?\¿«3‹‰'Ôú/œ³í#S|QZl_}|"‡RKf¿@jôü¦ÛbnýqžMå(Ë Äž_àvœÿnÍ5Ì _¥>oû­¾L¤Áò‘ÃjWQ‰~|5ÝÐK•[…ð9•…=æzaðûxQVé7qv±§×DÅâãH!…lKJÞuk›'%\¸%o{ì­ñNŽfHÞ¡¶µú‘'XÁ£*˜JX!8r^Á÷"pl9k÷Ý3=»äyÓ{MÊÆØÂ 9ÒÂØHŸT& o‹‚«HÔeüÔüé b>G%«î¨7:P5Vzù„š{}6£É]{¹”èOÿëÔñ)-o0ã§óVMN'ttæ=$Ü[¿Ù¦ ‡êƒU*Ó1dÆ"Zð{ÂësaÁP7Ämvy¬B¡Ñüo6ý`«z‰èD/à†æuލ*¥ì`@#¸Sæ4É%! %þÕÆ i°TOI¢Œ¿obÂUѲÏ?7y¥¯pwÛÂuÒ|Ñ—qå²'§L»Ÿììr"¦ °q2aCÆE¿N/·æT˜%fŒí÷.4­;!ÆÔJ„”Ûègþó(R½™wo¸ù˜1Ëí~Y“ýH#I|1±¸”S¯¶j¯ÅêXE±|Hpz Øc”ýk ¢\~™åÿ^8ÇQÚS•¯lh©ŠÏv)+ Zqµ*€Ûö@¦D qW{ê1fc%_ûc›sÐ?ž‘ùyʈÂ3[Ñ8-ç7h —ó×HÛSco  gå2"µ9LvU BO÷š™ŸBDy"¢øxÀTØJ¢“‹rRûªÀñK&¢`Û¬¦ 4à=Úf¸ö£+† 0Â’/sPʆ^Em—jÄir¢XC½´ x¥ó»Ö°M±4˜tÅÒtÓqoãøjuÔU¤Óû´_›õªÀ«Jx'£J·Ò}°Š¨NýîLPÿßðX~~¤ šìú0åßæd¥çY-])àR›H£Â¬£_–"¯}öÅçÁ7ò23«hxyï \¶õÃàõêÑy×âRC¨€ù-LÉq6´zÂéQxùeïGJdÓæ¹4za÷‡œ/7vå.BÝÓi^Úú:k«š"×d•ž6"ŸŠÁ¤ºÃÐP®hd€ŒŒåǺ,pºš›Dië1GO2ÇSÇv®Ýˆ¥þå¿ñÆlgáS~.k¹:óÔÊ?šÈ¤gŠ9܃Ÿ@Mø“f‹=#ȈçÔt ¨ÌFgáU—¿} s{úS‹ÔGô€ 뎽™‘¨}*®ë!Õ:V”`Aâ3sCUÚÒÂæÌ^“Æ’«Õ j} èd:¶ÅT­ûT×®ýäãZr*ÛÕ‘‰\lR¬]+ÓRQôÞI€ßrU®Ôþb  ³åÓep~Mc`VCV °^%÷˜ŒøóV‹‹îW@ ÷h›ïaÁ|QlE0 p`xò&b= J!G‡àZãÝèHÁå¡ÑV8ÖÿSzKÍÓîà~µÒÔôµEùEå£+%ï6³\¿éÿTu3Y“VÑÎF„Y”OÄçÏ|Ò…Ÿ§‹1kG²=PNÆY@˜FÆä¶`Pe¯ð*·W0ñáÝXl³¿OÀ§Ö*%ÿtQï%¿ñè-iÐ r`X…Ö¬?Îc ] ¢ó‘Õ¬ëüýt¡µ B._Üð"0Ú¬ÐU¦âÝM“NeÐ(&ýo!Ê6z@D[—ÂOÛd(nNáDãùTm7`dÐô…q%@Ùϯ>öÌcñ­dˆÂ!× Ruš™ô‘'¥Yœ@ÖwÀ¯ÀùwÎ=å){¿O®¥}HM`—©uÏ+~‚ËÓUz•áy¶Sü%™`á'ÔÔ9F3B® B¯ôHXâRŽñe wƒ½©è@>†8Äî‘û䜎A€ÈöžOÄâãšrÛ ýl´¿—¾—~_©:ôËË““ñ[¥ì@çÉéÈS†vžŒ‰±Ð÷ɹtÕ[¢Î&ådS™šªÀ\[ ûTk¨™lø:¨LçâÍ@ž=óIîŠ ªBÿQ¿_à¿´µ‚5¿8t†ëÎ<Í«•?&®9£XÔ©Fi˜ÏŽ¥ÊÕ´`ˆ4|‰“ÚÆMæŽÝ.ÜûT$‰Nzp¤êU³v¶RV‚좪ƒiMŽý;µl§µ.çøU$3L Í‚?¦[ŒÖ“GÄ'c|w»v8 ÚEVôÑGÒ´j“$¢VZÁ©/5‘Õ¤•ñ¦Tés1昻ÅÈc|U„€’ßi„:àð¥Å]XŽì±½Q  !úßÚ6 [šf`´!9*nqtžÙåy´4šJÄëÍ´ÃwÁtý+t|úbHõJàŸX>”Döãî°¶¾Äÿ>e·JsÍ¥u:>’üš˜ýCÁhŒD<Üg­èhfG¥/„×´ðß¿çYy^„&ÏA·ìò[„( ²œvp³Š-U¦ ç1ůÜž’zhÐ NâuGäщÛi*<ôHž_%xóqî©‚•"3UÀm)¨˜™Û F™Ê¸wáfþþ«;Õß'âüÞ›ÇËù~†àòýÆém½±uZˆÓ6»Œ8úG]â‡Ú˜‘$µC×…Eã$û…­B`·£»ÔnRºãñ19u®¶ùµ„GÍw£G°ak—]\ïªêµ‰'¹ îï0=rJj’“®J×'M!±Ùv£tsfò3n„óÂQQ°!±ŒÆæKé˜@sïñÜÌPÝRì` î z<ÆOÆãVwnží«5A\ÛJMCµmNÄ„¿¡­Å¦„nMöÈÚ“¼l¤¡MÞC—æéoå3âR—鹪ç ‡ùÚ}ø`úJÀÃ9ù{1(¦ô-}‚ó7?4I…`•ÄééN Òõ]æä¥*åС‹¹f7à\€°2=yëÅÑŽù59uŽ9ZOêEõÕNÈé¡ð×ËWXdEéSðX#Ô>Zz«Pˆî¿G·Š›C8Cypq@5ä4ˆë‘0G.*Å»‚É;y.£I<àÍ@ƒ‚Öʽ`+'°HLJ©-*SjÍ.þZu…É—«oÎèl4œíM¨ÒY•4ëôÁ Ä OL£)ZÝÞ72ÅG;Ò … †2øãƒ%i|aiÍw°&°å?z·;LëÙº'´ÔkžÊfý^ë±CžIßrøfíö§õÄ@³o™Î #¡u¼Tr÷ät×öÄ¥,W6Ñì‹7æó,têß5Ab‹?)Û ‚’}R‰(82¤RºÞħäÅgYÒ›&d;Ÿ°àðùÀòP²»ùµ’às#>x‰‚Ôfeç€{!'ôÄ·!5É3»lÊ­ƒ/»Šþì”Xz|«Ùi';Î,-è– §“ F'V½ o§­ ¨±p˜‹û3¸ªôÕkß÷=¦×ó¢¬;1ë('t„e®uÏò¡+u`‡Ž„]£–2áež,3­°GŸëŸÓJ_uQï°ÆýTPÊX©ƒŽØ|A;$Ôí¡Ê®–] ·l:ýÁ:žÙæ3Æ~çyÐ6@Ñ@QÀ}mÐf`ݪêm-IÔCíVZAÔ¨Á5tóGÇ}T-µ}˜\_úÝ{™Õãwvn ¢ C8ð3v/jÿJBN<Ú`8ÆFaREšËòòÁ \BhÛ=`mT@ ÑèÂ×ÑÂn–¦™Ë¾TKÀÂ/¶w %Ý ’?%VÿáˆEé!Ø‹‹œ{Þ}rlÏ0@ÞßI7eâ%áŸç¢ {ÆiAÀt‰ ¤ÆöuèÖBÏœ*µ£³ƒ"ÖìÖ5=É^ìß·qk»)7((*ÈDÜ@ ÀZ/:OÅeàK¾Ï©õmX[4©||ĪÀ1\²†;™.9å¨òy^ý¸¨Ž_êE3Ž¹ÖƒS{¸þG°Ü'Wð3S˜”ö.ÃUÊÎÝS¥ŠÒ¡ddáU[pÀUÏ&ó&ço4p×P`ŽmÒtt>ËŠÖ<²ŒÇ[½iãÞØ'JUNÒKEDsà8ÄIÊ-w‡'ðÊæ@¬‡Ï'!ÖÒ1õCT1oïÆ‚7 cæ+d‡>žup™Œ…(ƒžq'ü æ' ±t– E6ÁÒÀý‡§’ÝžXud „Ø® “¿¼Ðçkâé¥y÷…7óx¼7ÐâŠÔíçɦ¶'Ù³&ëåÐŒ„A¨L/Ú¸…»‘økÑ¿—@ò¹-x.Îä*c<¯¾qÈW…ãGÓ/³`ë›ßa%8óßÑØ¾ªãÿd2´$Q&ÄÔxXÓJ;¤t.š!„ñm åŸ;´|ï¶Ìïô`ý`<›‘_ÒËtV ËI|×Ýkˆ«öY¥mW´˜sUÛÒT*¤ÁB™É¸èÉ!¤¤!6^ ÒÔ/Fý–ÓF…W px½ÐU-pŠöÅ› O×OõÑ:öý"O©®ï¡]•¦zѹÃoµÔI«jgGåVÍÂ=µû¨oå3;Ùó•èYGRµÀ:A\SÅ(Ð2€Úä¥j@IyeI‡›®yšEtô_™·Æ—™4KÚ$ È¶ýtù#‚ÄÇj&ꉖjzd^ŠÃ§Zê镼ö$ÝØ…ÀáÓlÄõE°žWo;½¸eË”‚Ÿ—•²çiúþÅPXmjÆÄ]‘C&ådè$Üí:îS”GEáÅÄp¸îÇa zˆ~ÌeØ1Ø[Ú™±¦/8Ý@ôE_eÿ¥ýÖ9iêâÂ’˜ç­©âÅ ®dmWsCYíyI[ñò±1C#ùsš € ¿V†ÊõÜø©qøjnÍ{ÓeÁ^݈],‡¨å½ê™>~Œ®”݃uìš:Ä ‰r[m øž=ìî¢ ·uP(}è¿vÚr’„WÜ%d*N¡aÝÏ:Z9GÀšŠÉØxŠ™²‹å:ÎläFFì¥ò»yê)X]-‘ö†ª±­Uz¦X¥¨r˹àaà¦Ý*s*rÕÐÕ¬Hü RG効\š?'!ÿÎW ‹Í7-Іç¥EýõA#yy÷G3æ¯_Æg•üg·³5Å3„ê^;«\8 î[‡_€}ìc¥èɰRÙ c17‡g²5Áƒ˜†“ñ¹ó#JRbuŠAYþÃj§²è/µó€w«…mζsj—® ìSÑoF6)°ŸŒ`DÖÃ]iVeÄd§[~«Ð…ru @m“ä”ïJp˰b˜bm4äO+ÿÇŸPzG¥BÌes|áŒ0÷ îß¼%Óø÷?+A;ãe^_2ºIòŒÏ’Û²$Ýç©gV‚¾àXâö6 «F—5H¿úKiÚÎÙÌ@dš2Õ1ÄØèqGÈY!Ýtoej¦šÑFœš±.„KøæOÇ„Èô-ÔEû«H3i‹*ÇŠR¤PÊoVw(‘LH~ÄÌDÇÞKnÿ󬻸Ö/ÜD£…i â!aR½`O™½×½‘ïÖIÁ¶é•rN<†mdYÍ[íX%™ŸöŽí)ƒµ©öFœ‘HþY¨°EÍØ£œÎT!¢hBägQœþä„`~¸cßï™Â²hWÄ&ÃÄ’ÊTUv5B+âãªy'½i‡dZövúI-7ÑŒ:¸ùµó"¸ádé:’¶I—ÏEªà¶LB `þލõPŠN99{ •xd“C©(T÷8n^Â** –øvnîë;z±£~¬óÉÿK’)X­–R[ä…Ü!®»æ:é(³X„Z*wwÙÒ«0¹å‹^šjŠtìê· ð™*0õHŠñÕ[³-Q—(RáåÜ ¿\¦Pc;AgÊœÙô;XØl°¹#Juš Nu»ë#G›Ü}ý—}?hVãm‰!3cŒ â©àŸü3÷~!òLÖÖ& ÕHãttíhgÓö”PG±ÅÕÝo‚â8ž½FùæëãQGúEê{¢©†%>¦üéY‹¡jÎWBV Ò‚‰Pb’­TL0RöÒ㢾æ^#înÈh Ã‹ha¿7ê8q)-²ô ö‘_?ëðôC[‹P·Ì&bà詪Ãà†–aF‚°fîш˶g @A‹^|Vü™Ú[úuVðEqåsÒœGñPƒîc8K*x=iyØ,Ã.Ž…ëUyÅ<ƒ¹ :¬õŠ ‡nËÉÜøHh µ+xn”Ÿ]õË- ê=Я&x+ÛYtg wþÈc£™F°ºSÓ½A ;p°¦šÌêÄ“ŒâƒÌr=4_Èb>Yª ôý®ã``¸ý6 ™òÁíkâ>|-W럙¤^”~R&@»MõŒ¤ö«]¤"oÀ*>àö›5$›TtxÒÉO­ùa ñ„ ÂÓϽù—*.?cü¢ .•-.˜fkMÞºö™ÒVUèâ8ð\k+, ­¤šˆž‘XÄ­¦q’)™Ïž\ § ªòü>ú)Fî÷a‰i7:ýÇi£„ÚU±aA g`@¡A0ÐVѵ…a«?×oavÆí­ôϣ℉/Víž*nZÚÿX¼x_?q+ƒ—Ãâï!ÐáÑeÜŒªf¤¶a#¢'ci„f–R3M¯ýõÀeŒA<(±¯]Ä£â&RÍfµ)¨š_néVùÞóé #•µÈ߸’RNG~„+÷J{TµC]ŸgT²:1žéւи A\w”P|$Å$Ä݃ôœ¡ÎwY©ÕÖs. Å'3g%ä-_ƒ.*‹Û¤|Ft|ólƒ¼B”$·Q|Jñüq&3|Šºáº§Sõç¨,´†<‰‚4±¢À•iuôÓÆØ—"+>22ÚcøWqàΙ6.|jÍù¦–_€ðÉE•ïºT±„’¦iN«óêj#¡5Ÿh2ðâ?qZ_ãÛø5`fk@ÐO‹Ÿê†!-J2ðG9SÍÝÖã qŽ wüœ¶Šéìã} ®B2Ê p5ÏhB$Ò)­F¦É!Œ¨ÏTºP]FÅò{‡hV*¥ ñÀd¼q}¿(§@ï¯k_5Ù2™á¸B·HêZ8å &¤(ÉÆ™-Å´^çðvýדŒÍS wâß<J3°EÜz*Q¦ÃÍ’$›Ÿ`;8© R9±ÙihgZT0˜GÄôrˆ¸&Ť×FϾ^FÁjQ™)·ÓwšQ^³¶ð¤ÃÙËëlÏoB‡J.}¬Ã[Ý pÍàŽ$Ñk Dèà÷K#.ŽvÞ5šêùv.cïD #TC «©}%öÌã_¯:aKbÈ6pÖ×ATËZôÅùj†µ5+zÙG^•‰äÔý_Ôöwõ…£Ôžô­èÕåñÊOúl¨H´Ò•1n”ÙƒnQtÊA[ò—ß:&”øsE­:Ùü¯ñ‹3²”ÎÚNºõd’TõÌ"©¿ÂÒ문È.«Â©®u…ïÉŠVrJóéGâff,ÖÎ;-ØqaÖR3¬ãf.)+¯d…"SÙת÷¨‹½Û\xÍýê pLž¡°`%Ì_`—•9VÅe9°o!Qàƒ$u[ˆCŸš&/…Ðn°WƒîáÈÌE>t"¤ûý8ò•έS/Á— s‘\W~“Çä” ô®á5×쮂8 hf„$ßzœ”””úôNo'™Y7|“§Ìã\`lÏm?ÄÀýö&ûÙ% 2ºVO².ð,Ç>Šê€%Ùl), flvæfÝ¿¼þ^9Sµüì;éá|@3ö`­z}Õx£,8z ––KÞ›MÄé $}èå…ã¿¶¿1œ¦/ò-„Œ‡™¿(bOùoÌMÀh]OG'ƒJK6Ó;D6ù%%•ÏLÃÞèÞ”œ®*÷£eJOÈv¾éÇZÕ:$9 V€%$"­“Z‡¬1ªô¯ƒÙܪµ=9ò)¢P:ƒSOŸ«›Ä_]1 ¡³+ _¥Äsr¨ôß÷ÎëqàEBêT˜É…o>à)!Ú¨SbŒ·à)yº´äCÏ¡KÀ`gL¦Q#w©6.mw#21ó+ŠßxˆA˜=êö{vµGµçgM™ØdlÅi²”(шKì·8ÃÛÙ‡ €bU=< x/øì_mgï|‰ºeÚ%Ö¼§²[@³LÓAÙl¸ÁÇäy©Òœ$ëýrç²¥Ùjl@DÏ”a½÷§1I §Ë9WŽ=ˆ5ŠWù27Ô_¿`Ñë},:‘¾{Š" eúÌÌ•¸/h%ŒµÖü ègŠ–Bâ¿U»ƒØc3Òhh½soˆËR£8¤è×A7¹ =õtU.gª@ÿ@ð¼LG`ãö­õƒÜ$·§ÿCV½À´¿Ä~<í¥-,»]bÆè¶‘I©ífF¥úÌWB­k™þM Œ²á€¨Qh!ÿk@‰båxè¿/…c†¿Xjvæ•1¹iâ9—¤ {ôÆ«É1¥Ó©3\nõcØ4Wh£§¬Œ¥üÈa¶Øl^–׈L[ ‘·™†È¤]](•“û02®ÎŠ5˜c[HŸ!ëòôl  tƒKdˆ(5wÀZ}üà <¡† SL‚sYå=«{EÛ);\0ýÁ2mn—Èá$¹—þû œSöéŠ8{´‹¬»›' »Øh`Ë;ž£ï¼øRuõº~}»â¨[€ö!µÙ²g‰|«-1ÎóâλÏ¿¾?àÆ\H7 Ýûmðíy[žÀ}Â09цؑÐs`| á=Í®¡¬üò¡"jÀoÝáz)ºz+ %2CŒ!l¿Gï ¥Jæ[£ ›nNq‰÷¡­Ïͱ]9MÇË;}âúGÇì · œHpáÀ 36kP²ÁDm¡Ï«<ªÀ²ƒ‘ÕÛ:ò¢/MÉDÖ¾¦1Ëì²^ aO%…AiÄGÞf·zˆwXå:Úg*±ðçлÀ‰á]r™þA9ÕŘÈsÀÍÐ~ù)Ö·š?#‰àÝŒÅs¢;†ܯà’¶«ßW}ĉâAÜ…ƒ·ŠõWàPæû6Wöx‘þÏIóÝ©“YpžgÑoÎlÛëeaÇ´˜®š9Ç( è">œþ‚ìÅú¹Â 2çkIØùÑ­ßV½ÿZûVIÿ÷\Q±ÒŽ:¶E”í»aXš]»:e¡Äêñ¾%1¥¾Fõèîî™»ˆNoñü*œ[ yÀ¸‚DÇZ¼ ޾ÑwX«„3d­¨¢òeyôh`¦HæÇ OJ€Fo6·´©Æ|…w8¥=ÑÁÚã&µ?žšž×g$+hv­04æã>·‰‚Ϻ@Xœÿ_óG@†PŒÉØæTz€7H7gVñ8 ínr–è óÛWû[Ógdz3øÆ\Á½2“I‘sÈŽÙVÛû˪t{xlYí2ƒj{±7£¹â-×AÈœ¹¢c–'ç{WÊ%Öp»ïñF·,Y‹¾<•†Ö[㯿ØÉIeG[Û¥Óõÿ0Zó-‰˜“:XG»¶m,OdAU÷7§óG†Äî¦çîçÞׯémZÍvu5É.¬Çöö`eURwÈë.ëg:¼Z0Ú¶XM'ùAl^CÎA1»­üd׬ž' Æ9÷¼æœ꟡Í D[` øÉí1°'x¤9p"; Ù¸g ‡4¨Y÷M?¾¡½Û&Lœ™f~ü ¯ÛÁUÉcéx¾äŽ¢cózQŽ›kx€ŽåAª)Ÿ iýÐ5ɇT³¿UÌ›^þKà^kCÓÀj²»õý€GÊDy•: bœôJCËê‘o¤@Ò§™}\2þEXñ‘²åFÊ•ôçÐ2χ)y …ôbƒ"L5=iˆž¥ª‹Q|ë¬'[æzõÁØ€4xŒ„¥ÞÇqM’ Bhɨ?®Ö5NfÏÂòsLÐ/‹†ÓàDî+Dýõë/\«*ƒôß ,«ZƒÅõ<›2cG#·LÍ{i[z×ú'_.~¼7øæ&o5=…\–.ŒöDoÓ¦Ö(»0rFÛÈ­N©áíVêÊÑNÆÚ½¿#2žó¿ßí{;àéÚ2eC#<R’Ë—"ÙL²_YbU“äù›2ÐɸU-Ʀ4MlGÇu ݱÈñ ˜Ç•ÕÌÁ‡_í’j9ì«£¹‡PtKEû•I¡ ›ÝÑÓ¯gäZ+&€)¨‡ºÙ4÷eÈ€“¨¢ÒE 6Ýıêru¨Ï:=?M!/2!ý8RŽ-fÇ¥Èúº~Ïã¼ß<j­Aî¦~»Õ¿eyÿ‚Dm—E@6FÂZÏ¢¹]j”>ÁkÞùòý@ö+ zL¹I6.ìJ¢/ú¸®fIGlÌìb¡_†ó%ê ,É‘?¿Òž{bá‘j‘ ubwÄéCáþ“Vç9‹8"ÌT¢ôœŒåÄìšAzʧ[÷C‘“1¤‚Azô§ Ÿi/b—NÊC ›î÷ã6ÏkÂ|[Ζ“¶v!ȯ %"·ME-µ·­½ ìH—ˆhp–Ôzž;Í:˜þŠ ë¤Ru+¡½fž•Õòh8®Í·gK®²jŸ{ßȇû›ÐrðUDõ!ƒpp8Øç©rÀ ,½ññÜ!S‘÷d² žmÉkcó~…4ä9ÕÚo0Fü®”ßõȼ™+=3;D#³ûèâÙ©©¼¹ÜglWg¡À’çà ù+-)ýø§cŠÃÓIÑÄ8¡×]µ†—¥x0;â[ã€6© G`ÇíF­*ë¨ø)bÜÙlzÝ&cß9‰eŒ² >g)CË·jMÆ£¯57ù:Õp}æÔ?îKXHQ—–ö=_Êîé0ÂE±úCÚäÉÚ+Ïä¤ÕR—Ï“è=’lGñ@®hÄÏÄʪ¶®«‡D¹Î5=x1ê| qË–ä]úÿ àÕ Eßñ˜1B~»Éo¶Áº½Ym§8ælMÂËmH…¹ä¡F_R„¿AÑÓp1->lSÏY¥1ð[ka§(ì›$ÖœñÇr¥;ÄÐËÍ(FF}ît™E¶ÏZõ>¹õk,I=ÆeÇNîÆUêT“Šv”€=°ù”ICûÿB++¸ ¥À4Ÿ¬XWœÚ°#iFlÀYØh1V–ÄAŠÍŠqÕ«úÎÛFnI(“íΟŽùË-‹-Üá« &Ó¿2Q@Aó[q¯©ó­ètQŽ‹‚cÉô Nò~«5"ôVdA…ÏŠŽæ#ÎC޳@3}·»T‡¾/ÍŽÏÅ0Çdñ;{$Jìûß"é’_–y,F{ÌÌe6ÙZf£*EH¸VPÎOÊÜ=D<ÖM›~pj06t(là ŽvÎó³º,¹1®Cs•ÛѬI?^ÜSˆ„Ä•ê™s7T7¹…ØF›³ ‰¸a8jêa>¿KP½þ´E–€ÆÕÆy tT05O!NÎÓ`;úôVÍíRÃdULe\”MB<²ïpÑgå¬ÒξcW~íq2ÔÑLwcŠàíWwôÄp]¹—ÁúQ¸‘É©Ø%kâ©·`o­ÞÆsÉÝ -£Ýßå@¿-§èxÇv2Öë™þmR=]–~–óQáu³_AÑ”owxOxÙ¸®¯ËwþéþûÄÛì p½Dù©’ÒeÍO1GÇ® 4§!”Æ-$ c û§ÞñAZá‡aéמ„Çu¬#&—·³Ÿ‘ÿжúßÖ!?FõÁ:óÿ<ÝênͪM»6݉àqÞ®;ÜxS€%q¾+Àã-ïúE½Þ¯ˆšeKG­cò Ðd=nøCøÆÕê¶ph#ºpþqë_f÷‡àý) …#cŸØÏÆÏ ‚~#ÞªÀ~r·ç×GebÛ Årÿ–Ä<ŸðËR"¡•?IUÞrliþ÷üšLÝ­mOõ iÔ1Hð¶öåÇDßbªàw ?{ktj¤$ÑßÊPèI%¹,#ÀG½†šE¤±DÂQs3uzÑxlŸA_ú⬠I«O¿uUhet£R)Ϲx ±Ÿ%É»ž$; Ä§nûÿ:’Ï7ÖÕæPäÔõ%ÒO¼È"ð‰ê¢˜[ý¤§d0LfŒøKß|+þxóÃýØõ5–q”p "8÷‹kí‹'|ß¿Ÿcp®vTi=T ì-„1F½|¡¸¹lô¬]÷Qg]W˜haN¶„¢P†43À2!xÔxJ|Ï™Ä7,®²}„tiwöiö¤xZy™MƒFLžcÇÕY¾ÄâeñsHÐ,'Ÿ¾ÄDß¹Ä[’&æ&1k‰cˆùRÚíé"̯ñl¡îÕl>30°màq­úŸ&©ºž>#ÑâÀŠx±æíǼ?+2¬$;,Ü’îGjwk Ò×6LûÀdÌ›éÕÙÆÀw@KWm“ƒ­Ò:êo}Ñ¡¾–\¯•ôKœJù–^w¬Ël Ð‹qÿœz5y–¦wb‰wŸv»k>–•ðHÆ- Üü7ú8ÎTЄ¡°ÒŸ‹áwÆC÷> PGÛ‚¢€2ÿ!v\§-ä”èÏ7 RL¼;¾úŸÞ&‰“ñ%¶LáDÈA4“þ4fA¿‡÷%;‚IØ0ê‚d†¹ñf1€uݹÂÏæ‰p@§/VTUûÍgÐÕSÛqx4nk´‹]Fáßw“—IÒc-78ÛþÕ6ê€ ‚Õ_P‰1òYˆYd\xàï¤ÛÞ:–³ y¾V¡Áb_å—Jƒx+vóU¥û ˜ÿËt¹';%?4ר¤h–noQB[å‰$’‡Ï‹øxW@´#U½CPmµÁ§)ÐAh Þ3YÇ:×Ð;V¡’¥´iYõÙ»ŠõfƒÖëDñ.0‰PÈ!+Ü—Á&ô³*$ìSTS¯WõÖòQѹ™•ORü¹À+îQ%>Ï sàÀc« hà¦>¢èQ¦êÚEè­«OJÕð†¦ƒ¤øm½J*KÕ“'±ZpÉâåt½jq!y Öƒ¬*d桊€´Ôê´ñ©b kC¼1‰{+ÉL—›:(TÛmëËi|²·gŠ/ë) ¢aø.7w<‹Gôâ×5ÁJfz%ÿ qŸô…¶È P~بïd—yÜŒð¯ä~û­?»å8™Ÿ->˜Ö ôZ«ò/ùßÄí(Tž‘oSæ¾Ò$ˆ{¸×†¼}ÐN«§µ*µÑwàI#Ð?ôWýkX=‚{4»³QžEÒŸó1/Çd…Òå¥%í(¨±.V–¡¼ÃY*ùŠ|‡“¬º ‹¸Ê«¶ÿ.¸òÑÝ…‹S¦»œP!Y²^›Ûà-ÏâãÒg÷áK‰e7QÛKºXöû½šªĄ̃:Ù@{œOô2™ÚÞ „kr²GåŸHÇÉ8ìþ\”,/T˜aÏ;…0wdôÖ0޵’΀K›ÃÝ3ª}¿ uáÞMa@rÎÜAÏÅ-„¦QR9N(œJ:„U4(C Aí¤ÆqÎiW÷%/dÅý—2CÒAv›×;Ü? é†„ŠªæÆ$‡­5wè…¬jLôRkyxY+¤¼!9žÄÌß8!…;5 ú±1ÒÙa¯­ìÓÙ»Ô»8üDªäªW?U‹” …c8SÕúïžââC¶”¿z3­³Â´]9ô½ÅÑHáó¯â6·'«Õ”ÞvC¿OßbN2Ì&íš"õqLô½n|qÖQ*sý3ÈEÖè='k®ÄB›® nÂ?ñ/VKm^íøh¡ÇÈÆõjIÎ]At×@PBûôE^æåw-:NA?Äùx,WÅѾ'K0Füm(8‰Ùíàn R#%T›&8äÓá(mnòÜPÝ#?êÕõ‡‹ÝŒÇŸ¨cÅg…ÓB»Á8]J÷Ìn¢ÉÇݼó’_·ÏéÎ6þk9ŒLh#…hDdD1 ¼Â®&=7¤d§½p’×AE&¨Wͨ'9@ŽÚ™^L‹>‹¸#%¦”2ˆÚ{ûè¼”¼:Y)kãÒ3FÃãn;féÏ‹8e>­µ£ *â6m¨µ5Ù¥ÇÌ`”ªKóõïlšñ!×y»ÚDðy}ûÉhˆÁ†$òüo·Ìˆà­ÄânKÕ*#º¦ìD#Ÿã¨¸ùÛ‹öj¹)ªaºÓÀ¡R`_¯œÑrÊ-½Aöh/2èa_p1%w Xásïq å…Ô²Œ¥– ·nÔ)»ÿ2­ƒÎ®»¨ìOÏF´þ Ú€¶‰‹RÜHX·\îÜa¿píçøÊ§ƒ–¶˜‚Oé¡Áб˜÷éG{MͲ¬váò ¸ÎüQEÌ£Ib¸ìjS1òúá`Å€ÝÖB½€šÕì[‰ß¨‡ûŽVƲ´}Ðw¤1S| mQOÒU¼¼7ãE´ò¢X<ÒxLK£Ñ¾+. 3‹ëùƒE£Á´¸$ç$%ì¬(·¸=·t{-ž¨¨[ʺ΃'w¯¯çÿóÙl‰1䯴à­\/%U;¶¢É–/*EŠM*ybÖÊY & {VW‹FŒÅ°Ÿ”ö#Ìb«=SLJ<=L‰ÍóÜš Їê/Ô™<ÜÊÀy°=è_x"KJ‰ÐJid¯çÈ=v÷è`ìw‰FþMÆÄµp|(a–?4_>i‘’ZZ»PbÆÐh7Ø1‡25hyr©‚×g%·á°¬ÚDÌÚÏõkÈ·¢•š¨‡É]Öð‰üö™éQƒäÛÒT€jNK›sß– ««ˆBgåVÕH9oD×÷Ý*÷B_Ð|ò¢¡Q†v ²[nóV¨mly¤^ÛtÖ~+.‹Åg z™®(5C¢4v_À€sÍm£¨ØˆJ‡è à§.¨ P¹o» ׄ!÷Ÿj– GŽÈdtb5š¯$H™±Rè·µ]°Ö›wdñe¡¶*HÐ'ÿÌ$îÚ¨r :GÆ;!½G0èÃ=׸MÖÆL õv2”º •þ4Z†‘FÔ\Ò ÖL¤Sº>a®Œb¡¤a_j4e\û{¼ |ÿyÕü-ÏŽ-FIvj‡ÛSr4åfíDj×(õâýòˆ€¦ï8Ó¼J‡Øœ‡C…ŸMI1jOÊ*‡}2OÐN5½ ¬ÏFÄ€y’;‰v [UÒˆú2œN§  jß3Á4#…cÙÉS“³e†Ú±ï LžWnRv`”^Yîöulÿ€—m ¤ªoQú¨/OnÓ¡‰T.®‡+£ì2Oc„WEk¿DÎÚɘ|6p¬÷†ìÅúO*Ó>øÿ(,ÿ(0ü4§2&Ëù]E˜¥ ¶|c¶-("ü§ÞÓÀIþ'û6P³Ð(Èß²ÐÌðYþÂÕ±c‡IŸ­q”C‘ê%[=èð›; TÖïP’ÅîJúC}½§Ž/y’p¿èH©öm$™ªE[¿ºX Ó{›G5o .<ÆIÊï…4¸ëçÜ(¬È”¹eä½Xòã(úr¯§„’~Ò&_‘´¯X9¾ƒîŸá3V(vqZ/~ÝNÛV7¹ÓÏ¿«(Œü0 Ä¿ã7ƒ.]s»*jJüOÔôÂÎóRð©Î¹  +U›–ldžG[­PJ,aÇ<„Bö²Iט®+’¬óº°ÅðÌí{ZÙÅ·!2'±YBòWWn›¯—ÄäŸB£P½t}Œµ¾õ·­oÓœïUÉþ%D9]cÊP®{µžõ ¯fšÅ>GA2\t~-Wº³Þ\|8â£])p(ú”*jùÖ˜»áBÖ<²9‰¹ÑïËþÈ‘Þ"ÑÇP=¥ô-™ùÞæë<¤ ^ûóƒ©çÚ‘îExÞBì— Iy*Ñ›ê3ÇRíÏKÓi=Ʀ<2¬#›YtŽwæIëí4æ €æ1äÔ’ðµk†èÕ˜[¥¾Ìˤ-·(¬÷IF¤ôÅ ™3˜¶1&NŒÝô_Œ€Ôþ5é þçÜ:CÇš™‰< ð¥usë$ ª$ÅX‰Õì"N®õðs=•DýAÿØUÁ—À»9rôäíZt&~`ñ°ŠÆD`­â<0‹{‘º%uMöìïúš£þog‚V:>sK›¦÷=ëæJq̈v”ôª`_[–QÌQÏZÞ÷V\Ѝ0¡8BtÙ˜ü‹ÙÅg—(NÍšÞívÒþÚù“öêÔ€!¹nRÈ7j§Ð²jÑ^ÚlÊo?Ùô$–1ýµä9g5w¶.l„Ó7<@SOEÃe=h…§3N³¹ê¨×aûNâÙ$ÅvÄ+ìCB̓ñPÁ‘e#„ºÊ€Ç²Uƒ®Khó9mwß Äêål+Vt}-†·3ÿOöÝ+±"n;éæ}6~‡I"…e>ï@Žå}A˜…Šûx0|]*=€TE¶ª $çv#Åhì°¥—À'åü¡mBh‰¯tÞ¥Xt,E&$rnÃÓ©ÚYRÜóLÉe–N:_±×б°¿ã*N{îñ 3£=ÃÞçê±PáÖ€ïGSi§„ ƒX «ü@yžª_ȱ·:¾4VE^êU· Óñ¿žƒPîbÜaÁ¹ÂÛà¡ebUÀTî2Fæ»D:%\i]×6¦ ¼ÌY0‹ t‹Û«£Ô9¶­¯•-­ðFö6n‰mÔЗ—Íà÷e@ ˜ ðôOÜ~Üéò7gÝOèk 9Š’Ñú[³È F5Ӧᙶ–/ÜØ+»ßz<%5+È}¯{okRQ´Zýw®Û©LÊhí÷,F¼¿Žà=³G¸ û #$× ñ5Iµ¬Wzö˜ ŸÕºY‰õF¾Žº‡#4ベ%í'8ñ^¥Å˜PþßmÜF0ˆ4#ÊHò]PôEN¤•D ×U>IŒy1×zs»˜ÐF¤ÅÛfÔ$ÏOCͼŒä=; BÙDþݸJ(ªw —wú×áÍ”±=‰¥ÄŒ7úN€íüK•îš š;˜UµXækÒ˜„ȳ×wÚ‚El¼Qy@qÔ‰3‚)FdPj96î“Q®£"Z«nî:…l²Q¦Å~æ®ó:á"ÕïÁk±§Ë¤ê¾ ý$”ù=†7ì­ˆ/~Ý”kLw MÙòçmI G/ÆxNI-gÅ}aÏÇñ8™¸©/Ž7½ËžÞÂ%l X-Ó3Ç›Ä`éT¸äè gmÊMÔP‡=˜ýC«bv†úér®w« ¡r£gM|nJ92GÎøÍËÒ®Ðì8Xräå>…UVÎi€ÃÌ.sã#üFÇÇB8‹}^¥¢rñ‚O c :Ðù¤"œlùÒ4~Òüw ™~ZWÅÙBÚˈaq#mÇ”Ïn#èòwêÜ}Smñð×E¸Üqü¼ÀÄÜ=€Ó:¿¡Yþ’ù7z™Ö¤•áŽRÇ.¬H­ùÿ*˜*û;á“°CuY]X*¬Áëo†&"†áðÓý³vµÅ5¥»+C³°X‘meâ¬v4(Q¿†ù0­S«¸Å¿ÛÚáŒ^PS4•Ÿ”ä!áÓYqHúy|Èî–aý£(Wjÿù¹ïŽ»&}5ÿœ±…€-ä¹øíc¨“rÌ-݉‡AæÔ±CK2ßtÄ9Û ¡ð»FŽYqÓ}„O…dB[¨áÄ\ô8)<™Õ\È Qfà}ú²*?öÝ“s”C˜ãÉ KŠOé1^­± Çž; ¨ªÙdcßÜk‹éRPb¶Áèº%€†ß³3ÄtV´%ì¹Þv+°`dpšM–L p`y¬™3ÊMÏàiAú¿:Å 3:âÇ`çµüɆŸü¿FÝ¡UEæÍ ÑqDªn¥ôŽÄq|šq"½†¼Ûœ"7±Ž•iÒ…AÏѲº?ÎWÕ’¡)¨M::å4>Ç¥ó¬*ó¼™Ò53}ó@„Ä•þݾwÿãµ eE9M§S:Ö‚ [ûÍ]î{Ó=qÒÔâZß#…U¥´áŽ0XüÇex«d«{„‚œ‹S3ýG 4A–JnßlZ€å;–} ç4¦¼Ôs±9$ŽýÓ_ïéÈúsS® O1°º½ßLœó eH\çÓÖ:sË[ÓôHr‰Æ%òÏ‘ø~§3 ¨Ý²Ë‹‹¢9„1PåãºCèaÓG]Âõ1Ñ8lÕŠ6ö”;7ŽâuN ÷ÅÝ9i_؃óΡãÂó– ‰û{2¦Y/ÂÏ…"›¼ ²5&xGƒeæ¬ün|¹¬[PcžsE7ƒþl@lô- \_@B>ÉR´tlú^Q3(‚oR/úÆ£òÿ,$\%‚zÜyÝWmùlK¦?ªVŽ!¡+ôRŹa{ê‹ôŒ*%Ï öõºTÅÌxQ}«ñ¿[“ìV28ݯñËo(`Øê”vhþ)9/ä!%Ôçùû¶yÃ{749ýP™êe%Ì¿[±ëG‰G…á«?auLÕœ-‡£3Äþ£ºh>Ì‘.ˆ<±ßG4YÀ·Gë˜Z‘Qœ¯¬e3.ÿYU Ú+QWðü³kчñw÷›˜‰ïFf÷ìënÆÑ`n|Âó(y0½8TÔ¯,ô\Ð ®¹I/•uý)ߊçEX5„Vó!éR#º'#fr*o%"­¹~ÿ&}«kÀ¦§âô;tU Þ¶¢ï:•0½º¸|£ee_Jõ$ÿÔL'•k ô¶D#3`#SÛYSCÕ·Ô¶†Î7Djæ­ÙýJ¤æì†fìaÞàn¥„±(¾ÓÚ€êæ€騶t¡ÓAà{0ñ[ô 1;lb¦òùkÇoÍ5”¹@ø¯# ÇIz9«ä,ʼnýX±ü7Ç_ÌòŸ\s!m“\Ic-TÉEÖî“EŒ¢ RZb뽯 d•žÅ3–r /©§åÓKÆ[Ž„(ª6Ü¦æ ¤¼Ë…†ìÔyw†_Ô²-š€`°Öz¿Õ>¢Ò+,+[Ä¥j-GhÌõ¤È^\6po¨@Üãœ+àL4®’_à>Ç—5ŸÀeôl™wåVî©D¦é×~Î@ üÔ/gÓÝo#Œ˜†Ô¨Œð ~XãÙ¶åk!¤S–Úé> ¢Év—ùN/ñ¢ˆÒoò1GkââBâoþ4Âc™5Æ›‡š 4§>rrfÕŒä[lLר+dΓÉçkÐyt€ëþñE.Šúžr—ˆ]&Fu¦]ÃÈE#Oyä «›±ö‰O\7EAÛFÊ(4ed,úüÌÁM“×°Äò_ØqéxÈã2Mùð‰à¬'ÈLtÊ7(ê¾§akå ¦WxÇÃ2¨Ò“Ô¼ ”·)z¤sSÄo¢öcÃ9>*cÕ7AwÇFç©™Î9PqÎ^çQÚ©…ü!xô°ccÏW£1Þ¿ ÎÕŸd2$áC@,õ­o³¾ðd,åàbÀ¹·;{òvØS¯,^Ë1îÞ&=èj3‡%ã~}ס¸SUàÑhøn>øÆäŸäHôèsÑ”ZõV( Û„›ÓV/?íaæôBÄ`V{bÎBÖ0 ‰àIpwfŠ"SÀ¿=σ¢5uL_øs–5ÀÕÙ+Ý]~Aè 9wdF-Ë&³Àq9ö”Ü霯]œn§ƒˆÿ{K¸{C„VO"ØÖWåì±áÚ±”ír³+`À.åd×H`h»üKø4*§“¾ª+Å9>—NòÊå?KÍ//ÔõeUþ"4H¨ÓÌ÷ˆ‡s0`޵ÞÀQîwTOÁjÏO‘®«r².rÔ¯¬…Ë<=Íìž¼ž¼Þ“±40€éFbñÜð¼ÔÛe`¢”(¬€Ó9þÄihŽNUà˜O³Z §®ÎpÌ"ÌØ›&Âb˜3ä_®6½u8Û*­ f©±‹^F:^.âs"Ù5¼ÞéÛólgM€Ánë—“™~ºmãÆ(ÈmëM(ˆpPb‚c¼Ü–»ì#¹Ûåîì”4ÈñÏZì ‘Wï©ô$÷Èr›Ø'¶:Ø&ê¿U ߥþƒ*zhÏMÛôŒ:?¸rûKt•…'_ß=iD|Ô¨‘ ¹u~vä…GciLüß·~óymî5«Úò¸Fñ¿ímò_¼j]è¶õ~Ij‚ßïÎ`“9;f1ûJ½QöØ“…Å«xÂÄ ç'‡³Ïûð÷Û3Í7o0ÊP'˜Ô¥ô Å{BY(ÿª¤—r@¿ƒ8›å€L*ï Áëy—oßNÈ>ý©¢Ó~ï÷ sËáO«?ˆê‡8û7EÒŒmSü œj,±ÀúoízœÙX?l¬Œf¹Žp¹Z†>\­À†6Å'³ú½AÙ¢¶!“Ƭ<µ?BÔ[÷J~ ©õ‰¦>‚ÀLj6D“ûô¢Ð5ÿšGJ&œKÆñµãJ(qöç©{JñTù›C7?E©Ñå· ¶P¯Ê©CcA+Ü(òÛwŒ¼rL¾2,™·ãqÐ Å„Kð&¶üX`G÷úLiŸ1«Z+ê,Œ³¢Ž¿)Hc8_äUë[{]`{qz "ȲꉭK¤«ZÍxžòu‚Æ[HK€Ã4ÅjôŽNŒ\”;ûÌ•xæá5Åd_"Êã³É¶Å+~(V¬-†ý7©Z÷äÚ€;öövø¸ø3³¾··ôXÏÐn£úˆ U&=ï/‘þuþ6äÖñ)˜û)˜µ‘éDów±{äY:²)4–‰h“Öƒ®Ð€·81A)ujkóNoüÙË÷„–ëJA…SÌ*×½Âi?­êˆÞß2ÖNôæÝaÕŽ¬L!ŸG”Hµ‘ÓÖ»ú9–3ËÚ²ùÛêÛ±¾œkKø]À̓GÌðSkûúlœ§Ä\Ý™>n§_nÍÑcü6´:qÍUx­Ìîb÷”ûq#ä!²¬Åô0‡^¯áÐ*w *·ŒdÊuLCÏ¥tŽZ¥ä?µ ŸsR+¹‰[Zù9ìû˜eÙÀr¿EðìòÏ„S« tµîÆÃÕfGOÝÁ$IFŠÎnµcl†5ÍÀ5,½r £&†þ–fQ D#,c\t±¥™UÓ6‘5¢ÃLjÖ4² &ßõR*â< zEb&ì´ÄJ ¸ÖéÔž3óŽ âK!fˆó¼Í¡Ó³Ë/„ˆMû¥TÁß¼qîcŸeRÐ(‚[;‰Mœ rß<%÷̈X0º‰Øý²úRè¨véOèÓ¨BŽð~+K@Ö¤ñ°R¥=XÞ³£Z"VÂÜ)ý1Þ—† 3 ŸzQ¼ØLšâ¥ÎÅê¥FÔÜûˆüÃvI²Ô¡õ„ø‡¦­ûR43×KóVJ©Š+°îÅ=“Ê`Dò…àŒ#ð+à}•øUz0Þ…_¥Ë-ª[ •nòùœå <ì!åÚÿiDÿ„Õÿ4aoèЫØ{Ä;L±¢ 'Ò8Õ  =?îðî%?;ñ¹ÐÊ úÎÔP¡à,÷«¡“k@Ð!{ñ/S®žgzs=³`*ÅZ«QŠ¿–ßÊ\ôÊÿÓ%o$¡q­ü|ÃA;5 ìqœÎ1y–¸¬Œ í0LPÄ>L×ÐNˆ†u~Zhv|Ô¹)šƒ ña6k"öoˆ½’y: ždOüÒ²Ü@Ô­I ÷”ÒÝ!iˆ^ÒR= ‰1¤hž„ DËã®”ö,Ýèä5“o)Ö² WèpP2Â_®£vïèù´wtŽB– Ÿ~螊mÍ TÕû<DMó‹SIšPLº,ÃËàÿánˆ=íQÅÏFø4±‚N¸1Ì3ên1_‹?ÜÓŒ[À´oî?UŒŸ¢!{ß‘Á¶%ì]¿@Œ>õŒ£¼ÞÑ3OÏÃUö‰?xXȯÞqè€\víAn$7úÃò£hΤ x›ÞÞT÷‚œç£ï÷*‹ûμÐ\kà l„ouÇ œ­(S&4 ;KX[]GÆ ñöéÔ6˜ÏÈáÆ ½=Ç·ÕX1sw Zìý·|š(ä>q‡Ue±eB- m´ñîc²c¾âÃÕ”$°($I±üVBÃ%ú†M€Õ‹k°6çêx¬ã*¾¿-žÃ‚”Vd§R,»^üL™ Ï~êA¹?×…¡#¨Ðnë¬úÊclJ5_Óº‰Æ³Ñ终s®ÔƒO Ø*ök}žI2˜ë¼ïÿ>˜ÍÈÊØ$‰zÛ…ºR‘ºÄ¬<°qzèÐà¬ü›{ú{œª gVüß’îÐ=¿Ü#²qzS½”¢R«ñ0üëÁMÝ©@¿ÅÉV¯Di °,˽~ªòÂ~–6eT!ç 0zOg÷•²gYê9#^u;£yÀ†7©ý]+5îÁ;è~ü¸RAk¹$S­MšîE8j¿"°¹ ï]ÌÖ §#…g°™¨ÝÚrK“šµW~ÙÓðâbY½TP¡Ó¡Þ&’“ˆºÞéÿûº/|†KCßÀ‰Õ~\àQCNm@M_7?a#ôsTƒê( T%uÏGf˜C/w½Ô–öøòíDì´-f·.;ꮯª[çÐKÞÌSmK¯6V ¯!ê™ç.îÝ•âó|w Ù¥PHDpç°(WlEèzæöv+ðnl’ ú‚oäåé“g‘ÐÅÍëðèðm6ù_u @“W“ä2Ðc¬ñÒO{±M"6·BÕHÌÄÔïù’¤Þ–1ñCJÁR|Ú¢çj¹µ›AJµ i•N:¦ OýCc6T€)ȧîXþ¿w º¢KD&KÍ–;2ðÈ“b¨“ãäU€ØØ!õå)ÂŒr[ÆZÑÕûcÄæk)Ò+m¼èùhç—%ž‰ø•õ÷³¦év¼ÛŽ,ƒoÙ(ÚáB ‡΀—~ûÛCx¾D‰±œV†'íIΗEæ‚3VaŽ=/º Ù¤¹Âð®zçL¬ŒQ2è‘2¹ µã-¤~ˆ¶SeŠ÷3Èó…!Â{è‰Î›ž§ÝÛ›KÓäùdºðåνq³y\FM.¢«âýxôÞ #¯’e›&Ö|j n‰ò 9È˨îá×NnÊZ/ÁBÝÍÅ_!»eÁJs»x~¶(*q!1zÒ^Šþ[Ǫk{É%…ù¹×C滈Þ:ïÛíû¬8sP§à23/–˜¥Ñ£Ž?‘xšËŠ“­ÎË·OÃjNÈÃÈš&+PÓà¾<ÇÆ]ß³©'‡2² Å¡Â|J.ø¬s°¾Œ¬ ¾l8Yއ~ÚB·¥µÀòTªKr£¥=ÚPOÐCÔAÁ“5T²ˆ›räOhÅcs u§ñ™{:Ó(¤¨d-¿Ø>Ê‹ãˆIQq¨I“h Ð¥c=¨C*Ï¿ÂataŽR)R¶]Zó­Å<©2t˜½Ê´sî\xƽ ¨aÄ·Ý<–Ü gÍ–ë5ê×ÞE&èÌXg•§Åoi£=ÀP¶9“ùXÍ\Ìruš/Ëd yàÏŠ­˜ïÎÒ“‰†]+šËH ƒ\Uÿè|wˆ*ñmpd’¼Wø\ÒÆ˜£ûä`–kšÿiYR 8ßÿ{vuÀE¨œCDŽÚß?1ú{•"ST£yq"Æ{iRÓ¢cdx2Ñ5½`-Yy^ÀÓF&YŸíÓŒ°ïR‘µä"èÁ•ípñ×FË5¥N9¤[ê˜y‹­p/fðÄ?)¥Gà…ÖËEc‘(>5™ößðu”mœ¯z—qs€]O·ñ¬_'­6¦`êC»ÕOy‰W=¤d3(‰ö+Þ0h®ž’6¿…aó|÷}(ªÍ”5´åÜÎëYÎëYp<™RÖÃa·*ÁÑ0–µèFíÚâx¬ÂÛ_h"1rU­W\ü+£*zZrw{VÒ§ ‡âS.dB†î*KÀ{®Ëãßs]à¨aoFÞ4Ä FÞBÆy×ײY†0•èåd:"üS6ð(®áI]æAÂÝÇná^3 ëÞ^ï´å/Ôþˆ›®?êÈB£Á§büFÞeB:³¹1݈bÖv(US‰Œ´ýñûÓÊxäÌãÕÅ5å€ä!]‘©ºëtýná|=4`ýðj–—lX%òQÜ÷õO’ ñ)šý³ýφèkSð`^ç„ÌE%YŶWe$âûÔÖ¾´y_Î8‘‡®8>Ýeªûì¤~Ðü¤Wå ºMç“Ðßþõþ× õ¯·ä 9j¬Ëd£ó[dÙ¹ÛDß!Û7cåš)1QÝ5Ÿëа«Üg1Ë Ç>ØžÕ|‰'¼Çu ‹f+؜Ģo£¤Ìª>yC"æØ«¬™h|gÀ9^žNkx y)r~©¡Ó `d8ùðvZiZLÆ„MǶ-1ôõ䋲Kg1ó/8Ù²ñ·!9\ ’jèyìù»mb.“ɶû£Ç z²Ýˆo̼ù…aÎÓìï¦þv»¹¶!WgžvdÓäã‰6+¿³!]ÓÈÁ ×ç´UˆW_.—îtD¢•äÇýr,dY-ÌÉWª÷\‚ÎYŸ~Älé·ñV”7+ýÍ’íî;ËíP5R5¯ø1å.›'ŽMŽÍMß•`7Ä3ø‰ÎQIR˜ ŸÞs~e÷ ïðå ˆfCëò m#qðgå­¿òkwu(tìHšG¬ê+êÌãP¼CIŠG[ Œ/àéˆ?Æõð]Õ‚3!×¢hSP£Åy£bJ•Ôdé£Ö7 ì²Ë¬ïC‹bã.8¿,sx-Ë¿=P?yˆŒq‡ñ/pi}Y+Ð^å/ìËU®á[È–¿²NpêÀÎ_‘Û§9WRšKcPE®L›b!!tƒ8SQ%‘¨LÀ‘*Hؘƒ5Ø KíFýwÛêÐtú6Sì}*µ›§»±}Qþf;i‘-]è•f>ë¹À´< ¡oš²0ÜÚÜ*8XÄ4nö޼“"AüóZ„›9¾_ãÔ•°Ì……·Ö®{ß’ñŽãújÒªûÕèD%–ŠWÙMÈy#v™rã–¸û¢¸whói÷Uf±râÂyš¯ã}Öý(—ìD 2‘ʘ‘Ž‘¼Ïp=÷œ¤sPœ$oHÿ¤Q{ŒâF³´âD"j|`Õr©gwz˜[ª·¨fºÙ¿D,ÌäNÀÅ0-SŒ"Yâ.\™Õ„6TRR>V«£Fø»oðl¤~'ÑDù·¿ôz¶½XuD^Î"µaþɶ´ÜI‡Õ".ª™!¬gß´Ér ‡tŠZ zƒ&+újþ`þn_ÕG7=ë´D×Àæk'thX`¢Iþv=‘s©Ü­£,;ç M šâ†Å¢®4Õhu ½¾æôr†V@VüžÔ­¬%5môŽG šµü4gð`„0˜Sä½ûÅýn±ìRë¦Öè£0n¾ÜgPrÿÍ’\[ãߘ €Å ‹AÛ'­O›|ê¬Zp\èÝYE2äÎÌΙmióø¥6 ÄÖ>‹ÿrÓ¼áyR}})Aà´„¾ZTµÉޙͦe:†ÕrÝE„â5µ÷ ×R¥0øÚ7ßyÑ_K÷¿#\ŠBš? l/ð‘hO®Bõ]vYžx1G°‡ÎO ^Ae²¤ìܸ_URI[=åÊj©,]Ÿæ<}•]`lBOÒ/5Mú<ã~U$Xqp3×U^qí¤¶y=mA†?LЕ3Hï·«ÓƒmpØòÈ¢#]ô0S6sTÄâ¶qõ­±¸vjjP/òæÏEé0éÕj®2?ÖNQëáDé.2þé(÷wÓ½YàÓ-(hC¬òyqX²œ,}›Ýzþ ½AqÞ€À¦@c:tXÔ²G‰€h×{{†J÷Cð"ªÚe=ò(–‘‚n[4ÿˆëÑåUÐl(Ž KGÜ5« ¦#B dÓÓxs0m‰±÷øóÏdç{.ð˜*S·Ã¶œVöÐ&öaëWŸæ0T0I`ÖyÅ_˜ø<Döæ(Z±²rà#g—ô¸^i¿˜„ÖW•‘ƒ—·}·!8»Á”Ë.Ê Ûò ¶pGKÆ­~¯–³=kK%²ßÕ­«ËйV¿ ¾VÆXc³¡Ù(9þ4ÄD¸ (%s„F1¥S!én´nør"©Ì‚{[môSÏÄûÌM!š`†k½j?~]"d„È[Œn«¶p”:…+ðKÃBž2´•uEò³toápÑ5ÃWa‚K$¹sŸj¼¥CÀƒÀÍ®©Ì¬€çÚ&a§‚ÁÈ5F89Á6¦’ÎëÔ0¹7ø8‚bG±ZUƒ~dá\;æ‹‹©Äxì¬A~˜¯ª!¤,‹Úå¡b[žh¥Ô /ê.£ýZº‰W„º¤´pŽýÔެ—î3ü¤Še'„OПÜ·c#UЇ–ßõ¶ˆ$l¬I–¡ÚˆÈŽø‰©oV‘0Л§Š©™F‚HWgº¥'GnTl-‹n+3+ÀN– ¦ Hݯø™S̼Ðù) `ÏåníêhF٣Ѷïͪ‚Su„ÒAÉ5M”(Ù*û+áV9¶vØEgÚ”Œ ^½2–MÓ’ZñTÈZ@«÷kagh~Rj¦·ñY…Vë„Tg•ÞåZ¼C²—ó áóÛ?”£™¡Â׺ö<ˆI“·¨¯×ò.ƲèzWñ§v2­J(¼l-Ø Ë껕9¤ÔF¿ý¬×õ™¹o4A§j!êôóžØåË"оAŠ®]–íÅ%©‚9¨Æ¸Ø™¨±_Øs hÓ”d’SìyäPQ÷Gôý.¬qoì »>jÅÚè?•N%s[MÄWè´rÉRÃO<å¾öXÖï's„j~'tµ 2¸/1yA¿ËÕž™¹Vgu‡æ¾µ$:ݺërÂ`rŠÇüÉUJÒØÂöÍá›-L­[*€TÑéDÜ»òiÑz¡Þ–Ëa«cA;óðzN­·z¶'uŸ– ]B!Ç`ƒ0Qy>núhrw¶7º6Sq/žº\´£…ÍŸ¶}­Ÿ¼"c2Ÿow¡[#+l,÷G DŒfZ§< ^'î>ŒoU(u`u’<lÝ,ùûq ¦¯²›æ<4Ô­˜HÚèCx*׎²AÀÎ’ò{x$²áäý 3rlgDµ´äÜö‘–QHW|SDQZ›‰ÿ–‡òÌšÛB|²$3‡ Ë̶ |Zð…d뢢‰—7«¹\„潂z*ùÿgëùPÀK+ÈÔ"žšqh.gw]Êü™ü·(†”âÂKX"–Ô•ßà5ØH»ó¿žgþëæB Ð[ _ÀUï?NœN gWŽr&æ:…¹nÐìBʧ'‚T¡·¶pÞ=|è`\ö«\±¾§ƒÇj ‚ÇOLc§R(nW 4€¶ì‡RŒ‹æôs¯¶µò›xâ´—Œ¤”û¿¦{Î,ÑùC»­ÁÎVE!Ù› ô ÷p#úÞúAävmLyô¥ãCW²æã?4X¡¶Á•ôm¯å¬EyÝ%K^ÖìRøÚ– 0`¯¤øÒIïUº›ƒíÂ7›Ï›F‚oÿDY*VG£%½a·yõÑ»HâÛ7aN¡†¥î€RÜ—+†x„Ý÷o£Kjï—¿}Þ j9C6àm–ç¼×` ­u.²Ê-«,Pèän<ÂX±…Üð“Æ€º|~Ö›å:Là?AfÍÈ£nt†²CàkñÚ©,>G‡Ç{iuså8Æáåãô­$À…âõ²²ùœÞ!Oö>kɸaˆÓí¢û˜QÄo’1G»ò Îu*Mòm”Å/F¨a"¯W±ÓŒ´¹þ*Æ…ø@x b¼Us³7iXÜ÷sS£"ú«YnEÚáŽSôcNSä/Ð…Š!#¬Ïß!ŒÎÔŽêÆ¥,a¹NDV"¢-L`ª«°þ`º{­i‚ë¦c\ðëœ~ #†°ÝÆaSŠšåßC-"µAøt¢8ÿ*Áu® y¨ò`¸øLcbî0‹L6uíå[›+O™‡g·¡ LÀ˜ °.Fâ»­Þk3jÅH#éëÄë[¬G§7´õ¥ûa2´+°¶RbF´-.åµéš'Nц{Saó…»%爴—ž… G1 Sîø#ÔýN@Zù,eøÚ¯Kw_»ò=89s»‹S¹èYuDGÊŸÄ kd8LgôYèùÛ¼µ’ª1ûK28 iåù¿‰Ú)s17íYu©jÝBæ«þç”X €Á@4òjp*ѯ¬nô Ù(­8U¸ÕBá´š…ÚJ¼³Ó%Ùø=s1­"œ»L¼n=–L@&Ä£öŒÈh§5Ù'c7OØ2>1KëqCRÖ¶e× ’ƒL±&ãÖ “üýÑ7›)Nˆ7’äu­·O€©˜âf” 4½7—ž×^„­tŒ2‘° ‰ç%£Žù¨ÿ 31¡[Yeè˜ÔÖ ”åø&·Öˆn®•QšõDÇ­{7 |FJ«ÂÄIkÜtÐèE§Ì\¥9Ôê ­mÛUñÜ4¦îrmŽHάz˦'˜×bPù?¦ÂKñ‹þ0ëa䟥53Ê’„4¤ý>%å6BW–Â*)ÓJ]ôK€wà à•Å`JàMS=štSð‘ëK@¡äÎÕ4Ì»妧‘Þ°´?ËöC'˜ˆG8^ó•!6OO…ÆË`»V~Z×D m¶XÌç9µ6 akG¶JR)½uE‚l®}'Öbí5¬³ôlV€ó¡§P8ƒ{µ!f£«*ÍxÖu[OÚAŸÁ¸¨ù7_ÿßôÈÀ\´gweõxù·DcT»ýC»¤¸”þ”Ó»=ütžü'w…üïT“UÞç´×.¤ iÕ„PRTpÈζ´8Ÿ,ÀI^ F+ä%;Õÿ®Xrn@ú0߃É+1“6ÆL5rµÅ}Dº¹D6ág]?’(£¯‘«n[+î??¶ßzkÉ‘N§ÈŸQ;’ÕÒèöï·’š=QŽ£ë­JêÜšŠ2Ö/Þá|n#N½{„Œ³òUEgà ºÛþq¤<Ò^”R ±GáSjµÅDé,” †9È‘ cÐâlÆlë=IP8òqÕVRÔäH&ã‚<ºîMÄv&j‹iJøaiã­Ë?âôB¬+ e_ušÄ×2€Ž@•3}BZI“‹¤ÒÂEÅÅék÷µµ³®´èáðo?Â"$Išo%wlÕ1©j+«µO( .„ÿ\wÜJŸ¤×¥ÃÈyÕ“ ÛÀGU„­(¬ëç–åEé}¬Žpt˜ËËɧ?¹——We–¶ýÇÁ·\T;ÃÂ>ÒØ »ºlÅwâIÊ<Õ©Ôa‚C0‘Òš[,{h Ù`ÝÆ+§žM]‹<ýî°Þ¥YŠå†7-‘+_.©ŠVޤ¬ìp~®™Â"Mu¢¶ÄC²«ÊU“Ë”k‚λ£úèxU–´KÂdXa©UÄAV˜x®í— òÛBÜ;ÅÍRƒl¼²˜· .Pù³Ôºa p0ëhtåôø£?ݼ)앵²Y®`ÂqÃã¨J§ ·gEÊË=ûèÇL&ÂW ×{\¹áÌÛ(½/'õÀ•aKw xgí;`}ÔŠ¹ŠöøhG:(-I$Íᕵ9ÛÊøfg¨}Nº'o÷æ|39 n¹ôý°$ÁZ÷©7Êãy~’*ú”½áN:©íw4j†¬ DjÓeHX‡=˜"–7èõ…í3ì%€ðõ `—†c[l_DMý½Q iÊP³ E8e~»@#G2ö™ç4-Ø­%üƒjS8äo¿îê7¯jåsQe¥0Ç9ˆ=Uæ~ù-Õ¾ö_ -=äæÿ·`3ýÂÔ(äúª©íDEûQ«ýy¸œœÀÊìx”P0SP1™\ÎI³6T½fa­ZœæÄ­ý†YØ=¼œ ®n—/©Ò‡³%p¼~A àFÐ  d:Q¯YÀŒHP·é6tô¼\»¬Õ‚ᦤl-K½A3ç² ’V+Xî)EÍEõŠ éË«>n—p7´{Ø%KäwK}Ú0ñg‰ï&Éò;Z«:GÔ¥ 8™Ì}Úd”öß°T²‘Ó…T/Ï.ÈÉD¿4xníÖÞúTÇö(so§ÅˆZ<%–ßoVMëškGGn›Ó¼b0#yÝÉÈ~d×ŸÑ pi?Ù¶´E«ÖçI¤)(,›ß»åmÝÏ ZŸ½ÍûSì½CxÿTZÖÃÚ9  M ŽÒù°°åìAë@“Æ,¨òâ0¡úTŽ„}³"6,ÁЧùˆýÞ¡ê)·)a–ìcÃŒ}{`ü3gžE'úwè&ô0õÈZclÎV{ø÷¬ùn®R•vþûêx+Ù¡*eEKé¸&H`:y‹Gàl3Ô¶gÖÁ+ä;~¦†3þ1}·Öñ²³#äØŒ¾ÈKÓ~t”6UhJ%]oï,Ü…Ü.g«YWï6—}¾c¹`”€° å—eË|[„‚GÌsžAÁœ Ôts ”?±ïwezÇd~ç{s§üÃXòÇ^a¼ÀÎÕ˜ ×[YK¼i¾öŽªéxÜãlÞVú§½Ó~;5WÁqljeg$*Ò³šÀË1ƒ `⬿ˆ«Dˆš ?’&¼­´o´âO{Â:ãcÄÛ"Ñ9ìxÐøµô[MK¼Ôk´Îu¶X˜™:/%`ôJMqˆ;^ÄzJ¿ŽQÚæPI esôú‚}{ äµëážYZœhùíÂí+ëPcø)Ÿ‰ý(Ùʺî Ïϲk‹Èù<Œ{–“¸ kÝm~H6¢϶+zm«3·uoÏq0ãÅ#ö€Òú–S††Ûƒ:gÊ”8«OW\/ÔöoÊ)Èû½NÖšö}æG„–Ì îé›Ë¡OSkûžc&. ÌÚj•=N°‘ ªÈÞ¾rD#6mx÷8×¾XâX¯ÁÔ&<íÚ¬5°g‹6(‘ ­_ŸT<›ظvü­iŒ±­ÎÍ7§#vSŠwþ]Ë%¡Win±Lh|ëý•ò Ø÷õ­Nõ0¹â¬¸Cx´šû $uÜXesâMJY:DEQùOø; ãU¹“Ïv:8¨Ëƒ¤“í¬’FHîÁH{¬ý¸Àòš§˜ü/pü7!<Ú(&4#ûôknf„‰œžÇDsæ„9-ó¬ üŽ¡ÛZ–‘ˆÔÚâ®j~0¤h\ÔöŠ ƒÜD•áñìm¥Ìy³fQ[„5hfXODZ„Aj‡Ú:'@Ô÷2`&,ºc+"£êøÀß@.ËœfÍ3–8—Œà}wú’ü¡Z¡ŒÇ í™à$LÃúöxjwÕ³~]Á,x¡®†PÿzfÒF‡Ï/¸çðÉHmΊùµ° HBó;i²üÊvcÂýEkÉ­š¥–àýÄÏÉùTÇ!ÿØ€lÕÙ–ü:=žìKÊÐÃs›r~ËC "“þÀñº3UöT>1›4õT8Â#}:cϧÁY¼P*ù­vÊ\U‹½«þìÕŒ'q60CÐÆæ%sÎQ²5ù~´jë2Ðö×ܾi9¦O§o“¯hM½«Q¾*z”4¸c¸¯jþ âN&úOÕ¾jpjèl¢ !";óŒz]Ï·TiR§ #™ßð›Ä׿×LÑ”\×fÚ˜T.í3ÕèÇçøY••m.pËÿh‚C˜„­¡ùñmtõ%$ÌvrúÐ1 ‚懪"Š™ÖÚAk¢jq(»i ^cãì’ˆ,Izç¡®dd†ÿô|BméÈKà?Õb^•/­£Ó·® €™ý&œ"!µèªb Ç&+õµÑ†˜áÜr³pQsHë=üï/§˜ÂÇ7AüKx©áV䯯!U¯ÝFÃd¨çü9HHˆ[¨°¸%^L?øs*IÆ”ÒÒÁí? ‡$‘ÓØ>Z3ï™ÓLBYBŒL냲›ÊÍà«%q)ÂDÃoûx+-"^Æ;]À&eŒ ¦±Ö'ÏwHQ€‚(ͪôfQ…íNFbqRP€YÛÅp¥¶ ÓZmV°Üà_]M$\°–ðÝfÙóذʇn¢øC §ƒ«‚<þÅØ× ·‡šàøöZN—…†{>+2%Pä¨ý ëqôð=ÿ‚,4õݡ黶¡i#¤å‹öÓH‰(?i*8½¤–;Ì­>Üs4‰¾´Lh„œ"Þ&U /–÷±ü§*5™ï°PØAºyµ©?ûQųW×þ¿sb7ôÇ>Õ 5#â*I€Ä±ù¦_u¸þjvŠú2?3›É&r/âjUï\úç]‡¡B*>L—E-°¹íïbKÔßåï*7 7¾ÐJ ªÅV ™º*ñq4ß3Ö»çe™Ìê³ß?fµøŸ·· N²÷£¦¶øÅj¡{ÐüöåÒ‘¯éDd–8õo… O”^.Âím;’‹šuoÊ)’ý ÃÿmVéÊ6Ko‹î+GÚ[xÜ›øSF=v‘sãA:; Fù é2‹´‚Cò^Cœ¼8©®ÖÅï=¹‰k!.Ê/&*±Óôý—xS9°‰˜ž_·Ø>o-)„ I¼FÇapµÇºÉi$ȳáÆ6éb½DF}®,}¡qúÅÐs0»ðš‚/o·Ã}KÚ¶"šûݘUÌßÊR+>z÷ÄvýqXo•;§‚]á_ó}Z‚ãô|Ü–´âÚusÜÖîjä5Æå%ÎÍÉ\C;›'Œªëm4L¥YÑc®n÷f C xm‹TÑ?^å㨙/ÄùôÛ«dô“æŠécÛÁWTôŸ~Öm¬¨ˆØÁñ;=ýÚ/i +J·fY5÷¦Q}ræežeØ+xŠãu5ô2M¶pc``²S#7P‡¦Á® JLÕ$ ¤Ó]ñ¤6Üùg3@‰Ò¥¾Äøòç_§66==½7F5>‚ W?;¢ì“KF†HÑÿöd3!´Æ„˼]pG _j+OµÊ¹K×Cn)Ô>Üd‘ÛÌ/¬=ÈDbFÐ¥ËLY~M=ŒÈ´f¬E×2‘¼ÚHþ€ù°­œ43Ì€¸U’%~CÀî2HÐÑš˜Gg4’ÕeGDúˆ,»±·0”ž¥?Y&R*œûÿ¯Iõ½(÷Ä÷]Bþä6n1´Á6‚JGdÓ%9¡©]—Qd—]«§i½ñ¯N£)u˜ßÊùQÉ%„é8ÂÊ"š¸3ÀÀâòJEöïùa9R÷2ó0•KßQS‡ÉQ¦ô+Ír.cå.;:™€ Iæè™'²Ú9û<¶÷–ù…FÑû$ƒ»hº“õ²KÝ«ᘅ°ÈÑHå‡Ìçú>Üùgæ\[Ц¾W ï²’‘Ö¶^CÎxk]z¾þ;|Пgém(¬‹ÇycIW-3&ÕºäÕ¸ž§óÅ­TO°«îh-0œ_I/N1ì±±«|Y¡ÿX ½ý_ Ò´Èþ++eS7á7L«o@‹­*`ýw‡wCL«¯ƒòH.Àypr#ÉÒ­Pa–hþâ̤Ö*7¨2¸xzr‡–^IMÉšõoJ·Õ,<‹Zpm¨ðUªÀ8vô€º#Çüi°R“]âäSÂHkd&¢->eµøÐùxx zeŽ.WÄp–ßÐX¦O>>ÄAPC9ÝØ–ü,rÐü|,“åØvµ‹ÆÚ­qÆ{ßR¬3ZLÐQ®…"¡¹’Ê«ÐÆ¦óךÂöšÓlâ†ÏºÝj©p !ÌN­Ï ­`uh)•ÜþÑ ÖVÞ3áý÷Uo·ðHV >ï54XÛSQOX6Î%1æ_2•}H\…A œt°‹G¿PÎ& az©fÍ;’þ¾”‚Uã{z&ÆÆ§‡x"iÎÎ:-b+éŒu[aqÚo´¿îôÜÝÑYÖ}ÖjT3`ßxõ²¯²Ø«3VsKÐûè!’S%f7[J$¥[Lú{¦­£«'¢·!imÑÿ†Ìš-QèÅOš eruÑ0ÙUа;QÆÌâ(L E×ú˜€&´ñT;Ëú|²âáCÉÄö°ò¬Ò&iâcµC¬–MqðwËÞn)£ê=ÎH–²½Ff¦LÜ—7Ÿ½JFiBM¥‡!Âå;›|0U¼ÐhÇ Óëž'^C2pþc‰ÄÁÞ™„1ßQõ8(|Û?õž?Ù—äADH’#ÉDŠ(Ç$¨…œ•ýHEH<Ññ?æïåô;`Hdk‹’¢Ôƒõ²`E}ô3w2Á—v~éC"Ä‚ …±pc‘uJ—Ú{+2|P(#ߣòçú—fžhд\19þ*Œ ŒÀv)w™ð‡ Ê´¥CüÁ‚N®˜Õ+[>Ó$I «i#ÔD5é‡4Ø[ì SÒD^Ò§Òç5ì¹Þ —’|,íŸÔ©x®¨}ZOî,ÿ_ù¼va-†uÍf…éQò¢öÞ+sø…ÔDê,æô1Ϩ¡’ìÖWØ[Â2¤$ÝÚ[æù'Wq%cx³ y1l ¼³×œ‡ŠÐg›b6+®XN ‹5/‚€Í K*‰òI5“Ì¥ˆ !ÿ ØÞ¸4Ÿöò¢ŸéB¾Š+'@µ¡Ö”±ú:SÂÕáÒ@‹?n¼*<­xx+x_§u  ­Æ½Û±4“)3)©·Z$ïmÝí1ðŠÙ¿—>¹íè¨9ø:‚K7‚Ùß'®K*_jä“z?½ë°œ´®NOzƒÓø›ù<÷Íãî…LeWí×Á‚n«PHâÄŒÔ΃1ê„éÿë¥i—¤ÉG~ûˆ|dTݨB¿cv1Àâµ{¾ÄÇ_ù°I@þ8þ…Wð 7M¯“"üvŒ§WÊ=€ñ0iÃð[ë}Ù è,„1gsªuWj ™ïVãLVf2°¢ÚTΉo³»{àɦÃóì‚E#´7~[%Á\ÒÚ¤ Ø“6üzãö9ì`…õ¶|E¸Z梳òb`ð!š·cíó'ï”jÖÜBÉrÙÏɳեIÚ|ÈBt¨ó®-Ê`)ÁÒ´K¯”È!~jÒÎJÓ×&¥n-'iÄCÅÒ%½Køõ§æZˆ`Ãó©úyÿe‰wñ`8rØ Cú­ògìwXŸ—x$º,^R2gÌtО‡UÞ©­mœ© 1 xºÖmÜðÚ<€Þ_ÇÃ=ÜîÝ\¦Ž!mÏ>˜6œµ¢@¯þp^O€/†bí26æÜ3êñ´l;À²ÜÞV‚õtážz²°† ¿£ÿOó8Úú6œ[+´5ÅÙAÙ´ðTz£j£®IéËhg’kÔð‰‹´Ä}.œ”<^HI¯%%­†v…Ü/L–佨/WÅVÍØxã•FLûtÆtºnîÍïùÿ’§— ÅœI9èμh×X;·:q¸”3&÷ò aw¦Ñ9^køÄï7"?”é¸?·‰›@ÙÕY¬íFüìrd5Jß&g|UÃßËXvƒ†–‡Zƒ°â€v3+´ÒuµL!+\(^:¸ÐUæ¿H¿~7O'§­Ëöë®*G…Œ8Wk%¤¦ÔnÊh²õPd«š |9Cû0lv…Ä×äÀµå˜‚øEIê›/úJdÚäÇN ¸yƒ)qÁg°2•öÖº›Dëk€Ø”kÜ^bŒÖq9©œ9 j†J}a¬Ó‡-õ…ÃÚža Þ‡·ÑŸ¶·Fa :¿D’¼N_þŒÁ™?§hÝ1˜$‰ÕçÛí|¿Á‹æîSzU®¡Yì†ahM4Q¾Šç¿*.ûXŠ.œÅʱ¸½ÅÿåÛËQs K_þ›£>®Ž)¤h(¡_ÔaÜò´M1°ò -9…"U?´’ç†jþP­™×ë0¨o"_¼{ä©=SLËxëÓÜJ€éΆ§ìÖøñûgnº0á¡’](4æ.þÅó†ss±A0–!}ý½Ïi Îâ…‹§ˆDÖö8;àªkþ½¼¨—ÊO}¾ÁX¦`Â\R‡úoQ[®d,ç.KmE“……Úð/hß$C𤾸rm@Rò­©¾þóPE5 H,žû£U~ú\mÖb5çFÔõ>T!”á Tè!â‹øìÍ_ ßÙ›9!=7„żÈh]¤g„vñz†O(F­ÖÄr4E­j¨}õŠ_e°âŸ;“ðc®f†ÞsSxsÍÛ«Év{+BíØégj´‹l<£‰hec5©žHO´ª³qTÑúÄÙAµÍ¡5³èÅòx ÃظO?ÅuÀÏ@©ôÑ¿îIÇÊý"÷fjR1¼(úE?¼ÌTÙ3bVS ùËa§Á»¤-NmÍщa—"˜U…Q}«KcFb‡Qž¨oš¬K¥¿·¦Ã)† ÐÙSÛ’lÉÁuΟnWït’ì=s\¦ºÖ˜Ò¢¬Êk­`.Tü® 2°{»6íë!ÊžŠÅCç ’[²a6„Ræ@𣊹ª°·‹vŽ&&— ÄÛ5¹ôt±¡Ô}*šÑ¾þýŒ';ˆŒüWºÖ>Ê!3ã.­ÄQ3š1B!±Sw£³³$Öj't‰sŸÿß“EØÎŠ/z‹:5Á}}\FL G÷84݆¼È#…èv¢Ã‘.ÕÚã€${fˆ5¸‹<ÿÅ®a³Øî™ÔŒÒ[ºÞ[XÈôº¬äfèÉïDÓâ%ÊMÜår¥°ÍÇG榦DR7tå<÷®dKž«ò%è'ZŽMèç¿ÌØâ·‘h?Ípôp ÞÕâ–:Ô*Ô¤á¾ùò, <¹ÎɃßüÐîNƒ(w¨ü²é¾Û†u@îäÇœ~óž>w鵪 p´-“3ïÕÿŠºgFH´ü Gx·²¿ÙùÄ<»W«Ë_QÓo¾Õß,¯ §Ç¥qŸÁ#“?Hà K¨²MF¸3¹A)Ëvv 6³Êùy—øÚ"Aü/N6õ¶ÅdbVPBãýü™ÖÈ·ñ£ˆSÂ2õže¬Ì¡›ò—1ÍÓ‚œc¸®9P‡Âê|ޏ.œ ó¥£!Ôö3+„Œnr‚Ó®~—À‡Œ}ZÔÍ–»Yºf}ð¡G2”MÎNø#[gÄãL ÀÎdÙÓr!œ5¾¹ð¦òÒÎ@äê³F¿˜7åó9 öGâôKZ콡6`´y» hí5²›fV:汬àg]…ìÌL¿”x0|dµ-,ÂÁw Û xl»¨@½í¶:ûP¨/{SÓ/ÅÅ@XY³Eø÷3t$É4Ý››b' ð`¼Í¢_wÓfŠR“4vÉyô¤­ëw£˜Ú~m±.¢a½°‚»ª'{?¡gBÍÙ?4på6¿‹1^P]sã3×£ææž®_lr×Ê“Vrë,º»ÜÞDìÓ i!?ñ{ØÀ ƒs5|ýñrÄ|;õµ¬_7:Q¬­å‘ãÚnõ¼wÅ¥!" n@¤ë=Õ÷2¯ÐÞ€ ¢a)tþÊ„Œ /%N³ùœ4PÁi?®Ï¹ .*çŠ$÷Ð.¯.¶P¶áô_:ýe%1Ðç/}u?Èïžò¢c YÑÖ,è=¢ºícª¦5(6Z¨ðÿ†êq³Æü¡ÜoE ¦kÿ¸"ÎU•yÈè©øGã™K@­0c>±KÔÊìáqö‰8JRŸlm~m MGØ,y‘ÌËå¡ô¸ÿ6ñ•KßQ2ˆ6j*f,Ü ¹=“¹î.R‘[€!I-•[΄¬óÑê´:ÌýŸìy1nZZK1Ù©䉀o …4nÜG!ˆfjs¶©\]˳ À÷8ÍŒË V]S<æNÌ)¢W&›q’•X™ $Âqx(ÕG9Íù§‚[IìVU`ƒ¸'%ž ñå.¼ë'A§[óhR¥Ì»ÝwWX*']  ÷aÀ3™ÀýiIT®€i:_‹4w‡ æv©ï8Á·$£ø…¡Bm=£$ûn]êXšDÂ$Bœ^Àø4~ù¿u(ð#$«{öœ³~ø?«pÑΞA\í:%ÈüU¥ÃÇ>ëwž4È6%¦Y¦ù2éãQMʃÃÒ6ÝAã‚`E™j‡V†×¨·Y—=³Iìq§ìZ´­„”ü Ç¡ÇCü,SsÉÝßqÛø§¾Üž ¥*þ&k䟤Ÿ˜‡ŽŒa6'ñƒ]„ž6~VçQFÀ@Ÿ;^{¡dPcxïaxÊÔ•Fç B¼L ,ä=x6µMqÃa9õ0Ç^°•+€<Ÿ»ŸpÚÍàL¸ÝõF@ÞíìUš1Fk%9‘xñô¾^¥ßg¶ÿTäùŒ‡‚5Žó£oŸ³?µl«ðÔA™1šXÉö,¹½‹èc•¾ÕCP OLáÛKÚùe+F/ýy‡4uÀ±]¬e 'ÊLÿÅm¢àhööz¶*qH4âèÑ~¾x08“Ðt\O‡QZ7÷¸ñÐ7ñäѸãMã˜L³f ÒÏþšwÏqò»™V{0÷æÈþ6!Ärͺ™÷ AÀƒ¸e8ô‡»âA²8‚z[xâùL–«Ç¨6¹!´|@“.ýÉNM*ŽMØh˜€,AººÜú*d*Oe‹ÚÌ+xYœVÇ¡…ÉÃdÙ¶/[%o+*_B´ò¼P\€”ú,ÕV´žñÕîƒÙ«Yôäû[øo#{%T\ÊŸ›aô³piÔ¤+Ç'Œ`§#awú®V9Ô° òû6rAY‘#“?¹E€è³9dpÝP'ùÿ Spç:åî)pC i÷0Ów¥…T?ER‘ €›%6â ®Ïòº¦tyTð†Ò kB^eàôÐás%Ѐµ"Õ1à¨sÔÍ‹ñîºãrhÙoiâíìÕÙtê‹Íί0käðI¼X:`ËDà«kª._2—okDµ÷‰<¡ÙÜ´#ç«WMqÇ[Ð@¡pkNª‚7ÉjsŒVž.ˆ›mDóÖ&Áq¿ÄvFÍ\a}ѧº•FÃY(N¡&j:Q”2Wf"}Ÿî¬ØädâÔ€U¶‘’zš˜~Ið_{KèúD,b¬ß Ñ19Ù±”&Ý…ªb!—Q»'\z'åwÔ$ÀÞ_Ô´<…û‘)\(L¬D¹˜«£å‚ªËevºÚ®Dþh­g"žH?EÝ“ÍPrÂñÝxEïðKÆãý(ìXùêv‚3Òßõô9Èöæ œÞȆ]!Šr›1»hµ ùœ'ó×| I4àòcÌO#ûºþõ£‹G´Ë ±*„þuÜå[C=¶7wAù‚ÖRÐ}Ì4ÉH=,׿²ß)ýSÙt­µ¹·Œ-sì_ÚÌóOß'œ«©ö1<+0‹K¿£Þå²òuyzš=º18ÔQP&>™ž- ¯©¼xGÜÝ4J°©½ä<ò6 ·wÑ€ŽwŒ‚èþ ?};BåX…¦ðýŠ´OԄꡪÑzM5÷Žã *Hi%ã訖Þý¾}©•U¤$«wÃpõ^VÓ–Q¿t oÙyÛƒ Ì«f^¥M"³ýžFБÛ ,ô­ï@wžÑS/ð^uea:¦ü&G[›ÄõÈÛ²øËz‡Œ@£HIB·ã˜<úùH;FÊ‘Ø+`{C°/öÖ·QÛ.¦:ïæ8×jlëµ'Oò^›ëíËIOjˆ1²EL~ "5£†)¿¤šéЦÀ9…ÔJ…Ù)Øžl‚ÊÉù‹ŸsƒqÊj¸Á!$0èùçïö4vÜòŽ:à–}Ñ’Ó"Õ2+[Ûœ†ò-¶–$öǨ{eK^aP´WûDwñ\ÿ¨çŠnrO>/›¶·ú‘Z)’i³¦P1ÅIÆÈc­]œ9k¢asALºüÇUÝZÇÂl”ÒxŽäÔÃ’3y/ä­ï!™«š_¥ÖögB!g¹+ü0TÔX¥>·ž­W"†c¡kv©²¡J{¯c@­AAë=¤û¹Ô÷w-Îñyve;¤kä]ÃQ1 {‚Û„oªAß»Õ30u- „‰k€¿œ¬¤ëñÁIJ+$ÿ Ž0u[»™|6>舎Càd²ŽðBõ]¥m-S%fPCã9â GØ ÿ=XÁ!†2v X†áGZ5€&¶±MŒkÒ' k Ú? ½A!´1%‹m1æÝóuöº‹/…5‡œ죑Ïʬ0<GABëcžsñIï.â©­ Td$g}PuïrÔD£9Эߔú²…WêÝ5xJí’àžUiR$H™ô¢EFiUÕ&ˆ5U%×îóBpZh./‚àô&­t`48£Nag„ÇrqÂV…Hõ2˜}ovÓèЕw¤@Åž0»®ÆìÝ#’"Mdo]’ù\—V{¥Ã¢ÙN/ØÞqÖY”ÍŒ ¢e[Îeˆc á2U ŽCÉú(Î(cŠ“ÇÒÞGõ26Ú­PoL¬‚ƒ!ƒÖíY²Þ€Qñ¢Io¹jÿ¬u=\Ùp‘¦B¬8쵹枠[€H۽ꅚØyÍgBØÐI„BXTO:§6 }ÄE9ñ>êÔœÅF©öèfTºHæï³ rëK@PœÂ‡©óAfàd¬ï ÍVÇP1<Ü.{‰ÔÓˆ€˜/Oùma¢Ã3Ç|òRÛÓj•è³{³æÊ©Õf++õ˜#F â`êªãh6vJ즨åˆÉÖ)Õ1˜Ö!ŠÙ É!ð ˜{S d¨ÉÎK)yÈJÔ¡«æùÓAܸ“ô¨Ýg4 ‰cøy¯Øíå_^µ6U|$%6´*9Ïݹ['Ò­VO3Ch£_Òʧšd(wE$*óD” þñµ!w@Ðõ쮃/BG)sÆŒ*}!ècüš–'k¿qýàÊÝ]õ¥9è[Tòù΢•@ŒúwÈYTDx1\ž§¡‹ÖÔè ;ªÅβº8ÏU°èÎŒC3ão€&du‚‡‹QúV²íÞê~ÂŒº‰Ô =m‚¡YA°Yjí¨Au4pd>pÑ™ŽÄŸ–Ò÷™Í¨ rõšM_wÛÌ÷‹ ÇÈ9²Ÿs0Œþ¿Ã žÜ ²Ä3]}´ëEè|(xðï#®_Üûcýá×i¿ýØS‚-ú‰Gô×ë&LÁ†OÄŠøôã{V" w É‘=Àƒ1,ðTTH–?Kási¹ÑB\øXyþ»÷‘…ù‡½_ý\ÖÇdÄg¢š¹õý_‹y>ºØ¯ªLk ß°8õ#/Bº»õž[õ¸Î€VXÄ­‰+®ÌÞRtÛ)Éß*!  µ ýZ9èvq×çk¢£Í¶ÓN›*߿©›ÆÎc­ФÈ#†`a;_o ¹úİÈtç‡ÍgºC§ÃÃËlkµ%±µ–TîÊ5ÎÚ øAâoÌ2.q4uÑÕliBáq¶Ü8"Lµ$âδ¡M„»ñ"©œÅTÞ™…j\9µçÉà®^<’:‰Ü3 ãŸB-¢ú|¼¤œP Ý%j_UvG¸¼Gô(;øpC:~>ïx•¯|ÈU¹³ s¡+¦¸²à•5åÞÕ­Þ9•Ÿ[{Š @0Kð&å™=oÞ¶aÀWº ë~à’§­Æ7ä¬é Rðt}â‡×–¸»Ió©–»Œõ¿D Æ>¿hTãú¯Í;¦EÌ’yž¸JÑ~Jh>¸¬ÐØ\Õxä‰ÕŸöZšP—dn©&ÎæÊØÞ2Œš]¿Î5婆÷¤òXs¥J|œƒ{¸‰ð€^/TRÖ‡3V€^áƒ:`Í=Wøév»RT†)$hýŸ’cmÃרEÇ1£w9t½cƒ¼ã`« zŠð´¬7‹;2Á^휋ë_†ÿcYž¥™øŠÆŽŽæ"ºGtš¨·ï¼Ú—›¼HÙ©¼U_ï ñ„£ã½š¤•DËÉ^3öf«Xdr˜áÖ…Üùıw¾ÑBtÒj¤äÝZ8sx¹d kñ7»Ç2Š4Ðs§K}5T¬û»-Aù(õÙÒG+®!îî±ú‚r²OfÇewðÎ$mt¡Û’$’:Ïk•`ídî½¢ÀjtI$jqyH ½lš¹Mÿpºö²Ì8˜$‚¿E› ®[ Ÿ=‡qÐz¦q·ÃõÌ–hR —–²Ó‰l8ÄÔ´S]ð`¡Oãã°þü±PÝÑ›îK°S¬@(fÆ u0‰"QPc¸,: •©›¶9A^à  Ùî±h¡ÃÔUÜLãê1# `’–ó…þ€˜ïô^i¬Ô;Cn°"õOáHg“1ð*4%/ªZV¯ˆxÉájÅn 0Å5l“&ë£ÕÜ¢¸Ý»<^˜#[–eëdhw‡ÀϺ̜F î¼)úBWP×ôæ¶ìN’m¹½|¿Ü@3Qh6yI±7Mç1Îô­A)Q‡³+„ükGeÿ\x˜ø¢3böù"ønÇúÁ?cx¢!ñè¦L¢}ÿc¨Üþú¶tô×¹È×rz©ø›Þ[4®a€‡7ec,eйõÒûœ»Äô$˜O S67§µyÑcƒè[Gz:Д"j: Ì ‹U 8W:äí¿t3Í6@J#8³C0õáÍæMl×€Jåzâ]Dü[þx3'JZ<–,ö̮Ӷ"ˆÜpú¤šNU‹×GÕDsMÔÔȱ²ª· y»oÔê SBW…œ·§šB#´h~&hœCž’¤âxÚ÷žYöȘÑ`jR;wÁÀwC‰¼«ž%ö™¸—Z(E¨Rõmüí>J›^>K$VøØ$cƒ¼Õ.¤ðp‡Õc8Ñ’¾îœUÉS nŒc ˜äSfí8£Ná^PÅÃqŽÿáØVÄT‘Ìô¥ÃgFx™Öm° q†5gŠZ`Öþš#„¿¹:yUuH³sp¾ñ9€Â-äAVïÝgÁŽÒEÚòç©ß¦ý̳»¬³SÜÜút+#*f~Cy`úäIZEbÂóM„ÄãbP{{´¼5Õ Ò£ç [$”ÌÛÓY>Zmšï%øËdÜ!}a%{ðž\ ¹/ŒY…Õ`>TwîëÜíÞê?¡¤ºåЂß{½OÙ3®ÉšE§Ô±Þ:°¨ú°DÄ1o_$‘¸#Ïá€Û=ç\°ƒõçÏç°â*w·ïãC(ˆÂ#}T^ùó±î(bXcʇwE º6·bIéÂrásmVoLŸí­üx ]  µ4RôÆž¤w˜q~Û`ÞáARî¢ÿµ„_@}-¡Òˆs,~áQøïæsð¬ÑÍðf3Æ\3™Q—-&$GJ%Ïõ…í±é#GOã I„ožÚåñ e…ÔËh££„ääÿM)¸âGR¼!¡›Œ€% ª©Ö•·xú@£?ïŸ"Sä_¼jM²hŽ’ˆ…|¥ÕÕñÆœp,>ËØ] /ÎQ·˜YX˜²|Õr›ŒaFB{#br..z( §N¥àÚ«þ© iÇÐÙ«ö³?!iªM0Y'ƒð#®“Ai)Ì47iµˆß)Wb…ÑuTýu ¥v“PË‹º[ÃÄ*¬ÀÂÏ(È(˜GX´vø7Ï;îa~à•´\)ð^B Ž–&@éthW…SMñ§Uö•c¢w2û¥Ž14¨}¶È|Õí¢“nðß$CT€,™Ûù÷v.ŒŽàJy[¶$ª‘¼¹Óژи°ßáÇ,î¾×አĖ_¬Yr¢YGð1«þ@†W9vëÐ`ÿV±°nÉ}žäîpМÈ‹mÀ÷")/L„ˆã‚Š#¸tOÕ47tÉË:yýIr†Ó™ÕfIk¾ÕyP†Ü)*>¹cò|¯Kûé2eÅR‰S÷³Uö½v_·„'wˆvšR†ä«Ì3ïõ°§jsöíÏM÷âU¦[ WŸÛ7gÅi» œP¼™ùLóÏÀðæß…ã¡íºÛ+'ù¡Û¥Ó#s|ytôöö=Ÿò¿Rp±Llóï°ù§4O‰Ýˆ„—- }PPõTŒS¬!¥ ;ç“‘¨»Àˆã^ K‰9;-ŠÒbH€¿vı†¡…YAN1#>B¾2x+êŠùz×&1¾=V¹½c#“þ[jÍêzà|æxž=<–#-·(mk+t_å–bŽ„‚äÖ¶V(Q“ Ãi }(íœÊW%@‡]#–w Jï/yËRÍøOdèxhqºø6ÿÇ5±@ó¾ê…Þ5׿¢ãÄŸ¬õÃ÷_(8ô‘Z¿¡·fYgܽ¼ °·Ú[ò®ÏÝ0“·í6~¼í!dñI¡¶Š³ùvºqI?1Û +lš––µ6‚œ@›.lجe›|>îÓ"ð·Â)šÁµà'f#:kÁ)$Tþ1ÓóÑÖÍ’ × ­c*³—’ªdµŸ¡éƒ“^š# øîÃ0o%‰bÞ¹Oe¥üøý—$,·Ž++þëÜŽ{xQ0ðd]$Œ©·›~ç̲3 |]íR]Püú©Ê -Rœ{d-¼au(Yð–MXØÏ©¹’'̪À^é QéE‡¤Úbi1çë÷ ÿ`Um>«/ÂÀMÅŰöEYl§.»æÎ:¡"Œáþq6£Ðo|mj/óT¦ÞórŽÔ X£ˆ/σü1ÿÃ⨬þÏ£ µ½¨ÞƒÄvQºÙʋƵ³%;°¥êT„|½ Nû•ÚFfB\<‘)œóHÃÞel‹W‹œ'mlœ¥šd|<ûGÊͤ܆ÆÅO_$”ÇË(?dVßûè°Çï7ï[%ãCqaGW¢$ÃŽ±N”¨ÌÝ:JùBþ:­ZÕGzæÖLJØž%Vá°Ùf¸}|ã_Ò9¾K‹±¼-ØMÄB€äAÃÞñã‘T‰w1‰Ì¿~û÷¥Ø%ý^]RsÙ³Sýyii9¤¿Q¿Sä"A˜BCÀÊØœuõ`åÞÐè0½lˆØ·ä?u/ÃêW¨,Ç€ÿÇÍæ-lH&Ô±<Š#ßy °ópG´®œôdz54|%/EŽXîŠ ?|ŽjÌ Û(ÂFÀ>·"¨„Tñ,WÍçÍÞqçH²êÀBÛ¦n2YwÄÆ¨'˜#*ŠƒPüøŠ&ANš£WŽã?3=½~8å…Í’D]Ï?ß7ÇRk´A³S?¯MÖG9º«(ðÍwgb“£ØùìwTPÏla0ù4è}ù¨"„a*ˆ€> ‹Ýöøµ›NN­H&Œ»™$¥V¼c¬²‰OS–Ôt‹²ø–Á VÖ¨ƒVZcíÊI³KTˆLböRĬí þ|vŠkð)ª»Oœï=TwÅjH‚¤QbÛ:–³rð ` Ç1b¡§Þ8 'zšÄ~ËÍ:¸pó8ÄÄd‘˜œDžù»ü‘­ŸÈ@u%óÁàíê|ÿÇ‚Y'é-/`?ë/•=ŽgvŽ]½Õ¦ÙJ#‚«Ó¹î‡Ö/â¬öÿ){—d½&%e’Ž ·Ó˜4Ïû¾„͇»`ã8ÙιV yÊ Å?L€ò܇‡lû4ÀW4§HЋ%_–b?WÍ ÅšŽÙšCƒ=ã˜×·çǯ5}‹öû<Ç;E+°ç¦,>;ÑzfÎ`•füRŸÁ[^Òq hž*½Êؤj«L•Ñk߯mò‹Eï®w$<;Ûc㤣’pÎ‘Ðø@Fðiˆ:…'Z'ŽHÇ`ž2ñœ›ó¸Ç%ö8¬­ ¸µyñ÷ÖÖw¿–‹ãùúpdÂìZZÌ€¶°´t«©1õ_p¿ëÛµô£FUÛﯱ·e ;VÕ ]u¾Gg+—£m{@->ôM6ø.×T[õÝ+£ §º‹ì©‘`"ø\ŹzÊEïikÂýæŒãÞöÉ<ÄÍçrêœÙµ š1Ž«=’NÖàLWJ·|aÞàb#8è ä%²ÄÞ”-¥wÄžÙ¢  Áâw”Á¾³âîâ*öÜ@zÂtzÁ/cDO纳•íœÎšèi„~ìpÎ_P´›²ÆÕÒ¸GÜ£æmþÇ¿I™l—3&&vðÕ™Ñ6œZû> SU <#Æ}r/M~ú=vÛ^ssõ´ÒÒkx¾r/˜­£\Ç&“Ý¥“ã}O:Iº¯õ+ü; ̧œÙàw¤¡A<èˆÆ¢ÜÎÁ•ʹ¨3xxY˜’+Uf8£2&T|ÝÊF-¤Þ¤b¨ÞO!\ày÷%%­Ê8U¦0V?œ®>IXý¯!z +eIˆ:@6¥j:ѲYüeÁ täGñÙÞJudÐXZÒaÈ%ÿ;­)”ë¤ÿ tÜãÖ ë_‚ 2„PkcàûÊú›#éj7N}Ûëˆ|n+Åæ»åïG’AN¿w»4Ï‚„»‰Žp¢]¥VàãHc‚cˆbqÈÛ¬ÑZÙpõ<‡¢¼}öl‹Sî¸ O*Ü9ß6ò¢¿ØLÉõ­üF3YI&%Ääõo?[»”;…jªY*u£¦E½¹Á3ë±)>þ{©XßÎso™ÊQ/ÂÌ ŠÎýø¨’ÕŽK¹þ€}N„•e߸¿WaļPâ­òIo(#"8J¯u{þ³>Þ¶ã“–ë1e°Õ„ñ[ðª&³ç£û\«&އGlCŽÍ°®íU/Ç‹y)‚¢'̱t¥®~¦ÔM\ØI›I-lfŠÙóyˆw ªN#䦛èNŒ‹ˆFJ!vIz9­Ûž¼yž²–Ô0öS×}í ¸[Vw”ðàET\óXx+;[õ\ä[v9e è㥷‚T°Ýް²ý¡{ øÚˆ‚ÖrOQÎUò Þkd(°¢-÷Þ =ÿÐZBk¯º‡;‡rç¾±wŸ&ÜXâxPu™ÖHÉSšœV¸çƒUѲ³·v'þÚjfç’€wÆÓ2ÒùÏŒw”Æâ)uÀ¹#D0YGb|úE1ùòúöM$ ædkæÓL79);[/~¶r8ë£Q@¦á`¡z'eD.*ÑÂjgc MßГì>24¯ L^ø^«¤ ¨ídš„B®…‹e£ÉeÛhO£>™¼æ%ìCí«[”³›­7¬] šì’¶ÍÈý±&y¯”8 @9½Ê·Xùž ¡ñ{3˜¸âWûâU /°Ç·NŒ-?÷E”B­Lšr`ŸÂŠo Ó˜?b­E/Â*zoÉÁû»køö™n1»½.Ü+². _êRðˆÔ‘ãi¹/ìëp©årƒ±dJ:3~+™Á¹löÈq*-ÓìdvÜ€|D‹FÏ÷êÔî›k,!P¥IÈêéøðÔn¤Oáê{™Ê9ôy&â!q‰wáºpo8aéÄOÕè±SŠãƒG£Ï 8Q›P`FU¯¼8& 4-ሠÝIÉ* hÔÖvh¥?µO«Í­l5ÎåcTbæÝOÌc»ëk†MoOU¦QÞ÷ƒ-Ó¥¡ð§üùŠê†H½-æÊq=%È/ŽW >–âP¸_a[¸¯3ÁUr$Ðw4àñH»Æ‡@ÇLþXÃÖz•V(FÈ÷,C8†?ˆKÓ@·ôͶRùt!Ïëã ¨˜vŽðX]ãSìâ[Bå4p-ùõ³ÊEÙÎòJ‹ôï0@H~tb‹Í.¨´<âkÆãÖËô4ëâB4˜ép9=t³áE{”±m¨f} c¨nÈp«4嵎X÷Ïüf&¡\ê˰Ø/úïâl|£F0`¡7–Ù`3ý¢ ØMÓ‰ ¹@è﮾(e®zrÁ_ #¸ÕÛ\[.èíLÉ¡ìÝ·Æã¼âh¤ž"ûŽ˜UAI Q町éP™Ö¶H‚LwÍIÇ‘?¸É£,]®ô»ˆÚí!xo~´›n ´°o¥V¯Ñµí?_¾Ätìø;ûÉÒàn±¤WêÑ#UŠBwÍV¬3 ÑqãÜSœD%¯—u6Ù©üæiIéöqH©.šÎÇúxÂiˆcïáªQ-õ w²Ú½ï)Wcá³ì&a\:6^†óŠŽeáH¡pj=òù‹ÈÃ&QÜša²º<Ž/Ù¡fÀËDÀ2ïáîØ½¸r59k öüŠ¡ £Õév4øb ¾×#ªÁj¾,^Sd«ç÷3ù°˜s1šÊP¶Ö17T‚C͇êùY‚{@!»ä!‚-8¬Ôú½´ôÁ|j¡p+À +ªÂb–§úؼt²;ü»?$F¼$ Ylž 8­ùÛŒtbÁ6Uóì1Ëv¦[æ@IB‡fÃDAâõ<½¡À…²SEãç"Órë xáoæö¾êü}Ýþå=Tî›Òv/µdLBȺ1B.lüù|E²ß?’ï«mÌ )·b!&Q¨ƒhëHõÉAß<$ŠŸ%ÁLZ%ûŸûÂÔµ–’…‹$ ñ^“ö÷†ÿv:IòËõ¾¿F4Ú·Á÷…NΞÜSRå(!eä&bi~€4µÊ^£L€4²VØŠýõüÁmÙ>øTý>„ ú-R˜X‰ƒ³Û@<žš@àíõœßUøœ?øOu-Úµué]ìÓAÃøÒk’ 1Yj…WIJÀã|½y½`²Ø¼ë:pÀ{K½£ré‡'z˜åtLKx[áLÇaWÚoã¥÷î³bW ß0$ola£e’Ái&5Ùu6ñW¿TÚSÌOÇÃhÑ£½ÑTîˆm¹á °(Á–$K¯4 hj%ývÀ¶qY§%“VÊÐ*’Òp-©ÅãÚØ¯\@Ø øRÛ:èstڈЗAÒÄ.ð,¨ÄÿûaÖ®À î(BXh.­‘O Îíî0!b=¿U¤ä³ZžI ’É×ãR“U yÎL6Ú•@r¬´Ž©¿M‹­|‚£jVB¬ äí«A«¡©à©Á¤‹‚KLÖx¥°¥îDÛ¶xü§º%¢ †H·Œ¾–©¶,²üV.§Šs%Ë$–î© NVù®>·_ ÙåüÞ²`â?½ëÇú¹­Êhøf%œÝþÜ,ž.t;Z¤;<÷é€uÜß—C—*_#rØ%SÓóU|é†VG¨7¢Ÿ™üuYÖÐÑo ÁÒþÉ–ô(ìçáK]è ‹,Ñøõ&Hb‘&•ŠÍ>êz,ßµ$¨´w qO• å*Q|Æ¿ y™±Ò׆4L€sIºe°»žH{nòUÎ2SÁëIX)uÝ=Á —ÕjNàûQo°í5ä£È9ס·35JKuðšì×ùÓ†W˜ÓÉ_*³¾GFe)¨Ë³‘—Ïs3ÇÙç`|ãXpJß"4êíÓ}p†?keëCE¡ë5ä3ô3ê¬âèp îÿš*Àb±(q¥Þ¬WˆzÅÚ3¿t¦×¨Hª´9,Þß´oÏ }¾2dgð]A¡|8”Àñ.òojZ¥°,Æúpn È:_iè}(I~Û,ƬlÁò¥Ñú—£¦•i†*< ;ûÌœÑ=›VÊ—FõsÔûÜ¢¾€ü)à‡G1¼ÂˆæŒ V*`hûó?ø¦¶`Œ(Ë€»µ¯Ï³°T3AÑy<‚É?Õ½’·àŸ£mbOApLè#€AªhÂúÓ f¢[‰|á´F°ù å/Sý õléI?mW¸$’1;ò™ á?oòRîš×°àØ,¡ÝJüÒe´yC¬Î–ÎjpÄO‡¿%ªfKáÇ[¼¼ ‘ðõ1Ð ‰•õš¶T#k_`"XW˜½Þ-G‘œSè3´–ôF®ÄCú4ðI™O[J4†Õ\Žû95¹æ]:Y“Ê7ù—÷† Ôý³¨ë”¨G¡ªw?o©Ò˜¾úãnĈ€ø^uߘ‰µÓÄ«pÂÊðzl%Úµ­®~9ŒÑ‚E½ì©Ì_ù“¤w~ ‹ŠHµÔÚǪÔg€@4áVólj'pC`ò,™9Zhj¦ˆfõ¿¥¬]ˆÆòïI\XZRug„0¿¬ÍOg—"áTÔt» x±â%£VWЀÐð`™½˜ cÆ6yÕ³Ì8Ÿ ƒ’íØRo(¹)w/¬íç´Whç¾”ßd`ð9®¡™‘§¿T¡¦ú ÙÖ‚(ø”ú¿¬ˆÀÛt²®X:af,/æ Çi0 _é·…-ü[¦ë®%VÞyÖKH—ÅÕè Yjdª_E™öÐ=é¡aúƒŠáÄÙ;Èž B¬>»ü’Ñûl_51/Ø1¤•ÒWOÌggÚËm,²‡ %3Øeç…«¼;':Ù úl_8Êg“¯•V†š ¡ÁÕ¢¿I»¬Ü§½˜õX“‚™mÝÂUKð6ÚÚL*…bDc¨ÂJüa.ÜzGÊáƒ\sÆhÚÇ1"K‘²¬˜Xµ/¶6ÿ:²"q@à¯&ôBNáíl É_kÙ«9Æ8‘#2þP(‡ÐN~ݬ,©ó[ O’®qïH‚†;rYŽ Ä§cø¬î6XñÙὫ­®òÈ …’E4Óæ”×ãÊ5Ê3)D3mbØái·Jªyþ…J³€k<´T.œ»SX¹ÀØúà½^gZ u¬mé1tm³‰‚î<‘cûl`ôbu›BúŸ¥ L7Îq­vÒz*lÉN£;j ¸¬8Ã;³ÇàW£Y|s¬¨x;ÍÉÁ{æ×Vu'¨oô¤Ö˜+Ñ AªóŽˆ~Áˆ_ €‹ŠÅ·“•Ó»HÝynêM ÇåxBŒ} íX›ÂrhnRä—RÑšª0bH¶hÍ`8lƒ%ýîCn£ˆŽ»SBÞHEwŽPJtý—s09þÒFùâ¡k·d(.ÐêÜBÆìƒ¨k°bþ6Y>CE~3Fó’üP:s¸Û¥}¤Ð 'k.ÁÝõBÓâê›bfŸM“=þÌ<Éëáÿò©è{„Þ‚oŠc”кha®àƒ¦½rn[ÇŒcU£º:™²êÞú†ö®€sª±ï-ÆU•ißbÊÜm0Ö}$©±”:c:<ó‹ÎÆ,üÁÅýš,;åÅÉ¥³½Ý© FlKýHŸ·'lî÷×äcvç>,Fž‰røû9„ä@ö|Pƒü¯wwònQõK”«†?Ôüp}éæw„Uæ€AbÌ“ÏBjÔ©íFFrªpÔè£,XÝýœ| WÉÝÝ‹å)ÃÍ-x„L(ˆÖ¥ÃRIHŠ©¡×¨–Ð]Dÿ_Ëd¶!…o³hB6t”~!36]a€J}óNñqAéKõ<Ìuj¦Z.ûvV÷JÏy*9fÇÿ‹X¤WÊQ'/D«2|½£é4ÄÊRÁO–=™EÌ8&æ_‘VQÿE ï¥Ô¤ÖÒQ‘Í‘õÆÈRÊç¢;s„ý^xåM2P}]YKØi±éé5rÈ*\jw˜ºbxû zNÔÚ¶Ø)ºa-¤Ñ…ý´N@MØÆë­dÂu]ç<˜Yl[¼ÁHÝ»8™q‡•ª›…¦Óyã[f¥zÖ"Cí>xÈn Çç°þg9GÀ—wcï—–$kŒ‹™š!ù,9H#s“òd¯H¢}«\ç1ž46-´Ð)Þ¥¤0ƒcÁ¦¬‚g‹™¬;`ޝB³’«Ì“õ Vð þ›H~.äÃ9¾.=}©/c-hF\æ=JLŽZåªQw®‹gçü3í"Suü‰ì“âèÑ+*³Ø tÌzÃÚAxqÇÂV€!ÏftÔi§¶öO#>³´Ž»øêFèBƒ  ä«Ý5’î®›ËÒLôï;ÏL°oCÜÓôU£\ˆ-[¦GðÒd¢$6cPlÚ$vŽp˜Êš¢t¹Â4™L;äþ“) âua)_³X/w ¥5BÆ^²•¿ ºD‹¾ÛöÁX¸yt3åH¾ÇÞ“ßL&,…ë¨W/ˆùpݾ€×àdawÚ(4@^p6IMý»Ÿ¥ôݲGâˆàœ¯¨§û‚ úˆgJMiÏøß‰ô»Y,Í1šÁ¿¨î[5Å]J¯LMÇc˜ªpŸ>õ&4Fïc1áX±tWUíIò8¡p£ƒ«~Òý%ß)LÓò¶²Ùi SÄÿû@毪°9íÌå«çƒ.‡™`ZÑvpͦáF`™²Þ+¥„"J¶¤®Þ«ïÙdƶZ+­ì£aƒç îÕÉkº*W‚5&’8@[­Ø HC_ÐæïWySßáXòÜ&}Z!ÙíÕKüÒã…„é¨ Zu¨à÷dï;jmòìvÒ£ úŸöÿM£©ó­DžÞY£*Ô Ô~J1jH½Ñ«[•D üó~?¶˜a­æY’²$ï,`‚Hªëñš(~¶]iV`9àîV¾ ˜‡Zì †CäØÎðqÂþW ®5³BM÷ãk¹€¦¸E€ð”cüsÆì¬IM{I-6 £§,K § )híT›]$¸õyÕ±"•‡-¨‡x¡ ß%Îj¦¿,UßÒK‘\ÕÐEú׿åÓa(4 1ÿe?¼AÌfõ¹ºbë$YüŸ óo×faN€ƒ¿§³¶X+ë·Ç ñÜ"þqÌ#ý –¦\ f¹â\Y„¬,iíWÐRHßçS¡ÞñÉ$:=­©¸L²0bŠ…é+i*0ú\Ö„|pgé-µÖ¼²;Â[Œå[ÊONèÂ(‘*ÃóôîÉ¡áä_Éq…, ;¦×ä„eÛm!ü˜¤L;o-¾OI¶=ÆðãƒK¡zðyS†Öž£ì¾‹Ú¥ƒ€»³|´y Kw÷’R\,Zhà5³ý:Ù3ÿG{ÖÍç^¨ÈÚŽÇÙán¶/8纫MžZqöÇ g«'Hêø0ìäGùpäŸ,“ÚÝh¨å³;ÑNùGz¯–Ô;3 "¡èåyhIzª±°iìgH— óívèîsÐ m@§1Õc›±ˆð|ZNÑXZð×í"³•øØã3Š#)„(O¬83mÜD ¹îëï;ô¿Öç A ÿC0ëq®[–Øù†Ë7}ös2!ì.øáhkbdùo"ƳŸ!BŒ·Qµ.räŒßBq…È¢'½¬Co) ]궬SÏ:ÓÄ$ á"‡Nõ¼ÁãõÑ`õ¼†ft°ùмÜOñl§é› Ù ß²EHM‡àÿhÆ‚‡Ì§÷ð bå'¸DŠb., 4aÕLWÝ’°l#{Ë W?^õYåÿžXè8˜@ÃªŽ£v(#/9Uà¶>Sáß0K!7¤á;Ý(V@;KÔ­QŽyæ|$Àܾ£ù«8»+{*;ØSi~ûéžTRfX;iÁdö¸º)FÜ\pòSŒÀ¼—›¤uɽ»ç’-ŽW¼“›w>^Äoô·ÕSløàºÉj‡Ò(Óñ¢8Bt7 »©÷§xÁèKè¤9N{Çq*$I«õá¼'±·§á;ëj†L6’LC࣒”‰wp%e9W8lŠyÉdá=ÙÁàúæ°•³n ­£gvο“ÞZš]Ý%‡D E¯­á(ƒÏ®2ã8O»3 ž²CÝ•ŒS$w/Ý—j… [£ôš,£ZÿôkåÈ.»zƒY¯#ní®‡ÉæsoQ`סT¢žl4ƒ 2-ÙÀÙ‘a`4&²ÑZ©Gåòóî¿¿F$|{2©,˜žŠò-èÑõVtJâ$iÝdBaí7ë›ýÏëb,è '¼9²’b‚ë ´GÒúù×*êÖtOš’ß”V¨Wý·tÂ¥¬äÌt,—÷æ«a\S ]àËIÅ  ôqþ”ºx<œ÷@¡cdܤÒ¸Õ0›ÙõQ‹âˆÌlëtC˜’¶ú3íPÕæµÚËžqŰ­ã‘ï ®ã—¨£ÐÕ¯Ø ƒ»[þ#òþÛº.Vl‰Ÿ#Ç´LõF]¨Wd]Þ¬Äþ…Š‰â‰µðvÂíYŽ¢Õ³P…öèq{QÇѓb¢2ŽèÕÏa+šÎ]+Œ„Y†:½¥IröŒ!õ¯0Óç# à4åÜw™ÕÏ_Ø+ÍêïÒÒµ8rT õ ®] bU"Ê2Ôþʶ¸âvˆ KÆ^b$è)—]—µGvÄm=÷:Á¥—’?7Àð9åLœ›ˆ!0Â!É•sÒuÙ¯zm‹ãàíÔ=†€ä•}¦ÔÑÔI'­þæ‡3ó*‹eãTK%ÞgÛûÙ·‰À²E­¾}±ŒÉd¢ê.òÀ„Gaèa" Ú<ÌbÔ)jš»‘V60'Bäà w™±Q.Ï·ßÖdg| 1e‹÷´@9[£™ÖÖy~ëÁ^QªˆEŸàVkz¶ÖðT”)—XýÅuXf{ZsQÕ gai`ýƒ,ª42Ìýíµ ÂBð“ƒDÞ±fP©¹OLf1ëŠüªa»ýUJÃËM‰Ô„ ï|ŠÓ*däxw¿º¹†-ìé v˜-+å²±NƒklnFFO†ÑÁù¢P~e= ïÃηp¤ZpÉàcÖ$ìÑóêrG¨Áwš}„Ù!ÅdÑ"L;c*—x»Cµ|€@GOåssd}aU5Êç1¼ŽÔ§ +b1Ë@G(%KP—Õô޽á?ˆÖ!š!©–HkøüôCtœ]Ð>t‹C꺘ÌÌX¨ÝÖôÏ¿¦è×CQ#! |@Ðñ®˜£úf5ë­O|ð•¼X%Qu‚ %·Ò7o)Ï (m: Ùq‘Ô…DHŸë ÷éÇùSñ tÜÒ­þÐ’¼‘ºñ ]MEW­p‚.X°ÝÉÛ?ÚÎÈ<‚ý7÷.ß–ñm”‘ˆ6$)ÔsØtçÝ”;U/ð>g64;*|:u:¤ã%wä!_”Îa¥‘w6tdÄ£¼cÏuàÁ_õücà‘r$¢àdb„eí›ÿeìÅÚ ,Hº{]S’›±~]VÏ ¹'xüFÂý§†ëÌ0!Uß<> ay‘yL®ê Ý¥8'k¥ áìMîÐh‹Õ§Ÿš#ZÄàoF8ò\mp‰7̲½ÀÃvTíXÚÇÁÆê~z©HÙëáæ3ƒ¯ìý€Å;2óV2lox¤THJwûc’»ç§ñ]VÚ!wc%yÏ+«3{é®ýq9n3B\¿²9r·2œFùå2î l'µ†ý-ðùøÌiBgvU‚”'ºx]ÓÁqáÂð)r3Lôx <üÞs@´*rÌ;Œb°‡oQê~LºùûŒ~ÜÿZ˜iC·ó¯×F,fÕU`|k§b`ø‹´];ØôÈ?}FoÍXiWoÑÆÆÊ>„ÓÇM…‚´p¸“¡>û\2³•ºÈ@70çAÁ AÞñÔD@د+Ù 2eä1ž¡¥SwCêÊùª~æß=ÃÀ–¼‘ÝÏT®¢~±Ï·œ¡FD‹1Aû,¢ÐX_å»f BeŸìà‰k·2Û÷?ëúªp\«ä1±Í@[+ViŒ×bûGÕ·_±ã'ÞñgÁ:Ó@«((Û`b:ëNî‰å¢0)¯Ô}uU˜cK¢%ò†-ëpÞú÷g eý†ãs…³sBê˹<å=ãûÖz²BÖm‹c_îSI¾Š{ùÙÁ†w.•E±$C>âár±Ø[³J ¥Üh×kkâÅÌhÅæ+‹ ÇÉÞa Þ|MHbʇ\ÜוN䆃IµZm„ù5àÔ¸¬£üv¦ªï~2«ï>V²Š%8i¤0ýŸö )ñSÍËiG‰Ç€f;¾`ωržÓ|EMÔoߎžÜ«`<éW¶A)QÑɉAŒ¥Á '$Z\V:¾;çó.yñÖ«¥ÊùúŸfõŒÌ6 f@I¸/e `—¾ížv«à@7HágD8îÅꉩ¦Ùïµ"xhšœN6Øq“”m¦ÓRbxîöÅÅ«b“[¥‡†Þ1b:BÖ¶ŽFBö«y”jñX1r5¸“Î Ü]…¤Æ[.lñ­š6‹´aÍÎáZ´åJ×T[F‰ï¦z¶5| ¼Nî·xW:oÔ¾Ú1fÜ_ê%Õ­³Iþ"õmݘ­ÛR~Õ ×¨ðxF:åeì`O¦dd¸Ý©]ðæ]c3~˜ =ÓhÝìÃm ·ƒÍòÿ”òneÙsŸìU”â§!àÁÿÈ®Ïe0|¦,õsߪÅîø8pW ÓžùŠËÑkkæ/T>¶«AÚ‹~22ò÷]Ì‹’¼ÜTΙIJµ± ¢O»;xÉ”9OGÚ/É5ë³tQ©¡ß·¡µI–Mº–¤Óuý¼àæqˆì*Å¿R]M» ¶‚ª9`‚v¾ÍÅ9^Û¶£M÷Ì%ÆzÙ J,ÌçñKµþH­¢P+“6cú>ܸ¥Sô`L‡þjíø›®!ÂùÛ}tß9ÉJ°ÂÝvöbuÎÛ¤²l ¨ŒÑ€ÿ @+ ã\ .”¨?'Tï.õÆ•5ÌP·ÝÄ¿V¶«¯È9RÃ'ÕBŒI™Ø¿!êÐ Äᇖû¤­øÝA*'î‘Qüê@ U"ÉËÌvï&´\¬f]MËÉCÝ(nÈü#é÷¯£ âØ´°¬oÑã™ÉRcñaàI sîáä´k§µǣؠýüC8´:0v³ðí?ú€ã§×™$Ge°M¯gÝ•vÀž œ|ûFoHÈ”ŠéfG­’9ew R¢;8>5)¢µç³Ó”Uß$aIÄ´Z–º•v>[ˆ  F|yØ&ï;š§âNrQG´AsUû1›ý®Ÿ½ööU-ƒü¹m1Åù )ð„¢ºr2)!z¦ô'T·¥1nûUìL Ï ømtT6Ýa+ m?s„«²Lä5&êH³g^©â§€ÙokþÞt_¼=êÈ8'ƒákàlŸ© ö‰I ¡­ŽvmÕxqegºkeN<¢Ù16Þ•¸Ú­ÏÌ41× `º¹}ЛŸÊjmœ­óÖkx®©Wâ 1bØ nÑ.NÀ­G§òe¤6²§ZñLªržû¦S“ìºïâ<èëèâË’l¸î °·Úø^3a;`Þ°qi3ÑF¿ÚK_¢«ÉA¨ÇNƮŸ1“GÌÉd™F”âc-ÇÆzÉC|r¬ÐÁïy”‹ÖÒX£ô^÷™œÖ8¨¿ẄN/ÁsƒL»«ÍÃÈü#væ?pfó‹Ük¤×oq¶6pº=öE%fŠ—åtèxdH ?¬´ÿtÙ[ôø§EÕk  à Jkâ ÌÎµÕæl»Í}¦Ú˜ƒr‚mâ(Ã~·=~¢+öDIºðÅûÁ±˜>;.aŠr¿LÕã__K¤ˆîVÞš83È?ö¦}Œ›®¼x`ßâÑCz$.N‡˜ô4¿´ sj„øÂnyCflñt¶ãÌÇç'êÂ_½8‚qF„80'%Á{CM ã väpëÙzü"]ºëeåo÷¸DˆÆ–£öŠ2ü°Ã*it£÷lç*˜Fr8Û›Cê„F]tÌ ‡?}Jþo;mÒwUdÚp¥ÖP–ŽëC;6Cñ dÀÍLín†¸IQv¨FÛJéÏåÎTËÑ}ß©=Rœ”=,.¥D®gï.žøžª—¶Tµ³Z•À°ÓÂeM·#À~εÒÐ…dévd<âÝÑ¢…&ìk6—£Îˆ³ñ÷¹j²R±l½ é^…!Óç$6Ú†÷(×ÛYÀ—Þkd¿Ëo3ùïô¢Sä=­œ®C…‡¹—8 8³4 ‰¶åòOË4ßpÑhÀëѵåRº*|%CX,y fˆ‘lúš²ié«`AéRh–»4$ÿ±½eÍÔßs/ƒ´ä÷%çþ|6o=ÅÍ‹Šµ[GÍê/´«çÍ»!ÀJ:ê44©®yÚ5¢ßg·ÞûÓį¨Vù>³…]×¹î‘>æPr\§feÖgi?!b}I¸›ÁÁ¤çvûÄé;`T_Z/͆°Fåÿ×þh¨Œ6¸—ÓÕ…š6ÿÔVR’x”— XÑá€Ñ×LŠÄ µÏy¶T ‹^C.î”X°%å¬Ú¬@HYu\È×ÖAömèô“õukAevðcGÍG:´´ÆÎÃ&v|Rûž÷ßÞæÆp[©²’w"r²D!eQõIíò­ÛŽ9 ÝEÛÊÉt?Ð`Õ‡Öò3UK³I>J(º1pãÁ~H3mRôj‹Ú¦ÙŠOTãsF­Ù-ý¡ç4—›Â”xÿ«¨UïÛ£m[mfF¿@ÿža)åpÁ¶œ­¶èkÕ‰­!ðJ— < 9öž‘û£K[Ac ¸3GÛ/@š‚]Üw¶oÿ„Ê. ³—È…ìØ0~î-ÐëLŒxç(¡®‡ƒæÝOK»¨‘\}a%QÀæƒ WŸŒÏ•ùÑÊ?²âÞ•m? °D€oÙ__Õè;>(ø}¡ë@2pÛ¾>/g9ìW_O¨É¤$8üñã/º&Ô‚êñ”÷#0l®YqÝ`Öü#2:ÿ­Ü¡xU$ÑØ»­-3•}a2'3çĺPùÀ'6¢bËßy “kO,ïä¬4E|µú×£qï ÁªÏ6º¿ou½˜ÉŽÁc ,àI³4ˆñ/`ÒÁT:É8¡?º\hoébz45jâk‘%º ÎR[ê.·&z†Û¤›å+ü0³†ß²o VUI»|ØB9+gfKa–~g+£œ*>´¼R5‹À"‰ÿ-NÚ|H®À©6ùߦù%Oh,|y£Ç×Á Îaý©5‰è\[|< z&ªÖfaj67ݧ"*àÂÍrÕI/ÜÅ{f­€ø(ãÒžÑWß@Ol¹.e>cB.îÔ,àè$Íu3ÖV$$,μò|¼³àV†Í@5ÙŸcÞØ=LPcxQLÿÊ—yò Dq g¾Ÿ„ö;ûô+Y/ª'CîĨԫ–  fØõñ5µ„va, ¦a¢`h¶BóÙ:o»¯Iñj¶ç½h ZðF‚Ä£-çA&´ÈËWmtÑ|dXx$”ÙßÀ@°Êµ-ÁÞÁpÎÈ®œÍHÅÁ=4½Š„ɘGn£JÆ»-FÝÑG sû¼à!|Œœ¥ÉiÜ XïŒp¡&7ÔÀ*Œ§Xi$öïD(|Pì;¼€–íô"%ÎIßìñö¯É…ë j“FË›šÖûNý䙨|2ý‚äèëiÆPK±=6ÑN>Êqbä;R2hA¥”ŸåÈ´¤ ­†TR1€±mKçW[‹n¿†FcL£0¾•¢÷$=$Жã]†§}û}5ÿaAŽm“A¤Üd¿ TÍ/ÓTú€z#"•ý™'N¯¥Oõ‹÷®]{iæ‘ ›Ïm&üO°¾kN/˜Äæ‡høßF”æ¾Å¿½FÇ«ud çÿ˜± /oÚ½í÷—yš£n_.zJj;nNŒÓ&C¶¡×1²œšÝOâÅœªÈÉÁ¡%é_„l‰©>è/sŽ+^­ÇÊf8š‚vo¶Ú6åY¦ËBðMI {,×f¤·(¡8ÕaLo¾<×7(¡¦Äf"]?­]EòƒKAq%¼…Gu^ÌíùãûßB'ˆõÉxéÄz)û°‹¾VÇÉøŽZãl¼ùÄ!€¢a(ŽtÝsŠKäŠ-…Yi©™ecžàáêHL&šo§âÄ +ß2=úK­MPèöl°é7¦F¦Ö|q‹Ö›Gž7¯rðÑ• VÛŸ>)ÖUL >û”Ìp åyåEuÚŽbóázš—ЄOŸkmá&"gÿÿ–'(6ÍDÓ•ƒÈD½’ a+  ˆPÈf{ð‚ÀÙ”-ñü€¯«Z/ì3mᥖ9ÂÚÅ%¨GÇçØ7‰5R‚®8_)³ ŽÕ¶"‹X À]ËtOH›²j]´ƒ×ý7[6½ffõ¿ý¨5heO&Dè­Ú,]‚æ«ßæ¯ÂåjSY_‘BÍæZj#9 ´±ˆ–íÌa'²Ç6Ò ýðH“þo0ög®âU1?Wc£idEïlŽhî ÷yíc˜V­ü;ôU½~üƒ­íŒ¥ùŒkfšfö™mÅR2†Ê#ͧ}ŠÁ›i¯¼Ï2¢ÿ¸ÔÕࡵ@È.[&0Ó\µßÁ†¿ÌoD^×i'”7šè×X®zmñ'5BÞ€”æ"Û‹/½ãýˆ¤=@ÛÐä4ûÂ94¼ QwbÀ…ƒðƒØGJñ°F~‹ƒ?n\uÍ30s´ÙåZv_ãîÿ,Æ!³Á_ÎûZ¿î³ý2–tœYûãêqrÔ„±[U@t„´°°ôB‡¿F‰…$Åéš<þùé°÷BÈ2~nÀk˜ ƒŸðÃ"1„b±Á)¸~§ë¸ÙP;è–Ê+% ‘™ÄG@“êUÎ'd}²KÄÔÀ»»\hæ¿î‘§VAÞÃMñÖ~8’Ÿ ï«5uÀ1PiqJ¤a±B=;#a|¾¶ × Ébos3ýˆGå‰û€A¨‹MsÚ¡Æäü¶Öàó0Küîde›Ùä*%Ø-Cãdú@r)Ü@®p) Ë»äk¦?òg¥ŽÆ+8HÐã/wöñØ u÷çÕ×oÕ–ˆštßʹ?X+:mТV}•ý 5î "~Z‡þß;F-_Æ¡§zâò'AOsaIo¿%ä…iËj»õf%U!€Çù´ 3SпÈBªRð¸r ³d| òÙ1¯¾Ø•ªtR>ßEõc¡H;Â"®Ð¬…îŽåS_—ߟ¢ì$-ë ¤¬C¶ú•j£—m.çn† ¼©*ß`( UBÇB¦b)à/ (pk:€7|T—Mùh¹ISÍ›Ñçÿ&Í|(»/mÃjÛG;9Ý Bí ót>'âðêd>æ`æxÏ)u‰Ç…g$ŸÓóÈa™Ù˜Ca)¿À 1¨ÿºjVz¾ˆ¥ßy>s˹‰@8W¬÷ÌÙÕ¿yhÒ´cã{ èZ'ãy"QاP"¶oåVn€ßÉúÑéê”A);Á·ƒ>‹Œ+K¿¶]Ó¼®K±±í¾=A²JG_iâüßÀ÷­y6a¢$È'OFªL[rIwi g]ÊD¤¬o¦y4­ ¨cãòa;ýføÂƒ¹¤¥|Ÿ(¿§ÌhŒsï²§.!'}Žo«WzÂÕ%'>ÏMÚ= NÄ;N¿p8û“L¥Ò#Òµª·Ëø`û&QF GMÚÂ(– 64å¡sx÷¤‹9†'^Õ™«ø„öº_Τwº‡ad„3ìÄza¼]Ítéç^Søqkho£·ì# ™qó¨;ì¬f `6éÞéòâܦ­ ‚Þƒdû„xïüÈ9žér}ÐÞú¸âšåM{Õš*Ìeâp5ráÐ^x›VÒÊZ¬B ÅØ³¾ÆÉ^Ò3æþÅ ³º%SÖ[ ÕßþNsÕ´ž£ï—¦¯GgGSvf s»ý C*Bœª ¨Zj8ŸŸO»Úi5Ô­û;íd‡¬ê:7…_ìýóÔ父ðN/ =ÃAWÞ4f`$P&äc*è•< Ò¹ìÿ¾ë•,åþc§´Àw ûáóI èh_Qó('ËrÀºpH‹4W›µN\à8·IË-Ë$µÄ¿í há®ùì0i}> o-:ßn…‡çþW•ƒBqe± úU$¦ðý Ÿ·H°ÆbcˆÉæŠr1|™b{‡«BF‚!5¸à^¶ÉسY¬©N}áöC²qò¹ ¼Ñøh<¯GÊÕÞž\Å¥‚šÂ%Ks¯ŽXõŠKââio˜¸<ž$œÞëkXêú–úEUóVÆ3Çf)“CRyõ7ƒVÌNrMB¥@^³?Î…ý÷Å{8%q~[©Ñð[¶¹oÍ·3‰ïMhG¯$ÖéTÂMkm_˜ß×%["¯hUÝCj¬o¯+æÓóEðŶšóÜkC*IO ¶<¶}%ŒoIÃpVÑÔ+†´Å²õ=ä´mc¡ O“˜Œ‰-œK5•XÂÓÉ£ù=:›ë[cGÊìŠOÓí8—®ÅÀý¯õë? ºJ3h­ýÍD7)ëóCyœ„e ³vŒIl¨îºP0-CHbÀ•îhrI„Ƨ¼™ ¹­$È"¾YÊYoj LŸÈ}ø V™›ô)ÉNŸÿ‚r8I5õ’Ðíf•ÏIçÙLœdïsgB)–PÔk„ ZJ‹¥[ÆŸ¸`žÆI±nàê,årʉesôý)ɬֈƒHhƒ­žÙ™‚†14»0޶€rÔ‡fmŸ<¼6»#©õ„#0å!ô •‡ñŠVØ#šõP/Kza¸S:núG2¨¾Ž–žÈÚ5Õi +u6$ò±ø,ñ[°þi*g ÿsg¯tsÈÿCgDzŒÕ2çûV%M{ÝaïÈŸàßxèz.p‘yœ?ÈIñ ä2A©i@F§ƒ&BS•uÆœ›ýI§keˆ=D¢¿żv12ž¶’Áš¡Û½GÖËÝÅb„„PÀé2*ª=7œŒÏÝN~!ÄŸVb'Oü“ÈšI›)½Í¾ Y†»(e;í¤]~sçBî—££û¸«ÃßrÓÉ]÷KFzWAe!¶γÜy\ i |*?pÒ6Ô×/OkpÞ Ä¢Ú®Ÿª¾Þ3VZÓöLø^Ÿš½úðFûUj".bõ¼ƒ‡Í°+Zý5¯å›eIm)fÐð­ã™¨ŸNŒy_ñ­nÿÙ"Ì,´½dÝoW‚Ñ»Q¤i(™Ì3=‚ÇP1my>Ø õ@½s$1DUþ <5'´vò’Fñ®Š¨güÂ<¢¼{,í»Ÿ€Zlßðekf{C™|Tד[è…dLmÌF%ípâGKÙ”~j«´éóT+‰\*±e³7Ì»úÔ oŽ<‘Òžl·¶ˆ ·)VRÒ™˜}È{j8r!5#¢Ÿ2C$LÉù^r7Ò¿J ý ÿX/÷vI?ÂI†7Ó³u‹M¤ðjMDÕ0 ó]  ¤ã¢Sœ$ßj(·q¤¶K£(–›‘!C#E‰õƒ—.–8»ˆµ 5x÷zôÑe–<8(€ Môý€»w![;¶øøóüÀÿ𸖴¸J’;]*>b0TVË pa,2ÅÔô aüh0FC31y2ÀÓˆ-» ªÓ&¹šæ¬ßäÚyhé.sää^8¦yqàü7¦+ƒ:Åœ,b«…¸¥½¿6—X{VÐÎ#6:©‘:q–ˆNQ)= WÇO¢gÑŽAEÒüÁRdòã¯Ùó;ÐÒjË-ë‘ñ+JŒûùžFËÙ‰-í?ëËÞ.á…‚ "$“]e÷žiÕÀL ? ‰ùx-C½ò…ê6ę̂‚™Fh>q̦îžñ+hMÀ'Ç¿šúW[šäë¨Å®y'a´Ð)A¬µI‡VO°7=©É­%–ßKV)aše‹“× ]‰ ñÎýÌÔð[·$`xÑoR¯éz2y­Öú“Y|{ïååkª£—ðu¸VqqŒIƒKøŽÜù6ñŸHš/W–ÉXé°:5]ÉýÒ-F÷æñqÃÁÚ¼öž F›g‚’|¶hA·ª?|™µÍ¤‡9BÎö#_ÑË8:xù°¿4õpã8œb0½Ga½ŽQu‚[™‹:iÆ >æãû”Žgî'|{<„Þö"öà U(¨D®òÄ!u±å¥1:/¯Ò¯P“}ûû7øŽB6ìfÍ÷)½.S4kùØç”2Ÿ‘‡„Y„—–ª uNy„s,NB+š7ßß pƒý$ ½Æ>ÍžØ÷ð³mù! k×§ÿ´ íâ‹>äšØSÄóDG]iW¤76 GTËÕ|÷âiá3cÈwÂjþÍÝÙöC¿•}éGÓm[xp™(#„A;ó%’eÑõUjþg”µÞÞ`VœM³³ ¿“à@þ ^§^6ø EÞ¹~2açþ‹•$äo´ö€G©-µÛŽ —eS´¤¹Ìí#Ð"ʸ¼”>9‰ÎÚW2Ñ!ú[õ›ÇJ˜b1¬©¢ô§1çê†,ñ#F|ÛåfmÞ?CûÝ)›ÚµÔÍ×ýÒʃð2tœó¯ÁtÁœ­h¨s„äããÙ` ’s˜Fˆt0$Åe­`¾Z;WÆûÅÒ…"ªE”†q%MÖ>«ø{œl¶Êw`õÀ(XFÁÔðZx|½gRão²ÐÁ}æ]pw7²¬×·ÎSÂa±œ*WÁ ¨Éiýã…Èg´Š)›BïÔ-‰Ã·L]‰«p±Y÷5r !´Pœ‹É­5sšnÜ=9ÄWà4°‘J™.,@Ö Xµ´³ºöб}o…0²‰tÈâAâÌønz¦3kÃXEŠÌ2¾áÑç.IÚ§×î×å ·ÁSç—6jeeWÒŽÈ@æ PwÂ1¶g,‰¨ÿ`ŒY®ð?Až”0Ðßj~‹€ú[s Yfˆ!.4 u|UαTÞmD߉qõCŒŸ×àe–¼¹ÿ©%y³¬ï÷ɽŸï»ŽæÞþÛß>]5VÝJÉdg²t* Ü*xS¡ÿ•{‘¿`ç ¡¸~Æ‘÷µ7ª §[ä0R£( Y1M𢆟zrsNÔYÎ?!@uƒB>°q\ùFø z„W½VGk*€ëŒ Pölõfö*ÜΧøVlé ¾´Ý§”ÒºY¥ê]n^+[öe¢1¯à…|'²Z»ÒrçÊáѯ7Ô¿Óð@:LòC_aØ€=’9D›ßÐÔh£ H•–´WŠm¦C“ÇIU þrÅÞ½"¬º%€Oå=+Sdõrf°ÝÍdx1“3ÆÄÛ Ì'£KF€xVwðé1§ë„£m¹‰ Í»_ñhÎÞŒ4.–ì¶CÍ9ò=Ã3»pŸYünô>CËÆßÖ¿ÀêB¿FÝ×üÑî3Äò]AàL?G¢£$…·­WÐî­(ö2:uE€Êcå;ïcX Î|@f±A‘†Šý`zŠª,‰RtþÁ¬$/߬q®ß°6íàâ þÞW‡Ý¨­)ƒœùầõ\ÜgoÀAgÃÎíˆ5%Ý7(׌]òF•oÜvúØ.ÉÚV$O;huZ[¿øS^Ó”ÜÀ[cKâ·¹’ZUTg/=>,a/ì0‹ikÇ[ê÷Và"m†#É8 ! ñùM²ÌÞ»„ïØÖFfRCQµdÿ¹ê%n†J¼Ákx%`ËÖäÞN²¼`űbË7ÑÚ—Ç=]äïÉ`ñ'W39÷Ü|pï5—ý?Øö$g–΀æ^¡¡\;bÏÖ[§YÐYadkJ}Gf¢p›¾Û6–,¥¢»õ~Ù¦¯¢ s] ë «h7Zç„úVIþ!<6=;eMµ2‰í¬z«îÇ vJß!Â<:ó@/ÊynpŽ¿ùý~úÖ˜ª3¹“9¢ßØÔŽå¬S×ûT ã„jd;Á³_ûÐ =M952°Ö$E` ¹‰Rì ¹zܵ$mCòÈ.<<ÙÚbI›ˆOÝ…ïI¨/áv“£Ê/) $¸qT†–—ƒ « ¥ÄFì K]‰¤:I„Ó€äù^Øügˆ}ZÃa ßÈêll0÷Ä`Œc¯`BGÇÞkDT;ÔE^.œä·‰gj”õÛÍ–9qDÖ†<é(ÂàxRÅjÂ[Ê%ªÊ†ÂÏ*ËùÇwfP0÷?¥d\ l%íKð²OpÂéãªݽ¼9—¯Bdtþ56XC»¤Ï¡û¨"ÆDBé’Î𭉆å¦ã˜=Ô¾‰Ñ’ý@IÿOq*#A‡Ü}hr²5?pRÝ7LJØ F »Ì[˜}ÙÞÂö DE¬,ó(”éJ‹WRó*OŠòš…Ž]½_Ì![­·ƒ¯ô"“ŠüÕÿ`pcçЇiŠûÉ£+d¡fЇ:q»° ÿ†d¡}ÓhÚZ5Á†à“{‘¨uRAî¸ûèèñ¸ÖmÝ›‘ kEãÃâØÚnLÄ‚U$»ïÊÅc´kÝgiŸìC[+‚sÀiA 1„™9~ÓºUÝ Øçé¥Ý°s³ò)¸%Žþ­¡}Íÿ2€áD‘]̃bmmrí^³2ÅøÖâ)±™`2§ç±ÇÔÎ,0&¾2ð‚Ê£<¢+ ¿üÖ½ (ù¹ßHôÃ;ʰܪÔE§(ñáññí¶ŠŽ(VªPÐ/¢ê)2Y¬O¾EÎÀ (‘dYSA}PV—k2‰Yüzg·üÀ,â%>*P?rq®Ttt5*ƒ¢‰U†!8µ.Êo?0ã|¸—Ý:7"“¤l±¶~RAœÚõ}k¦|ÊJõ1Á)Rm~~ƒŽæV"¬tÙK±±ÿÑÀ¤®+ÚaÀ~¦Ù[È7•‡Îd=#{?ƒj–ý óþÄïl»”ðiùße _ÕÞÖ?‰žsÔ/Üms\âÒë_s=Ü™.bècZΞƒ”G¯2@ààã޾Ä^ÖÊ…;J é@YÚË‹B#b0ÙÖká7þÛÑ"^9±[wᕎ”˜ðŒ:I*‰sW6oe œ7~è€ ¶äâÕu»#³4Ò$ЛÜj:äÒ¨·&0,FÙ_fà¬ÅªQy£ÔùEN#@Š&FëÙ®ì%áøÐ” ÁüùpÐ Ìð¾jÄÝûJåÓ3ïsçO­C|²U‘p‹Ãýˆn‚ƒWEn’Π ÔÑ{@êX.˜- Wý4Òâ…ÐyH(/»FPo×ß#5p áz#›#¥>ÒmÊhk¹U\sgM"u]r¼ªñªï÷’ú—Ò•®ÙcÙô(¦¾í/ÇÀaTµ×5!¾ïgSÂÿ‘†âÂFϺ¡¢ñ~¡z©tgç÷"éo£Œ©Nâ² CV ßö²ºˆ‰<9G–äI05C€# »i½’ì‹¢2;<#¢d¤ z“(_eN_¸‚ïM¼å}ýG…°s¬°”Eѱ)«ê ^‚·?Pâ•™1#BÁÿaËžcûÇ‚J¸ »„V§'ú²¼’f¡t©lŠ["6iŽ;Ä4;#é“þÁ†AÕG& uiÌÓ¿Â}QUZ£þ+hžö÷*÷ï Q4-,øÙŸ3lJØ(Üö‹q ‹$M×8•­ÀoصyÛtÝ×—•üë"ëm޾‰wd¦iUk‹ÞÖ'c&f÷RáóͳAf¦Æ¸læÔp\Íc~Pß½fÿw¶_a¦¶J*iø6þdTILÎbÈ€¸|…[¹Zø£Ö5'=õ_‡¡,Ê1D¸,¿àÞ·øÌJOX[êEDüõÿæ-d`Ág'ÎÅÄôjAgn' éaˆìžBŸÜu<íð޵5úñÔ¾…{rM™´Q:®cÑæ˜ßp,]·qªï&yDû£Ý½…]S¾+Ák†O©=R“Ã)v^ärSz:ƒí¯Dö|ÖpuX¼¦O_Ö3žzG£ò[z‚EcñCá&>®¤¡…»žd¯\ýòFtCÕíõ¦ÝPÔ2ß­¿q×Äö¢ýõ2øƒ3 Ý=žlÿ×/78±«Œ¦)Þ(ЉëX«ò‹Üã £®Ð$gÉ•…G¾-ÝÉzxd=StÁ——÷CGv æ§û^81"§ GUû²ÿ› *®÷ gG…ÓÃY) ÷ 3“Â%;Ž2]­IÁ¸z~yjüH_ñ¸›ŒÈw÷èüÒê‚S@^ob%Ù´úº[œ¢4“¦’d† $H5 !#fIÁÕUL†e¼ýt [߀7΂j‘"ý4 ÔÏXYÞ(žC5ãÅö‘Þå!rÇösÅ)&¯¨iÔíVSô¹Î2Ö™¦¸ûï•Þ5y¹çÓÿF¨Å¿‘'#«XÌ %­éCQomÚ‰ðþ`œ‚WQ©Zʰpj¶óýÜ@ãÆŒ°ÕPsåÿûX¤,•#³NpL ”žž åKøûzÂp䦥¿w}äïáµü®¬yŒ…A¶¥m¦‘€Qanqêª(p·md¤Æ†ø 2Àz»ã)q Ÿe2]“I[°ÖÇH†Ÿ?7?Ðá0lÀ~ cò•©¡0Š@7ä,ãÛ)¾±‡¸,S‹ç9ζ¢à E¿nð­Ì¨¼ 0A­Ûj„#l¼+Ê»7ò`èe7+|ËØ…ÛðëïÅÆ¬>™sSC„:PY?¤×õ ¶$Nÿä 7`7YêYb7L{õ7‰®íMqÚ§ïñ¯ö3JÓÇä}û¯J+hᨪh«Ô’'´œ‚¬•‹)ò(F ¹S¬;ÔÐûÞ5›íºì`-Ÿu¦+Œ/f5çÌÊçÓUÀ¾šŸfÂo÷rBnI+()‹G ´]|^;],µAÊ¥ˆØD›¢]ÉgQ2Y"`(™ e×7ËV ŠŒÓ eWŽ•`º(¦ ®{»™[=ƒƒûÉH° x›lÉ9Fm2ÆbÊTS¡¶Û¹æ‚7¬eiÑ•&MO'IXWÄfl¦KžLxšKWä(fÄn¯š‚ oå^P'ÎòJ ^=[¦$ðú®èÄ碣¡œ•Ý‘¶ p–%GAMkêy޶ìšë¼ €42γ@ؼD!éî„„™‰“ÆY@ÒâW¼¿HMvùeTöZ\÷ã;2 ràâBúÄ¢€.{0)òðe?ü?B?¿¹é„s»'ƒ-ÞE‰öµ@- †.·ku%ù'7üÒþáHÛ¸BðlP@N”Šâú§ÙT¹KQÅFEM—­Æ7gˆ q8…îµFfÝ»u-Õ<³E„ՆüÃpà´JP‚n:µuXØÀ€‰ö‰¡Ðý„jÂú«õØÓ©XaÃ3^kAê£kJ×`\2¯{Ñþ¬'B ¶=‡ÇM†´’Ju¨¡œÇêcÇbº„‘âp\ÕÒèÅè&óRBC¹ô?,föZÊ9„ߥ;s~´SåP{2EgŸËçØ_LåÞ%&ã(’‘˜.ÕÖÏJÖYâ«u©Ýd‡Í®'¸‘e‘X\ÌBC5?' !µþä ŸZaÿµp$úÆèD< ÃÉs[«dúv§—Å9IdqZƠΕËJnDò½É&N!‘bH!vHâéjê„–$e…m¦ª¢8äËÈ›‰“ïžðœ6Ï%ž/½Ó;–ßã…HÛ§A)<£&N›KrýN쓘٠™:P4õ,×üîøP_‡ÎÄf$0Ý×ÇþÓÖj¦}²äu° HWL b<…OàÔ»³ Ôq$·šAH°±ôödÈ@·ÇLíèd½B†9Évܪ:•ì¾XѪô Œé®üÞqMð€lÏq+ÙÑîBÑ<Õv ”r;¤5ŸµÆ¨–vÀáÅ܃T$ÙÑË3*ue²Oã@Þ ´?CæVz"¬AÒíl°¨§,7Ñùi»ñžU)8éŠX$€•ªžøRÌ ÚbßÅðK ã”O=ñ¶éŠÂ¬‡ëmhçx¤;3«ÆPÉøQ”"?`5E,b¥ürN ZéÓÇpÝL-±3’ál+ùί)s}äl—Â.¬_®ÕÅ#i^ý3N’l—A¨ƒVF=;ôÓôÞF„yÑ*ô-³U©«4ÿø‰ÃŸ¾+ç w<ª‡p‡ä™qÊ0\DUIT²éš? þˆÓ2º„L±#*:~‘oKmó¢Qp¹ìu±–cÂîqËO¿Íáp´ÆûgºŒxÄdhÉC DÝܧò;¢iVòœeiéJ÷ 秇|3YE«„Sº¨±Ý¶n5ä«i01^y!é´îwð…Hàç|='è“ýÂúY¤bÏ@}Ów\z«uø¹Í­¦8L‘äm$;G©`BÚ_00Åv˜åK„5'.ô°² Ï&ƒnðu/NÓ8;Ò·ÒÄ(pPÌVî\ìÖãG|”•­ŽËwž ã a6®fì¯ÔPCDN_©€/BÀí[«ª4*;6úvMÙ¨™ûòºn¼Q¤½2ÏCÉ?»>×HB銒Wº‡…„¯ÔmZšE ç™fÆ€i¹üÃŒÅV×ò´ò^¹.yŠù%YYO¬ÈèêJ¢PžM*ߌÙù?d2È‹’¹“ó§¨Qfe¤¨´t“›þ ñã{¡ˆ$À žã& «W½¡é~C6 jX5r-4?3O)ç}=Æÿdî^™ƒv‰ó#8c zXl¾ ©Ï_ȃpòÐB{ÊM¼å-¡"°gÒù8 µûƒÁ;zræÞºüOs'ðå8‡ìíÉZÌS`•Åð/j‚`:¶ ¤ô÷ÆËÇ’÷~›qKÞ7fØþܦ;N,J¾ª~CÇþÇiC𓹻‰ååÄgã¯Ñ™òCô® ʇ1ÖÀ®¬Á·ŽŽÔsë=Ù ûìeâó@ðk(@K¾ÊJy ”Ç£»YB¤¢ÜuøöÙ~3Òâ&C¹3ïßÁDÀÓñ|’Þ¢®ííAÕcÏRW4.ß‚ÒsJ"̧˜ˆ‘¦/zÃ&£©JFéÿŒ´ZèIsU ú}‰¬T°E'>§$úÖrìû¥2<~°„J°iÉ®ºh&Ë»3fèzÇňE(cþ¤¼1¶Œ•u¤¿÷!QÃÌÁŠRʰsÎrÔ_Q2Ï!i¬Ö‚¶c!sั‘= ìP¨Û‡íi€b %Iµ¡qÄ-'iýœÂ´Ún\ƒ¸>ÆÛC`„^6æqg誵M`Ϩä±Íäu1- ª)Ë3JMõÈÅ[šjå™ÐFqº[4˜%µÐsß’Â[ˆž­Ô@Ï ©¢ß ŸÄ±2¤n>êÛU-²é†Áo=Ãm«²MPÎëN³x)l8†ã}\cZ?ʹ©žM)`&¸óÚ‰m.˜ûËVmï÷8ø¾Ñ\åÜk¶:™M «|póQVóɄΔΈmCÏÜ»_¸W¢¤K\ª§ÃlÈVŸ™zñ$+}¸ÿE…Jµ_ü÷ˆ=ïï1H¶zÕ0}D¡²zdÐûºÇŽáæÊïÛŸå¥Øè½p(G¶çmŽ£¿½1û8Ì !Y¯Rö P[ï}¦g%´¯¸È/‰æú­ê?šŸÊ|}£»d‡†jÈyË„ÛwnŒ9˜ ìV- ëúZ({c¤$˜â |±ÅE,º‡R}l .‚ð7S½Bƒhúþ?] |£ œ£’ F95å[O¦ú'JTcÒ`èÿ8««>®Òv-òúóÃ×p4+¯|rߣ9á[ÿ0&xè=[Çf_€ÁB|s#gòzËm<µíŽË/qGªÖ¨ô7᪜ÂÓNŠØÖ p>z©{Ã-‘Èà3ϧLuENU3ílˆ+ºþ#*¶°ó©\Â7Ǹ”ÄÆ I˜*F€5ÓN›rN‡.ÌS3õU-4ܧÏeãeáø‰ä†ÀÙê!pñC§ÇA2ZÔj7Öco‰k™Ø¹ÚöišÊ›…8PÀy­'¶1dl=]—0esb€Ï8^¾oeDÝÿ7u ÛÈÙ’}•íJ¾öl§‹ä 9Dø]: ¹çËõi8ït©2 rþ<Ì"mìœnã7N–¨x©"WhíÇÝ:G‹¿¾íA²ÃC/V9Åš®B˜/Š„‘HU$üwÕ0%pu A£S÷N„®eXÍ¡0ÝZð»›­ÈäjaŸ\¯Êž6BÀVbZ\‡¹Í}†ôÁÉj˜àÌ\bhnÃäv«Šf‹u^Xíýú°!Kº—ô‹êÙ·U(#Îþ¯ƒKR„÷2$¹Š®p°™pgq´ná&ú°™Ä[Á Ó(Ä>øíÑnfK3†m,SDón|¸¼¿UË¡²+/Ýû(}À¦¥Øˆ$ù KÍÙ¡©Bæ\6Ž™=z4aœ:Mk"xûgG÷¢+6•K˜jiªúª/nÅ¡žuƒÉéNÇHþ,/ii¢ž¿ñ‚°bu¾)/g„¹ dˆé]­­i0:? °Ê‰8L‰S¤Ëßå¡ë¾ÖÐBÚhCªôÉyDß9úÇ¢¿s]§GÿS¹ ÏG– Xÿ"–““†xoH—í¡8*…afºÀ•¥–ç ¿gtjÂB#>#îTË£:-–HXà2+¯Ý„Î)ÕÊnL³<³kìš®±àªïli-GÃïþé[Hãq^’Ë~L²¹I |`Ôû¬.–­!«fÉvüt.Tl>)zVpÄÿ’¿øÔä*“O»BÖ§Ä0a(“hŒ‚³CHÚºêlÚÀÅRG‡ìz»´ÔíÊÉ©€ãýìNR‘Ma¼™ûñâ‹é¼ØDB©1<¨jÅÃ¥q?x‰P!þÿšN‘½>£ææ‚•Ðw.‹lýä7ÉVð­7kXÕ»­ìÍpàbõ  ÇdÚ_lìÐPô®0á8μˆzÆ(O­ ÿgEôŸÞënµ“¤3KU|Eè¢iÎà •Ú} Æ8Z'RjLÔ =Ë Þ¹¹;4Ô¤Û ›h>B¶1×j‹Ì7¶‰–U[+œ<˜R2)kÇ—›B½£F±Xã…—·BCÿjd ê.ïej`sexSk‰Ñ¨’‘_&£™b~Ӗܧ؀9Ò†ô¹?ýþSͺ{vQů>œx¯ÙÂ\i;+ü£rs'æMèT½J2 Jë)z®ÃöA¹÷@w†IU¾Ð!3Ìønç:]Iq?oYé&[]Ãô?mãŽ|P["_-âmíGBI¤Ù㪀¼œDØ¢­‡(¯ŠÛ9OG­xi$ýB% =º7HVÁPNT™Ñͱ&  ,L¡)l0,hq¼šÓ»“ÃÄ.ýÓÉæËŽùx𢽉û³{É[ì7÷w¦WRꜧèä3ÐKÞ¶Å‹‰Ë„¤•€¤EE·#„ç³6€#®éŽãh‡*åÀ®+úC‡í·Èkð}†®²NGv– JñÚ¬Í×ä°Q„ý}SwË×î±|p,ÑøÃ„Óž},âl{°*:¸ù`èé°ì[[²âmýÞ‘Š;¾V‹az¹þÊãO-~¸#S§t,q4(-n|Ùÿàêk¸CýAALö/ÝÇ|H¸DšÔó[·ßߣ+•Þ™ñHVã1¿³…N‚tžE©è~Ûñ›ŽÒVÎpŽá7ko #_ûõ‘(³æ4Y*:¤”FBE/ˆ ä¶‹h3ù 4ÈË/}M£L©EÓÃ7pê@,yØôðûn´‰‰„’¸ËÜû‘‚Ù¨Œ†,¦EƒŠókÑbº"DCYóIìÃÍþœëÁÉÂÑ®A²1VÕr§„L:Öõóc˜DîTÄØ{fìX5˜jùµ– …ˆ0Éײ-¶–{Î}°Sd˜äs7á æ«êeæ?¡Ù4ùŸ…ŒnÑB¸—Ú‰,¤žŠMá®ê ´CØ0>¢ãÞÄ×çˆÉxŠïâuìUÁel^LaÆZ›Ó™º1½8E“¡ô“¡[K“U̦²k*g† àà ß5NÎeÇ­Àöר΢äÒm­ uÅ{J-ű½ƒ½BãŽÆ`"E2Ø:|dýû"÷€T‡£d˜ÛÕEÏ .6}ùŒÂ¯’¯ø.S¸ þ2²N[¬ÜëþÇ  @miàÑdRŸ)ª Ç‚TõÜçεåÞ¹d­m`!•¢´%èBxsе>.-5Ÿ8òÓkN{êÌeˆ0U¼‹‡ô‹ÖÄ š˜q¹¦|¥`Û0àEðCògç¥ é `Pä„ëuQ–7GŸ.>Þ#¹ýô@OÑGÓÐÞóä š{§cXÛÖ²´ÿ«ëçcÎ/<§ã.«©ÏCœÝ{£â¥*œÆ ÿÊaĶ —Ä#¯ åP[¿æB³ÛIS %kZ½ú“ñBÈxS™ EI'ûu w4(œÕ±LƒØMèÌ5P¹Br­ld>êIâa+Žyà[ñT‘AÎOM’êe»»QȯäRL&ÒIaÐ&ˆ„¯®Çø Ë…q'ZVƒ}kÊE¿Òðj{MìÇdÜEIçvÖDÁdx„gX¥n¥\ÑÍêÁ!„]À«µåqÕ˜A@¦?Øw•àF'"HûÏ Ú{›{4ŸKØl*\ã„ÒïùŠÖ禿ô¦õžd× Íx&õ÷ü‹×ræ ìϳH«|f.íjäÚ„§þÕN¡:úŸPFƒ›e¹rž‚4lµÀmÁÃØ0çê½Ùþ© =þ¾¶” ›‹Y|¯¦KëÖÅ_õÉÀˆ2û¶Ö+®CM¦e²»Ô~i{µÆ{Ú7cº£Kö™í@Úâ¡õ¹ìrd x®ï5Zä(!Kä¸M²ŸkûÊCv5ë&_ˆóEJöçܠǬ‹ÊN5»¹Qž=°Ê / r:–ø>^Ïm>(…%yõ§æ”29ªäjò‰M” n#¯†ÇigÚI÷Ûù6Í0~ý.Õ˜“+ÒÈÿpÂWªÈ©?îÝ-³½ àeÔç¼#‘↠s>³­gΕ9Òñ½Ä2hC§Š Ždaº=uX•ŸÖÕéjù¿–¯zaäÐþTëòr&cxiäƒûV¯Ä[›0ùSˆZ¢;²–ºtø½å†‹#€½^[¶:×›§É)ΚicB¿vò‡$”ÔY£Ûç6Àu7Ê#geVmÔå<ϳJ¨Lúc6¤'!bâ6”Žô0¾>-»Ú=žBO„âØ™vž&fx“IhAø{ @Û]ÁÕNbÕ’\•@™+ðžßw«Ä胳¶=h!¹Ec2ñ,,ÀKÆÜz2ªÝ½í¦)Þ5KÕGíU7¢S¸ëH¸®óÌI Ý7øi“¹9ü?ʘз¯ájó77å+U5Ä ¿hg¤Ë*†3xCÞ¹1?¿, ÂÆ†`jòªtÊ·* WöO ³Rø\ `*±Òo½Aà?ïØŠ}—ÑhKé±xs¯°*KÙlqfÝP ^˜®™ƒ/àºW{Y1eCwèzC­õŠS_ÖËœÓ4oߣ¶ReÈ_IõÊtB÷.Àr¸ìèT­Wƒdú`æSцN¶¦gº?ÓIê¡¥U‚g9%†S±1`ß”bA·„Äs(š!˂ܱ–*߆ËÙ¼\I“pïg§?ªfÚºD¸·ºKZ^þCy2ˆ8a¸Ê†YÇQLÆ¥‘A:5™§Û©Ûó!ÚqJ`Y/S»qé<ùl¸<<”óŽßñøZ4Ÿ´ŠÅë x£3l;â 6Â¥Úwï_;|³†&»­ì%ùìwÍð‡PX]pÞ É-}Ë­¸6Ì9Áþ÷ÈÎñiÐ'.´hNÞ._Ë¿YãPX;粈÷v­¹uY†É¤5Û˜¶†Ú•˜_(#[ûG¤9;l[§°`EÓŠ"4úÅëÓ T3ËžVu'¾®³Å¬I ’^ @Bm+©PÍ3¶`Ìü$A‘½b‘[£Ér)û+ K lET'¦ôÆÐ?8«b3+Y; ›ÊePv|† ± ç„©÷À;ÿnt•£ëõªGã}á„ØiþŠ«žL|æ²(t›·—¬Ì›4Pò, ¶XÞƒ¯Q­‰ø?ýNÁN.¯\ã@®  ¤9 òÅž¸ÐÓ?ô-DÑ7ñcªcX0³íÜ~ŧÐχ蚛øP«Ùb6]Ò<Õ7ò/~.:#ˆ£-¡Ø¬]êªìÄ$G~£¿½°Ê ÏÒ„”¾€ªb™ÿÁ¯Ì•˜ú™Rg$ Ñ/|=$ÃÀ¬<ÓÿùQŠ$ßç­nÚ 1cr¿áÀÅÜ7)íÂÁ©1-3ó4úGû¬W`âqyö6IõÖ5æùõÎÙ’’%©ÙÑý®µ†N=J‘ÅÇæQ Ïà sJ5hÈ÷¸×OÇïœFÆä D·Aìâ—² gYß(­Õ÷P- š¥+t»_1å­Ôóv;-k¸q©À~(vBp÷ÿ¹æÿlóØî6Z¾¾vöÎ+`2òÕMm:\#ä$^Ëš¬€yRO2ž¾ >þ‚oP‚~{³\RM o¢[QjÎÝ!Ytü.Š™Ú«ªuº.„›„\”/TèDlý?uƒºSa‰ëŸîâ´rôV¿×𦟬KÃÁ®;°å‚ÈÌôý™¯vº1¿o¤pž¡¼ŠÆ*s43kôö B‘”ìÞáÎs¥óÎîÌ,[Iô$ºƒ¨ó˜h“bP"àœÊ–2‹¨S¥ ›æÛÒ£¥Ê_ä‡Ë ª¬^õèªÉÃd$}Îm&] lšo®–àjÁÀ˜Í™È¯Cµ%?îEe?EÜñ£Î0¶ia]yú¹ ý;Lö‚WµÖè[&hßZ—@053^@µÛQõï'/mjO{«[ å‹iaÉÌ^Ó]Σۈƒ¡¼ {Œ£ZÉ7k×u‰O!³ç"ˆÝ­åªÈ!@YH$‚h@ö]9ñÅòÕåÛÔ0–Ôó°ˆ-§|¦mTk‹â%À¡LŸ”O~OÎ[+/)ÛÏþõД¿P§_(ì¬Éù.Ø·|­Ñ=ó'!í‰@v¥Ž>q噤PQ¢áöŒÑ3UçÑ…¼qǵ¡Ôg˜V¡7?§]O C’ìý¶ä£óÛµVn£ýÞT7 …~Nh™mUXWÖ‚ s¡ƒ²ë§|8ÿ.º ºBûxdµsR©†ðÊeiXGl–´UMÓ“†…~zÅJ¿ ÀoWxÐrùÿa¹ÄkÚ•Íÿˆ@æpÔÿ|å F¿ÙþÅ*!l¾— ¸èÜhù~ôx`áH =a(‰ X²nþ ‡N }¦Dq‘Ã;1ÀŒÐZHV@|Ó0ÔžçíRcO&¯†ãnPôuÆm8š”–6å„ÎQœú$6_»qÏ(r6v6OVßÅÿcH޾׫„˜gý!1•ýÓº“ŽKØ=Ç_¨ÏûmÊÄßgЇ>—…é©©¼a£EA6gHf¼‡þÊÈAÓ}10 Ýd^mi%ÿW”ï& $7Œ z|ÍõÂË@÷˜¹Ôš *Ú?Ębçî q jÙ\æ+!kTíèÔÈRgér,¡×x°_°Yj&ùÝ”ÓDÚêOEÛ§g1É [Ì܈¶ø6kj-Î…”uNñåV Àw¹îð¢2‘ÉB‚"÷hÄV|Šƒ^ÁaNªµ·Ç³mþ–I¥NˆÃŒ|öÌ4}øT=„‹s¦­Yê—IJ/SõU¸A¥¸«°Vá37-ž'D¨hs=ŸçŸ#be»+AŸ­mv ½Ý‹[ÜáÎhŒ¶Î^ãñáŽs°´¡»«Îžzé–Ê“³ï¬R+tÖŽgÉÇ­éâý¼f¶B".Ç?Ÿè–niÔ½@QØÙédÍe ¼ýG8¼Þö&¨Ó˜tSžj>|d;k¡w™ÌC.+Zœù9;Ò&ôÊE£ú¬¡éE gš `KïÊ6‰’Ç$7ÛÙd-­¦k1º.l÷q™®i$×lG¾ÃÌ®äæ6½ |(¹•­ú‘/¸ìø†*7l °âfôÅÿéIFvÀí®îÎ%€×±c"Y¤øZùhiñÔ½­3ƒ $€—},Á9IOF.-{Û_c<ÝÜñQåMJØ'3LnÈ:·afI‘YÁ!áþÁøl*çwªâ€¤T=€'×°³¯tJ`?8tH ¤ì_5•hóÓ–¨9’îØCñ]û.c•¬|ÇÇÌ:¬fP¿´$ˆV§‹›#È­0h†#¸;âï[ceY(v[(2¼O,Ýw ÀÐtÇÀˆúêøqÖª/ò­›%YRNÿœ>!æÉ5ËP¦Øræ„•!üs ²렿¾®5¹ ¥al­Ë¡“7sÿúت˜à#É« BzRþ[¤UŠtÿØö‚õ›Ã›î‰,R_°ønc 6‰¼ýŠNëÖ¨XÙÎhǧe_+¤'Lòæq>sêëÂ{éN¶÷å§@=¸3›ž#¾‰P[;‘<ùp¯tµÓ„d3RbI öZªëÿÑ㸆wûû­7 Nà ¤®'&»Ž:u´º¸«÷”:0òdþ¯½ÓƒTZ{UVlH7q2ÍÀ©¡¼ƒêë1 Õùxí(÷CÉŽ¢ïëÑÉåÍè+7VKtÝÿ8‹nÅÂ’ÈfD®8oÑæh£j+¬nªÉÂ'mGê®ñÙJ›_eD\žÜ2QW1t’M'ç¾Õ¡.e˜…F6Oq]Ù*2tkLLiŸ=°É)ý:.NcŒ–³4ÿê6g5¢ç¥ïMã«¿¢éÇ-=Ò§ìç=%ª ö•Vâ1@Aýsõ&Ö¤þ32 Ö,”õNÃÚA Û’´=ê c8-L%*ߌt#9BE&å©?þ]à z9'æ¿á^tP…©ð3O~œª›Âï™°¤Nî)]éDk­fB@%èVÁ1¸pyd¬ÑÃU§ì“zdT“ÐnˆÄ 5Þ˜‰RèŸÁ^ë¿H>9,{f Y?¯"‘5QY¨6Ø›KÅŒ»~¢.}+ûͤ ¯>š.4aÔ˜é1 ¢Ïz`i1콈·Œ‚ÌE8¾0<,ØwJÊ.M˜‡Äl–¬ò ì<~}L?žÍ¼Ðy4H5ÅÞé,°~]Ô­A¼2/G¼âd½saõÀë ôÌÕYõ¤Œ@Ø>(3WA1<.o„:ÖÙ‘.’h x‰¬+§e.0Åc„n4_èZ3s [#¥ËÿŠÞÖpÒF ‘lBý¡¤¯½ÎH•;×äÝ Ð¦ úÎM’¢Ù†N~3BÊ¢3ó0€"g¶FS;Ç‹i$jÀh+Â`D <êY{I°ËHÓ5V¿¿Ít‘èD8q°×_¿×÷;wF oô£6ï ‚òüÊ.û$‹•@Én<•Ó6hÉ&ëìÜïšëãiw ÒĪtD|`ø”Áj¼.¤Nõ—àƒÉ“!  E›v!ZA)L¸àM½Šv ª.j -ì€[ͪ„y÷Ýk¿hxˆ˜YlPb9ëV_íl]ê'ÿ)#'×>Ôxå¾ðÛÏ;Ñ+§0mÍF0Ë!u£¼ÍŒƒ(‘¾Gûœe‚„$@jž(5¨4’WäH’‚YX‹nÎðà‘·³ˆ‰@ùDœ9¾¼T´u«Â M­3 +ÇLŠÇÔ%¹…Àÿ§ òº¥$¤:IpœÉj@Ø7#°Ôo\Z¤çª!±1ú"¥ž„ø}ƒøáé(‘÷´/»BdP{°(H¬Â|aMWüç=ah4ìíÐÄ_€áÙ>‰[ñÙ_¥œ<\øˬJ?œ›É~GʦnO4®tÐr¥ ¼  —û)l•¡e¿zTkº"é˜iŽæWiš]àæŠï±—à°„?8ºM&¾Äø˜'XèÄDº&¤ýDy› ¬ ÆG¦o`7®£-“ã¨[’‹WcBãóô°tÕø+Ë´òž•åNQ3wŠGa’îÅ õÞž”fª·Xl´+AþuÜ¡é}è;dbæŒòs(/´r´k;ï:ÈT44ÛÜÓ ˜gÞ %÷OÌÍø„Tøîqd\)§( vÀ5À£F.ƒh,Í]Y f眫–uÓ”ÅÞtwúlþ /ܬ 2̃óûÒ«cßY½HK˜ÐÊ+ &ìån–èÁÒú@©qUédgžI/r¿2Z­–!YzÚLðè"ÏHKc G¯N %Å_ÀÓpyÏž¶À‡[ªT„˜/¿Áɂ횔@Lmú4x L[^½ Û ˆsÝ%{béͨ»ÇŽRdµÕÂb½8"ê¸ÑPšU»´Tª¶L‡qÌwIm·ÚgÍö-âвí ÌéG-‰ëà"¼¯¼Ê=[©¬â¹'y‹,"ë­8· »†°þo [P+D|… s”9I ü`~A=Ó²#ûhnÒ46Ò±Ý5*ÃÔÍ(ÞCÕ, y–1”&ˆ á0àÌücI¶Ùn´o]y'qüÊ6¢ÄŸá /dwE·c3ß'Ÿ£&-)´£ 0%¦V/‹À$ܼÊo¦”ÙÎsðB qÃÕÐþmÑ]¶¹ßðg\^âÙ,i>äb‚7ù)­ˆì0Vïý· uxˆ„£3ä¦WÁíY|Ž¿»b¯xØÄ*gÇWhÝ2É”m'`ä46Ž-†aU»¦ªÚ¡ÍêóÖ¨e”Ò|µY‹ŽË¨ìæÂã‘À4×w°Û?ìäÑ1"íé¿{ËTàÿ¨ÑZ_-dWF_« zc4¯ã.æ˜>É7úœ›œðŽ»KLH„–|¿ªü‹ô71úÓ´Üèi”[,H“Ö90i:œTNwb¢È«€½ótY£3õɼ€™!‡¯A‰7j™—òhù˜è`¡†éç'cê†óeAoLSÖ"мÞ!Ø’¯$WðЩ³y9N65nöWÙž³V Ó̺°#íˆúV¬ÐmÄD¦º#Ðâº0¹Ê±ú–Én^ÞYÆúa"u=šæ`¦UŸ_bæ “ -C.Ú¢}™«}Ÿx?+`e?Œcîרœé›N¬9Y›}O›ÊÒUxŸÇ2&€}°ÊAÃ׋8ÓK™©B$„ïaöÊec]óŠÀ5<Œ,(0È©ƒ,~í¿Âx>Ô<¤]QÐ hþ"“ñþ™Xç<èRžÌUæƒ:ší á$¯„1|J±«ÏH Ú€ÓL,8i|}Zî…hN5ã ·`ZYõ š„&Ç0š¡Uí†H¬…Ú¹ì L/Њä.òµ¯È“‹ž­ç~G¯?œmlŠ@ù 9u]®¸¤1.×™)à÷‘· LÛv«C%RozìˆdØŸŸö‰Çiàé§-óµØV§iOÙJsÃmÎÌt€Œ]2ë&­- ^«Sk9£!¼Qe’P ¼êÜ•<®N%hkELBéÐhÛE‚ŒÃ%ÀjÉ l}IšòäºC÷ÕóžJ¯ƒ½æý©å‡„û™exüËFÕWF¹åVKÕéõ˜MtسÆÖ׺.< Tê¬Ègpd4XhÛö7ŒÆîÖ˜­öš&°Ìá¶V˜Ètµ.¬ôŠ6yY‹15Œb\`ùÚ;¥Àot‰²LýÈ^Æéé_йŒêK_ dEÓŠŠ™¬ä#àˆ’®Ž×  /»Ä“ *}±Ê4sʆKA¦½'ö†¹‡ûy`Qt§ <ð‘ΤÅöRM’”Açwl[Àx'´…»A;_PKòßiï°Ç^Jœœ8±:Ó¾~cúô†¹97¥7Pª`6СZ䉶!7"*m0C‹¹Ûû\hø|D’€ÛžgB7ï·¸õŠÝØdO kk¶Vº_F1&â³/ýÂn]+ Ú=…Vfl±>5&uñ`ÖÇúÀê-0O”˜m€—öw3Ätn |Ì }¥u„O]¢`‚ å5ºèN•s1UÄŸîñªc3“†C%ÜA. Ѷ<‰åNYoûì™tÎê^´‡+é»g¥|>ôÎøºÅir¿¥È@§ “wj¥o -yè$Ï¥M»cUö³ºLUªé™8·éÉT«zMãõ›Ì ð@O/ GÜV­·“sTh”­Š‘îaºR_Œòõžo& µýìnðc,(±Öoã9txÏ&–.„0$Lžäƒ^àM¹}Su/ÏC#ê3£Xm5¨€ó[Ú×÷¨uxäuß\ËMe3Òb,<æLÉ•ãw«ì…42Þ}æ'ô”wZlen¦ªò!cþxrû„eéã¾&r›Ê…z{¨Ôò}m*q —bžS0ÀB ˆ6„¾‡ç@DÝ“€%ø.HGsÄ<öÖÇŽ«K @“l ëÒFxÔMqóÙS^#±Qx,·bJöF`e­ ¶QOñò Dè}Œk´Z.a¡,Úé\uŠÕOEÀF=Ô+’„_‘ˆ7&Óz›jÔà?­'* læ"ßÜ7Üz]kÅ(_–àÕŠ6Åc P†*¤ö>ˆ?9dfPŽn{UÔ¬_$„Âs y“³m?E2 ŠÀ4'â‚6¬dƒØ%Ò–\Ã"Ä™®ÿíˆ*uäcñ.ñ"{JµJÖóø¿&“°å¦ÞåBY"ƒ@­ÙHœýmæ;¶¢eKú²̼3'j;EÏ£ñ/ þùUJ°Àê…’WÅ» M:hö÷wæYúã}耸4k%¼è‚œP„â»ü5«•O¢=(®úNÀËÍ©'ÿnWÙIuù|êbO¨Vï ^z™ü™$­´o-%%4ÃÛ”%x¼ƒ7åõU[/¨ ÕCFÔyr×}†Í;ç©DÆ™8̺’Ùî–FT1dˆô¡#CRÌ‹³ýO¿ôl?M·ø7Ÿ&ùô蘶/RL†¶Ê ;ϤÊÖefÁ¥âP•®ƒì”DCæYTR[$ ¯aYó—ÈÔtudÙºüþ{KiFµCft$šØÑ+ŽΗœk[²è×A5þ1:Sà'sdB¤\¨‰µ6•tBÿU¾gý‚ÕˆáօȤ¤&‰'ÔUËèçãêÜÄüÄs²3—‹Ü6I`J=§¡îê•IÃô $ß=xi“‡¼é¡§.ó÷SÙ—®­{H„ªaò^ŸÍ.¸ƒrÓq{£`…<j?$ÚqÑ[¸Ö »6Þçfc¡VWÿN”Mìu'k¨WîÉê¨SD p‰Ÿ„‡\£b!,áDÁÞ¸©­5]†»ð‘w·¥Âr¨H[…ðWÿǼÉEpó˜+ñ/0S8‡¾§Ö£‘3º›þ`Qõó“£f¥GÍî]O1²–Œi¬x–hîëöw Á䱂™UvK`ê…ËÔñÙýLüXr„ÓÈígrÒ“q€@êJO¾—|Ùâ,ù„:œ™“¿ýÿÿm£¬b4üœø+­>»EL˜ìì ~!É)MÄÊ$ôâ´2‘Äøå[놠ÿ£)Ö ë?g/Õ9©:ê g/iõÏ•PÇ ŠªòØ@™Û ^µÐÔ·Žÿö„z73=±’zULÀN¦”D=DÆ»œ[}êmÑâ–†:*(N+îŠARÉËø=­;© ˆSLu(UÑîó„µ×@÷H#ëG4ùd>4ˆÉèáÞÜç» KS°*a4’˜:sñ¢îò«ŸôÚú§Y\Ï4iƒƒs1ÛÀ©Bt%ô©Žì? xèϵðJ‘søè¶‚}\žUŠVñ •ø6ÑŠmÒŒJ‹ÉA‘pÈ"}ñé´(>Ô²×lÞ~E<ÙÀÏ„” Vcñáä+«ÊsµÊhj|KQj¨½LAÝëôÌHyÐRFG\0]« KÏv·mùoyÜ1'iü¬âœî˜ ´)xËÍã5(¤Å®$Ü ñnó»UÕòRö?tØu«#á´ñ†=MÞ‡ÝÑ+tìM(j"«j¯ü–ÐFït²_wF¤±Ì–çѲ²^ Øa‚y¸’zy|Ë&U®˜;„l Ó£täH%|ý·É»þw½fšš¢Ýe‘Ý&:σdŠ«~]°ý?‹E8ÑZ9Œóéçô×§“ s·líQ*¨4%Ë;%¨P³{j–þîŸ\t1o‹¹ÌJo½¢rë©2‘Û–¡‰¿É]ýYMtXÿ_›iܳK½‡õ±pbüRÕ^í&Œ`9­¶"ÒŠ7½ ÈÖ^­Úû1ï[%fël€/Xš_ÿ{"ÝX¥ŒOĹõLßËíJ;ÎèFÐ?£-f`ƒ˜ÚVŠË^˲b•e€Ê@L¾¤¹5¤±ÌYת {ë†uWA0çÖ‡µ"Жßó}þL¾·ü7D MƒgT¨–÷Œ Njí2æeûĘRl¾F™¢¯~óMÜ¿®¹ŸçZpáã,ìðŠSkÚŒèTÏ<0q.u,;+ÍjÛ²Ðÿ†Áü3½Ü",Å«a1!ž,`!Cã߈î±Ó©5ïŸB0ò™ðlçcòXkuÞWðÁÆË²›Cüw»Ýsÿ»(Äz—Xâ?úüÂq§üÇ!ÖifKŽ¢æ4&¨Òij^ è}B¯)O]òFòe2‘kП ™H2â]AðCjAW[Õ[/ê²:¯R]A\nGÕ|{àLj ðrÃTÛòœ ¿xãÖÆ#êw2õýËaÌÁã6ëÕzíHµ–À‰nKXúÐFD1ÕuûÝwò]ŸgÿïÇ¡÷\¯=ÒÞªìé®5‹Âüü»#]Úú¤hˆm~êuˆ#^ÁãO_PzÝËùŠŸ‡†d¡e=›û>74‹ß «¯Û!ËêzÏ8kL ÚGÿí¸‡'›AYÀuAáæŸpJ›œ§Üž7˜ùðˆJíd|r‰ðÝÛj|")4°›b(õ_V‚ºRQñíE÷SÖ-¬¿,`ËÄEãO7îY}—çø¬QáæY!ULB"¨R—LxÍÌ®ï'5'5¹ìÓÑ Kô(üÓÏîµG7Ÿ¹ˆ‹ïÐFíZWì›ëæV¼ç$ä$nD½º]¸àÂOc±( ³4HǹâÕ6s¶ýàXKˆ/Ò]àæ&夕H'-'Ø>›nõý‹X°@˜a…f%… Cðsžgšz":xó}#Žcà wRBÅ.ØÕ 1ÿÑ ¶Ï|ÝßGDˆ½Û摃2l\ö0pM•ÂܘÎܨÙÝYj'9–ÉÿÄ6Y¹!C°…bô‘š[6×ú[ÔÑd"Üy? ¸H>¨/g»%¹É0¾åÔeûƒåî>NõÒ5<.éx¥…P7ó:ü ºPk‹m[ÍŽ •ºÂ×Änž†eÝ7÷Ê••¹`¼:¹ž šV¸Ë¬>Ñð9­¯ðЇÌ&w$´DçNÝ™à×^”òö×Ìà}Üu`]ªÖ¹í¦!=_/ Èñ¬§1›èÀ¨½ÊoT—Cºt¸®Ìi™íºÒd,nIBº¢ØÒ}‡í ‡ÊæÙÄC²¨0¶m‚§j·±WcuŠˆïk|Yf^&xžN<ŸKéSñ4s8e—W YúW^Kù­WìhæeHaê÷|ö[W«¾Œ5ÿ 8Ý?4 3ÈŸÁÎÎ2ðG.(QØ]¶³|¿Ðœš]dUŒÑ«Ï_úD¼h-3‚º}mÂ/(C}9åT „`4öªZdÒu0B*d¼” ëï"Úç9¥x €‰sýP4P‡Ž-.Ú·ý“Ûšj2b‘ÛèüÀGazNÛ QË Gú‹ #†õ…#Q”×<0S ¡}‰*p©ñ^‘f»0xÞ—×14Ì[Oaã<¯Tô¡Ã8s¥hã ™o2{ aVy6ÂÝÛ^#ÓN¨`@(n?äsÊ# .N{ˆí˜ò"죿¼ÆÖ Í¡9Ì€÷Æ–„N”¶$*˜a Â1cõ¾K"NLCmÉæ“kz!”ÇÂâ.¦cÿy©UX…X….ëþàø~wGè‰C/ ‚ˆ<º6½C` ^ã”ÜÛÏëÇq(’‘·½%ƒ½21KN¨Þ#2}<³AMþîEاê®p4{U¬²„Fx‘+}ÇuŒ×ælðÛ„IS†³µˆ²›ÚÁ¬M?o’9µ9‚›vwÔ(hýóÇ©æÁÝ»²Î˜÷ŽÑÕ?f³Á4’î8Ÿœ¢UhuÜ‘Q‘CMy3•<©9Èléž ¿ú5ó uz¿ˆ¥rB9‡ ÿ¢”åõkÐ×x4‘@é÷»RPÏ?ê·ek² ¶V)ÀÊPy´7›0¥ÉO·ú*½µÇûÍSã)Ïã…,VãyÂðyŠÈЉ”Y»ƒ öX [jŒ±š€¿[ ®W¹lvÿ‹fýHuǧÀç6Fo..qðé˜-øN@Ϧã=èïXA,O°¥™2éÏ–÷ëþb…´ˆÕdj‰Góï•–ë{zV'd×Q(šceæß:»;B P:ßb΋!g«)ËÕÑ`·Ã­¼†¼á¼Ã`ñR ,•è¨ÜÁ°ÌCú "0? ÂŒÍÊeÀ•Ïñ%ó §Ý Šº4…ÀU–²MïçsEû^(0Šî¬Å‘ ®âlúO©ï!±å!Û³‘ØNÏ'øriºót6¬ö¾%°Eh@îᎩR²¨·m™|k;‡ŠR“±ÙÅrpÛѼ1jnð;Äyä6j9ÄMäÛÜ^Ýn¸ߢރè_k,^H8Y™Çwßäï:f¸%è7¦/“ò‰Æ Ðp÷Iˆs7³|²a•^Þb`Âl—¥¿G íhyvðßX×#q0ËEM°ÊS&œ& gÈY™ðx­3ñc·½A'9ÔÿŠåáA`¬PZ¿I±÷áãZ³[jY M>Èr$2:#†Oï?CY×¥>fè.‘(ÌŸ«Š«r¹¥É{ÒÞê nòUn%TÐgÏø;‹Þ݈1¥œ˜ÛÞ/Δ(ÉÉsÇœ°›16ˆàÄ Šbè§I/r³±ÑÙÊò)Æ uJäP•å1† näñò:Ñ÷ù|¤-Šn;%8xéûQ‘HÅríwA Þ|EŽ¡ŽvÀkNqLR' .íë®Ãç8Pÿ|<8:(¬ê‰ú¼ƒ{yãÆøöY"¿C¥Å,]Ôâà °ûœävÛy8äF™¼¬¶FpŽj§ô}íµÏ×\vB'»;¹2Æð'4@âÙ”ÿ±êŸaû÷ƒßófI­çƒòí¦^dØ›Q6K³p½ È'3¡!Д)á)^êš6ÕÖ!;Bã™ßùá:êGnÙ5å»9"šiižËbú£Þ§_äs?¿l¾ézøó$ÐrRŠ+¸™ä½ô&Ï MœÁ§¶¿ùׂêLœµô$ [„oBùoC»^d_ÍC\ÆÝóÎËÔ&Xð;š;)¬’ŽÃÑw+ë@’º3â$¡u&9pi%]½8g•¼KU‰ê¸5V?Ha^e“FœI˜Ä¯ àc¡Ê°^ÌôXêƒ4— ¾´»_šØ"ÆïŠZ_S|7ˆÂ«¨]Ê‘úGæ²>º‹Í´Šp¨-&!Žäㆱâá´FÚ+yH0¥5Ýyã«ê¼ƒT÷Mu^·‰dCc4öų•*ýc#^þÈÂÙuCh: ª÷;å<ÕŒY-]1 ¢ŸU,IS˜?“'³ ;)ý›tþüi3¦qŸ >Ñ寫ƾÿz9îI Ý³ÊE{„6ÆU•5Å¿²F',‡\‰Q¶„ånùÌ.H}Øy°RÁÈ t‚Q˜t!v\Æ'GŒ¸Ç•WzÊA›Á›â/¥ò×g½Áæ|+T2éˆí5±Û`o3áÞÉšB¨Û6+ãø^L|"•þy¼OœéÏàhÜ Û´ .ßLûU£4ïijA!ýžúþ©Ýó¨‘Íd-D6SÜ»OU¶iÖHóƒmšãdÇð³dÃå&XúP,‚•­^‘ÓÃÝëÜ~€æuÝz–u™ë~0tbó‚HacTXä.ÖWÚ"âX]e+S›ÖDÖu"Зø±õ¡È¾pzý˜µÄÓèo"<{KwÎì“v5õ'˜™Õ,Ù‘£ ®a5$~ö¡y7˜6J“5Ó~FT$ÏÊ+]* ¿ôÝã®- Ëã$®Uü_÷ôÊ]ÍžjqÔ³íêZÎ.€êUÛî îÀK7òpê­IxuDabS¼â»A.?F³ ô’nK·‡ôÙøÄc3ŠKÿÓÏOª£ 9ãSг>îÒÜ^æ8ÓÂq8ãpèCÙ³C9QdëQÓ`Âü¬¾Xk³ÁÞi°®‚'0¿ âPyr4ÍVïp­ÿQź¾$mW›(BVi({D_7„7œñÍV‚ÍÃàÝ2ó·ÞÆVÕè¼%W¨'lYã¾¼µ–#8ƺo£ ¨oÍI8´A!- …R ªü$_T`[¸¸×4J7Âßf/#ò4Ì­ñ~JÓ yïtg¨DžÎ×£EÒ\KŒÓܵ8¦6ÛÿRÇv2ÜHK¬÷åFqº‚"fcN|¬*”¹k(^™å™™šólc–XmXya)F8Ó&@qd´®õËÆóÅ£Û„NgÆ)ô&\Ã>Ã@’ sÕÿú£>ÜüÀmIu ÏÇñ-Ñý¥æUʑ֤®0S.²/è»ÏÖ¤þÍåæãøuÆàƒ—ráÉÊñfHšàZq„D?‡ŒîÐ8‰1¡Îÿzÿ¯„À±žP…äv/=š›Td<c/8|м¿½¾Ñåía«u°|OÉâá˜Lk`ÆgÀÑÈ+ÄO?z¤îTUt’ZÐ÷Ü1pÒ¸–·À¾pÐÍÑ'f"Q©º«G¡õpP˜'‘|¤”€A/šó­ôEÚˆ+¥ãÖwIG5§LÅÌ8ïd²@PâkÿФoúè©Ü ,2\™‚i6¢uó²v~íM°ê£ÓJ%k§Š]Æûr"`Í,–÷«”¶Á3¥Jé;÷ÅcD(3Uy–Ü8è87ï^éè¶Ðð3#âÐ,S*Eæ"/¨°Ž9ÇÅKê·Q n– eâ¥W°(Õ#ÚÕøxhW˹ì&‡¯fü$I{»Eï…á†=²ï'*%ì±ð‰4$ø—<‰|DŒiY¹Õaéb^ƒHóšÅ(¡Þ-9øRÔ.âK äåédAÖSÏIMÇýýo×ñØÁækOˆÿ"QL:Épm¢ºµ …Œ±éŒcû?†<µ~§½pœ·ÎÕìsˆpy‡Þ;ºg‚<ÒNSÐq²Õ…øg&:}è9ê2ÎÚNãùôŽþü4º“^ÔF§XNLÑ+Ù[+3s#È)_zÔyúü5øF¾§òiuÞ¥xì‘o- €[‹&—riÚ·û£Sf_þAá°ìRÖ®rü¹ñƒi-¹N"¶)G˜Ä n]÷ÿÏ#»˜p‚Y§?@I.†V¬¿ô$s‘ž7«ñÂëŸ^ƒ”‡ý •¡1¡ùi4(¶ý3e²á*Gq¹ò›xE‹M¤!_Fp1u¡&sÑÓçC@áÏLÏî¶VyF©Á4ˆ¯ëÓ|€Š?º£t¡**U½È¢Òm›+Õ<’ËþÚ3*‘Q% ¸Lqp’ÆÝa¥Œæ@3nÄØ5|í!»C{êú¶ˆ›µ¬±„<$峎Ǵ1iú+¬?üAk@2:G ùœû~l¼·ŽzÖx­ GoİTáý¬¹Ÿ²þã•Íü  Õ_âÞª9ILå2vàYåðp>ýÆòƒ«¬œ”7¯Œû$]KJ7:–I`yHüRÅ9£"oTÛÙ‰Åóê+xmá)˜ÿÜÒž]`:†Ý©à OdÛô^Ly$ˆ’Ô–2³ç‹Ù¨t8µ´6Ñhž‚}Z™×¬6‚^ëº4¨9H~ü¨}ŒðÊMêõtÇÆÊEÃ÷_ÉG+E‹«"Gw£8L½zL§:ZŠýÕ‘œö6Ç.æJƒû2<‹QyÏA¡ tA?Í0ÑçžW/¿MA"Ë-ÎLI{Çé’›dù ¦T݇@NÁÏp ú-ªÙlªmŸÖXm•áÚ|0bç³¤× £<À6ÐQ¦VÊJy匿DÉ40`'º -’¦$¢ Š´ü^ܪ®]?Ž¡Ÿ°ý9¶Je ŠìáK™õë¡H4ÒËù3ƧóŒøbûõ‰çïЋº+›~¡©…]©‚ÏËdbÔ'<“Ñ8ÀM1ù§;NhM¬e¤&ƒô"@¶UO9³Ø–m­{¥Ê˜fЇyª7\àR„u¸4F²g?6ÓØš¼›{woÉ=|¸oØ#I.ð៕H‚’Ôüä¤X,ßÒ°r 4fÙ»š‹,¡§¿¹ÿ°gÔ>CÙ6øõÇúwiÛ„ý_úbŸè²lHÍò½ÈS<‚š®HíI}_†ÿ[A{³Æ««0.ðAåðÕlZ„™Z$ò÷{%vÍœ-WýCÂ{mï3bÊ“èæ¥þ;U7”«1ŸÐpè\¹åèÜŸwAæ«mA×å«m¸"†'(u¥Ž…&zr.Ü ÞN©hþš9¹?lTD‡©#8?c°‘‚§àûó<êL9o¼l1Thv›”{ÍýÌÑ¥`pð ?þ† (–R“룬ð—‹|UÆž7œÀAclž«NQ_ |%Ár…úùTƒC^F†H÷ú£"fô¶ªþàS6ÔœvàV+ªhÏ÷ÿ#>hêrMûXÊBˆÚ„_9DèV›O^%„ºs‚¹i|«²ÉžW„Þý'#Üe bôØý¡1ô ;2Z`#`Hþ›-´ƒáº¥µU3>ï?‹Œ²²Kä+ð€Ú­sêÏ·Þþ¸ÞîåØN¸/[œe‹Ê†°ô‹ÎÆàë#»ÇdÆ)1½}ɬØ!ÕúƉ° ?€áÛ‘Óˆâ'¥ºÒ΄-öê´Õ¸ÁÐJ AIÄ[vWñè}-îÌÒÐÆöȡ NmõÍÖÒGÍsÍä)BÑ-9»²]’E$7™·'œZÉuÿxO@×Ö&+ j@-yÑF#ñº1Ÿ„{ýZ4H³²ïÎ#°ªØXÓàßĶË3l_ÕÿüRYÌ`¯ôŸu莀–Ó Éô.ERÅL%Ò§Kóîùïbæ™™Bù}†éŒ(”DXxüÁÓ©þJtà9L¼4Ÿ²¾ƒ¬géçcïè”§Uý—Ági¹ŸË³¹ÃnÊÐä<ÿö’å}¥šL$”X!ñ9f}:³u3¾Ã­j€2„Úzݬ¾z™­ ,¿µzßZ"?M\' Áè(…8È0æ.{ 0Åôõ Jïu<ÞG\½\àÄ;f¹ßl3Ë¢KG…¯¦ˆoi1%'©k@k§"èX€m)6A$D Ak—êH[7÷”?Û&yUQÔ‚?TDÛx´¶À¤(ÕèØé);¨x4œ¼€Ü9Óº÷ðAš¨cªI¶QQ’¾)cÖ\èÝçÕ¤³˜>ؼtÌÿuýšï᜘R÷a†à1%(ÃRHáÁ­Åß>AÇMŽbíÓ’ÝŠ6´§óh— Ú{Ö‰~'îr®å5*'à–NRÆ‘ú +©3Îü߮˺>EzÜlÑW»…P^%pº À~œ=¹F$Î÷^*Êz§y[”×ê Õ“Iù¿+9°Hš@*T'D–1C»J‚ŽçËHÆO{ ûÒ'l}'Õí%¾Ù½ìtÌç=Ûé ·vÿÁM3ð( û]ÍL¹¥kæu^·ÂºsgŠrßûyÀÑ<¯vi جÞš}µ¿핞”µü{ëU2ø¾Wcà 4$ù&xh5Pš[Yx¹k £ï)HÖZÙÙÉâ€b´/ä"bY¹ýæ5ŸS·ÿ›3¸‹4(MúyWMo& äØ¡íèƒW›Þ¡+]¤Ž¯?uk§ùw”×Ãk‰æÇ¬‘¶vþºVI6y1çi´¬lFc/Y {Œ…öû3ž(ˆ‘¶Ïÿ™GÚP ›¨ž¯È¼ôË¥ŽE³ÏûªD_˜Óœ¢tÂÁÈVy8Sl/*)ñyÏŠÌ*Æ#ÝÈVžï.US˜’JJËÁg¸h@gqÕžÿl¥“ïÎ;ØE˸tX©è³ŸN½Aê3vÎU}¨.§"¢ÍI‰ùj—‘4Ÿÿ%žÞæój5Ñ¥ÖTj%Ñ5+ó5JÐ!FÍÊMwgÑqzÂè Ô”ú½öQN½~òÒ#JóKyàuäÛƒ”‚r-ua¼ÊŒÆOëf°6—#7•ˆ_,îfÿáúÈ–]ý±Ñº¼Œ¾…ðïö}<ék÷Õ·ÛP.WB'˜ EÙJéÑjJ‹zjR3·lÉ£Ýd;µá±ò"ò£­$`2ã“€^ü$ éä‘Áy^ ¡©Ú‘ã}þcO¾ %@Ä?S\²ŒøcÓ^ ¥Ëày0˜¤€š{ãœ/„QlÆEïÙ=!´ú8€CÁ«¬Nµ‹ª"Ü'wSß•{ojõqs’ÁäÔѸýh ˜Y6’§¼Èîª;˜¦W°"%º€.Ýyuò–ð¯Ú¯ˆþÑÖ¶iÖÅ CS³ÜÿŠX(3ˆÁ9\“ ›t¸À{]ŽòÅ!Ñä5:œ·óÂP åí' >ûËäX.ݡªF¹[!Ç›>^À —Áæúʺçs žS*"<6ÿÆ·GSÈ*… Ýëÿ@ú¿ºÛÏZËr.X´¹gÛÖÏáS6hï(´ïÏxÜL†ÛÁÐÁß7éÆÝß%6dMØ0¤T¡BÏØóx1ô[rè=+¤/¬¤ýmZ‡§PO±c’´QSƒ0ãGçP÷¸%¤UUºkÝíGW€¦uRê˜@èÁÔe]†FPÀ¢ZK×5çóm0 ˜Ú!‚ˆÎZx*.m<2ò5='9¨It#õ³ŠP(ûCZ²£qûJüåAYÚÛjiRj/—GÓÈÜÐUý&*Ä% ,A¸0@r NV¨§³…ˆ^±ªÜB®{ÙvömÝÆ†hô€È@жðòyäÑ/{}Š;eŠÞ$•¼.—€±ýéÈ.Ö:Š]΀×W¹îeFË‘Ðv®Eà“°…}ôŒ3[øï“üD‡lÃn½¾÷f]"y?¦þð1z0K‡¼2øÏUˆ<—ˆ«²¢Š´}Sñø ßÇ(>¸®&`7ÈÓ•aô:˜ü§ ³”ã=ãïÚ½±Ì*ç,¢9˜Ži¬RªÅ5V'Û{†Žßê|Ó/BUDä÷2ÃÒô „»AWüØEáä³~n…| ‹wçÐ>%ÛÓ0òEŸ&!‡ŒIR>4šQê•“Óác$N¢ÍÂ1QçŒ_@§kÒ9eq=»¯8³JÇ‹5ñ¢ây\ø=túîP ¶ŽÞÀD ˜§HÀg{çÿØbç+ÍAè.Žc5²-Ò]ýg ŒØ-_C#!òËWYÔi£#Š #ÆüGz6§ñóp½ýeV}#K~/Ÿd):ûk½¯©Ðs•{š°ý»ö¼fó:þ“ÒX¸F£!½/€üS‚ô“–TG…vTþîüØlbmI¼»Œ[{É]»FT†¡[ëÌžrá7·š@•xÌ›R(û’öá^俳^k—Úâõ‘çœÙ& ©_5 ¦ÍòÚ@@[uIó lñó΄¿2X¦fXZÀ™{48^Éc9lý7,gà`N—k÷N–´aƒ³Š‘bÞÕé*‚“s§0ì$ @¶Ï(P—B&P>¡Øô³úÏ¿Ñí†G*N²(0¥“-*<œCÞsÝèÈÖt—rYÀ¤íT¬bp…ÚŸ‰ÉCeËŸu@þõ¶ú|¹hCYc““ý:1JÀ¤÷ò7kÞ÷Mæ:ï\Î]…—W3óªZ|r=·¿ÈŹ—àWâMn†‹È—XâiCuÌ›e6ÆdêŽ)ý¢MArÀ•¤Ä+† j¼´V zàS,Qý ¨ÌQ`Õ×Ý5ž¼Ü‡r 8NOb'{ ûìßûˆÀÌÃÂ?æUÃ}ªÌ=¦GÇkÓÔ!†˜Çg¿má¨ÕvïÊ0™º×Ú‰ND¹+ÍȈè¿vÀ èz|s Ø:žZ\þâeç—¸dÕ(Âå3FóAñëO…/Øcˆ6(–ÜhaªšŠÉ]ËôÞ¡éY}ŒÌ¿HNËáš쌻¸ü…8‚/?Ë®£H†é<š˜þºBŠTÄK°IOx\rò³½þ-3T[rËp2¦8™ì€NBšj?Žq»BËó^!ñ‡’8+SÚiF£‡#u£à­Z½–1lëKmÿ¦ ûœ“Cÿ¥)²Éœ²•[VÊ·øœ^H[K“bçKçþ…=øÊŠ0¬4½<®—B«U«†=rËíºLÅ¥Z’ 'ÄêT™{µR“Ý0ÌPÑR’D6°}ÂáhËÃR; '‹ü¢dMŸ ¿'AÆÆ\Fþsö¼¯ILzøGœ• e»î€¯Ý’–‹5‚p‘F*9Îoz¥<¤ì±.ÕÜhê%°¸±¨›J”Ô÷íËòÐ(î H ú]ÝÃgÁ<àyO[Âzù[ý°ËãŒNAÍØ™Ìö,F÷&hC0Ô­¥BÑù5¬_à5 Çø†ìª†K8¢ó#ksÕ×à˜ÄPePH }$“2°)Ï|õþ¨ì}pË3“±®™Žóæ+Ÿ7‚s4ÿhD6õ$àT~Š£¾áÜN¡7²œœšOÓJÊõ ¸x#\Aħuö%|­r„¤ÜSËÖ—æ¡ÌR·5#r̽z¸¶w,F†{fSa¿¿boìøàçPº±.&ßßâ [ú®=ŸI»0—Ã`èroù8èœì޵…•iMÑÃX2RÌùXÆnƒz7F«è (´¦ªÍ†ØF6¦#îWd¸65:¾‘LÿÝüËð8ŽæÈsQÕÉ£ö€“iA“B ^å6Žïädª)ƒ»ÚöFê ÑîÚg÷bY·•Ðäw /2ÐÐ\G"1Èx³N—Ïœ:8æ:,FÒ£¤+C¿LÒ˜"AÁVÞ$Ô´Îâ÷ùjƸ7¬*Ürjxb ²¶ÆÅ˜/­‡UMͬ“ .BþßB7>Q%hÁ¾Zî…õO–ì¢r·Zü…É&ªK’ª6#í„ ‡àgil¿0ȼ“¹„0. ~;RëK¦ÉåHÔ;<2È‹P{Ô™ZC é$ýN¶h:)9 ÛX ¼Gj¨¶Ã z½¸îÐÞ²™·Ejõ𲹫ßÄXzXrÛŠ•Q¾LöÊDx'! ΊT‘8ÍŒ; ›J‹÷uŸM¾78ÑÌI³=d„$w2°g goöÁíÒJ`9‘Á_l)‹Ì…®‰ÇÎ*ª´‘y&:Ð*â&ø#óŽ² ƒžð#ì×5¯6ešurÊ?Ü®Ên~ æ×ˆîke2>:’¬œ¤íÿS-{1 " þ<ä‡^í}¢Á²÷ÇRŽWÝ-þHl¿q#€Ù¾ð¢& ç0À¥ïš8›0°ÒaIÂ+¬3ï?H5Ýÿ Ô:•”ÒºÞ÷ Üã„@* Ôµ2‚SÂr£D`ŠÓv3Ô‡ƒ= ]bÇs+”d”N¦’ðÑE ÅN¢*0;I¦@R±1Ç>ãOòà MQ4Æž_-¡x¡Ð¬u¼6l+«ä‘Z~ú Î¹Ûâx›¹ÿQ 0>íæþkýk-y>²’,h6¸óñ6YóèÔŽ(¬ }øŠKà/çI¤¾æk¿Xtñ⩌ÅÉ-t–l_?Ê?e£„­×¹1ÙØ³µÒh9[î<Û>6Sqì´kïÎe*-–Gºõö7XÝÚéØß ‘âʽÚ4O{t”§>ba1F¹û'¸» íˆ]Ÿ¤ÀffV‹‹~'¾0⢴zô\¾jiz ¸§ÓšãºÍCéímñÓ–Ÿ©˜[䈡ÿg5+Ð|­FŠAݯRCödØ>@t´«bœSkA‚Òì̓ҊÞôù¯æi\Y.e^ÎyGv ê}•Õ©èW¨L6Cpül¨¢ü ¬3l¿ÒÜ8E‡Ìïûî=On(<0ž °péýñáÔpvV¨™3SþƒæOJ!žºÏEí£W6V®oœ`k…c³D¨ÙdÓÃWaì™îá®# ¢˜¸d[fDƒžs'3Þ#Ý+ !I´/Å1•­9U3À ÄЩ€™à3¨ðM*bÈ»‹¸°»tÒ/_Œ,'yhT¼6º»ï'¡Ì9&|×òƒOŒkŠ5„|1p}iÂ*¥RQU›šP‘ÏÜEŸs™¯™uïϦÀ¯·”º3*NÎ$Z²±Õáï‰'^ý¼b[Ö¯²d¢:°åÛÕï–]¶‹§–!|rÁ̵*i×ÊWÈäöÝ“âë[™,‘\$ññ ÀM›¸ZYo%‡›`IÍa‹Ù7‰¬ÔœåG=°ö½fÇqÀ"»DŽӕÏòCyGw[j9ãkz8аb:SÈSÚ›mãóÜr]# L&ì`s[.tûP1j %º_òCÛø!PبC¶dZwÏVùš(ßþ³V£Üö˹(`§@G\¿$T騮‡üÔñP»©ÁC¦¬IÛƒØÅ‹×'5lå9´Îzí…ˆµ¡–ÔþçrHð\ ‡éa±˜úy93U‹‘D­üŠ) ¢HÈpÒMÿVðA_Þqaå(k£Aæ«,e ìü¡~mh¯˜ˆƒxÎûv<g› Iøpþˆ9uWYñv“©;Fj–妧A}•‘uMd¢n‡v½qŽõF‹‰$ª–{(ÓâTwa£ì]ƒ•ô"ŒÅر*9«s.¿‘ÌâFòxΩBfµ¯¢n µ_Œr]$&…sã#5Â(w\/Q:ŒFÝ‹bÕ¦ÕÜç LdÂÃÔ›8ḭ̀RU.Èœ†á%g±éa¢¤/Šââà_ B⸗éAN|áYqõk[¿$ 1gÄÜf##»JÖÙ·=µ1ý­Oïx£Å™î½ –™¤*C›Ô.Ž`2ËgâØ*&l-’Á€/Ãýö¿:gì*dâNíçšš_–.m•ÅL#¤#¦ÑRÖÐPì`C#ú¶ödÕÐ ,Ù94Œ:œe aµ†®3yâuB‘¿¶‹qŒ¬¡tË( c°bK/fv1'Ô†®*®ùA›*v^Wri#P‹NŒ'r.÷ÍÒß^´:ƒcݾš}(\¨˜À VP“Å,ÞÊ4±jv.¡3Qtcþæ°·޹†3Zd÷1ìÑèú¼Õÿ†ÐC×_Úô·QNE(“ëo­&)ñÿ.àòq®^ì2ó‡­MÇE¯œ€ 8áî;¡[x”¦rܰý›Ï#Hz:#4„Kå `iÅÞÆF:š·@Ï6w%¡=Ä·{*ùÀø'¢À$¹´®ôàËÌf±u%3§gà`–?GJ¹±º742˜ò­ƒ«ß…¤þ¨v¤úÆÄ+.AöÛÂJW S²ÙÀÇ‘ÊZ¥*4Œ]ç7Ï›X#Œ-ÚG°/’Ö;¦1ã;éßPü¸ªá)–Ð\ªµ7¡¸À"6cº‚UÌ:Áï*ÐY!í¯Ð¤”¢„Zr/™/Ûèmú…·ñ*eÔk`²ÀUøN¿öF†¿¶ôÆÍás©jèýœ¦eþËþ?œËŸ„s¥EHȸ|‡¶Çøx2‘âòoùÌŒvñè¹)Ò“ŽÂÔîØN]Kšìð.Ò×bëkžH+»wWþ›žPê¬Cèô×.¦e Kį”neÇÇfP™¸b²±q§Íy¦bÑw‹—]:ö>JR¨Ìs“4”_²À(ÇCg’«Ø ’P•ŠHÍDÓw 쮈¿P!ÏͰ’j9äAÍÊ4CH£ È™l³-ó…•w H!pÈV:'°˜6#:†2 +ñ¨xèŽ³Ú Ì½é‰ßN—ÜtÏõïÅK§¯ï-‡¶kÎý‚Ñ>+^†Ë©ÔR°þJy•æö­ã6Z°Àå ÝòGh ;­p¯ÍýsA¿8ˆPŠp—ç[5tíܦ4>þ”Pfamà"ôšØzÄ:´‚ó}¥ [ÝÜØP(mªÔ¦gé'O½ã±ÌýDñ=•¼CUëDfÎþWª|©šø}wT3džIU ü¶_ ^Go[ÇW˜Y`é®TmÌ­ÿ‹:§$õsCmþ ^lƒ ¾Käóy—û3-={f×BäÖíÆACŠøˆ{LRG*=ç~³f«ö­™ŽÉ[Ü—½Øƒ ©,1ÔS¥aát]e7|õÿ“…&w Ä6Ͱ2òÃ"æÌM˜ƒç~Lïþ¹rAsÂË<ÕÔvH“bßfDŒ/$ÌÖ/Ëê1èæ2’z$MY  |ð´g²{¢šwµùTÙÞ/뢮ñûðšU“Ì^H‚h ³?=°r w¶A ‡p“3ƒrÝU³[G¤”ñ¯2²ž ô#l2=™*N^ÈUr梱ÅÛƒ¢À¨ÇŽKÏ{ô` ‰¨_ù@Û×¥ßΧ®étLO‹;²7×áÜ`»ð¶$€j’-ƒ8ðبû—1Ù·æ÷%9øÑ âãZñÇV—M¤3—÷™àkÞÑÛÎs­Ã{ÕK°À¬¸1•f%O=>MÛ8Øv;ZC׳Îmk\3[ñ1 œÍ¯{'däŒnÌ]Á°ä¦ƒKÅÖªéLl9ü €L72½”$ ÍàO Äd¥Ök|ñå 9ýgW蓸Ÿ”ÝwÆ™6,!fgíÛiκÊ)KÃCäv6˜´W™ÂIx•ÏU-‹\{’ëXØn$ÛFw®Ã¸r4à™; >Á÷Äò’£\;fÍB >•³÷Û®L)rg(úK-ñÓsÔ?¤Eì®úNúH„±®4„rcž¬ÙùSZ/À ó­÷q¿7l4Ódœ³ ˜MWi>—Ð/ÐËù˜ÇÝ:Ίˆoƒ¹Ææf8IFëÄaå}$€¦›/ß<ëW)ìÖB‹ØûZDº]|½¿e­fVs;ÌóóƒaœÇ×y{¢Æb ‘¬ß£&°«·‹ÑŽ Ê}Å?à0¡wÃH$$ÛXhkÔÆ²ï‡–.C…£“Ñ+ÙôWAÂé ØÐðr+kq™&ýÿŽ^ 2µ+ÞfËþRɉà‰Ï¾>ÆE÷yĦ¯Â«Í%‡Ëy@_ßÛZà»\É3±[‡*X²x{ÒHmŒu^B9¢ü±‡ 9\‹"…#Î_½¿î­ ƒ°¾Ü¹)²ù2{#Ÿz>þM 2^„É›íèÏ­³C×I)8eˆö„¬…—…b•Y’køÞm–L=*\’d¨ `(ÎJ5@Vüƒë$ÛÆ>÷¥Ÿ')ÝW”‹‘LÕÆ±ZXZ¢O wé»Òæ*bž`^­û“RЧ“U2„w“ñw¥Èå'ˆ@˜ùa›Í[žç¹óQEîÍÍ)2¬êWYÀ&ϳ“ÔÞ;'ª­LÒñ-jÁS4ã‹k´xnâì7’E,O™¼RnŽ¢΀+ šš×Fù¸4ìkúÈß1;S7þú*Õ@m9¶ÍÅõîuð#CNl:×¢óeVæ©ÁcJÞË´hóÉFÒjËsä oxòòbê”l¸Lmò¶5È ’ ´H^œ0ÛÙ[0Hßû¡ÉG.a2ž+ ”ô·'dŠÅfór£J¾D¢RÛ#FÇ¿>6IÓ¥¿Q÷(L4pH«`øpØL;·¨]$NÈò`©‰ §±?AD!9Öi9Ýk`)£–ß…pf¿³µ x)O˜%ÙŸ ŸJZ¼Šÿ ú™èuª®™>1ÍI’â(ĆçyÛ ãß‚eÞ04O?É!Î1J˜xÃ:e¤üÅ*KëòBÞT 3œ€ü!÷=åŠBc@è+mÞøž‘›®6´R8`!/cçƒÕ%…ûêpç³ôûúŽ$°ôCKý’>V¤!YʸB6<ÖpIð¶ ‘(RMs»‰GI²ç¸!D¹ O(m[5ÖY½];@FÝ´û-‹}Ü£kóÄaÞë‚­œÛé.­.N¢úËCò£uª«‡rûf}Ú¿ëÏ€šÚßÔRö-›‰ÄûÿÐù£u÷ÎN…1‹ï½÷öÿCK21­–iVèéyJé ”‘³‘;Ën± 5ĸd‡_õ­A׊ ¬‹H[Ñ÷ Žn†ÙDûÎÃIdòS•ô‚Ü>µ2Éq±ü\›_²9vä¯Z5„«äáŸd+ t;˜°$kf»¾ÃŽæ÷Ÿ6ì@§ý4½PA!"“¨›ˆq_®Á.@I/nµü”RéýI¼/ƒ}ºÌþ¤‹¬°O~QÑçsûÛ,…0à:ÛŽfr=‹™å K•Euxé½0 rŒ‘í¡¸>d·"Žœ/Ûm5ç¢Ð{=mRó½Íòü+­h#J|„8WøNñsàÑcÑå¼%rÝ¯ä ¶;k;²eÊÍÓž}„¹Xõ‹%"Ô2h†Í gU"ÇÍaé«HÏ ‡EœS)yTîO?·ÑèAµEýåzOŽ ïž&trÐØ‡/)sÑÕãÕæ–æí}ª».…Q¢+²©÷²»bÞÆ–Ð4$†Q¿;ÿ«¡ÿg ˆI}qî¤B¸ Êz…pëªg6ȳñëRÏ‚¶Ð b•Æd³D{C'²Ÿ£ ‹6}]Ù0ã›»Ýu‚lž)è3q&yÁ à@RÎÖá¹lAf,¹d« ã»ÖáO¯¢¡ïw=D"Jç+Ôà »6ìSÜoÝâEA&É2Ng8ñ!éš/—õ7Çg¾)§v1O²’°.UžëÒ)Vqs¯ ›e‰•¬$¹yeª'Åt!bÊ©…&0gûØÌLa ¤Ž6$Õú‡S‡y³g'‰Û¹ ‹ŽšÅ2ܸީò7Ò“ÍfilR|¸Uph­)\@Æ—ý²D‡&Äc±±hlNrFö]ãúm£Šp5î2ª{ÐS°w%È~JÍB#tâOeﻈwóa p¬~J,©Dƒ²Î¤ :efí3ÿûxøÊi÷ )\d#F(4x³#„2|/š>ëþ~„¬kàd Ú´ï\i-v2ìÎd„ƒÞ¾$cCÀÈ1Ezú ° O$‹ÙuÑ6(#³4È®åé¹ß:+ UÑ”ëp è x£+ÃY)¥|g`4ÈD:rΉ}ì ¯¶èwcjPƒÃ5]ûËÖ»mû´Æ¦QWÛUëÒ×›³ <Ï‚²ã\$A¥•²úÕÔ83ºã ¾g~Aˆ´IÅ¥:9ùvO‡f{ËýKw4[‚·B(ä˜âG—³žžÆžŽ„-Oþª`r%Ù˜š„+ nes¾Åî„Ó?rïOO4o DR³®GX]QŽu±°•žìŸ3ÀʲžØ´¦Q2V¼@«ÎdXÛ|Új™œ3"Ñô5Ó®¶QŸ*.ÞÚ®Z,wÊö*âøÃº«‘”üe‚ ¿U­¹Yäi°]B]Û^ðéã$ÝHYœHµþˆ¶´åù Û5 (çй²åÒJÎÙ–Ð?»ÈëvèƒîrÇŠ“|2 ·*Ì#³Bô⃠ÿŽ6cÃßËU¤_zÃoîö‰3Ba„’6Εÿsñ‰!(ANs{׳~÷ðiÖk”d ìµ;íø+ jóÒžð¡„ Ù>ðOMæ ØðÉL3ŒÐaUIb&Ö¯g²·Ib(NãÌúzÈÊÒˆðŽXM€¤ÒŸ‹±û²¬lïW⤠‰;TÂþ!Î'Ô‚~‡ì4È%àcú>HG,Ü@¿s¨šˆJmÇã%Hÿ6Mص²YÆ®U¬œ#ÚìP¸–¬•­c1¾†çÚ\žëù?Jm}¹¢mÐh†¤KñÙ%ºã6!¶§¡ñ·ø½ös 6ÅÙûÐnB°ÌÛ•–Ž·ü5Ñ& ×ÀØPøn hrÆ7)©»UÀ±}#°6,ç#)ÓmœxwçŠHs»vÒÛmûgìùo(ô˜¶ÊÌ1⹡–w®uql$1 Í{_·ëF C&*ˆdaêÂ’b¢CØ‹Wù+¿k;ßßE%¢š!=o.ÊÅç>¤rîdduWTËÆª€šýºÃÓ¸û¦wÓ" ßj;ÖòÏ‘E@ë]˜ZñiBª‘[ŽExËUòGCà+÷÷RºWhß®ü´Ï]êJ—zsqÓ j™)T [Ÿv,T:ê8ä]»ãˆ(Ç[‘šŠcÌøóº]‡,óŸ‡È§µqB!â‰G„tÁ»ÄÂYr£—%>= ‹¿Vƒ‡g>ž–XzLB/ÆÜÁÏo™½ö ML6Œßî qMc%¬ÀßXr,˜´|É£›æÃ_P6ôköœ=In·lÚÆQø—š·’âQ’‚Õ]xd„bÀ4զз|üD0XóÁ‰D|¤~ŽCýGÕc³ã°ï‰ž ê²1\Œi²/Öi‡ö¨ðIhÎ(&Ïá¾1\‹üÅ£$^¬ó²ê] ÚõI£3"UcjçÄgå·Håè–Úb[çŸSÜaúFÕ}1ÔnR2QˆMÀÝðà§9P}¦nÕm’•’¶_miw…zÏFî¿óéW\MQù*ÎvñV¤œ{æ Oõ/"¹äqMýøJ³¹Êa¶fƒÁ—εþËqâ:ËäáW> stream ¢¼¸‘õÖä¡Uš¾(óËIì«ôwçÝê!1ìËJ,¨+ZËàù¶Å+.$rvqä/ÁË#c¬\‡_¶dá~$ÄŠŒô/몞>UùµÞ ë®Ë^¿Ø¼è ÆÒ »ó}búP`ˬx#rCŒóe¢a…ÓÝï}óWÚGXÉ#='7ý‰Êõ9ƒÒÄÉûÝU4£¨ÌoË6íCºÅuò»æCËî1ª} ãÅ"ÌjcH,Q`p»bWO=/ ¥VëÔ2ka³—ÅO' œMôK¾{Hyí\•õàö—Ò÷êŠ|¶Ð/-¯Â$=ߪëMƒ­³à¤fßOJ< (zö¨¢uhï7¡ënûժї]A/Çã¹¶»©]{È‹XÙmІ€ËÏEfó§W~úÃÜ迦HþB’«à@íü»-oí ·Ë:,?–ž^Έ0DMÅÆ"wW¾žõlˆ!ÿljùFŠðûªÃø%GÕ³‡ž•Z‰È¹à ”ýÒ¹Ï[Vß8JxšöÅ¿÷»<ü ¼–çGIvv†Ç8;8´7†ÜÀ ¹¯Ž½ü`tXºDœèÈ_`7„´$m‡¿7šL¬ÓKå€àœïS’e Y»£Ñcî8ä`Fö”¼ˆ\8ªÇÞ\‹) ¿Ჩ ÿ÷vmå;­­!VláPã*%‚.À-…Œ&UÁ´øýùx9_c­–óÑegS™rÂÂð¹óQ-kIpñüÿ£¥Ðü™s¬O¾g&f"UwøJ\jO?šÇñ>'±{Tv™²¨­@O  Þ8wÔV]íö¯0Ï?igÜ-5ý­×·íèö±Â?…Ïdk•iÂ0Wåæoµ Ÿ)â4b¢Ö¹Ú%‡Â³Õ¿€ Ê5H–K8Q¤4˜Aå;ˆ„Wù;v€2ü± F#å$X…$ ª[>9¥ U°¢…ø\L VÅš.ñ¨Z°.$ÿ±´†Ï9`p7E»F÷RìGt~Z_U*`´Úã%±7Ð<øêÐ Zc òìûÑ¿µ8züáš`É]ꆷEkç"«D¾Ú±!\m@€À#ª÷ßa÷õ9Òc@ͤðôÊÓâ ÁÈGK³Â+¨×=Ý%ý Ê.ÈäX'“]áÛïÁ^*q,1j½bÄvyj7X;ñÄ%1½ð]^¼LëUâÔØ‡°ÿ/“šÎÛdëj‘ŒÉ…„)˜Œ8ÜÉjÅXjºŽlÍ›q\«?.× ÞÊJõ“zÀˆÓÎcæ®ÆÖÝÙ L§ÎrwΧsG}+r¹VÕC9˜8ˆtÒà&>].ÔƒæWö¡´ðÑ^^V¥ÓB„Åå9‘L”$OÃKÃt5t3>q‚¨!·j¨†¬Ï®¿zç«se­À›¬÷Ûû¬:K¼*©ê¨Vœ³O¸§‡6)B´Vþ¡÷ñ¹£¬/QEd’wði°óD…3²Ævíóê`zº³~‹åp»…ÓjýÍæ[XGá¬é:eP@”=ÿÅ3}´ÿ”’ÝÞËæ½°YˬýµØ^ýÿÝtré¢ÿÇp/à£>7­ˆ^»OÃÊÿŬQj(…3IQO´–M9KÔPÀ± ?Ñç-Ù4¾á®ø)Q$Ô v÷ º¢"Q èÜ”³ÍŒ-LRþkÑgTm´¶QЬ­¿,¢nõ3a.Ì.ÚøÄ{e ¬zØÔr4 šv–ÉóÝ! £{i¸éþw<)ü÷7üñœ;- ·£_®£„ÁOúœ»)¼È{IÌ]Üé!ÍC^x3ü&›u W»(…«PnYCQÂÁ&0qLD‘æÑýõy©w ñ»]c©Ì2Œ´M8ÈRŒ#˜°¹Š§•[³7S¬âç ª=kó4¦ÎJ;øÅ”Çy¦zè6ÚÍ¥®‘a/Zw¨Æb_…Û§sëPèBămt2xçâ·¯éªÜ¨ßÓ›vgX=b¤ÙZôÎãö“Ÿ¸s'‡oc—°ž¯É8yöXµf,!PìïæÜòg¡%9«ZP¿òWëÞ×Z¤±qîÉ0ÖpFU^g¨úgѱ‰J2,*í[F“ÙØXå}]P²sî¶Ì wÔ®ãÝ…Ù>Ú™%‡5øoZöZ3é ѸÔî(e›,»)¾†-Oà§Ðù˜²P)F›T²¾Ð·ð[õ(phj>ŒË[ÌBÎójŠ,爼ŒºzeÅÌýÄ&ŸQ­ðäQŸuRÍz3¢eˆÈ°îﯜ˜£à>­§Îÿl\4AP·,¬ï¤}WÊ)|Q­ßÆáó'u’ÝÂÅÑ´HŽôd}LH€Rl¯ÆÎƒ~<¤÷¦ÿï¯Û“ Eœ±s±‚˜`t#Ò\ö¢v Nφ֎?²œL‚°Ïü¨š•-Ó ëâçi6ØzƒÐV_™‚T °-÷âULÞãcH%(wÐ`DÁáÍ„c@o®ìdÿ¾º„“V¿<ÆÝÖñ8ò⥂oéoŒú?FÌçQ?¸ãŠý4£LO:&!øM5F3Ar¯þ•§šR¨_›sÏa6gF ’e@{ ¥‡CjK…‹Æ/-õw2°NâmÎ8—QôKÊþâÃûiA†HNmÉRÜTƒ§CÚA1«‘4æ›ùÒ?“ˆy¶õiÆyBGÈn¥ýÛx‹(NUª1k¾Mùdô4EÀõQ(Zç1#â½°[w.”ôU3AÂêÐm«Ö~±¾xM•™hwÖ¶§üêcD»Ù<¤\'ÆO/ÞP§ýäàyi€•GyIzÛº?•OÎ0 ¶M4é;IÐ_Öv*øsDÌjž´Á|Q¸ªšãaû=éB‚Oôy^ÆÜYëúóÜ¥×÷ð N툎˜š©'½ Iš;Åž¤ßÞ4~mï?YÝ/åð€þ•Ä_Óûª£ä,’Ÿ)aÕµ™š'È9·øŸ6$¾ÀƵAZJàÐÉ"ã•Ñ`aR´éóŒu|çÓ¹Ñj+±ïÍ2wÛêH¿.îÀlcMNËùÚLEd+Ûé†â~âë •—³ò—Ôº†¹È ÑTJ÷—hȘh?¤C+¡YT^§hbhH:¶ØZ·§êÃÖwÿò÷4Y.&óB'€†ÙØÍR¦GC ÜGéû¼ãŸTiL&t»¬ÍïdÛ5¶k­W!LºÖ ]ƒù6òÄSÐ7ØÌPÍ’óõlã†\qU„ˆÖ~v»lºnòAzWÔÓèùÛW„I¿¤'ò Å㪲}ÝrTW’ö^y*S È_st=D èµnø!ëËžçÃŒœH?ªP¤ÖI›‹bYN«ã¹ F<:æw×$Ðýyzí8lyCŸìa Ù&þ&Wí1ee›¼(ã@–Dý¹°ó˽ÐcÚ]æ_ÿuÙÉËãy"ÇÑûÛ°Wg ˜øeï`‘-1Ih§ˆº!˜Õf—Rô“ƒ½bU1!Þ×o!Î}}û 1òI¿â14]ëÔ øMæà—‘ÿèœ`0I¡{v9 c«'‚Ñq%’¼Ûæ¾¶vUPÓ˜ó÷¤WÖâvW¬¤>‚åÞçeýÁú&Çœ]dý©è”¯õÅÄ#22ôl¬Ñ\ø‰  {pDrÓÑô‹ž !»}ÝÿsšÌãÏ‹ÐIofncŠŒ6„Zˆ¿ tëú°o´“ñW"çœýølj<Œuj`ÜŠ-Ç|S ô©ü6Ó Ì<Êÿa`Œmv¡¾o^x:ç0B K³ã oê5·÷ökUœ —Ѽ#f  k>|nVñP¯»Pb‘y\W¹+X÷¬êÏB~ò†{òq{£cjºìš:u×–Áâ7ˆ<@úxzëw«~Ÿ"ÿ(GâôTÆ9F,ûKR~r«Êþ_üµ~ön’|¯­þô˜"idÚèÇñ´ÿ¥ÿ\.ÙÁï³Ó)Ïä³Ð;4¹c3{?žã$اq|ÀµR7dèÔ1w"÷cTa?,Ð"Éú‡ó¡ Uav“ÓßöhJ?îQI¸Dí™=VgXi1CG„‡Ê‹vÞˆ˜°ör(‰W¼œði-w:»¥¹-«É„ßFÅöM¢4ûõÀWšƒ¯Uˆ~Oú˜¤ârذ"¢fû½;PÈØƒ­ìÕ Hµ~B(ßRŒC– ÿÖo›ÜUÒRËĹ’Û9 £¼+d#ºtrI¢`yÊ–n±ðÇžEàAŠvaÐËåÏx¥¾6ã›`-<¾m›tÅ7(ÁàŒþc ÅP£>ôKBÝ´Ì*sÓZû@ß@ÙG77ŒÉéŠLÓÞ¼jJÃR ±§‘tq~”E ñÐ.(#oûåóôIá‰1ipÌß¼A¦‹ ¯¸À®<…ß­N’ž‚#ÆÖ&ýÕbZ›± ñßgLvÙ@\÷‡i~»ýýø}½#6 Þ ’n×Á²Ê¢-RÈ|¶˜`­F„j¥uÿè£Þf¨É6C»íäѵ­‰/jÃ=ȈI‹Hƒ¢¼“Ã…pÅ õï„¡Ê~醛›´ÞƒdD–¨îS¨}$QíF@uC#BSZ>Ò ÒÎ$µc#»6z°+ó–>Tr—èT†²ˆSš#ÂX'c,ï„z'|øõ­!î[(LŒÏöÔ”ðâQ©rº*ü’†›Ñxð•‰$õ¢±CP1ÖÄVÊÓã¾nvx^SØyÂØ+¢J­XëbT½ú¯xïÔì–âÁéÍâCÕM¤¬P¿¢¡246P Ñ]›“Û‡/ç•>³ìÖ@ ÚÛÏüÍQ+-mx“‘7bâGl¹j•n)ŒIùBÙÇ”ú=“$Ö¶añ\vÑ‚JFǬ 09y)ˆì£ ƒ˜ZãÏäŒ=Ê¥þêð%yœ©—iH>–Ž#ÿ€#%’jLÕ# }+5>y!mÊœdýb§*&d¾sFx…=ÒZøé˜é ª–hlÃm‚‘{T•S¶M¿PhoüxÉtÒïèÄP~NÓ {ÙÂM\îµ¥ö8ÿDµz$Ùõ‹çtˆ¶! XM¶pä|HÃâIc0æò=*¯ÇJq\\6¥/TŽÞÊvP©F=†Eªògqˆtõ@Å®¢·jõŽ¡DÓ£µ§˜ ñò•f¾Fwt¥Aòä›Ñö ŒÐ3J`¿Ý;vƒëÍ ÷"~f+Cõoã #W½öf-{Šè›rÌV’‹£Ø‘§ì„ß×Äñ¹]Ÿ£sÛÅ;Ë%hA\Á #?š.Òá„þa~´ 戾^™îá²@ªí÷Wˆ›Ï-èCAR;:å<"±²…nÈ (ò$nq‰\Û¸&}ÜY¤k¨ÉÊ»¨4Á N‹V4µQ£ò'ù‡v„b•€&CFÖ­#>/®7ýäãÿŒTÅpØ®.<ö€ã ¶¾…¼·ÎõBr‡‰hÇ°Ú ]ÊÐ.ŽÏ¨ Xø¶i¡oÑÿËò¹þNÄ0…„ôþ»ÇåStp-Íñ¦3{ÏŸÍ@¢Äñ²-P´$Š!` Ÿ9]úöµ×;ÏùÞöÜéæ;¾äKG¥*|ÆÿëN‹¦«„D.³EúU«¶§ yYÆ1O*ÎgUªw¸z´í™FŸF¿Öy®røv[¨7¹ î1ôÅírc€wLã+ -ô (ø¬íÄ·Y݄Ě“–Uš(Ÿ–*®Pòa%z8[šfUºGdp³ƒÌ›@¦vóÌ<™AÅéªÙp¶ýƒ¥¦ :wðŒE¸´Æ$ùÕ|Åå ¾%Æ)>Lôô<>ósêþ§ôî{âã<ªÉ/žóÿ ýœ—œ Gÿ#+ÿ4—n­¯Hðh‘Éh‚æ.ït2Õž˜=þ«àa8=i!^¹(°ªj­Þ…ÄÊïˆ)Šþ iÝM¬ÚaäÇÙK-I½ü“PYœhÍõufÖCV£Ê‰OÓø|âmW+K4ÍÅ7µ·9KØ[]•ò€©3ò;[–5c†j;ìà#ƒdÙ†=íþgTuÚã¼T úTÝ€Té)JZ$Q»}ZÐ, N±¿Û”‡ðK¡tˆ¿ûŠ>? ä߀æËܱ“vî„Ó:5¼‚*胴âz¸°XaD&}ãwµª™Âœ¯e?;ƒGvwW7`|ý­L‹6ÇŠ;;d[Э]è6fu@èý}Í÷o2 0AfÉÛKšxP•¼E§`£.½B Êñ±óœÒoOJÕ¢Šl#Ï…äˆ÷ûo/b>¸ÒHo¸ySr§gµ00Ö26LX¸îÙ)Šmɦ<^òÔ~Ï/Y{IÜþ z£~gŸ4©ÆØ3Ǻ€;¡·ß_ÀùlñÙ9z¼ïùsL¦öóà?ev •,TJuåP"Ú÷ŽË»W®•Œ³Œ¨ÝÜ®sI¡Ñݾ4ÒðmÏ8 …š•7”~”°€[ÿ bÀ÷í(ƒÈ‰"=Ÿ¯kUå ƒeúä' Æ•8ÐÃhK$ùR׆Ôü8+,!1/´Íß'8%šÑ+C9,Œ¯^æ.%å3Öóÿ?¯ðÃÙ`ðªc­™ª‰E Ó ^REžeƒÊòÖþÅ—/MW„*…·…guÆÐTHfgvîÏ’‡3–3 `–ã²1qä¾òÜŒ±Gé9riïýn=ÇÏ­Ëè}“7š¦ é§Ò;Oü\8Ôöü¥Â¶ÃŒÞË͜اÚÎ PjµÏ€(Uh;tîlœ­âDDËm¶P¹ ]µÍ´Þc©tÇ–BgUÉm@Ò€îñÑÒfèàÄ­–€_%Hvî¯ð:7}ئ‰Z|¬a!ðÿ°ÑƒY²Se™G¹,äNÖf˜ð„ua¿…]gJ؆ B’Y–@¤˜Ç¿½*¿d*jØß]® #°ÓÃàÌK’jÇ wHÆgNäâóæÝìPü"4Æ:E7 µÇ/g¾ >:$ù¸k•üž,±«‚XL»Q3ÙŒ¶—›ð¬$ˆÔ™wï,-0 ¦QËé@55µ(‰gøU¯j ÄiÖ¼›çvÛ ¦C`eµµTÉt’iT Ù"í%oöÓ á}äõ#ϳ§™:â ôØS§#˜ÛÁ['q ü±{Çó8$¢çNì—˜~ v("Š‘1¬Ù8Ê’Öñ”zÇ›œòÍŠ?BXO‰àÔˆZŠ¤Í¾ÏÁÍ;5àðÄÚÒÆ$‹·aè¼»6 a u´µß>e*’¼È‹-õ¬zhRblpBéA­Q­vMÙ„)@`¸Ðj¤®¡”RY8xÐFØO£Ž¨îM­¸z…-[ˆGWlºÔ; þ~É­žº¤tî¼)i¿ªš@×6ÍÕ`üÌ:Z|ì/¯è|Ó dBŸ)8ö/Ï9/W9U—UÔx$ç»Õ#áÕÜKþ͉Ÿïö‡>ÆMÖöÊÉDe(kñÏ%óçÙ]ž±(Ë8˜;˜­Ù¢gá-mÒN×­½²hAÏ“CŸD²ÔâÆp¼@Ì?^ÌÈÜÏïÁ"Ú"³1äHgQ£eø›Qq$3ÿ(¿ay3½ÖSc,{‰ç‚eïÖ|ÏzB²™¬>ê"<ÛŠFé4àõ˜õµùà0ÓV¥pÍ<=­”Óg2Z¶ôsL¥ÙµÝ¹8 g€ªA²íªD¸Î…A®·{Â‚ŽŠé™úÈYíö(uÞ ]©›Qï˜I²°úLHõëÓܯëô¥‡Ó68Qfÿ&¬Äº û'F)¼ºš·‹ÄàÝK‡UÝÄã}ä[Z^ðàè\ÿí?¨«ñ•úQ‹Çñg“´{Ñ)íÙAçi…¿Kk‰<€Ïˆ E^i!0Ê·ìáW3–:,Öä¿nêI;îýi“TÄ·ÓK&nqÙóÒb×Rã/P‚’Ͼ_x˜j:³ýnIÀŸ÷§5à"—çsí–€Ù¡ª ß–(r’¬•yhƒæeìbÊüq¥ ‰k:ƒæ‘œ(úH2¶RÉc/†Êƒï"#øy 53Z|åõXÐU¸ƒÁûom—æ“àÃ뤑³lNë:ù!Erˆ)Çô‹ËÉ?(`÷êXd'"毴=1™\H¥¤>g-ýþÚÌw&¬ŠÉ¼“FZpTÄX@‡J@¬ÖŒRÝ¿Qý²ÁÄÞÈ©­wôS°¾Ä½M­)êí"Bp(Ì}= .ø¹ÿeuYè¡—;rŽ'¼e28?õ<ïH¡YnýÂf•›Ãß)ø¡Ëú;øº¨sPXCñm‡ @”·P3x¥º…&Íø `‚Ö7”¶9’4"¢¬Z Z¼ÎÊ¢©ƒSU;‚É–Èc7|\ª×-IØùÁ«*æÒ!²Ïbšð¹¿…ÒGxë/è /ä‘*c{&àlãü,÷ÅaŸô 'â\kÙ:a߇óœI7ñJ&ôõ³4­…Ä ü;u³báJÕ ¦ Ð[õ¹ÒøÔÇï. ¼I˜Éi Êüù€¥·Þ$ˆ.ñ÷ñ“:iBß´€¹¨c4èUî ®PÜY¥ærÍmXËCE›ë#j6ŸC¼GKô!GóŠ{é´QæRŸ;„¤œªý:«ƒš8k¼/ßÉ–}Ãn}ñ&[F–ÓÁ”2µ:þÚÕQÊip¼³I hOÿ6‘HôäE’l¼s¡#ùjJR†É0Þ<¯­¼±Ö¶²+øp‡0D *ÞØ»‘)‹^ÅŸÅÈ®-¶t<&š¶’ ?Âé].÷Ó°Ó§”¬íšŠ¼¥TV ˜QbmEˆK(%$íQTc®)Íq·Õ"wÑbz]ÒùÈ_òdàá·j”RëŽ G1™õÉÄcŠg¦÷(ߥ¶{#}K?QÔ€CÞCŒKØ §‘ПZ>àõÆÍ)5uõ7™çam¹pœ'Os,ÎfUÒ±9Úÿá”—ý{–ÊÍ&¼—FêÌ;*{’kÚØ„Nê­)Ümlˆª¦i‡# Â ¼>it/ÆÑ³’iÚ%ÌR÷k~N.ÑÉ÷fnLºÿ‰eràÑ—5ÖM5ëË6 8䘻,úŠa.É`Có„AfÝoäz°¨æiÈsÊìß@f|€H¼8Ñ.ô`ò5ÖÓ¡4ÊI×I„êš¹Ž£"‚Q•ŽØh°9lPŒíp£ ö=VJZl¼°&Í“}“ùíðTÉu­SF:p8²©¹öÙ ÎÁÔIçù}…c+ê´yÚ~óÝM@Ÿ¹Å>gÕ·}gÉ,Áø?Ô#ŃfÆ'’;6â)×Ño|fõAz9ãë7#ãK nè…ÙóäüZ]àë ›ÈRpG½ýa¤’‡^”˰ÿ ¾-@ÐÓ¡-+5ÃPJ°²5yÝ•µŽzrçä§;xïl÷ ëéÛq 0Ë04•Îz&ȵ>8]ë¾3í ï w´Ùsˆóµ¬Þnfˆg“¤ÒdÁF‰Ý b"ã×I&‡ƒ”F0@Bleå“óS#žRLÔ´_é5³†Éƒ®~©Œ–È"ƒ¢ÀAż|V‹Ì«#ë¶SŠÌŒ) ÉÊNîô×ÊÈtyó´vœµ)£Àƒ:ià‹Q¹6ÉKÙÕ~01'7·²ZÁRÐE úÓ¬ŽW°¤9KÏ#î­::ÙiYmüåD¶u©·Âòà€„”ËBÅðÂ’í–ƒ—cC#¥mé‚öã¢þ¯0£à+å»CWòD ·eò€ •º_VÒûr¶‰J2Wuï\ý‹Û&•|g¦:<ªY¨î ¶;P ²˜hãåÄ«6D<Ÿ¡µ -(])Ÿ)3Öi{:%Öf‡(T,ž=ÐÆH.Âýä÷…íܺ´¾%à…Ð0Dñ³Ô‰ ÿ¤vô°OnúoEF,«+¦¿DŽ QÁõÁã÷^´Ò£»ÜLðdÝõ¼(@.!dŒÅititÁì8©äô&ô@î)_Ø’€¹s—›MõÇ}ú±Øÿ6؇W{Ø)Ýù测TÙ€z¥™§¡¼¡éýŠðâ#\¹- ï[z‘õzn KÌ÷'êØ¦5fAcÿ¶Sâ«­mo†-ÀD×{Á+ÏÏSÕæÎì Ñ·ÿ1dÏã‰&7莑‚‘x0Æó½mýÙY°»C þ[)€²Ïš±ÎûùÎ’A>S]£[TnžÆdˆC¹Y‰I(A16 êPWˆ°±æÑƒãö ½d^b¨@*4\As,Ý u*ÒÏ.´úvœÍ.Û™>sþHeŸõê?ÅÁ“jeÔ(ÍÍ::U&òê×%p¹›šžt)8C¬ÏèýM%’íz¹‹YÈ¡Ž!}VRdOkŠæ]ªÑÝi] dFƒï¸9ÕÔÀàfÏZÓ¡y@ô>YfÜ4;É€²ø$V /Ë1+^šxSzø0}n“³!OAçó5gþ ݨ’>ê}Úßù~•Ãtö9Ý*ÛˆÉÙqr"ÞÑš:c0FRaž P[¸—±ºdº5ºbCbµ†×…ÿk6r([hè1Ì>Ô\¢‘x6BB{3VD½u¾¼íB‹E>UKwѪŠN¥¹ñÞ3^¸ðâ‚ÿ!ˆiçVÛ•‰³ñÇ.\•éÎò8–˜(•Ð$mYÒ!Åd1„õ¨s:“ùpáÒdw^N².*£áœÓ;Ò$þ¿@{¶þ Y¡o5³"OHrv‹S7ŸÙ§ï]…ÖÁg­ß(#”*é€ÅÓ¿ f7€á`[õŤۙ#Ù/ük¯õŸ+Ù äb'mÙà|8c«˜@•„¦‘¨Œ¯0èžÍ;:YämüÊhN¡©Åô ÎèH@2ÚLLÄV)Íñ×Cž`ÁeMP½}¶:¿QëÜÉŠH¢¬¬DM1W“©{"Ž¿½ÄÅ lzÈ£ùÒºš5r| è@<ýär|uTz ”Œ0 žÊÒ8Z®gÅoȦÐd¢á¦1;pC´yÆ2ò3E¾ …)XcÀɪœZAlnaÕ8ÀÛF þtd¼ØS¨³Dð8›é))¬XìŠÏÑlnèpöÉ ¬Sîãmu£Â¯Ýª7µ³”´eç~BoÓ½¶¹‡Ë<w€hoV¦+°Šü„ z÷ÎáÔ7û»ëL¹+[ªŽvÔ’Ë®<œIðé7 ìLJ¤Þ-“08¯¥ñlÔmn»VÍ]„«¦Þ‘lµ0†3™Ëw®q?tLônž[¹NkýåoÃŒ¨õTPŠ‚xÊCÉØm.Ö | E‹"à p¿ö¿ñµB¨P†ÍÕ;V_ל‡зwóuŽíí$ôÐôÁæFHäwfÓÔÞühw©Pvmn”ì´åcgýg@ËNÿ4‘%K( JÆ%Sb¿,¨u'|?>öðúOcò·'¯’߈AV ­6{´Ñhr¡yƒŒ½,ÎÈEç5it:Ÿ¹.‰×±6ý‘Ié¶DcþF^®ˆã¸ö0Ü>蕨 ââ|™çLzI’¨„€åÖ<™:€‘ä<{ïÑð; lÆ0œð¶ü@Š!åçi¹àÅhµÚÙi×Ü›Ð)W°ÕCPê *:ŠÑêQö³Q5M̺|õÃ+ÝÛ –©x.†pžúËŠ@¡émtÅšƒÛoc^JœL¯5ß ý }S]Ø,X9 ©JÉÀÃón•·é„,Qy)¥ŠHñOÖ­e¤¥l Áöôºi4Ão¶÷.m¤°5¡G'EQ:ò /ý ©æ:<èóoÄŸ] ~ÞÏ ¸¹ðûÖ¥¢év|¤t¨­éZ0骟b^pøyYNw2ÂØc­HÖ?üræ¤\ [wu¢¿cÔFΫZ>š¶å;5=ävÒbSŠtÙ¡mlì5*Õ¢Î@–C§”z÷ _‡G8ú(|Uq6`ä™=6è¾Ë'Fòüž†é”^À¸ðZÐJFaÈ®*»àrD‰Zë"moÃKê(æÏzOåÿIbÍFƒœ×tñéЏƒ0÷¸_2Û⺘šòI•È#dÖõbOÃÏ † Ík ÆüKö¸pË0qÅöÞ”Ñêâãã¢AòÊéÉíæy«ù©ÕÖöC}צ‰çG3ÔAø©¸ßÎ78Å:Lâa¾8¹¾XU Ó‡Mä£S(OÆvD`òUŒ±€¹ýO–ÙÒLF„ãƒÒ‘cï,Ç’³;¹ÉTêêãé§$Í6{2pÜGÆÅ "‚%t£M…ϼ‹0Xò­fub5øúRxšÞÎôŠ®]ÎétRMN,Q¬$mÙ÷Ñ^k岘Ëðç“m$> oou¯k4DTÿo縨pJÂõðî‡üÅ·µm|êQhd “x|ˆõ˜Ô8X ÈŽ6ôuú9–š^+uH¯àxøijEè]¶×¦~ÑÊ?Úy‹÷£[ñÞPSîËÛG{)«É¹Ç›‹>$^”cqfÙ÷쨙UꧤN¾šq[ncW†óìYdÚú h 5¼kŒÍ<ÓC¯¶z¿¯k­qÕѯú_…22^*áÅ. ._‹“ôÆÃ:³hv×÷Ïkj‘n$þµy GãÎÛæ13…n^iܲùgáåˆÆè¸V{žããDQ+êáÖ´‚é}1ô“$¼«ƒ•[;*x-Êj‹¹ù U#¶Í¸A>µPß <­hë80 =Î`g]O¥Òê|7å¦ã\MåVü|ÁQ †ø‡”¸m½ÀÙÉ *£¨Æòh8”?™$-(-nk‘¶dŠz¨m¯šNöú²}ËøÏvÛîzmSúeb„EÎSµàNFL‹0,Íxo´öÓGß)½¾ ¢Ï95²¤uáú½THÝB¿àHctŽ:íúÜrl§æ›ŸPÅ Ê[Uå¾UÒ;Þq;ÓW…(­q˜JsmÐ_®ßϸkà¡Oã¢í»A8¢a£o;6)¿ËD6±p–7ÍÖ¦5j&ØÒ\×£S³UݺqbZwž vΤ€‰^Zž Üȇvì-žPU†:aad A", $åÔýµvhV:ç>øÚÞâp¢ã…ïÄÝSHi:CFb|á&œ3Ênl·t¬òÊy¤zdª® ÒÑóÖT/Ëð‚id )@]¢m0Ÿâ, ¸y%¹ØSÇc~ ¹=ª¾ÕŠm‘ç&ÈK[ì&ɠ;¼Ö°2#~@!b¯‚Ÿ' ¶e=7™ØZüݾdÜ>ø¡E[®QÂ$‘nrÒÝxvÏJÂðçŠ\•À¶êÆÄÀÉÒ((÷LGŠ&Ø5‚´á}Eðµukx];F;yß[+ï{˜„”33¶†Üâ'¨3"–oi©cþ˜÷ÄÍ•Ž×) ô·¢Siü4  ã„“|I­Keü¦ÀD‰Í‹×P&èyifUÂýð+ƒ¹¤Pó“âº5”¤"®¬õkŽÓîz¶”€&²fÇP¿}÷Ð5òŸ–jÝ߯µX ó dOØø—í{ÁÕ‰,Hãëxþ$`íéEVLÁ©¯ú˜º6ÓNwM—vuùÿ-ÑòÒÍß5éݧ˜TG’ K‚ Ý.àãIw¦Û2°u³›£Æn‚`΢L{45Qçf*¦³{Áó<$zZMQŽ`FXªÛ”^ÎÍŒ Çò‡uÕŒcžã®o 1NÅøF5ÄesÀÍІ<å%×jÃ÷oôÖÉ¢¥j¨·±=þ&^«ú²MiµÃÞ,¤`ï«¥;ëgómd|æçIçM¯§rjpµ™ÄwH€½UÂõÛ¾EÞN`ç½Öë:M}…¼îÞ²{b}?ëŠÓ1uJ%o,Iñ:ð÷´óEÚá–¹^]ËèÅç7) ~ ^*gô¯§91Y†‘;5´Ñ-âSAWòK_×µ’–ªõ;[°¾äJ-¯qœ2ADí©FnUÝM¶*#}†l¯··CýSPâTUje¼’ºÍNTª‹ Þ=/*bg ìB¡ƒžd×Fª ©N8‘ôI¥XóU•uº!o— :l@¶ÃôÜ. {j/š ¤Ú%}ޱúðEío…¶Ç7þ×£÷•‚TW±Ó£/S„$t#úàÈ`ßÔ{€‚*’÷¬CäÌúˆ™*m­~nYÅ5-B*6²:€CÀú °F$‘Û¯æg}€ÛÞ$tŒJ”}7{û0:Šê)—’‘ç‚¶„ì›g>ދ猠ªÖ¨M+À†Z[© ËG(˜}øŠ.ú+pêpm7H/ ÐGÓ,ikôâ_¹ñO²! FG|ʰÈ ®·È›xëLqt5%Re4ERÔ|tÃ1*¿Òë¼£ØwÈO¤‹5»-¹ö§o)šbޝtqk*õóüŸóŲ ð[»ËgJ˜üþ]ûíž»Üô³[U„¶ä•ä æWªž¹:‚‘|:„±¶ÕbÊYŸp´…aÑp¿¢ÖËý)×áuznïÂä#¢÷ºoêËíZiÓͽÒ}›ûa3$™»yd¥+MòÝý7 Õ×»VHí ï £ò ¿@°ˆ-æjmúƒËQb¢“R7í89Q 5Ís½&ho•gEvü© k ÿŸ,÷&ß¿+qh/¤{c‹@íP¯w_ïøzjµzœ÷"úýâ&Äù´ °™ˆ6ˆŸt BôôK¥(^wÐÆ®H÷B˜U¦u<,ƒë \›Ò¶5o É×i†Ùmôƒ¦w%ýÜó!¦ÁÈÂL?SŒ£¾ä¦û.ìe…±X!Œ ‘Œ$)™R[;`cDİJ¯Ã=vO„vŒèex…Ù'Áæ‚Evýoa{5ü^ŒNpå >@d Híx¬Ÿ&Ž[LŽ|ëd~¶¦!¶¼›Í+/õnI)Änz|,ÄüØOqHŽHN_–8ƒÄ7ÍÂéÑ$³“¹á|.¿–;ãŠë7)àÁ¾_¶Ñ;ÖAÛ§‡¹")ù=1½øàtÐï `-Y™tPâ1ñ/“¹¼¼á3èäèÑI" t¦<æ(xLcˆ‡­?nÀÅÙ_ý:ŒY{C;^àÄÀqŸÇ€üR¤Ã3ZþšÜ<^[.NSe[}?-è(gžêŽ—A?ÿôõ—ÙÐÖÝ6óævÙ‰›¸).g|뼌ɱM…oC‡Ð,BB@QÐd{ á'Cóš2b•ûc zO/ª+Ùy„¦U°â°ë3²Ì.*šSóz0LHÿ}pÈïЌnInƒâŽR|s¥ä)ªS3w¬ÿ°:e…{wÕ®ñYŠúH?ñÏkèh…èE-pä.¯íÄà”ö~»Ä׊Ë3ÿFèüIDýÒFh²¬¥o½ž0i¶ÙÛêªÖä‹9ó7uÞ/º Š«·¬AHv¡¡£h—2ÅÅ4ŒTß„—²6Ð>=Ê ‹çV½•³É RûÆ'쬔 YbŽƒ2'jñXU&µXÆùa{ ‡Ÿ 6}û–ò°ÁÌPurÍ$­P’ç ãé!ÌTò’ÁlµíVô‰ëÇR”¼Å€kÌo¹EbÔägYŽ6fðM{Ž/›–X®ŽA6åGðow Gx§ŸíuwӇנ;]n)³:± ¹.&¾»rf†¼ÎJûlâéjœ ¯›Új1*ciªÀC%š°ÖcMÀ¯ó¸ãÑ65©‹ƒÜ’Å-¡„oŒ=50oÓ ‰~èõ!Ãý9† –ͿĞV#TÍÏ„ßÓÀ}ĵ ¯+Œ5l¼lqZή8>ȱÃ5Ƀ£íÁW cUñÕK*Éuæ«‹ôqUÿ˜ækyî:Q(? SLOdù á:70­÷›Í™ÒîÉi=³­#°Ü O¥•:×$Ézk­Í07õlÇ÷ü¯ìî*O$0N¦Á“èë&Ï®™›!ÞÔÉýÔò¿4oìC%}È(p—`‘‡3€àP‹:¾VíIP¿0ôŸ¹v€Ü‚Ð,é ¨iˆ6í1𠯫ÿ€de^VhF4È¥²S0]‘‚ðeIWw‰¸~ÌŒüp7Þi|·¨P ÕITœh(ã)þˆë³ó-ŠÜ1ÿþ>|šeE¬ëÞÒ#¬81ˆÁS±uûÌâ@‚G£L~ܘÉE”vûþ­ ²˜6Ï‘Æ%ç|äÀnˆÎDúGô ¢ÒÆ*_X ôïV%þŽ Û‰]œ ¨Û¬3KÆÏÎZ)pÑ@õJIDWˆ€KC4 þÅ›vû&`<ÄûìïB"îÈ¢C¯¶’–ÍR}_°”¶õJ>Ú|ÚKCìákïÿIÂߢøx”¾-^\SÁ¢õ¯‡scš§Ž†ÄpU›¦D™«÷[‘9ŠÄ Oí%Gßü¨r §], \úi†ù˱aü‚u ÄÚ“[Ȧ™BtÏkíüÿPe=Ú­€Ô0ÜäQ»‹uÉ þ·¥>!vû’««]Œ+GŠbd*ü}*ÿ»¦ùç$f6‘/ѵÆZ༌SÏ)N xØ=åûî=¬TsЛ¡8‡ÑHíØ'$F˜o9¨êŽÛJQ§†ÙUÙ°YíÝvåäÜÅ>…v…nä½u·ƒÂ½K[œ¤ )Ê‚@ݺЦ‹#‚ÿ¤(£*$žyÿ¥_— ïUÐ#÷Cøt½{G‚ñ© è¥ZúÛp ÄE‘ÝHúþµ]QÉ3^¿%@Äð웨ÃïâkÈœø¯ŒŽGóõQÕ—cÑT| ÔàPòÜ®SøIljOZ‡8·¥!š½fVš_“2z1A¦PÑ M–¹©(¥i Ž¥$¡©Ég@ë:À¢ÿXŠƒ·÷=@ ?Ì»JþØ×°¤½$MÈž—q7Kê)i*ê³l$RÕ1¤Ÿ}e^´±x’„=WB.¢mô QjpPð¢0G°6O…<¾VnA®Ï)¯.×_ú [[4%Õ³‹<©µºXQmä2UYåÖßu–l—{,ó,^”6Þ•O¶5¦¼Î­+ÚC'½ÒT1è èÊ=® í÷NöoáÛ´Õa‹ì ×" wà«Þ(…®ì„úÌu5=Í& Ÿ Cw?õÅÃ9êÛŸÇÇ=^»öqŸ»a?rã±4è ÿÍöýkdñ¬²%[ÊàõÕ^+ Uû¿ _¢2¦<¯åû QûJÊö²¬9k4— ?˜ÁÁ­ÃŸJzÀÃL$â}t>Ó‡$;‚Å%­I—u3Í¿Em§ð_$‹µ›)lê‡ùö I[ÐôÞÙò Y4/Úí©âB¢ qVœUˆˆÜOÂFÔê0¦ûµ¾íË´Aj gŽ™Fë€ØƒÊýNò8$ˆa°{¡ªèqÛRËf¤aV1N'Ü"ʃ0_%!Ÿö;–-‡Ž^€`ÅÃÔ+ Kõ»Ov,I¾$«¶›F¾C‰ên&’îkñlÍ>V¶d3n9?c´ŽÇùQƒ‡²iIw a‘P>ì`œ3b“ñDøÌáA'Ühˆ9ù¦ýK‘Sñ![…fÍÏÄ (F° Û¢Fá(¾ÛÑÏ,c~X–ó% ¬ùüyÊ^/™¥ã"õdãom^·³«œ¦bå«,÷{%8ç¹ýµ·Zà¹CO™ÌnÞpæÃ¨8e‰#¤„ýg‰15VieÜMÜÓ¶8‚[÷H'”c—¤ ù†ù뜛ôz;ù‹þ×™‘§ï*sd™›f]“ÑÛ€þ,£í–Tsܹ¢µÆ‹ïÅM•>2HC× ów`;Sxܳ<KÅÕ“5>€Ú@œx blë™c¶\{ûÿM«é}ý<,X›ZitÛ/ªÐçPDÏ\½R(±¸v:Iþƒ{M}C$¹œ'ñS?€1å‰ò\…J7ÈÙ»¢ê9W&¾ ŒHyu솼‹B/u×ÈĈP“jODäG<_‰,©¨!/XÓwšû3m„’ˆñ{]J¦È ä»Qò«–.Xǽ*xÃÆo¥™­(€Åh¤ØÏå#>Õû:H[¶X:X·W“,¶ÔròǪ±ê7“Î(§2ÑG_6žTnU|îQD;–.å^d.óú¢Íí#¢;öè&ÔYm¾,±ãçqü×ëÄ€ø±ôê#ôÞC™œìÒE@ƒ¼žQfÙ9Y[u¹L1ô~þö[¸]jUžÝ—8ü 56œ7Eã8L|I«¹Ç 2Ë3&,ȈÓÑB8׿àr.ÙGŽÿÛÙ¼èW¼oÐg° }¶AtÈú2x2Í Q~—MI?-‹æ¡Uzzxã¨Øwâ(k’Š™•° ”*ƒ}ÀŒú"˜Cñvu–¿|ö.°§èy޽ùš1`™Lpÿ±=°Vq¿6KÄ2JUÐW¯Ýc~Ë@k FqI™£o;hSþ¼â7m_8<«}×oz£5­¤r`e9üÍ9ǵãþ,Ö¾ qX¬H}÷~ïäô]ñf÷i|ý‡™Œ¼%Øk—epˆU¿ùFY!Æ`tÖVêßål ¹V€åšÐSyîhd=°$¦~7E:k7Ùían^1í†aÌÿPšàgg¡Š¸“°" ÄõVÆ×€_ë2!êêåñ§ìú½/ä`‹m5M袣³ÏõtBuxë4³9õÕ/ LŸM:¶µ.ü«æþÈQôB>0NO²…ð$Ú8‰ÔºröYR—c‰Í.~Ii€Ü\ª³žYkvÕÛÍ))—é}ÅmëðP¿EÆ(ÖqŠbÒVe]~Õ8 Ù}é-(ŽÊZíì†5ëx‘ð¡v¦õ.â%ñ”C˜>ä6ÍL5ꎙæOVI<<ïì˜/Zu"ºwLu®B^Y­§g³^ PKWµÄëå§Æp= VoÛñüƒY*¡«§ó…_Ùq²Q{a‹%zú±ES^½W-gFRÒ]®ŸÒOæ´»á ¸g-Ò;˸íªs332Ž£{«Ž˜s‰wa0ü±(‚ lF>>°51«è~|aV1ý„ Õ&ÒÖx’¼° ¼®JΈ‰Í©©Ê¸`iôx Y£·ç¤–Ã&ò¯²#µyퟴز€®Ç·¼œ@zàˆÔªä@u¿ß8´Ê•ÁÀ {Û(Ȇ ôwuÌ“¥ÉÍ"éÒ€‘w>äOÈ™=NY§]j˜áŒ Dz`õqôî]€u%•¬ú*©4®.³– ßJÑ*yiW½®É4”dÐ.·Õ^rIlBì_ ³d1²ÊÌ.Њ÷-øEŒ8 mÚbUô¹®:rţȺ9}Ñ·ˆaÚ~Q™¹ Á(¨JU4jáç¦p‚Â\ѯÁ;Bë¹½X¬ªÒ".­¥âGTÖ‹(T‘Ãxz‹¬Q ¬…àU€‹H²¡×ÜP7œiÑ_…]TÛoJÖ]ûøºÕÞ_˜ŸŠ-øË &lYÝ¿X&àø÷‡Aãž(˜J~ÓKf$÷Šsטr€s8·ëkYívO°_-çeÁ/á0bg:ðÓç´® {6[rœúk]ÅßAfÚ:GœAÏ‹öâÅw¢Â¿†üÌàUg÷Qw”ñEÂqü&Ã];ÕW®% “^Èäcwï,nÞÖ—àÔÃúz÷“äB}~F̃õ‰NTè ¶xfiŒ±Ÿ»ØêˆRÙ"HÔ6Õx; ø1Nöÿ–Yf¸eŽfS£Ú8µ×ê«ü²_9=)÷Xju/c¯­÷™‚n–7Œ:…ê²f z?¨ghÀ¸ çøÂø®î~ 7ÁaKÖM&*Í`•ƒÐî&~~f×ÉY6Â,ÛbáEˆwWwŒ‚ÎL6ŠExN'#¾%CÒ29…á„¡4ÅÇ#T~/Étù̲ˆ'»ÏmêÍè>-Ê‹Ëà¶å…†E_ÝÅn#°Þ°æ~}NO=ʰßu¶¨o7h†áà+¢°Ó)…2ÝÙ0@çé½ùyÜ_¬+Œ¦K|þ‡k,ñŸ‹”[ôËmÌG#¿"Î?ÓKè¾Ù Û¯ß[ÄUð¼V†ŸåÃy}´àÍCÐ7÷ ^&Á=·P­±7]C*,S—ÛËczXµ…„ÈG0°–ªK‘Œ"Ænµ Ñ{.Dø’ìøarÁÆð»ƒ*†Ö&ý¸#W(ö)/|J>O‡f<|’ºs„ˇ žBL¶•tìg]g–$÷.tqÞÍɘèÈ¿mœ#Þ…Y<+Íd&u²‚ÀLI&ÿ§ç{Á·ÇÎv¾ d©{ͽsÛUç ‡h˜9 ƒUj.¨'›¼Ÿ<‘67Iê5lwÜÏŸÍ•UÞR<ÍìH¿2½ÒºÍgŒQwš¦&Ž[Ö¸ÿ<û¯fv>‡ù¡¦!ŽùÁ8àÉ(tI€ Ã3£… ÛîàLѵoV;eæE!wxG©&GAT~A”ñµsÊ Ö@Ö—'FY¾k´CZ»çY(R¿µÑ Û|>ycÙ3D]êÐäg,(Ú/(Ù4?ô¨}še´ô¾´Àg„ncQÐÈQ穼ôküGÉ„ä4ËDa-Š}Ž$]+³²€<¦5)§|¹‡˜Õ;`ÅM޹ñ=å¤}‡çõ'œY‚©ß\2b9•‹Å_zØ[Ö+ó‘<ÔÏžfö&I¹ðX”£ÿ¨20mMdNŸ7¯,«¤w<€Ü B<“œmfE¶#{JО¼+bÁÁíÁëœKº|XK¢Èö°ÍëêV¾t zsÌðôa“°íZÞ  6¤­ç*$å>¦âbÈÖl±bé*ÍfÐéfþÈ 3º$j¯Ö^a#S Óc—… FÙònJ¼ŠeåÀaelPÈòí§+:¶ƒÍÕª»ÇB*ê.i1GÛdç¤ÎU7‰d“‘§ÊŠëo›oÛeÃ8ÝVãNôÁDW•¾›¶ÛŸÑÅ47ð© aòÖy¹8{ís±íÇŒ×÷…òìäw#Ë Ü2¶ñ7ëÈXn*;·XLÈ©-ÃÑÔ̾wWºˆ{YÞ:o²10B,«Žñ¡#ï†nÆÌã®ÑŽô7!Øß¦XéÏ6PÄW"×èsž-e¿úèåU$– ‡‘ßpÝÒë_z&¤´l슼Š"-ÑÿÍ øv¢Ø…6üh«{ïÑ|õënÿ¯]¦íwE¬U}à»kìåS˜”‘{uÿùQÄ}=¼:ËÜ»oÇÞÓå% QtÕWÌ>Þ¡$®°stfgÉ@ YdNS‰:“ßfô— žT¾dôøs®‡ÝÉô.%Dm/ŠVûñ‚ÚÄ›‚âe7ÌÄTg¤Œœ÷«þÊ_¤ã·ç.“ÓL˜^ÞÔW­%*¾ÆóÒÊvúÃÍB ‚z™MÖSÁyd,žµÿò˜Û?5yÑaFåÿgÈâó d>xØËíhº}™2®eõ ˜¬Ñ,¦¶k …ê–öX¾jÀ™?r£§é(#%ôês‹ž¥Fnú½¸·ü%F-œ'¡sPWÈ \Ûqzî Æ‹¼Ü5Æ…G¹§*NP5-êåTwÓÆ½wë]ô©wWîÄ‚{ê·jX~ÑVîr…Ón¯)8ÙyÕ:ÌWå~H\ñºØe¤˜zê¿7û½ä[–“‚ÌVûlž¡TÇ3 è˜p1Ó4Hg(¸ûJ7 ©¥¯ ß8m4/ØÅݦ£ô¦š¨ÿÿ`£eÏ+a6ŽþérÁ•zÄ‚óK»o3ûR¦ÍÔ$w1Š%í䇦¯·I3P7Ñppï”3{Q¡_²â-æo?ÙêŸrµðtW4÷!] A}˜‹ @!‡ „ýq4ÅÀ‹5üb%¡óŽ•Âÿâ¾Mضõ}çS@£ó´£Ôž¯àØbîƒ3o[Ù.…!»üEþX —z¡Lï«Û¹=âdƒH»$Ý?òa6S-Ó'„ØÒ$ëσ#„™xŒ$O®ÉAJÑÌI>E«v0‹Ìü!ö½;ëÿKì&¦Í\ü› 'Ô£j ÊYòJ„5I _1©ÞÁystqîõóz›¶¾Euð>@}Oú!QŒ‚›UÂøz©ËÒ>{ÆÖh[\58îÇñ¦0b•Ëß<«ßÐ Kä6ˆ“,töã©%ÒðOÀ˾y6¥hbÑÆ9¶Î%‰EŶHðì_(1=ɰHç잃 Â6±ÇÇq|ñ~˜@_)â'¼™ïg,›XzÜÄÌ4a¤ûŒ…®ˆ¹…VOãE>žk`…Ñ]ή(~ºÃh¡+ÑT·€Pª]—}Àð\6½sÝÌ{ÿüQ– p~7¬Ž¨~( ‚t©F<ÍÊ31¢2EZöÁ™à'Dòáìn$÷dzÉgÞ¨N¸Epn—Î8ÙÒ¶Aª¢Àc«ø»YùŒâþÖn>B¥ÞJªß‹éçy0®ãM¨å\\ ÒB^ëãúë¦ÔÂìtJ®ÊƒP;OÖä² 2a·GÒü0žæŠý®@ 1zj`šo垀iñy„œÑl ˜ïL£coÆÊfЮ­ÎX&‡L«BAuÍ?T¡ñ"6?_.[£‚©DN6¶åЛ:pW«‚³Šýôf0¾³®6Ðä?ÏÞ¿Bjª±Qש»ñ:6TvRÚ³íiXBE†’}À”±“µmÂ/µ©‰o-½ îëy¡Ù-´…Úöh73›‚¬/ϸ~8ä¼Åz¯G¶"Ñï6.F IÈ©mwß­Ñ]ÅÉgò¶•S=޸ǙÙQ2õ)ï»mIi–*(z㽸¬+M!ý+$ï‘-Ø9çœ\‘hÌ“Ø&³}Y9µU¿ò\}ÖÿÈTr©Ú˜_]»ð›gÇêçh AEY× [à‘Åp!_€ÞØ¤ÓÆMli@dÙjÁŠ@W2S}ÖÊdë>²ÕOùܲ掫晗ÞY–7¼([ëóó•’¯ÑdÐ íšÚÅ:ùÃôÅr™•™'ö!N”¡mZ²ÅÕöK°¤øc·(¸³¡ñGá‰~Hì$p½§–ÂÏ”:šQ‚û0¢Óº¼åÁ¾í0©…Ù •%R÷RÓÚ"~$.XC™ê_¢á42ûîŒù*©Ü‘⟑òÜòøRØÏŽ ” ù/ ’(ßãý7 ‰3ìÒ¹@È®%¯Éþ–ç³Ç·ç`Ȇ€€Ý†kŽ•†ßsíìú„bñv‘ºZ"»_ÌèÚFÒÜÃÊö¥ŒŸ¤o|‚«³²ñ¨®Î;j1'ܨ rþC¯ÈN%q¶ZÞ_—¯ÉË™®ä9ì–„:Çè  C|'ÓxÖ _8LÚ}H2¦@guÑ»QƒÀÚ[™)ínÛwž&žÂuBØÑ£*$ò­­ ÁËuÛAêwXO=Ö]JçÏÝž]¥+>`¡Fq¬øŽ»gk@vË›ò#Å_TgžÃC+u7µeU÷M½ÿ 6~3×1ç ôâfcÍËZcˆmá GV׿&\ª…MºYxÇs`c¾Ö< ™¤¨Bx„ÅÙXº_ï™w¡-ôª¸ƒDhj µèþ”ÞGQeŸkÐù.ø¼^dù¹!Z+æPø}+TSJÂfgî‘•íÖ"<]T*E0;bÅ3½¤­¸œ)€pŠ.©óƒŠ0S¬ ~ ÓkO¨êòZ‘¨:¡Šægn4ÌzIý¸’2½y¶_£æìi¤RÛc}õæ%†¿G‰ÉwNgéé¸CÚV%í–—?ÌÈ´†Qí«/ÞtKr1GúÀÑ7Bì?Oìmq„Ý¢`[LõÉ›õ;“Ó<Ûî´Ã"ÂãDÕ(0Øs^¥‚ó…ž;y†÷+­F‰w¼¯Dväßãºî@áH›:©ï×K\yó&ûÏö.¯+!9˜uíÙ‹ÞPÈýVý”ÕPÏÃzÁ9á×û)©Ftô&šz¼êJõ17XBõäšeéJÞ@‰+SÿvÄl6Tiƒ»ûV¨za 5õÚHÕ”†‡øEW½ÌÚWk|R>©ÁIÖ¢‰ÚDµê±>"ð\ìúóÚâÛU¬‘ wjoφÎGŸ’@ÙåHjìkÈ[Z*<¬·ëÞÞdôÀôXø‚ ùËT WU5`æ–hM×´$Êz-#xã×LÒ]øºÿ<‹†œ^D”î&΀¢ûé؃”@ci¬/ØҤß.#ÇZï)ù®gOÞãÍÕô€9ÁùQÅe’Î(êCl¥å´.†¶‚˜ÏÒ¡PúNƒz‰º¯Aµô`'–C™árµS[½´~í²æ«ŒnÎÈ[ ø¨V= Dˆ3Шâ‚ÕyLK€Å…ù9úýg-îU2êÖh{ó6~ÓÒšjÊÄKôrF,‡Äƒr@Ï÷ ƒ“™›àãµJ%˜#•òJÏ#M°°à˜Âdþ¨LË¥xRvc‘uÅ2Í7|õòdÿé¬7½’K¢ô¡M컵NWÒÚÜdžYÞfe-7˜ˆÆè>㫸Dà£1M.mškîKòtÛŒY¹Fþ ’›m¦µ#©ìöuRĉvzËo %Fò©pî?¢Ä075Œ­Øídïìi?x½Nx7Ó]e8c#mýw9_0îÌåI"£4 yëo츢õĽ¼³§dËuëhû9µÐ_ V%–57qÐÉèÄ9#ŒP‘»~j¾c´ÑߺïÚ—¤ìAŸ»ÿ£Š»ªœišMÆë‹ÕSZ’ƒ“`ÚÐãÊ3’íʹ‚n¦oòòÕkÁÞ†T°ñoìüתB>f^ñ°‚æp!ä4C:þO#keLX܈gµ ¶pÊKþ ¹5½X8®*ðí5ó”’ÿ›øaæøIVö”äÑ_0Ɇëv¨;aÕ/ÅÒ Fçâ:äö'¡Ï‰%%ۜƘzÃÄIÁ{¡g¨Ê ÎéLßz=ØàBF|¦?o¾yæq3”ìg¸~c«ÊúöX{)“¿µl_zEöã·Á˜´8œL€ÅèubOÈŠ˜Ø¶™_F±lìšÉ=Îï#wýePqHù˵8üQ õhgªf(±³,'?zcy­ôöoˆøÙ ˆ£2¼ÓIòȨwÊZÈPW¡âœýöÛÆâa g07ù׳Šppn¨–ÅäÜÍžâ//m¥OÅlßWf\Î…›Ÿ@²Yˆ®}«G=¡¼¸¿qðIZé)HØfväs £wÜRÃçrPGаÂQÕT¿â¸k+ Ž~«_Vžyi›ÔÃóqqQ¯ ”éìÈö%„Û7Å5^d 6ôþî`—6›A|Ó*.(ïE=È­Í/ûÐsÁ¸øÅ·Q4†;ŒIS‡}=¢½Z5ý³ÃZÈUû#g‡—ý.¹Çvƒ†rAìÍK[5LXšÕaÇõ ¹›Æ+^Š?hk¥¨Ì6à€Çå¿*Ûá¤0Ó6/Wx4Ã0]š0® ÕD±HúQÌG>ëwíú·Ýªõš":ƒäê*ÒUr–G®N·We»\ä“óÙ“çp#ô2òûe™‚àÈdÁz1‹€£õqyN„ÛSô‚ åØI®Øi¶ˆó+±ú×vÎ_.Ul«ˆ†‘ðŠk´º Y¿ªé[–'c"9¤ßϾ0à :ë–}¨ªsÂò¥r²›j|„ÆÊ }=•/÷Âv‚¦êM–ˆä]’ì¯@dv÷I÷×Éõv=ÚM«)°·›=«ªU!%‘»:f}Û¤öc²‹Ä.aLÝùªZyQ|“ñý±º><*r0.Vzº§‹TH½hÄlƒã¼#Ç|ÔÿÆ ËïD½fãH]h‹]˜Ø#j޹4J«Ñéb`DzÃpý‹V‡6{¤jœöÞ30~VëÔ'è7ÚEqa*êLÝ/ƒ¤ªŸAÇÇ®½’ÜAò ˆÅ…~¤BÀ„+ï1™#nÓEÒ 1xhX1/‹ìWYØÍ7⮯̢"ª1Ýn™KŠyÒÑ–…ž-iýS(ØtÍGüFÇòöZ¬WˆO…Ù•Ý·ú®×y†MÝÖ4bÔq|ÖWªHoý*yeÃ)ËQÀÂ=¢N }~ê0Ù%{x¨Y?7·2Èøø†|]½n'޵ÈíD¦b&‡<“ÄÕZ]“.zº»?â°ä%*Æ€Aí0N¶û/ŽS6ÿ­%KHÅ}Cå‚o‘wód‡ov‹R÷‚úhž›^pec礪¿1« Á/åv8ù\aZ¯”….¿sê º¯x†ÿwå¡Ãoa«Ø'‰ÒÔ¼vÏSÒg#WÔ©L!V™‹Œ¢âz¯C&Ì0 iÜðó}· c14ÄÁ·Ež5v¤%JH…Ì Dʼ軺Yù÷‡ý¹Æ3LøS»hã,Y gÁÌ0 [ô§ƒ›Å`Õ™•Ë òe>@—²ÉJ:ÿ–-PQ§¬CôéêEækK¸UAËŒ~î5ëB€ žaIcijØcRä­”EevòG.<ÅáfÏ/OÇ[Xÿ’k§$#âC8yçвI~ÅÙþT‰²Õ€Æ¤ì†&ëýЇ±¹zñçI_J”¬«åÛ9;]Èûåð° ªþsI<\x¤ü:`½žm*ß”•¨bo¥MÌÉ~ÁêöïEüq# 3>Îè0„á/ódžœyûö³H'§«=ò÷’´ÔüÁWŸˆ6¼òAÍ»ˆ1—x°õrÿ,9P!Ô4è6âôÁ**1Xma20Špi¢Ÿ^¿æLÁÉ€Hˆi#J ÈÝ ëè½|ØßÛœ Ô®ãUïÚ¢ކ¸rõ4e<°D@çfbCó€¦ÌIëF*Ûû®Ê<ÛÏýø<¥ß†Wzq°­¸Üέð†râ¦ÌÕ:²#¹çzÓÛ™×<­Øû³«y® û϶ &ÛÅ»m³h`ÿRw©ø*eÎ~†„‹B~uP¦Ñ?N=³Ÿ¼#I*£ÏâZqÒ!•ã¥`ÛµÚ ¹lž Û0OKs$_™LÕˆQ¾PP†ÈÚxž& ¸ÏM©â²¦!SÝŒ</”¿* (í£—Êd˜ÏÞ!›Ú¬¹äÍšÍVÛN—üo0 ­•ø»†~>”CáÀ¥Vô˜ØmW¯f9_“8†aË&ÚAc è4—ƒ!ï§׈õÎbõ$)Tv#½õÊâ¼À†I&\[<™»ýtÁçÛ†ÐüAúkýä xzú;ýuUu`õJÔdœ~™zg³/·%¥h´u¼/ícZêL8þ夆Þ[’ 5˜ÐEv¤†ìæs £^¤ü1 g¸¤æû‚=ç%bû- >ãšØ Xh‹Å)j‚!ž·Â/*q+âf&|u{Q’Y{3WC½lzcå…-ï¶§ xq4Y÷& ¯\¼gè 'PŒ¦ ÀQ•`ï¯øç[Ïa­9˜zl¾ áÔKJ¯žäÁ—ÄWÞ.¥×há*€¼-3S‡ú_n©¼ã”ˆQ-tŽc.öè¶X:D†¤-DÚ{Ï3BôäO ™m:÷Y/š¶5°·ŠÎ7x`Z΃Úp°&ñpêŸ)¨ý ÷s3-„€K÷ÞP‚ g&VÃ_ÑÛQä]é¢Ëâi·|õ`CuÙòbAMCHÚ>ÌÉJŽé¿>m+ì_i‹cj>¥Pð„UPµ ÝU£é0øþ ΞkÅ‹ø¿î+]oí[Í”/s%ò«í܃‚Jíò'4䆦þÌÝ`h™1ca>p*¤ ]¢¾ÞÃ=ˆ¡ôüò¡úÚcâMèN¾eZÏÅ&Y£ÃSÙâE \w†ó¨Þ€¤ ãñY±û¾’#.©‹éL¦VYpUo”ºÆÒÂ×%,V"Í0ú€ î†C¸1Ðww…þïÑôjŒ®Âÿ¶¾Íðâ_ŒÂ½Ïzðn„å] gaVÁåR¹ám³«÷àcâžsß9Ù°A÷Ž"í×QÂí%+¬Š/Yc’r†5!HH~ Å:sGÉp´…cá«lì…аl¯^k %oš%³küÀ«ßœz&o¦‹"õ‘}A9¼L«eµŸ{‚dòǯ¬[•½!”L Œ¿¹™„<—â–xÈ€a¿Qxñ@ú!o¤Ý î›BÈí‡ÅçeN¿øc¾w]Ö÷lõÙ×Û¾§‹)Χ½‚rÅH¬ˆ_h‰y’aEi×gñX·Ï_iÚcYöð+ˆ­Ì k?ÖRõMî³+/ö™Î+¼}"þjð®ú’tÓ* T’Ò‰‡¤0Ñd.o~U;AÚÈ?]Ó4ªuÊ_ºùm«£i¼£œÉó&„ótÑM KxÂi—÷°îò²?œ˜™àÞzÅ[V­{ ÿ bÌ&4ïKŽÆ©ŠÐ}øLÄV" BÚ"‘!›GU<¼Âޜ݉ôïÈéÒÔëPîBX};DFÕŽÆhíSTÔ}¤åì&÷‡Fk^ŸßN?Ôn‰b‰@ Ò~2ú§»«)‘d¤ÄB6ÊÐö9DçÇ¥ ¹íÉ­r@z‡ë{êžÄ^$ ÞÜí9êöQ0£¤ œânÝùAøÆJ4i.6UƒÇÎ:©gùWÁ8‹¤ÿ¤Ÿ|‚;õJd¾£ù² K…º­¢¸±µð$èÓÚônmÚavuݱzÉÜ¢XmW×Ë,gÜrìè®=‘›,T{ÃËè;ЖF"VhuÆö~Ç2;½Yù1¡º'ƒO|}2žÌ¿¨KœL™%}µßV{QV7(Õíîsµ‡¶R{°ë‡Y« “—8ôYm?czÿµ }WüÛÞIÔ§J¤¿ ZÜ*‚Å-˜ÔXóúd±žÓrx¬x*ìT¾ a'Ôäe¨ðñ²Pç3•}x ¥ÿN¯åó_Y¾Âd¾Î=Û[²À¥ÖË®m EwsìP_¸C#í{EѦ€?éuO}´:ãÆuÔ/öÄT°½FWªO“‡Ã^‰Ç¢oí}ó£q¥`Ūœ8¦ncÆÈÿ ýz³æž UíÃn%žuž¾@5Æ ÐJsz¹øâÒR2¾ t•*Øõ-Èl’ŠúžàÍ1§ÖŸâ êRôÕðk3_z¯.ióЭ9røÒ©Lo1c³~pþEy‘wˆ‚uÅ^àfšý,]ŽJo¬ \*gãôCh2“ƒ«7)¢wL‹Á2寓¼hö– ¯ÅÕ‘E/sç*j~‰p ™ia*Â~¡“mZ¥T³ì¶¯nP|æ9Ú^ô†ç÷{ý¨´ë”xWfO@¸›\÷5<³ÅoAèÍžÛ­ø!óã¤èÎmFÃÿ`²Ãÿ²ð¹«=¶@•Ñ Ñ'´¸Y:´s*·ñ2*ÏM¾½¢ä°Š©½£Ê†‡‡ô‡¦c¤›,‰Z8 +ÏÁÏ--=£Æ%]}ÓFð!õÃü ¯×˜°ôÅŸm“¿Z"#ü`â%k´ ÚÍJèJ>{7bØkïàÂñÈßG9íÅÕ;ÈÊu eï­hC„X/ïºÍ÷ô;÷k^76NÈu²Ò"¼§ñ’°) ³w?áòvIÚÌ-óÕOX’ î–uùb‡eYËÎî'Æ<—èýÔë4¤«vV¯¥úÌuî½¹r/-q÷h‘†F`뉖òµ†31´FPj™Lh>òêalê¥&% y´´"fßtff‡c{HûìIÉ‘GjÎÀ0 XVô\y‡¨Zžô !D;Ú“‘ÿ¿ŠÊ4u§Ð:4#5C¨ýü’Œ}V™‡*œÁÕ}ê¬Ü$rx °_–Ë÷C²•úÎPÅj,ƒ<~³ò(Ý…WQ†iw"7j§Ûø1™Ù“?T ;^1‰H¢Õ ®¡¯‡Æ“I‚ÏA?! ú©³úTÃ!ŠÐˆ~i½.wÏý:äuQq_‘  GJ2ï+ŒæÒ<ˆòªáaôÔ x‹œ xÑÈÆ†#áЋìm‡§*µ(D0oî_Ž˜Mu(~9{Es›Jó9Éh6{Vȱ4$$RƃO¿ckï¹4Ùl,˜ áj²¢àoWÅnTYx,yßndÁhévx¡qîí ¤Ç:Tf* ÐÕAn“¬ÏK36ËL±‹¥†Š #²ûzvjcÂÀ‚ýë ':ó»0¼Ëéo`sðþ¢M « =Ñi KÍ—Jº7Z†3sIQ§÷9"z­n}ãûî ¦«{Ó¾"PþvÙÞ, NÚHûŸ¥ƒÁ··õø¾FaâÇVIÿ3˜(lhõ£€ˆ§QéàlÄrÖ}iøI£t,TÜÀj²R^è°Q0P³Î7R¥#RÜÏ•lII£h€æ¼Å'×ïŽô®º‰‘aB×Âp kÙ÷ä!‹T"¦7¼»;'£K?á|cyˆâ…Žsu¯¼`“§‡ï˜NƒŠ1!è]É@¶o‡@¿M0eêˆåhH•ò{ p×é0uû³É1A¾r#æþÃË“hDN“ƒÒõ¸òø­C&÷·‹[¯/¨ÁÄB77³!™òFô srÖ¯mþKáÇ-–é€òìÑÈ•Ùí4«›¢,¥`»¸¶9á,‰„KÈZ·6û ª8oðxVPÚÁK«ÒÕüñe©Ú 3‘8ŒlL·m8è´F¯~rëö¼ö~—…­±ñ°6ž7¬ _ þë#-›ªèa³;ap‡EH69[ËqŸí¡•Èn¤ÄB¡¤W Š(„zð«mñÉz]8ï!±ðRƒ;FŒ)¥›Ó¸Å/"Çkˆå¾¡F¦öö¤²bÇ¾Ü ñZ‘t.íÄw™U…ÙÛÎЩ ²»ÝP 'ë?iMUD¾:ìiFE\Šì«¼¾È]ôböé™s˜Sa×Ýh!¹·?Å·¨Z* ô-ÅÈÝÆ~\ýr:äßêS©ZÛò±©ô‰yTåçƒ ÄPóßßYm˜¨"65ãW ç°|6àŸîbã'XeÃÄy5%×?Uʇʮƒ¡e—Úóíä „.럑_Q”lü&3Nôz¢£Ñ¶Ç6 ™[vƒý¿ÑõëȃYÙGI‹Å ø±¨c²¥Ûg¬¨ƒ´’[–åöUžü•Ø9Æè6ä5kÛáš>pb¿Ó‘-ĪÎòfÈÑÒ¢FŠ`•–1¡½5‘Ç K1‚ü¢'"¼ê[K´X®¦E“%{ÿ]²ª‰è7=Ęï§%x…·dñ‡­˜çPá×Sß<@ÙÚXì59è¾2†µ´Ç”Í=šq×ðV¹Wþ:΀A³§²¸Ñs*<)Be€G ÒT‚Àdký…Y: ËN„½ÿÓ·ØEïœÚñ‘I¸Ë]%¦Lšu€€U¡\Ø—ÿL&×ND9Þ_#—ö/¼a³û«×ú/ç5DaÊÌS¯çs!¨»Ãl)æÖﵯmñ+Ñlÿ€ÖÔ̾ iÎI{*Ôñ¿PÐF‘F2li˜q+JÑzj÷w"lîŽ>‚°ÙÁ‘ë‚ñœh¤6nŸOÜiŠž\ëˆ ,Ǻdtk-Ûχ…Q˜Í}Œ½]ÑÈÏMÒÅúçCH¡û¤ÕšÜŸPÁìÍV[,Wùs|VÞ;¼ùCV°æËB*6vÐËj°®usG¡Ü‚3I*Òj_­‡Òä…A2½§WÏl¶9M$u-(Â,L/eI–uy—LP3¸vCª·²Ež·Î_šÇD;ZÕÙ)=¹U<‘);<åUÄdd‰T³•7mž…ÆñIhJ8Š‚Uu‹¢ÆL˜Š½Píp4sž²A0ì~ïÈ—5¨³%Ôß_/lÕCeX ‡'¦Q…ÇÞä+µk°K¥6hÂèk<z.‚n?CõìßO*ƒükŽB<öü%ÎN¦¡KोÞJ¶5ŽÏš¯®Žá5í4iNpF‰¹íÝþªO)â9R–Ûâ1áxÊêÔhUÔ)0Puft!}º0Úµ)šBÜËÌ!ÀO÷uy#„ò®^,ºê¦(Ø×ÃÄåÀñu<ñ;ÎY®8hì§ðX7Øt@yíäü‹ú+ãJVU´¬º$’Õžz7>Å"t{Ùd¤Â@«à]’+÷ÿưeþT¸ êZ:[¥eÄ Ü¬‘¥”fÂ/ ÞêÄçÃh÷Ú$ÞÞ­à¶R©¢{\¢Tº&.·œ¬2Â5†C¤ûÇlSv›=ê¿ÞrNÿ?³¾ƒ Bô ˜C|ÄÚ?moÊaJUøM1–Û)Å+çfúb…€g‰àQ£ Ê„Ýeßÿ ¾ÚßþL2IÞlëÍM&\† ½p{J`—«*^1+€ªˆ™—4¿±Vž8[è³99Ç«™««h>6‹O¸'0<Äî”ÿv¦£ànNaOmuð„L*ÁIªÃGÿ5ÐiT‡¬“Ò]êOŸÜ0«Š2ÇÆìª“ü°âÚ8€Þ®žj[Ðg­[k×#¼GûÚZÛBé>ÊT§Ÿ“ìÀ¯š·‹2³ ˆÆŸ ªIuµY0H Q>Ç—ã#=ÄV|ªI ¿o,ßöo~>ºxWâ·,‹®r/~5=Gºê›s&K™¶v­…­<À ÿüL}FÇ€1¶[·„éÁÏü“ëé4«†x/Œ3òâ²…‰øÕŠ=³]Ô²?ζ+€‡BD–²u‚5œÒYxM¡R¹ˆwI!èÌO´«é—Ò+—3-T¼/õÇ÷S—;ª¤¤O½œÍ5Öã&P‰.Ó÷« Q ¯Ù‹²­˜öÕ™• µ¬Ìâ*{Xø—ªLêÍ ÿ k-q霟ɒ@uÑ/wŽ®JíH¥˜P8ûû q‚è0ÚôηÜTÚ‚ýaým ŽF“ÆVóÕƒôÖ‘âq¨Ø[<ˆÙ®Úú³.uŠ×Ú¯ç/Òžw&Ò¬¯¡‡úrY˘ ½¬hר¨=Þ}çËEŒˆoÑJÛÃßÊhòÈè:QÎjZ§öT©w­Ä“¿¦R:ĉ•€°öäŸßnSµ„lé³ôÊŠY³‚ݹM6êÚêE*û·ÐÉæ‹¼nƒF¬F ‡[¯Bz«-D4Ÿ/òp+€ãä’ÿw™æa™BfE[N‚,ä]Å©.ì! ØËÌ /þGõÏÙˆ'¢ÆH_ÅܹÆ|Œt¿çVi}ĩӔÄ'ÿfxÖƒ•jz%Âuðx¤“¸Åt|Äѱ~©pºBû"ÚE‰ËªhrÝ;Èïý9¦Œ-ÕMé"L[E< D €Ûàìæ…=EN·2ƒ¸gì(ŠmJÐïÒ…Öc£×PaóËÁ.__ËO”2:ŒÞðmIæ‘;/•VH—­ýuø¢yAÇv$?ClÓ_Ù2+yýî&⋵gBm{£iº‚’É~äxLFs µ0êHðŠ‹Ô˜ÓœG¦þERÆj¾™‰EXÏ °¾Ä‡ùZÙ7n>ºÅò}ƒYÜÌoT"½ÑðŠ·þñéS¢Þ’yiÃÓ0‡Ò¸Ç¼±³¦ÒÜMרxhßv¹(Ἀñ玊õ´lÃÀ'•LÅ ê\Ü:Büìò<æ®+íq{oÙÎ~‡zä–PÈbEÅæ™ð)ÎÅé(yƒ¨ýéã&S°s¯2dn±H1G‘Š9Úø.Ü@5ýjB’Tt¤ãÆ>óz€ëÚjƒµ€·(.Ô³ù\ßí)B牜.*ºJŒ ó—Zú/ç¯IdÆëÞµñ0¾çhƒ= Ê!V™úwËÝn%ñí@p슃YÔ¦ø–Ü:WЛˆÂˆ ´¸”5>© ~zJÛµ‰Ù…„Ÿ—baÅHùÑÒ&šZñ%6ÚïÏÂÇÜò¾Aá‰fŒxoÚ í¶;q\Ò§-©5Ü©åB¡ßetœñjЙl1ÄÍO îUÙ̹UJÌ”:yöÃB…ê§Ñ~ŠÍõój±#àð9™'Ç×`Óê`@i}¼åíS ;Ñ×I1…¤@üßùJnAq®oö‘ñŸ‡27¡$ý”âŒ÷™R9tû[Ë£?þr`µéCä–ÈM@²PþƒÌàǹ7ô[õ3Ú^¯•a,[=̉×ÅE%¢Æè¯¯9Ú*Ȉw'*Ÿ\NÕ¥g“uÑdýOA¥ƒÄNÏj`ö èÿéºÌtœÕZ|·Þy4aâ)˜nêö–ŒB²çrf¯"7~ ¼#ç§—[+Û1Yè©£Øt¸ï“Q 8çA1¼±)§OÂ&;üEED(²Ê%«ãÎ0ål}uântÈ({ï:x>ðÜF["‡Êà‡ÿ‹ ¨ó#Q׸|t´ÄРߺŽW*ž”ó¢äØ+Ä-é®[Õú*ASQ*Ÿì¦¢&³çÂ<1ؕuwÊJ¸¸zô¹Ä­bK3ÊÀ+sví¾Sy©‡ê0cGeMÔò{ùS^y‹Æ!áÀèß U,¥)úþ0ºéàK´WÉôV[ë¨ÆJ¯§L`¥È—•}œ‹T\» X©šÒôš–LNCÊCèsà\!iÛãè(€ðAtZ÷Ow—ÉWèY1nÈݘðQ¬¥ ZN÷Ÿ™ ’®ÚyÔâs„QdH"]Çor·lœTRV Ãë,SÖ­ëÞG ¥pÝsU¾"ŒªÃV厖WŒÉÅxËWµÕÒ—eýžÒ¢öSøùFž–ó(ÞÄ´žÆZ=¼šœe¯çQ@'FÆãêW@üûÜïe(@+`Ž/‘Ì5Äu¨ï#£Ö.Èþ_M=jÂ:b»ÕÃëµÿÂ3P^xéVv+Å/É €Ä‡,˜QìRINßý^…É«K.zóöÚ±ì~¥÷VH¼%§-ÐÖU%Ç4~Kzlw ~)T¿áJp„çàôëêâŸqØAÞË©†§Ä ´.dïÞ¿>IyÖ¤D¦®·Á㱊H«iÊ;öµ˜ª…xe7“ù„‹%ÌÍz”_6’šrgIö¼¬/fr“×MU§Y{Üu”h—œ9ò²ßp„]ãÏ1Ó¶¾0Âm¡ *jŒx¦‹´C‡ÅÙÜÑ–±µÜØ㎖“Êy8![<Škå¬@cEú"©#FÜ,–¿) °qÇ4»ï yìÚ„Ve…ÚjèqIJ!v”?ö?îå¹È$óP€Óq>wƒ¼­k³&bÍúò7 …#1E>…r„ W´¡ ¶®·d£ ž¨Ä:‰Ë÷·%)åv„B)[Ög¿¾öÙilÉÆÔ Vv£bªZFN§ /Σ–É߯Á ,Üó‘Â(š¦‹Ò5× ¦ `t_¹µÉÙ5X\IÎ)ø*"’¦Í æöS¹ç»š² ë)a =—Ð9M¤ðžº&£Ù3$,*oC’L}ÜãÒ©"¿° Ss½ÁÏþKäð5þ¬¸g­ø¬/éAò:oêCÔd§zªåÊè•)%OªÈ™œ}vH7ö”ïœ4nïnoà“%1•¦œ‹€­ðÀ¶ŠFê(·8Ô¶n$}#ªiûmA³IJñæ gÀ»Òž÷”–²Üs¾ä½›—;vòÍ?ŸÎ–ÍdÀ²ãé ´c¯¼-@ƒ&ßw"¸©5Ä1¶Áf¬ &ÍÉošZä“ÑN0ó_ªT<=OפŒ?ê<ò³¹PâSƒ¯Ö……å<¢ó S]Š/=Pš¾aq÷ž†p= /¿¦Ú¾ ÇRqRSQ›¡üÄcps Ù4ÏoæÍ%:í,SËsjÚô‰RÖ\Ÿ…Öâ¹~hYÆ­eIÝ댗&ó3éHlFŸ±ùìØ 4v\T¹8$(V¤ Œ°à®ûIÃCOÖG4QÉÓÝÅO›HK-w~…¿¨¼~êC«>]ù—ú[‰ÞHžJ-Zj{ßÔ쟡<¡ÛßÚ†7¦€Ùq•¨÷"„ ErrC]g¯ÚéŠáhÔc“@|[t‘·P`GpG«’üv¿£\‹¡Æ€E…L‰v³CìÖrëé ¹%Ií\AÜõ–Ô­þÉc‚ÜO t³ü’@Ù™åû;’ò‘°9'_09,Zƒ›Ãìâoï1×Ĭ/¸<Ý-çßE¥«f ;©{¸aË„ýžQ\\r†že Mâa[/ÈöYR}rö<¾aXMd`ñQ:ôWV— ;ª^Ø0_5¦a™Z¼{>›OŽj\Vlˆº9Í”tę̈ôŽÍ’j}Xó`º®3Úh˜£:¤‡åPªõœÃßNçÏ{{ðY…ÚÿÒô‡Î-‰g¶Y,ÎBM—¶EèIŒÃœT£Sw±Âß5Ì¥‹íl^¸/'\ö&7þÐtœsU±Oø¸iMVí“ñUS¨×˜Í_‰Ï¶àxä…mf1ÚT{·gŸÝ»3ly!jsýx.¢°kãl¾ÐÅzÎéÕ²üH0 1[¼›÷õØ­PzU:Àå/²kè>_ðJ. ΀‡ÏøÕ¼›d@6T<“–€ŠOf®‹$Š,öõNtÖuÜ}›Jĵ‹? ?w]jnZgWíõ¾« ¹ªµœLVó9áh¼nÚK‹|³Z„ÃóÏ#µÚ`¾Ì¯á or´·€~ÕÙb{Z+ݹYjZîUÀ˹9°à·¿jVZæù; ¾\ªÚ£Q¬5Mhhh^€æÈãĹ.×&ƒxÊ?äŸ]òsª45Eƒ\j£æ¯à5pÛLþ?Úˆe¨þÉq,àÕ-yY11Sç üå¾AÚb÷ªoG7\¦*ð&©x±_ÛDëDd>ÉtÏùFù“oµÊº %yÈP3.W3È»§ÊM»³Sùè?Ÿ“N~ ˜½TïMiž­ ¤a  ÝX*H9½Ä^ͤ؟O>ümé×õЙèoøœØáJY÷éIŸlJ0aQSôï‘»æ3¸,¬ªÿçV 1•) ¹€—* ²U~‹ áK±ºªBç@¥‰.gûÍFÙñcú 8²ÿµË )ö_+“"Csm8YÉ%yšY;S sÇ ¹;n»r‹äS,-*Š×±š+õ0¿­û¹=$‚Wn_”ÏOómôºÁEŠ­jóO‘ :o˜ì%üöqá>Ò€øŠÍ¢ÈÞ]]øÝ¥ŸÑ%¶šy ™ ¹b £Ý¶`&þ ÙG><::y'Mÿò×]”®rÃReã ‹¤7ió1ó!´#޾PJÄc)skmà eK¬›ê ÷Eùåë} ‚‘]_—È'£3e(S†Ý39‚y¥ÌnÑJÍzÆéC{Ý%u#qÂFÛGßÒr1Òtw?Ò–>à !¢÷`GKy¤[òí;ºcKCÌÂÐ,P+ÙÖÈ ·çwX;.ÁŠ¿ô.¤ïÅDl&ö\€>0\IÒí•9þJ|=ê’ìâœÞ“lüÄ(åz=£Ozðí*Úƒ2•ÎÜmÖ¶Áfw¢R³ZªÃÈda{Ÿâ)£êËÈÚ’›zUy»ß®ô€õ¼q”]ò¬d¾ìãúƒö1šjWÓ=Y~…ÒV²Ý֛ɪp‘»mÿ(4‡s“–¦‡¨ÄÃ&ø¨1¡ðŸIV!¯â ŽqûîÐÿ‰”ˆ*ÍGè:Xû£Y™ßÈŽ$[œB­$¾Ä“%R¥wEÓ•M¶PcGª*úvÖ(ÔÊ7ç„C™ô®ÙýwäJ»½O|[º¼}:$Ö#ôT±Ó ~âá¤ÓSäÙÇŸq¿ÖTzþÇÅN¨.yëÏ6'©Ð[aF¼~1ÇT³E²'¢ ¶9îC£bû'e0ô|K|ë|0Ãf,\ç™è;ÇO †JÚ¹k lø̽ˆ-v¤g0ˆ½¥íÈ2 5Ÿo#¾D t ­˜s:@¼ø ®pÏäÜ‚#åý†uä+ «9©Ç¯ \º^<ÅÞ ñENuKò3„M¹®â%1*®/SÓ*㺆E{ž™Šh¾?p\ƒ 䨒qÉòSvÃþä–_¡Zé°OŽuÈr"­Ú&fß%äñ-ˆßeNÅ_Ä)¾*U?M„ÚŒó¢óÜýæ5¨2ÃÅÃÑÌû á§4Û]¢‚Z¦ä£‹“ùОööÌóZ´ˆ÷éîþýYË4!³‘\©WI»ÁögH.ÄXâ³¹ðf)ø•f ÜƆ  ."öZfýP˜¿YÇ­!ÐûçT¶5ƒe¨p5wèL/*– Z ¼5MúýA”ªDÇÖsmeW kųÜà²l°ß9ª âõS7*eA9¿¤ã{ §œc3*+…N-`1DÅU>Ó¹3¤þ”`šû76ƈš ù(,=\Íûát {U .†œåûÓ a~‰bî"j>:Ô‚¬µ‚ôj4 øjWa‹.Á°Ûô>æªbÏ–`>*¼-‚º÷òkí 8Ø/ÑZQ('íó×"©s;+CóŠ ô×:MjŸ¦ÿäf`O “º²¾[´ÞËÛ-œ÷Žè6H"™lóËvêÖE¥˜ùÁ„ÏR{·Ñ1˜Msbç'³aÃQV]'"$çììÚÅ„Òì`¬GJÛÞÓ·;É0%ñZ«h…ä”–ož)žA`ú›&yÖã/•n€Wˆ+ÆÉ‚+!Èêvv•f†N³¸LPMBe˜¶ªàöŠéT¬ê´ŽÛy,‹º.€×ÌGd ñ2@—B—› ù Ÿ ¤‹ßY@_7òê( Çhd@<Å·à ÎŒ›4ÆfirÆÕébÚÁ24{¥sIwPés¨Ê`í%™ðräÆ2ßW%²)Z!›Àã°èÈ8èÞ|®2«*§<Ñ ñ¡h<çžÊ ˆ.G„õ§=³5!Ó13Vu@ÅŸQ¶Æ?·àØN%Öüäüéì+Ë´KPT“3ÀŸ5~€ØÙ2PRí]¢£uû±æ•ú·¯ß€»ÚøWv£“næ—V¨Œ'"BR&Lsj"†öî(_¢*ŽA*œÅk}0«ï³‡×v¯:\†QàG‰ˆ›µ¢µ<îq5¬ÆWwÝ×k :~žÇà(#o†Õ[®¯–ê3^%ûWmíܳ?‚üi]&±™j£_©‚ÚŠ0ãíÄN1*cf&düäÐ’q4¿<âÎÓÊeÒ½z2@Zø»!HrwæB-n*!â ¤‚­¥éHD—G”iuCbÆ©ÈPBÜ^b˜o|tƒ;ëViQ TϾ<äuPœú¥«èáâQÀŒæ»¤sR>e¹pÎY‡E¾‘< uiõê» HN¥àÐss| úËágÝ«ø ·IE–ˆ~!ªv¼.ß0ÓNä)äbÓ­ò›Òqk^ã„…@ý¨Qäx8T¶cž°V›uî‰é(Œ¼Ð%Z‘žp ¦ ÉÖν Àœ0r%xlõp»%B‚Áy¥õȾ«¬ˆúÕÒˆ§òJÀÿWIsœ3Ón÷TêŸì>ªÖ{d¤x9óå ãî©UNd tf¬ôÃbleäé_]L„î¼-sîX(h¸#R¡Qî Z•Jós¥Å”¼.[¨Ýç±^Fb ü1ùí;Ë„=˜‰.ª†²qЋÌ G„êèT<°M«”²‰S_X™Ãh×R¦ñghí¥åeÙÔÊÉ]Íìr|÷¥l1üzn!Ù¶¤)ù¨ÙxäïÝŒAdo‚Í+³ùmIØŽ×J;â¾u_ù2Ô×¶Óíì­ žäfÀv¡PÇŽ€'ÈãB÷àþ2¨ã}p{iíà‡ÖÊ’’'-Ç$qµèÒáìêBù碿çjMTP §ËÁg÷Üùx¤»÷¼Ð¹GSÕáì,˜á×­.¤‹Ì¿%µî?Ôz½iFl=-„zÄžg‘\èéž9:UV%tð[Ô„Mo¢¤ñ|~’mˆ/òé,¸ñ!=âý·°l­÷Õ!X§†uœó>Bc!Lò–©lFô&ÀªÌÌš*DaÎBÑÛWI¿ø ¡.š±4=ÍúòX·?µö㕸êÁ…ÜÈfl„_·ØÃò‹+`ÿZgjkhkQMÃ=£¨¯_ºïñj$ùê ð"‹¾PZ©¾«ãÌAµkŸÖaóáoß^Á;o‡™{Øq¥Zx®oJ¤~Ë((4·øT;Ïa$ž!„È'€, |…EVI¶³è_Ù·†rùQ=OÜåô0 3ûI˜×a”ä®Q9pĉÄW9¥5”u *¬º¯‹^.غA¹?1r@Q¦SÍ®*ÿ瀎´i£ÔztóKôŽØzJ?«ÑÏ'~O¤T"ãÜašñÂcìðaþ,"¦ÔŒ­Sô:LÄ­2‚›â” ôÔp—v¿¢í;#\ÜýüÛõðι}ÍÝȇÇ`Ž!à€ ò6–IOßgÂŒsxnKÔ•ˆœ²Aœ¨gjL;¸7NتÑáÓéÑʵ 5Š CíLÂ,˜˜ Ó}g)6îâvåÜŒOî…‹–*Õ‘ëq7S·eOSr'mº•‹»€”¢6oèÛ}Æù-iùÁÞݳ“+RfU£kž:å°xX²³›3,ƒ}6XWö!¬wÔð'¤† ¤‡~‘<ФðÛ·w˳³=Rí~m'žò¤¥ÝÿèìPkÍ}0’ÁœwR‹] +ÌÂU.8š]®^þÀƒ^/VZðÇFºå}šà+¨4¼™K%èÍ&¡‚µ²&&IoIÓ˜ZLÂ}žYØ5÷aI¦ƒ hYîÐÚþ›†¹`¹jTßË" ¶Æãû.¸ª—NPF™|µ¨6fÉPvpä€7ú@} ¢Í_æ›á"]êUÉÏ„Ž.ÒŒæ4‰h]ÓpD¯Ë¨ÞŸ‰àU ÿ”Îêøýû;.ÄrhÏ;¤?âQ•?ÀPmXë´£àž ”9¤¿ ‚–tê|÷4ÝÁË¿á©D?˜9pα¾ÏþÅ‚f•¿e",g9½Õþ³Qû%ì—ÅÞçßíp#•¯vÐ(Å¿|öß#¥V†4§z«•K\s8£Ý™d΂êwIhÄE>m¬9ŒK2üê\.tÀ`ò>Çáºq{íí†PÿálM³_ô§æˆkÏW ÿ'ßhß§ˆ®ä¥Ž&õÚ¬¯¿¼ÉMHa–”ð73 ¨ÚL&f<ÂÉ×TŽ» ¹A áSîäëmVJôq²Pé;&šÞ ž>9A 47€µäã]N¨¼f^À"Wu·ªä}A ¾—ª<üPé~ï¦;¶²:@2™i+³¥¾°gÃ(½I@•R d÷=ä®2>‘ŽÄ*˜^YyY‚ê&ÒªÐÍá6²âäux4ô§crFÃ.î²Þ°cU–®D=tB"7 öØßš8Ýûº-0Úºœâ?^â( ZCòÀt‘S RM«£åš¹€ª¨,¢¬ºªBo>¢{«? «QÜ|ÔI‚µQáÞMo)zÕ$eöÊʾz3{¯„ý(Jðâ†ìL7!@îÛI¼ò¢,®PõÄb@šW"»ˆƒ2ce»¼‡xò®GQaLÕðÕ“ šy¾¿ÔõÖVžŒ\nàêU¸8ëÌõCè„¢ —yz3ÄžDôÃòc’ ïz"‚¥JZ¸¥eô“< ‚èÊ=Ätëöf·ÄÉ¡µË8¡-x‡oÿÉJýÒzئØ’ò|ÉòƒÜH¢=9]š&tÖ—éü’yôuc$+1ýýÿg¸YiHU"!Xo ©‘9 9BóûRÍ ¡á°É|tÆ0+"vx4¡ß/ÍÁEÆ,j`_šÚTó|59»Ìšµ´–-CÙ`±Û©uu‡7v8—ñ¬›‹Ž)M,:U§°·lGáñÊ9Ø'_÷i"öê‚U5s»zM\ ™: zŽmVÓ‡?#bŒBWR0½¯\ä¤c„dñ÷×醤`rÑKV¼éU’€Õsðý÷6â=:@xõÌ9º‹9ìñ²„0‚ù½­Ë¾H$VþrR[•æ—¤Ç[¯£°ŽE#¸à)9Žž“& }«ðË …JFßßË™ÿRÆbG`[AÄÖ·äF#ŽY@lvk¯RÚ>Iÿ‰Ôy.Ñ}€;Oï.ÏRØVš^B‹`r3hyÂJ~Æ Nv9„”h6ÎÅC°Ï1¥+™?€|'™ÝR0öæüä8$ò¥ÝŸÿ6¨Õ7sŽÈZ_Œçõn§YAÿ)@¶>™Ñ «NäÂ.hCþ›¶ßj…Òœ¹ÚhO×"¥úU#Mz[u¤-¥å#$äZk‹HCåæÈ¦9¿èÕ£µ l?æ·V uz³^[/ÅQüàÞÙV¯ƒû¯6¢ª+y áßt‹ºv4C>åC`ar¬„>w1ix¤ð_Â×~í@ý"üA$C©¬Åоw»ÿǺC«}÷ÌN4z>g=µÃy+‰£]'XÓä~³VY#ÀiAŽëFôU©Ø¤S«TÎ’¼&O°Añ鈉¿úäÃÉTu„¤œQô©ã;d‹*ø¤u ’¥Äßkãî…¹Up‘Ôk¹©+¶þ©6¦. /Eì¶ŒLv´ƒCÌAmž„õ»^Ô%R›1#›ÀòxÇÿ3o²­‹>&©¨ìx™I!ûRqÚGò޵I~ŸÖq­vÀ«¬Õ΃£¾+7—\¤«#Š9ðwmûÛÈÊ_3Ü}"±ÿá«:±ž”äªÈÂ.7KÎÏ>§¡óÔµ îµQrÿUßþ~D,¥[6RvbÔdN{;P¸Ïþs¡%Tv;¸´ñÆÐ÷Å4 ¦zR.¸€Šeap)ã#^ݵX6—¤‚yï¾¹°ÉZMʺç„QÖà<XŸRõ K‘rBçLŸËÞé ÄHÉöÇpìXTFœ½$ëcÈJx¦%Ã~!v|šëx¯,jEc Ü«^ïãU©Ôš©ë€ßÞžqÝÒ¢"7a~-G*×W;ðõÒD+"ÓÓûR&EßIÈa+'¦l‡°7£LIßJ<1Á6³ έý~û çˆ$õ³¨È¥¼ÒQ^©Œºüž·UßY_Tf¶Wé­>KÀpÉ•Èü&iç­G9˜ ‚B·d WÕ¼ a";…Ô Ñ6O5¹Väì§¾ßd Å+iâ5¥Æc‹Ïw ëµ÷ŠL!ÂÍ ÆŽ¼XÔz åHw¿9zªµTgVEŽîõ@PMä]ºÁfodœ(£!Æ(HµK,.ʼêÄC-¸¥tmJŒ¹5%Wñª–«?•A‚§Ïx“ÞY€¦…Zln¹ÜHVâhQû{ï¢OqÞ:ò»›ëxP4—ì;‹õš¿¡å¯|Úå˜þKž¯[ÌÆÓk£ Y!ëÑ©k«Õm–›H|t="‘+ 6\$xsù¤DV†èô‹e½6“x”LG{G—@qsU—­$(“wl5Ì}‚‡Ág•©ë¢¹gþÚ³ùÐA(T9¢¹®ÓÈüà×?ZL½Ma2¾2«ýÆr1Ÿb•`óƒ2J™sµ´'/‰¡À–à!9ŠbxÜ»K"ð\Õ¯°_<ž<³äÇìê–hãaÑÔµëõv›/5†ù\X„8Ãf<ÍÙ6Û2{Æo+^éñkÎç.R¾ÿ-øšíÎÍçÆ÷^Óívö˜è´ØäœË9û(«Iiå~ù~Ó·.‡ ˆÖ^BÛáz%|„Soô‹™õþk3O0¼×½eÆÚ=Ö‰¥›#ºþ‹¤‘Ueüñ(Œíè!Ñ ]Ùˆ1‰Ɵ옹Þn%‰³á•ó·–ç‰éXÄ›r0à¹3p³‰¤-úºàÑ&…Æi.DÜö—÷elukt(^ä%ÖÇ“ü¹Cµ¼>‡?M†)ý<^GjÅC‘fëÞ_¦spU¸È ‚A±az4Çô™UÒ18ôt köYŸ-BøqŽW`f©å%y,žÔu–¾·ÏÂÕi _ü} @-p¥à¤1ÎÂä˜ð´Œ@£oå (݉x9·-’GY}dHòZ 2ßí{Hó¿«U(€¬«£‡ÐËŸÈœÞ/@÷h…GVU½"éßòŠ‡Ý—ò_!¨ÒÙ®‹(‹Áž„…5ÜøµÚN¿à¨ìÇ ½ÔÒ _î”ãñTaÀ£]WiÏÏkÑû%D’²YùÀÛ\ÆÍÇÔ HížØÍ?4ÄeíÝ;†xtÅQgÐ2þ‰æ,üè´©ñbCA}/1#2t]¶ åá½ óç£/²ŽmsFÖê_Ó’ N€Ì#×qµ0ö}ëáchýŠ©=øàéØhmÙ}«BŒ²;$ífP‚‹ů^ùù~G[ØÂø 1¿ ƒ7ØîÈîO÷Ã>¡4Ùq—>Š$yµó$ ³úV‹Ùzz¯™÷ Çàø!;”óIeû,Ó·TÉ›3Ñdþ¥3²Y3mIÍ7ìd>–õ£ˆ>¿bm5`ªžÀ©ø ƒüõyXßÓˆLøtrLüa‚ûp’§¥P/½À뱉à¦Á´ˆ¸¸™èÊòJ>ùK<Á×~PI§Óû7U÷b³@ËÅzÀyYÌâσ<'9t)ŽwEÕ:½¢@ãa“%3q*Y¯„aàp¼4„ЕÐÂÀþ 8õÏpTË ØúÝðÆÑkñ÷‡š€%³¾}G¿Å&îæa×Á™N_aY mj¼W—ÉÂTq7Úlr³¼V%¿prð ¬ùZõ8Ám8òg§"0ÛF]ãžàè0𬪰’5•ËôÏ&árѹ²KØôètÉÓ­ùîÎÀ I©ÞÕ2y„ØÆòÖuèÓÙæÚÒ\뜰#I$›Öàéšw¿1øJ<~7¿JC€Gó|l»:{Þ´T"¸FýW[3€3€Öâg ÉŽÑ~_ JM~vo•Î)†v ’ C¬Æ†·’ùôy–Єa ØÙtPõž£TôP¬°g¯Ïmc_qû ÚI®ë '63os'¿¢Æ˜sÉ´`§Ì'U}¤ÚW¥Xñ´4lÞŠ­‡£)èÎw²†à¶ÂuŸ¡)$Wc=DiJêÉGÃ5ßëTp\¬áJÊPvÝB´nÀ¯íT&89»¤èc¯µRÔ 4‰è5Á¸¯kxi)ðçßÑc!RŒ…0|÷´~_C]Ïó%°ƒ\4Ê‚*ÆN,È䯿 •¡ üÝ2àZw7XGªîJ†ê#·ß°b‰˜»ÛY­…2aÖ€ÁêJkEœKχ>ñ ÍæËzw„žã½°ÅCÌeÇ‹@€ü#oû¶~åâ®X~•.ÅÎJ8­fÛ ;¬·6QƒÝ!‹i/ÁO‰ùf/’>܆eûÜ™¯J°• ãXûÅñ³ÊC.Þ™£q—Z€CÛÐ@»WO8‘åÈŠ/ÁqòXòaà)Ÿô©îï XV`–+Ž@¹ÐW\Íš½§òýÌŠ°4âWzã»úaÂj©Fd¡;êÙók_œRéKÞô Û>€/ey‘ €y WgËÔSbîÎk@n¼|Maök–;Kd,Ø0Í6à3†±¡",9NˆíÊkãh•½¡&dè³­…±¦¦•‚,BÈþñYvç¶›ßB­LÛ”•ŸêLÉRà|Z®T üŽ-$‚Ñ¡«¥¥ëžÂ­ÇsºÝŽ»^l^4[›q×FÊ’ê4°Eôû(å½.Çè}v鋲 k,ë—Qù£Vp¢Æ#8TZÄ(ðÝ Û]„UJv˺@¡¡ÑË\8ûÅ‹"pw€¶”Pé‹2ãÒ6àɬI÷îîáú=(Ý6èç® >¶¨ цƒ|¿ý4%òg'êÔQÝäŠÙK=Ub‚èÅùÝ€PŸC·²ç‡Ÿw4ò<ËÉÊZ ”ÉVNµL[1RkÂ>kâé#£}Æ®¯ìˆ^ü)üáï%:ö‚ÇWºXì_,ÛoÁ«Üø©Sûcº@ìqÛùœ'Ê[AýÙªw€¦þ±óLÿwÆê3—U$Ðßð^¶‹ë>D€ 75Š ±(„žîÌÔŸZbÞåã‚ |ÉÁ€j:a}:>ç¯'@•f©|/yÈyaõ{2»x÷2j!™pEÎ6»0Ï3½ášPº->.þöUžAÑ¿?é ‹Hl6"nžRw'çF¤¸*ôØÿû1þÛ&L§[í0ÉõòîgÛ¢Œ®L‚¿wIÀ'ðŲ…¥HàŽGºT{?Ù@±z|ðŽ#­SC"w'p«‹¸ß¤Qe¡â³ÏH«Ý(Öª+%Pu›‰iÄPq®G6€ÐÅ>j¯xמ¬½8Ò©Õ;øHiˆI‰ùÖ€Ú#˜På(9ÀO®’KÛøß-ÒôHVAøÕð$b ÑŠ~é'\9"Íܽc6ì‹•¥ÖÊWvyT¹9 ¨<•IºïŠûœÏÁ6ö¯ÄSTÄîxdu¬“ÒWùo"üT ±ˆ€ö+/p§$Ýtòþ¾A”ª÷uöè›bÐÒŽÀõç²bà]'^˜öÞ[œû¢±¯íu£Iq±í'_l%µä…{ÛÒûd4d&ò¢4„"¢ ƒw^nŒyb?ÚRTðw–õf\ŸÚAÇë8\ ²~¯…]ý‡Äg)´îî˜?^X½mët u¤@zš®pÌí/›?öbE1á:ŠºøþëÐæsZöÏ på¿tEöIŽ·üŸ™#¾ž9Eqj¬ lj¸‘zͶH·ó?)a 0ŠÌnOM‰€ƒÔSŒ¡û™sçëªÙ(Dÿf½@¯ Û/W`Õ½&åTó”5¸O“ä*Á*Ì/$χ$ ëœSAÚ9'1Pß_ÆJÚ¦¢*j*jcϹ†’ÆÔC´,EâàÞK•Û,¯Ù+¿4ÅGy1ÈQ"†‰)m$ÙcOâ3]¾;]÷Ã@Ûô†v]}øÆ˜"xHÓ¦ë¸ˉQCµ½×±q¾¢yúPÒÜn‡c %ªc³wØËòyx_]ÆN«X¥òO—dðÇ´P?VóΞÙC7ÌÝÅSÁÞLµ ã0ý~ízŒê²,hH°+NEoP ¹ÉùÏ*a3Õ/ç§]Ü´¡;=­oçûÃQ&¹¿…ÔÅu%\¤r]Îm¿Šünê¿¥:ÙrË4Ï/Ôc™Áém¾‘|#´‹í­@XQtëñë~Eÿ*€m¯ó!Ò=ýêBIÑ+äuÖÃïçu‚¯Úȹa¬¦|œ.ò“‚'´Z ÚuI=)ê^eÔxMÔ îÁèkϸ ‹ºè°@:1~áýõua€tÀ¦b#9{VÈ’î)ÓÁ†ùˆ®dÖsã•§Q¸Úy@çìQ’ÿ©§ýyZ§ñ;ZÜ(;œ àVPÔÇÛ27m¢‹¢_¼€}_ÐÀ2µáŒ®z£Ù¼ÐŒdã‡yûFÆÐ|`ˆÒ¥ÆAðLÂ?J±›|Þ–ˆx€_‰‹—¸7Ó@0—R£…O.»85ÁÃDfnÝ_f€M5­>¥N 1!>üGªþõ_±–Ú”dsÞ.þ7‚€þ»ö31–ª@FÏôBú(9‹!Rbâä\€Ãõ¨Æ…O[¹VG‡àë;Ü£ån¬—×<õÍ!á6CN÷ó]†¹4I"ë |D‡ö!ÔŒê$3}kéo§(«ösf+Xu;>yS0¶^"Q°Ž™›ÑÜpÀò#Àf$9£rŸÐë:œÝÛ|«Áê ÷)’Í‹–8¹_€ª0¤Œái¯'÷Ñ'[»?ØZ|þÃ6ÎU.Sjà_…ËžE°øŠmK¬,QdÕ'rPD¾~h²¥`bágÑ÷°]  Îp•“6Ñ“—yì‚qÍ¥8VpíVMž&°ˆm9Îõ`ÿiB ¹G– E˜tl>ÿ—Á+cˆÖ GÜ9HQ*qÜn!7¸¸Üf(\Ðsæk|¡6Õ-Le™ºÏÄàb6¤[\Ù=–²ßVÔªU2JÒÏNC*–EûƉ†»b)ñîTbÊAéëÀÉ…ŒÁfÇ·ñ×7±=†®ŒHà>Ÿ©÷ùx¶°‚Póµ× Ï„›¾-‚•umðÿLˆOT·¾Cᬕ9ðClËv÷^Ÿ%ަjÉ>ÃjQ†ÉN5|>¡U%7t²ªþ.”Á}²l—A^Hxk$ fÂd2„Å#’;-˜TÌD¾!nYn í{m¯îŸËoó 3=‰¥ÌÀ“B©Ü€ß#ßEì×j ÒúT„žž¸óx%´RYªÎ]þ•7ž=4 ÉMF¾ÕÖ$OSZøh^ˆ–u`¸ÿºxàØ½{û¸/WG‹ÃŽÅçį’'tÚ¼ÄNÛ#rä>˜jþQ…úƒï% ÒÞµ)QÝ%à™à*-Økxæ6Ë/iÁö² )0Üœ¸¤Y–|Ú¬ëõ«x0Ç1kîCòÔ84AÂÐBßI@¡Nµ|2ªw$Z Zdp0¸úÍŽ™2€x½ûص¼±fØÒÕ;£”©pu¤ìÇ\pY¬ºl𫿉T†ê_ò× ÚÏø,¼ä³„%ø k ןZ·erÖAÌàMãfð"½Êv·µ³yô„Vx<iä|à%\ïŽà!„×§pdòAéúø‚ÀÙM|±ÏC¤_2{mÐXm~†›7?Þp)‘u¡È4="$i„ЫŠô­Ná KÈû;'Ó,L\BX;7¿Çk2Ïäý;¾,nÒPêŠnãYÿæge+KÂâOðÅxZ_7¨T¾tä±5aÈxK+œo1ºÝÃ{…Ê^ÀZ0*õÎø˜Á^6#Ë¿=;úgCÆ;ÑÇ¿œh;ä׃DsŠ+ ±—}dØ‹¡áCQ²:Ia N $ØoÝ¿y½Ëík†ýY_qv¾JO¹Ü™EµÛ× ƇÈÕœj{j,Ϩy ¤õðÆDmÿéIgÌ3WŸ½Ñ€é©ÿ3škÁìZ?ìÛºäLд¼‡˜Ž§ onH›ªQóšÐŸêâêä\Чè3ƒ¶Nz·c<>’½C*‹}.î+VçbÐHÖo_y¸•Åó¾èü–DÖ·˜ÿd>u¸¬%Ef…ÿ&|¿”žU¨ÇÙÔ×Â_p+oÉÚå`YšZÞ_†(ƒûe`9ÂxQÞÔGyÕtøX¨Ë¦ íñ')…,Iì†<üéß>%Ô¤Ž ÈÏG*;üÑ¿{£-ƒ¬eCNÔKŸïšý€-꿟ÜÖž§Òö;G¯°+"žâö ŽžéȦŒÃ7#ýS ;–\4­—U*ísàT‚àëˆÂ×¼.TV]1¦…Í7XÊßbØ Væ/°žõábŒ:…`I2ˆyh‘uŽ7aCŒ;ôn¤ÄÔy¸k~lKÚ‹ GSV`ÊŸÊ«N2–>:ÚKý—˜kQ>y‚¶Ä?:ixÆQþ[½ä¹-RsÆ,‚ÁVÇpÛÈe:1;&†áÛž¬·Š ôó·õÂÕ6åô°„~ûS0 .-7“–u¨œ5?PÖ‚cy=³‚ n’÷R|aÃãT€ãEBÁû¸šÜ@þªbcüŒšžZVs³ã[ÉÆaÃaº¤Õæ ¿- +G1>_Û†ecX‡¿·rP‰y;Dy•›Èq W endstream endobj 10 0 obj 46432 endobj 11 0 obj << /Name /Im3 /Type /XObject /Length 12 0 R /Filter /FlateDecode /Subtype /Image /Width 2085 /Height 1259 /BitsPerComponent 4 /ColorSpace [/Indexed /DeviceRGB 15 ] /SMask 9 0 R /Mask [0 0] >> stream Ì9bAÂ!V‚2–‰Ùw\ðÌ@·Öþ£ Oª5ÎÜ@ܽõÆIõúœ-¼_V‘ƒTnt|Òɰ ‚oÐ$1È@­Ò‚Ëæa±¦&ZM¬¹bp%"Ê“¼â´eæ•J©—ßd\'0è[)Žè ï“+éÜiÃÖNhÕž¹{Âã`pÿó»¤j/‘Ñ/¡(_b_‰•ðBó‘Ÿ›¯—=£ÀÄè/ˆ#j›yq*i`Êq×øá‡½ÞB¿Z–,~q jˆ“]+|Q¹§Ut’îÇ}k&ÿ,àÞ`v- JnJ£VÞ/,B,E£6ïU– if¢ÀÄlë”Áý—$¿«Ra¹¸Xm9„!Îôy&Ö¦† 5o·5§3)Íõ–óUˆ$¸Ç'šË7ßоðÚ àNLˆDäò×ÑÌÛCÒ5[)œJ’@}pË&&mד@·Eº+ëÝ_áЦQƒ¼KæÂû“l°¶·¦M ’‚Dà”{+k?åJùº!¸2)½¿HÁàÛHp“ çnâÔ<šEPŽ˜JHÚñóJEXüRr1ãn}à’J Såòþ¦Ãð&”äV¿ž Ú*Ιޙ·½Þ˰tý»1s9Bºe¸rEÿ kj˜jf;_\`Çj„ú%õå§c4(§ºÓX¢ îß™ 0—ø´ókIõ,Z!Ã份>` ·½m;´yš)b }fI œÙÒnä㼿«ý¡¢;-ýœ³3D‹†÷×OH†d…¯×Ÿ—6T¡13?ë2a!æV÷’d£Ð“R› dn|qjŒÖ¬çädßg¼Üchý†TÎÇ g¥æJ´sOGÆ7÷p6Íߣ€`Ô©ÿ޳ëðïÒéuÔÄM–vÞ8<̳Èf3V‘ýéuË8};÷QLLW¹‘ä½ú ^";툈NŸøq¼Ä—ªæ3²ßÇ zì,W*~Ïä.Ne˜òÁÜ,å™UÖC3Qì: «ù9dìÛ§†_xá4Yå•[YÕ,çШäç Ýüs±Z;RÔ§Spm:µ;zã§iÖoTøwú;JU$R;hÏÔ#YÜA¹ÍhM&çº:¸± x¹JY˨Ų岺œ?Õç~•ÎHí¢ç‡þ±«ç;ëwÜ 6ã9C’÷ž¢È$âEwŸYB€ÍÁý«Ù6Fá>HÞì§XW¿ÿ)è”Üz=š>[Q ¼ú\¼†œHæO†ÃóÙ2¢„×&gGóǯ÷þfësÏʋݑºMM@"gÅÝî2΄²RTô°sóˆ5Ið ‘ ,×f¿^DVüš¥îF´ÉuéTLTYµ5E“išÙê±Ù¢/´xÛ©½Ÿ÷S˜‰ky­5á[ÉÎ^Hƒ0­ÌŽ@lÆCðc”×@óóú{1YåNúº8«c×üMé3ÝT—ÛòËü«ËÅ^Ö'E_a*!…¹×ß&r´µ‰¯Ùg‚ŒÛZ H/xüÉdŒLÄÁzY¾xíÃþýðê`àG]ÔyX§,dk®+„Ä.Ú95µ+–ÕJ€¿O]˜¼àæ|Ž{P“| ÀTf5ÄO2z–È&®ngN±Uã |Âdf’|¦ÀG¨Uñµ¨`’uû¡F(/5¢óˆ )Ú²Í㩞Ï⨕PëmàKmDÒ/2%pÚñU9¡`¶É¡fÍÐÞ£ÌÐTÉ›Ò}sš+3Gs å’¦¨4ɸï@F7ò±æÌÑþxÅ èÆLm€ÃÃcÞ’GµC¥,jZ¼ºÆoîæ9'äMDB#’<Ú€:Ѹvq6R×¹b#ËÐ1ó~>(/ Ñ+±ÖÍ4ˆ°S<00a:«=ÂÖGÊs]Uæ¥ôÎCúذ˜ AµO±ü|;ý}Jz? ^BšHɨ˜Þ^oti.»| (R¢‰®ªäáW ü >´ú`ëÎÍw•Ê&,‘…óÞYF3‚Mw6‹A9zÆkIí™wôGVÉHÕÿ†LgCI`”½muçºY7ÁÑ_þ¼%F…7)â¶§¦=À%Xè¯ß ÆbM’5ŒÖë¤mkÊrœ ð±Ä7T‰BÆ ÝN˜–†\‹®›Œ0¡å‘¡wæ`“BÒ[æOŸ…þÀð>TÃuËÒ‰.l7L“ð‡¢W2Î,ç Ö as¨/hEC¥Ƚ^j±"ƒ¶kÿ™•š´.DiN³×TgõÔÊ& j¤ •||,N§SS Ë5U>MwœÎàjˆb1¼}H#Jçúýõ—z›…œšu„oË«LC-§°HŸØÇõÞS¿¿q¥…µHvH•X1*žþ¥«ÂœºÎvJ›˜"ž(‚ªò©™Ó‚¨[¼`ÚÏaé—Ñ—ý~k£ªÅUºÔ5*‹nöên®ª8=¹ó@zk7jú­WWŠØ4t*©e¡4UfctŠ0‹ZªÓÊ' °þÂ(e)ê5­,©qh»ÓT­×$] š!êˆV`-üeé¼ÉŽ(<ñçä[(ªù±ÉÎtƒ>ü ߆7ëf™Óë¬8M¶¯íkC¼B¹C÷pZC]§ÃêycšÂŠî^ze­*^ÎP¤Ç9*–‹˜`éŠ0 5ÿó\(aþÜÝ oç%ì.—s8Ú¤àÒþ8’@ᓚÈ&}bâ|i¼‹wmq/ché¥/@9S¦q ³{÷;u&Üï»&_–Øÿ¿­©Þ¶qîýš=ª"]9¥çËÇ«è$«hm#mÝâ…ÕRYzúLúPȤÔjùïr™ÖQÛ²³§éE4ÐÉI$âQÚxçTDS¾vÿš´áKk²Œ$¹ÃU©¥É$jȶð—_¬òº9,9H`f>"˜§àvµTÄŒ zô–W¼Q%›1öNŸxR+ƒg<òë>…W¯ß¹sâÅæH½:4ˆÝöN‚b.ÌŠ/0ì|úD¿9lz¿¤Aó3ës8[îTkJ;½Ìš™Võ=°i€Êhê·’¦IW„®\ACš²FÉC8Ù—øÊwâ+×Ï¡0ÅÝdn2òXÀqß”þ4»ðÖõyݾ¬ž ÏúHy§fJÒ¸A°7­fðú]º ”ߣW.|‚yé\ÇáÇåbM—;c–J9¢‘CMp¥§ã¼Œ£,¶f•+nun.„çz Ó¯° rIi¶‚‹*³´ì °Ÿ\ÀÃýƒº`]àÒ´r —cr]»†$ ÿíðŒ'm¾ä¼œT­ãÝQ¶ÍðaèÐh¦Êéešˆ\ç!Ú-N°sût±íMúƒs ÖÖRcÌ»Ÿ8’ÞeÃ/¤ï ‹P.§Z–ñ(V#GóÁUw°Óñén"+‰"_9žÕèÅ0°^ÙÖ-‹wcg­%°ðþ¶¬•â Ét¿·¬9ðñéo¬-ý›O´] Ý“œ d0tè{ÄËQ`R.Tòí¬A¹‘è¥8Nµq}[mgô½›o8âÜyå¸ºŽ£=êÃsðûޝ¤xû¿<|ú²Ùå]®œ­ûXá–ý|­ñŸ>’n­ïI×j¾ÄWj‡#°ÝEh ¹ö!€©0‡½šì9¼Ïîjd†âN>p©ù±GÔŽXm:€$’¨Ü?hŒå‚•Q€GFüqö+ýK4à.`yßrsw1_0P-•.e.ÜÃAC XµÁ×XIw3^pc·o»ÁÂ%™þ®PØŽ´_QѭДˆ'júÁGˆVƒÉ¿Ëå‚óuèDÌã#L°Ä!1ž OÏoú[n}ǘ%s1«º2mQÌÊa럧ӥ]Å[„‘ºW}´Åcj6Søí4å§DXðÄfiB?§]ÏÄÄÏû;Û£#ŸÉïXÈ–SÏ‹g%@Wiï?–!]¹†N“‰wh`…˜„ð†õÜAwü1¾Äç¦î2*µÁzlã»OJV37-¦xù¥HUÆ"4¤Ç‹/ââ¨kªeâ­ª‹á„1õF£‡œP¦ú‘^:îL!Ù÷~^—‘ª6¿{‹xÏìüêÉV§”:¿“YÃÍH §¢¸ÄìÄ6ö8úNRÏÁ±•‹#žN§ó†j[Bň+':^œ©\Æ¢VÅþ×£RƒcÀ9ÌØ%Â`XN#b缪#òÚE§¡ÅSú†ýZ¶ $œŠôC‹¢ô\û ⇠农 xä"‚´FÆ”}ÙZkŸÓ™3ÿƺð7ÄPeÂÚ—Å¥û¾j'Oä6%ž2weœ|³ldBzÎâ®PEÝç--6)¼Û(Pv5½M2¬þL‹oLG&`<U9¢¿j-š”'†½Æ±ÍËAÔ-Z·û2„XC·^uÜï¿ëãÆdáO}Aš$x`TÌ(Nš‡Œ„_}ã¥eógÅ>ªN[W£rôÿ0­'d#€lva‰¯‚uÖJŽ^s±5äø¬n(FÝu-MŸÐ*œ˜w?4añÕµÊY89)˜vCüç#¥sãÛþ‹Oq›³42nç7Øæi@ŒdûäU°2dÊ´b\ È9ñy>Øyj.‹Ë¾,AOÈŸ˜¯VÒÀ/‹Z4¯_S ð @ÓrÚf–‘»Þ>Èbÿòm¶luã"{dâê âûU95çab~Óó‰™eº€.8ÇÚ sÞlÇ-¨˜µè³’Û)4¯ûçH˜ÿ°ð"¼;™ÆGµ®ÒA‡Â*㟤U] yK§†þÄÝ©d´Î§ë9™Í¼À‰”ù ¡×äØ\%±Ù)×O·Sá5{&KØgæZ¦sÐVˆ ºÐó…[áù½×v°u½MÃ{=Ubx…ë>°µå¦,mˆ1ö-0Ôã&ãÔêiÜ?©³Ý£æ Ú(^VµUq€Cp¸áXE¹¿õÃÔEˆG¼‰¿)ý©'áQƒ å €F/â!)!q8ø"iÅ[”-}´P¢“¢P–¹äY‰1øl›v€îý¡Gû»amö;“–—ç?šÑ°LBN éAúôc-YfÍ O”ÛÆïgw*W"N+&-íûs:ª¶=¶Ê¤àØâ2й{1¿ž w}óõº7áÕä°wJ]EÞá­oRAŸ©ªAðTt ã\MiøÅýn*Iƺél5Sò´zÓLê8 8“·¤j«s•í(úy¿W˜0aØZ9»DÍ'JÖ!L³æÊtÂw–ĵhÍMmçC./&¬H];ÿÍÜ$[¢Oøc_4nà{]?H˜mºƒÖ¨¡Q— º¨ï¼°ªçMêºu‚H¿›>”žÏé€z qÔŸY¡É˜XÇ2à‚í²×q_,nmÆ›ç` ›a^⢛4L_çÐ'¯üTgÏõqM ðKMg~,ÐN<¸ZÈžF/øý– R!Z‰ ú{ye§ÝÖmå{z2¥&ÈÁëBç×%ˆ+#yI5‘×–ÿ-®µ·9÷ á/ˆ2“$³¾ÿ]´‰Ïþf•²ø¢È!zýY«í|ÆàÍz\“°sûÇðìMêš…ÆGÚw%O]'âijíSüAeäÕŒ_ö²¬],LB©—}«6Œí²p@äÁÛ¼.犾v U‘ÿ¡Wxà[—v»%Q]?dLè3¡Udd™ä`ê¾²ÖÑÆÿcÞ­€\ Õ’—à—¯Ìp”#¯ðÃÍ5Ȫ‹šåF9_Ѩ—|l"ã-›âO!Ößò¼:­…Ï÷ÈCŠP×»Ó…Îxâ–íÔ £x“ú.¶Ðž·‘KñrŠð<ìpT>»ët9rùp~A_Ý,¹®Z/cP”¡³Óî¿´t|;9n1ÀDÈR¶Á+œÿɧ㠦C‰ $9þ§ÌY^'…œýáèSÚilK'ÌÜ¿“PŠ RU{å¤ÑY+Nb$¹ ì]7Bo:#z©Bfš9Þ‘Ì»†KZÅ]6«@¨‹…d}Y* ØêÛq¾wƒl-§ßa‹ÔàÓ` `¬>%˜©æ}¨š¾Á¤ãe/‡´Qã˜Ê7ˆ>Ðn9Ö»s5LÑ’xŸÙI 'qÔ!ŒSu Ž&$þLª71 ¯m.áoÓgžÍi‘9¤Í·´Emó6v«ÈÈý)Ý6C¯«TÖ<ßVäí°¬Ñ“‹ŒÀ¯û¥íF@zåHº>­f'oþk´oÆê‰¦Hñ£!d´Ijè‰ÄÓ™äUë@ûòÄh‹ê¨+–-ÑÎôBÆØ¾ºP/ˆvFÒ£0D¶à'9pîšoÙ¤¿o­ëã )¶©Q@~qã* :Û mNQ­0 ?\dEÝÁ²îGìÈ Ë¨Óu3ê½M›å© ìð62K¶Î5'Ã-‘Ä_…R™â4{ÞâO,Ì$þ¸±y$#%úŸ"|XÍΔÌíáZëVB‰ƒEs‡â}¿{D™’d¸…öû’—NÜ»Ãô?âÑŸé‚@µ5—GŸ± ”-åÁq9UñÔè ´e£ë ¼½Aålä’ØMcÈÅì.W#oÉÊzÜ[1û„Iè’þƒ±êh!åZÌj™5GvÈQ¦ùÂ*„M—a—_ÕØë|mìg¶7é}´Ù§[ýfÎîzY=&_\è_Q2Pe uš9J(M½Pø8XWZQ(}N%Ы©üþz©†UĆÆæÿ=ÈQª¤ µÁh°áþ¬jâÚ͵$‘ h»ÚÞʼ– ÁeYf²OŽ’©ÈzÕÇ©­wO‡eùð¦7ÏXõ/ÖšCX?ˆ(÷6Ä¢ßUB*ÀòN/[.™‘í:X¦¨¤j”ÂáÅåî±ó¨ÆIÒ MfÎ ˆ/  {¸½2 ~ÅtÏgÆ] üm•±µáÞKÝø;Tç³ÛÙ5¶n‰ŒK«©@c®ƒ¹$%?½g?çd6p>x£­œLXÖ zÎúL„»½G*&(Öɦɹ™ÅÍÞÚËçyË{þ&„(õ‡ˆoë´ò¦Nàc7íH8¦[‹VŸ˜AH$iaúî¼Ã¶ùÝ´ ÷H»`~lJn­|T€3Üsj(ÅMû»]Ò•UNV jz`Ìì_!Ú±˜ q!{zÝÏ$)æãwP¶ÓWž_UUØoèw&Ow‘ö_£33}9Ë”3~ѹ²ˆïY.DO(£}ØhA£IfHvðߌjéUlÅqBg4Sˆ rÚÜJѦ#æä«fÐëlõåôö¯Áú# ³ÒŸs°@Í…) þ>Ði‡DP¼3H½˜àˆÅ¢4r%Èì”ÖØÝᙤD=貯øâЅߢ«œá•7M’ù¿{—4e­{‹­0éw~*ºÒ蘑¿Àbk•ˆYÜÁz ÇèSñÆaƒÊë¢ñòŠ-{6ЉÙk+Ȇeë«’&·ðIm‹FÎßš/µ0%ŽFÿßùþnFïåÓ±y×_ +:8ÍIB4ýÖY_{ZMµ[‡ôÜ_0#ËF1‚µDQüóTut*(ÛSÛ`£1‹‡sƒÿ¼2ðY¥Ÿv¶,JÝP^5¼ÃÞÜh14èuúä&Ö¤ý¼H t)&/¨,UçŽ\Ñû¹õêëæÛè ˆàé2s®DXï®Í@ö>y†T @Ï €ò¶J=Çn ¬îI£ÌC©j!ï@ÞÕS¯[„Θ΃êŠIØÎÜêqkÌ 9É'…öZGq£¹ê06qö:¡"œÙ§M~)@áY}×tN 6!µ>3ùÑ 3³'tÛ\òºÍ2âaìP¿ð™.›¢;wü´VÔ•7  `æc“»M+U£õ“3òáÆ^ hçOÏûb@Zß@{§–ÓxGq}Ŭ%6+ ¡`O Êl•ÃQNR¥ OGk‚wƒ7J€šnUÏy·€¢üá ”ˆÃ?Ÿœý, NLÀ«®fµ³i y,pÎ7„~0Qû Ñ îËâ4¿‰ŒíÔÉ«ë˜dÍ–Ã,¡Ç𛇯Òô+² ñk *éfÚ…ßýØ~ö¡p7ü4äi„ekhÿJô׎5%2U¨µlqÙÁ1%3–gfy’™¿2q¾¤Òœ˜*^œ–˜xOþ °o³D<ó‡á~fy¤ü³;zE(iÂtßJÝøXqô·€#&Æû©mnÛP~â"û‰‰| ŠÞ¿ëìéb,Ys>ÑNO÷쉚€òw½ZOZ›UwÅy* Á ÅIÄ_úNAà‘tØ®FXXÐôÍ÷< ›YݶÅkbÎÑ€n ҅騶£“L¹I(]ZÀ3X²Zº¤:\¿—N\ œt—Ô´-ÞrtÆx‡ ¼Z9 WŠe0Yb|Ð/¨vâ÷›8OÎ=ÒÃÝFÙ0¿S#P‰aì?nŸg“ˆ±‚ç÷=³6>UMC'Så›hê­c'üqëªãàlK5/w ¤‹ô2¯mç “Rr/€UåsI¶\Áì7_†³Ã’~PE·%<â‰pìÉð¦Z’Sž yÀVÂP¢øùÞ~à‹:ä¹§¥ÆÍ{Y»˜)xز¶¬õ}awQlÐà–ÚÌä$SchØ2ÃÙ‹…ø—ð‡¾Æ¶í²§ÓÜž’n†?îEE¿àâ9B†ëÁ‘™î&+Ž@ðÁRž¤ÞNÍ~9ÊM.ßÔÕ»·Y,t ‚eÀN|Uu¢­ðÜ»9*´–¶€êÐ7öüõ'Òx*¯Øµo/Ê‘ùéÎð2”ÓÌ:÷äÃMÂ5¡,÷‡Öë·Å¾˜ëýÿ`JAfRÀ¬Ì6µC=˜±i¯+s‚F±µå¥¼²¦h¯{’j8¿Xd ÇRÞ¦°«þ3gDZ k,×FqÝÅ¿°Û¢ü«š^[ùLJRµ)hru€±Â®RôMß»üØ%õøè×›p·§§…ˆ ´ä{‘ì6,}Ô÷jMÕFØaŒAIᓱ¤±«‚d'G\ŒâÐè’ä`!¹`—†PˆžuC;Ø}¼'hÔ4E¼1eEQÑãúE_ &Ëç.Šmþ™4N¾‡“xÝ¥MܱUcÒhÏê¨ô]Ž–Ÿ­¦3Ý\À.§Øå&¿‹ršÍƒ]v‡”‘%F?ŠGcšê{Ô{€¾mÅ%>”ýÝ3à L˜ó`AŸâµr™ÿ'«8”ºâú9,ÂÜkÐ$(ž7÷¥Ø¶ÀH;OI/§iÐSúüÔ­ Sw€G†#Tyó3BB.êÿhúU%ÚpìÞ+ˆç'ý‚㎠†SûÁVÜvÑ>§Ä1 µl0]ŒjWìeŠI,§¿ÒF«Øžˆüª'Ûƒr#DfüCo´ÜVÔ¤ö<ÕbM1%Ï] ÖŒEÜnõ]£Í%. ’ò)”4Þ//¤:x·"pØ ¬‹Xݰ&g†ÕLÿ|ŠÛþK¿<öDU½s£b•8‚U[–4W{DU[Ù¼-´|¦È¡evbkU1Ž`²Âaã•n …ÞZ¥uKã; îL¬âxMQ¡#ÁÁ¾*0zmšµLGEØçxÙDk /ßê£ÿE…|ðùÙO3>žé €íá]7ã-Ðô3Dž¾бÉéÃü!ž: ¾ÏUI£Ü£6}±hÌ^—½v`©Û°«©§½1¥dó\ž5Ä æ1\׃2Ù+ó¢^CY ¬üÄ «î•ÊÃ9NÏŒ÷§VžC¼™dÑ(‰ØÒøzïø¨Î{JdòEiÖèó¨/¡'0^–yŒ¥…ïËlñƶ÷< !ä±àt»I‘s’Iáz½ÕUÁjuFqX½ $= ¥V èZT:ÌöOŠæoª÷þØÿ¦QQ|é²F¦ücw~Ö¡ñãOv\(ü|ÏýŒ&)VјyK*íè³HzŽ9Ps*V™v*×jƒ»•2³—|§AœâÇËs (‚‰‹¦é‘#‘‡¼ª†Ý3 }c*#&à-UêF42¯ÇJ"gìèájE-£Yt}Æï9(€7î²ÁxñX9Nõ6z×Ëß{Ù§©”¨äʦ .jó¢ãTzNk ›ñ!Ýh‚…™J˜÷rõÁ»ø*”k‚>%(þÞu•Û„(ÏÎCƒ(ªN˾_\S"iƒ½ó¼h_ ÝñM´1“/ îa¹áOl¼EÈRÊÔuiß‹Šm´¤ºkÔëôƼœ‚ø¤2ν{)ÈÚòáîëìÎTÑc|Ô¬²ŽÒyO‘‡K“Í0ÓÜ@”•e³JÉÔn?¦µº'”™zl#Ô¤ßIÒaˆ®^ƒì##ï•þ®þø!LC…I‚¢ø’%˜„1Ò×-ĸjþåþÈUó›/Õc÷7ç,³Wg‘lÌÎq æ¤gOÇ Œ…6lÜ'h` ·º±oóÀò¹y§Ž\ئí'œ}8(ý&ÏÃÀüè .[gi|… k‚ò0å¦põN8 Ù“T6µ¢˜´s2N_ ü„:Ê€Mt›7ÖŠjà1m ¾ì¤íñ""bк„Ò¾&KÄæá—ßüøÜ‚-f)Ÿ±ÐC›cÎ pþ h‘¾1Ýüß]U_ä<½¼˜•ËJ?e‹Ä 6™7Ú†¬kLZYLN¦$­¯¶þESUÖò Ù5nY ëIóôcýìKÚè,FËuUÞ$/K~½k]€ñFÈc]€£žbB€’tfdŠ$ò+êo‹1°Û@žß½rÀý÷ Á­>ñ‘;¥âs<š‹[ÔY5Ê ¹ é™ E»`kÍT ¹ð7=Ñ×j½ÈÒ›ïf* ‹’ý1F¨¿ BÞXbÈ_¨³ Ó‰;—»Â ±ƒH·ÎÞé}óýŠ÷„¿× œõê7R ¾;~\•/Õ¶äÉΨ ¨#q8~Šº{ªÁ‘¨qà8ío:úO7XU"õB¤$3ûIXgÃgëÚšÒ­C±Þwé?¥.hÁ1Ç~Ád`ïuØ÷´ F<hµùD'5bÿ&‡¢(Ë&£1¿½q± 8 «253tVî÷˜1zÑÔEvåè—²Z˜øF†‹‘_É‚Ò7 \.2#&Ê?ââÖš2½0èèÆI€¥døf‡ô¾PýB@§ÐÑšÖ^ãr`C§ÉD4«øáÈ ¤TÄ«t"ˆþ1*ÞKy˜=þŠ$¸ œĬC/Oz!‹ŠSØ3ÍD½ïį™N_ð«Kôm –ÝP2F Ãn±â8´û‡«!*]õèA¨†ˆ“Œ«°r@óµ (3#¨4ÙÔí7–*(/HùÓca 6tD‹CTK6 ô¦>3ÅaT®’ÁL¥R=Ü•¢µóZ€#ªC¿¬ÊÒ«ø@)W½n­ H¾8© øBqÑÞPž¸Ó ¬jÔ³z5\ãçqÚÖjÑ“š_¡%kõsþUÎÑ’¦Ï¬cXþÅ­ïÑ¢·~’ îžÏ\%Ïï;ó„Z-‘=yå^¦l¸kàÚMÝÙz áu¯€D¿ÆB{4‰RKvÖ¹r$´æ¦ØÈÜÃûzÕZáx€P˜rã Ÿ²ñºÍ÷Òí¸Ð¹ä=dÉ=Œù$V›¨5Hæönò&9#•é³ö†_šêPÿ[ê®ß ϾALØлþôIª'¤JRéÆÄWALË®®ˆªB£„ }Iq(äøžÍA{ûT ̽¯ä¨ õáŒP½uÞ .Çô´†¤\róšVž÷0nã×hø¯*ŠÕSCý,˜ 5|a"KqYؾ4J¬ƒì-ŒqÃB 4ó¦¦É/Ëê Ïöeªƒy¦³4ʹ±B)-þê<•–ö˜JÄa~Ê¥xÝ¿\½¤Á⻑ÊX¨Ìâm<ɶk£­U‹5ÉþNÀàkg§R}5¸“gdm_ìdånŒóìª/§öä˜n»bcÆLé‹&kz}–—ߊÍB8ô™f6(D¼ëêÒMœ7/QçtÔ« wGX¬¿а.¡¾¶´›:jêº -ßÉ8ñŸ-´-¨Ã´Ï.<½Í ôÍœm‡6WÔ;Ô-éãBÉÈ SîòvÖ ìþ’#s„s"¤¸1¼›'k«ÿaÛªò>@Ã4 ïã¬ø2ž”î,Èí"}s"ZjsÈ’!¼ƒ½ÇÁJýÅgiíÊ: gr-Ðy­]tÿxÁZܲ“y©Ï7 ïX¿x‘ïÙXÂú›'ÕëâÚ\Iù›©¡ˆ–3vâÆ·/“Y+‡d`Ìu`7Û¹Ÿ¯W ‚|6l€l7í³¾´ªYY4ã©öÄÇ3ïŽlôÊI¯ÖcÐgSôÀŸŸ‰QSªÓˆÁ¹ß"ŽÏdgtzõy¦àiœA”™ÜJ±Ë°Œ x±Š]¾I‹Êy7ð$­kfL‚m_µ­(ÝKÙÝó\gTèñp& ž•RsÕ­5@±ö•„çŸ""ìÁ <}¿ÀKñÍM³ íKÓÚZ0Tõ) )‹÷ç3‚ËxÊéÄúišP˜h¸=Wöea„pÔ¾Þ,ĬÛ(±W>šÐ‘_ š(…Ó’,EYû§ÆV¦?øP‰ x‹ÛƒI Æ@â¦KÀ^šyÆ="š#ºú÷­/{;ºùN»8†ÂÆ7jøÙñFgeÇO×Êxú·[]Eþ?þ’„tÑÛ¦Ù¹˜_… SßK®Ÿ¥è¨ …¹Â’Õ·éM­Ð'ÃåAäÑ^jÒ«ê5ŠŠr…öÕŠGÓÜôc«lÃÝj 1¥";Wϧ?W†m&Öª¢€üg„¡!fÀM yŽü-¶¼ç¢±ú©ïod‚šš²•©ÎRŽGtІo.’#}m⌘¬“ìÈNçu)+ù«(CïQBúž±-‚XpiuüÇY¢ ¨¶*î‚”üê8ûAI?ɵÙýPÅ$ *e0J0†g$ãwͺ9–. ·ðbõñ°S ª—f])ø»e—0rŠ!q鉞`T¯D`›ºíG`ø^Š·w¦Ã™wŵû4)°x$UBšª–£Í 5 ¼×~òBTµÏŽ)¸LUO¿©!ÌÏá'ÚÀ êÀÃ$ý Ù-‚†¢Tî }ø› éŠXÄ€sÝ,` y‡®Ù¶7èÒ}FIößçQLŽF¹q&ž ¢²xÏGô£ÕAJˆkqöƒªÏäÚÍÙÏ–ÍÉ‘ž­8¶MùiÐwÂk½ùp¬zDšª¹K@iÚËô´Í£ÅϽ†lþcÔ“r± œƒº¾0vâ-(d–øÛŸÊmP4 [h¼ ¦ó/sAážµ,hoÏ;—2†a<Áa0?\ éÁĬ]Û%ÇêçÃÐã½8íÑ" H¥<]ìûëïvÿ·U0ÀT2ÑÊAk\$_·œ {™;Ý<€o½‰Õ f>ÿ»*Û½‹ ðúØŽ“{xý·î¤ ©ƒøýeå‘Åeø ºµ }Ó·nq˜þŸ (j¢Œ{Ûð·²È°"è!¹yò2ŽR"ºÌqBûÀªNEžW+‰ë‡EeägÜàâx ûðÑ\ÆÑðØg:C’5ˆ¥?óžSpÌâŸDmùHZãB|Þð$ùì~ùÙ¡†Ò`bþt9;ÍÖQc­®cIbúÁ þ"ÄÉ•¤FÞe5bÊ\˜`˜¢fb†~ô jPÅB sÜÎÔ 9’Ë~$‡DoZá°Q[[?6µs¸à\‘‚ ÉëÔ?ìå;ÛèÁyÎÈóÑ'†¨2u?¾Nþ!«m kÙ6üxN1ãÞ^÷ ‰âißûŠów{Ýñц‘èaípGþñ‡1íA¼ÀÎÅóA‚P±™¦¥@&È~!ƒþä §>¯©†Yße!Á™\,ª›9ù$‰èÙæp”†sþ7”˜[àÓ“[*G f+†çÎpŒ¦7~¿Þo1¥Ië1~á=½ öàçgƒ\"`‹B„P"1>NÊëÖ{ÑÙZf.°Zx¦ð 欹…±džúdòíJð.;×–=§Ãc¯StP‡+Ǧ9GšˆÜ4†ØsË'?þniÈEÄoèOgC;ާ?äY#oj¢.Š­U+]Ù8oú¨Y~äyªía–c\º3æä’é µ¡RÙ›o5pLªð¶3 ‰Ä‘À.£« ¹ àÖxLå"L‚…k¼Õ¶awWÙ2•B´ ÕÓm/x²“Ü[¦-z¤¿éÌÞ¤Õ kHª¹ßÒ?J}æŠdÑÁK²ä>hhsó/DQþ¼ò>ôì:z©¯åþÏ Úœk‰6$"Ùˆ"’%”\‡ L!¯òß°Ï8ÿDqΤ©t†äíl6šN•-‹]>…Á9·$–òⵉ¦Äµ[ o!Àÿú±*¸‘¤ÉlÍŸp@%5f™hƒ²¬¿$Ø_³æ½œ>T¬òòü±;t £û™åу€#‡¨EÁ}ªŽ“z€»È?Ìâ$üÇ^v›H`Uôn¼$Üå¹uÃ^¾ÌBm’kE #þÒ(¶æÓÂÅï¼±æŒíµ S<:‘ývq!t)’ãÔü6žPhQ\SØeƒ'okqò è®jæ§Fuׇ5v;Â)¥T˜%l¢Úª¸Õïè²Ç>ébÇ%ÁTʧ€tÄ?q—·W×ý7ÀͽŸœ{Ék~Qvæ.¢âžÌ1ÁK‰@ÆP ]‘ÉP Àÿdä»%w]“ÝÊ ¡³Éüeƒ3Ñû«z,´%å•ÁŠwÓÚ5LÚ©;Û”QîñŒgœ@†¥–RÆ$¦´ bl»ŸR꽸J°”òÑÅ8,h?_“ïUñÃj±þßã.iØy Ü3kÛ¢¨ ±@0&k™’´;ƒÆgÐjN ÚF¶¸Ö&˜p´ ™WmïL¯'ÍPú-WÄn#ƒrº N³¼b?)µe7סiØóc«ÕT+‘ûEº%eWØx–¾ëRÅóEéRc&R/‘9rô,‡`€ˆˆ?œ콶²IçæÍ"}»=€°R gí ô°Ié ‰ýaÀ’ék•˜É:\S¸GÎb cóiùÛý[É“*Q4äâM£¤áJÙ(DEj2ÄøMƒNbæv3]]›>ìdÃdÔ!dé&ìº7‘0¶›”ÞýQYg®³SQ­«î¢ž¶&í+‚kn¼ï«´;EãÅ,X*lëÝ©•ÄŠnþÈsêîI)äìë+>Î(ú ©rŽg·sÇËòá"cÏ(gêìW.š[‚ëäûcn¯âF”C}>$ë¨ ÊçÂa•#FáñÔ  _ik6ÞQ‰üð³Çß”æåË`­"U ZÒɽ–EÜBwo¯^ã µ/h©zý?4Ø4£3i–‰çð.n(«<T æÑbR³)Gù| ´¡¶y§×ÈU9¹¢×4ÿJ´·ÿ“ŸÛËà]Ÿ™XÚÉn ׎ðè",©Ô1Ìøôƒ¾Ĺ É£‰ìœ7_nôgóv¸öIÁØ^—}Q´ûÀ%¦€ÁÂm\ ™Y¦PR'¥îopíJI®ù¬…¡Lå¬sv&•ÈæéAÀ%µr½ ;,1˜_L5×`™‚Q²î¬ÞwË•ÅãŸÊŸÀQÌ +ó½£¶nûöF¢w…Où*23h¿t§)oºâQ5<åÏic*D¹ìh°–[a« ´­Ç|Fd[æDiá+d ‹ã=–Á&…g'„–¸~qæðmм5å&£#/û#/@V+ §þ„*?x˜¸þÏÝ}-UžzàÌÉ$ðræ'4Ö¬•Ü ?-ˆ`Ëø;ZkgÞJ­Ì`×ÅÊJ·²´žú]D+¥Æþq;˜ËÎîM0¸\tæÒtt\ªçÌøç¬º•œ—”.EÀÉš#B”P…‹Õu^{Mï-šyÏ  ¨]ÄA4iÙ4F!Í´"“B!]äFͨŽ[o\Ó°>§$Å/®ë·ü¼çÖÐ44o J±zêÑq9,n˜NꔊèOK˜ÑáBÏVYX¦x!öéjjâñ\|Â@ö¿ª,ãÍéYú,f$¸ô¢*¸*æLA“c#¦Äõ —7媼KT K vÆŸXñ„Ç{Í›ï:a¿||pÆ*ÎQÃÃæÈ²‚iØÅW11ŽÈ8?¡~˜/8’оÒÔÚT¯î•@µo£t¹p—Ñ´m1!,£tÒ·h]¸$7ÒN'iØ–ÏØÂÀµÔ6á!c‰€{æõ°›ÿûÖIÕsªr.ú¦ø *ª“Éîž°Lbi'j|“fvWÙ”+Ú‚÷º‹Š)æs½îëý5»ax…z‚=ÁoÆâ ªÙ]}\6ReuïïG rs_ˆ§Ù§=y8ƒá†@XØE’ž³0Ôã&…¹UÁ;r¡Þ¯%ñãíí"\¨(—Oì6?‡(&|÷ÐJ‚b ‘“5|Òa2­‚²…ˆ“Wã\_k[(£Gé ýÔìî(O»Ý9휭ñ„ +éÝ&2s¾ÇЍ+PðJ«T clÓ)–³Ó§HçKqgYRû¹51­FD{g}ÏjuMŸiùÏ(T!É=;Tó'ƒˆCß¶kUYqfI¨ŽÁ(É‘M ÇV¸€Ôñ78?štÕ¿Øw|"g鯊§ÂJCõ6…¸dBÍz±®¸Slî ¯þVK]Å`¯îqB¢§ÂØLø¨à»¡IxËÅ#7÷£¯Eµ t¶žø^a[ƒÌÆ\A»£¢¢}óÑý³êÐý¤†ÊNªJÛÌ[½LÁ¥:ïH(Þè­5ÔØ™É8únîªÂ õ—äSvG\ N¤­‹Ñ~Æ…*«Å”òbm²0]"Ìï:zÌùáß‘w¥FÏΕ/ÈHâ·éG†˜cïÉ ]*öµâçsmŠkLw%£ì »Sël‚»/ô-G?€/ÆL ­Žzû0Þtd”×¶}õqðÎSh#86þ窡aiÓhh˜tçÉ,e÷ÖàÖpB«×ç¾K³Ù 'ìh1Ëhç’Òo›K§)º<õiœ{NÎP¨/öôóP MøÜÁ骀ŽiJ“¼%ŠŸR—òÖƒuWGÿ<Ò_ä¼å·QÛ·"½òè·=éÄÅý¢þ~Êix°f†Ûr[ÓÚ¹œ4ù”©øi»@ž¡w“ùË»«àTåK8 ro`¯ò†œñ—ïÂýÒÀPY îêÏ/°/²[LcàCûCR‘ŠAìeo’ªSÀjà‹Ô·¡7PÿbÄ-JPÊEÌF u70ÔQùÅK”JUŒt»°ž†®3z/mSèzƒo¹2Š6µzG}—ò¹plæT‰ñJÃ4¬-c˜ŸbXʑ͵õ,ÙùDНæ×ûÞYûzA9+œÁ5Fêµçœ•A®âÌ=d‰ƒ¸—þ„CIÙ… )¸à5 £7ÝHߣÜû1SÎð eÕ+îYµX̵h¾éRÓwž“Þ¸êæJ–ÚELˆÆÆÚ«*5¢‚¥ ]©³ðNUMâ€écvåà{¼o}<’ÔåG~B.Òhæ: †<ÓÚ»’Ú,‘ÕIœÈàÁNnð(:âMÑÜ œ¤yèGÊÞ«Záá,ÞÜ$.‘.Øß{tÍ€Zžä@ƒ“ÄfÄôÛ'ª:‹QŸÜü~b­ò–Æs :þMXÁ·^5”Öšºì|±ÍƒSƒ34ðºwl%³²ÖÅÂTQeŽdÈ{xލ9ô”Õª9 8©ó¯ø>#:úÀÞsÈ]ô?P™Ã ²Â¸„Èw*7½>rc5ÒÕƒÐãÈò$BœNøù $r¨äg¾VCÇÿy8Lÿ?#¾<¨ké‰ ÌžXÀy*Dÿéìâð“*k!eó'Mù Bújwö²õC¤€×ÅlçÝGˆJÃ6]Ê•ìxå)u1AžQA´k¦"mƒOÿ§ôùJM:^Bù¨B¡ IPø½1·½ö‰ñ.΀|upðŠº¢¬“MUläºN°zñÔ5p©¼›ì”Ɯ̓›%¹Ïœò¨pª&ì–t@ ¥¥h-c¾ç•Zä ¯×|vâçû8RîîaoÑ”Š«7HŒg8" ‚|wX F^ Ms¥€UäÞIX楑nÅ„š[©åÑnÃÈ€Ö d Él¼}S—ƒ¹Uó¿ÆÉY FûµØõ„7]X 8Ä}½œìÂ/5!¹Ç’šÉ9zßB'©Öî=7‰•’tùãjIëU²b“×4~ÇáSb ëó ýwß™%ŽW‹71“ºs«™ì˜*w^ñ}Îv}‚wÏŠq2õ=t€ñ4Xñ T¹„Äîyb…Çs0Ÿ˜;c^ŒÈ°4ÐL$¤®&;ïÙ_ú=ˆ¦þÃ;[~#Ýtl{µÝ¤ƒ¿ ¯ž@;wò™šŒ 4ä€U€’ö¸é¡¼h*¬´OÌŸz㘭y)lèb€ouq—,TWÙs=ÔYãûäÐ7I‘ä±öDBØK!³Ú?Ëi󹌉"¸xçÿ1Ìíf¡Vƒùžõ»†Ù{àŠ&N¤R õüI† Xú©™ æÁÈçú*\ïuëéf$dÌk1DÁ¡4¦lRÈ1Þ°ž¨1»'æ¡Q™¨#ÔogÛ4‚}mý(!HÀñ¤$¯$hŸfßë˜e3‚ ã|Gö #â€ׯÅú™¤¶†GG©ŠyÙAüƒ­ê§oŸ@"¼Œ%†¢ç³AÌ»Úa3æßÆjYvûxöiçÅàD¸z-º²bJbê÷‡hÝ-L2›ŠökØé´xõ¯Eï¾ Lˆ œFÿ`“Åmõîáë z»iµï¾ö¿þ PcO¿ÿ®^aoÉÑȦۇ«°.T†T¼xGóøÕ˜÷&§ã&py$ ÄŒpÖ^.Ñ–:üØ÷ò v<{•1`Wh–誻‘Cgõ%L¢0»EÃJI@™sáEΈȗí2wQkCñù¤˜J󶃹 ÉÈz45Ñ…æp€ì³ú¨**B )c«®Ž‡föŽp¯X£Ý¦‚”mY™ñ¦ ;R ÄáXÌ9$>~Üc¼…D[ 9+_ëãÊeE,ïÐmÉtQb@ÆÝfðŸ“̸\ªÌà»DFêÆ#b$%Óº¥£>òn[$髼øûjÚeÐ|.ÎH{ Ì@å?I/êÞø˜ÏÍÚa”WÐh½/¡a­\èÌh:xïI*ohq5w'Q)ñœÃðâ` ·ž¶r. 8Z掙Xj z°Ë~)Àô‡èœ8ªkKcƒ¢’ý}ŸÍúl8”̱Ó;s_%:Žv±…¡’§Þ1mêPôu’¤Z)UJ£¯°)Ï‹ÍÅpwÖ ›á_lƒÂ:nNbfSßÎ¤Ì I˜ü 4q´(ðÑÂ1ñF ÙË¢]÷¶¿xAsf½BǺðÙ{ó|M„BWBHxøûk*A3ôbçí÷ö»ŸKÒ‡áw;-Ó⟳D]§?î+Ñ«×r~Ã+ħƒj8ÉVþGY5jÈjë€}©ùÕ¿wpF•DëJ…u‰ÞQËÈÃ'8Œ¬.†©6H7&óAT¬˜Ò;Á¡§·(—#¥„iØ_óŠù®ñdyi§9kÒWÆT”â°È Ögx¸¤†’h0¦fLCX¸·Ùl ‹‹vsBDÑà€r)Æ,;naE«4G¬åJ‚¡~î;ëLXK(†âƒ(WšQŽ^S¢Z߯j±ëFPî…Š‘0·bZßö‡»wó¢³»1ÃÎ|¤äGé ]Hpr`ƒ‡¸º0KÚÇæ %ÉvÜÕ ÿìÛ8ÜŠ®U@UßÞâ-ÿ’ãZ½¸K¬3bpu¾1ÓJ](ïylQRF“a~T^üíÜÄÞÒ$óÒ»@ØJÍì7¾}úxR ;eùÎkÑèåB]£¡¾ˆ‚+ü„Âýâ*©3lÜh„YÝÑ1F~«štе@ÀñÒ&ï(·ê÷‘óQT8R*w·bQ3Ô&wFÅÚ¢4¡Y­¹tnC6oy¸S‰=Fû•²xQÃö˜BO_ÜAÁ¶‹8sŸÈ¹éMÐ4œb³ÆÉ€¨¨'1žšÆ£ðPOëqSÎ@ˆY¡3/4O¼ú ‰6¸ðyt ê)ž€Ц“ >6BR4–@¡‘3kÍ>`Î| ƒo%8 –;€žýoZòZq% CûÐåí±GR›\ J²èA±§&›l”F¸ØZ t·Z$Ï¢ Ló2šõzIŠ4\8- ¬¢LÖ ñІŽöë".Àì¹&J–‰I'Ï֨΢‹SÒ‰Mä½TÎÊd掛dæu½´ñ¿~¶ }¬­þ™Æ¦õ@ê.ó¿…KŠñ–‚¶ È_×j¬¡¬ÐÑcFÑ/¡_²Üµ&¡>Dʄ՛YÂ7l`Sý¯q$‰:²ŽÙ”0yû«ÛñØ+'Ê—/1 @X´ŒU…ªß€‡^VI£q0>ñý ¿@ÌN@æÜ–‚ÑÞ‡Tð,|œ«0Œ6×Z{zˆ^E¢(m –†š»E™F`ìÆAÙ}zEÈéy1}"˜í1*-ÏødæÉ0ü˜±ç¶ýt”¥d¨"%®4×¢Ìøv ãͱ’,XVÈúgÍV;”ŽM*4ípÖj޲Ãa)Ë@ŠÏ𿯀¾é>n=‡Ã³þ_y¯ègG4yâiS˜©üy‚ɧªiô%S„^ººó¯þÌ&NkІêtQW¥êH4iOO1-´ýþ ‹b†Ñ½4ЄJt-j]}8õºJ=ð[ó(äýê“÷`(󞛾ž×-¼e x{OîÝÆ}<Ïþ,7©k¢›,÷4Ù{gƒƒ§N „¾â‘ ÆB+¼'ãï[d#C¾ž¿üÕà­å§F9ûÂS¼ˆüœb˜xþK´:Øãˆ,!ø!m„<ö>_à±Ù^œÜklþrFž»y³4~íÍ~îE¦*I…é#;’{ö'ßåü–êûõIc¸*} ím$ MÁYÄ/Té­ã8“("ì.kòtìcn'Z7…,Ë(Àï×yFe™Ú½ÐWv¥ñ® ’¦ÉçOëÅL¹N_4_r¥‘`yõ±f %R¬á  „7¤9ˆŒºOWEÙ1æ1âÞ[³‘I}%éý hUx']öÃIP‘&äýF¸âè©% ;#\¹—Har”Sn³ýOƒ< ¹éMàc–šèÙEŸ9ô0JŽ-ã™’Ãä6Â5^Ä¿äüÉýU%àÝÍ`3,¿zö3-H×»3i+É&´°-Q>RÂÖ%„,Ć&Ò_.hì¼é—¸Ölã Ìóªi[pÓÝÝòCÖD¼{TûWì;È2‡”;M×Z¥¾àPËÁ(ª …·Œ*à; Y§~Äm‰“âÔÛð§hº¡™ÀM4%4{?!€´ŸÙ,‰f óÙ+”À‹î"Ìà³DWiŸ?j~5%tê÷ÃY:":Íæ®T¯ï B°DgÒýÝÁ’ ¢!= —§ã•™6o‰‡'5è¡Ú ›ïaöÓµµ0€¼o}iž ‡`›}ci¿{==žÞ^ÙóLd~Ó®ùm[ \hIí˜4E ]FJj[þð( ".s·Ãe È ’2Ä)3Š£ |¼ð‘ YN)‰âøL¥™H¢LäÂ%˜›OÒµH®©’+ÈÕ"¨+0VêÜeüéËe.V.¡T«ÝGñ€Z=àô?À{¯â³í;DJ鼫/¦¥•ºzÒ®A° Æ­wµª¨Ê-d/0òìuÊ`‘ò¨à* C'ðK"û1 ´ êqN̼œ™€sN§ü#9Izþ¨O›„€^ýÇS››è¬Žc¬ A)e…Ô9ß¾ƒ¬Î„* Ô‡ÊØJß8Ô²i /mÖi(M>ì!([S âDÔ)P(VCKTçÕê¼rž&ã÷Âù~C‰ÐÙÕëÁ§ûeí°+ áº9¬cÍà²ÃÚxçŠÛ¼”ç?€o*—U³¿õ<ðú}iÉ£Tüuº8QÆ‹µ$03f‰…)ÚX ìÁwòѨö%é>5$üÖJAáw”#dø#Ðû¬‡ÐÇËM9ag[Íþ5õ¦^Ï/5àÛØ–T“YÓŸ…³€‡å1eìSªQÉÒÚÿËŽá÷€‰;1~X@–’F‰ W_V|GëÒbÅÂÞ–‰a#À¯ÄµxuÂ÷„€š\ÙÅîÊ fqpÕ¶X’6HCÃ'…Ʀp ¼ûj®…1>×­ÓýNçþ  ¾‘þÛ­sM´ °àÔaþÏ- 9Ü)§Ž”Uf é­ð¾·…§(wF™nIdã;!ð€•o>Ú­¬x]\ÀÔ'‡ë,4 ¶ÓcgæÊwèD§Qõt˨ÛÜÃçuEüKÙœ“ièc•º'ÍVapǹMh+-k ×çp3X•8ÙRå¡&'4<Õ`Zð°£"¨Ç5w¦#Èzœ†P VîTMg(pÀýØÕ ïf¦yfÛ©û5Â~´5®«CìïË­Tq»ÐóRQ•90ªãþôPøTûPôæãܸ¤…d*±"lº^‚;PØ’ÖOÙGÿÆèñ¹Ÿ~‹xU4öÝú „÷z &cý×oâÝñ'HÝ£G›?îq±Ðû,e¹˜à·gë½òB.W%o"¥l Êe•<¹3^Ê#ù`ÞšEê]Lº¸…™¡«ìe:UD[EæÏÞ)rû…–½¯ LI¨¹ƒìÙ(àŸoÒÛÕCÔÔàKÏé2­°êgò¯¶;è‘ÜfºOÄ*jöøV¸˜Eä?Kÿ­T ž8Sô^,}BÆÉõ4¸Ë‹[Ìš0¹ãƒ5jØ©¥ÖFê ðŠ(“ §‡{Š!®‚ÊxàÐìÆÀ¾NT-íè}Ÿ\5èW$ñâs-JÓǶ8£û™û@Ìþ!^½ìU0ÀªÎESŸM‹¥0ÛaŽL€A¢Ug¶ÔM¤a§-°ãÚ;F(ן6,­Í "‘h™dB ^ýñm`ƀ䰺Ul¤qõì»MÒ éø>Dj¯ƒgé»n„þ‰•Îeâ2Giiˆ9/Ì÷Ž}²ÛaªÊÀE“§ M;¦Ì“la¤¦ÝôVR¾ÒñyG1G´Þ÷)/®à.}€BòÔ(á±¥•¤+_ïk]@Ü 7¬WÛ@¨þ•K?ʃ;¶î ÅÞ±;à#Ñ’`×í…áߥªÈM,¦­Nø*}ÔìÈ%èÀ 2ÊDµôç¿¥‹#î!üDlYÏé´_S’¬³¨úL³úŠ-Çþ¦ r—¨ß¯“V^ÍÅÌ«k)cÞºIlÔ{¡b%þfWc½å¯–uCC±ueÚ˜Xßž±)Dg]ƒ.D¾k×@ÿ°aL%ÎËË9 Q¼˜ËÙ8 VËÒ¯}%ô˜2О²aRž²ô3ŠßPðHø¾È_×ã©CüÚJL>¼u­§–Øï”é[•dQ64h‚%ÿy!0\B[ˆÑÛ~OßÓ=¥Z$žªoW² ø_Û0›¥(‡yŽG…,TÅP{þ{H Ú(!DòàÊLL_ÑÈà%l«¥ãݽy¬³Œ xŠy¾ä¯(A÷e 1Ù³•#˜9¡Ý]ǨühÓkÕ§ÿ´Ydû…i9hæ£êƒ[ïØÂø‰F(}®*{ Ï\˜÷~Ýr¤ƒâ~@qŠj ‚A$Vñ·¥ágC‰Yƒ> ½üæM5Ôù•n®3mgZÙ[¬¹qÒu£µ =€R¡ÑÒf†\Å¥·Ç©”Í*sXÙíhÙB”E6ÿà„÷’6‹µ A¿É[mïX¥}¤¸¼>P4 !ÅȺA [ é¿s|™V¢ÍpZ\ß\qÉJa?'îų~9|úÇÝГ¶ë‹ÆQb¨isösÖP˱4zÓÌY(…~Q‹ÿpijØ­q[™-ªqÌ:Ez+ç6€Å7ëAÂa‰¤ôÈDêíÒžtšHè•’§<й2>K†!JÇWu£Ò0üŜϯdR¶†ã“˨=áôæÂÅ ­N-Ë6ªÍÓÓŒåâ­N§j»çQs°_ý>ºþÓ ßæÚý((‹!pr´ZC¾›ù]ðûãôŸn Èþ¯~Ïى覥Â'„o‘D¦««¥zÝ”ˆboì&¢H\> =$g.t›Ó nñÄÝÉgã®E1° ^Yf®l¹¾ þ~ûYµäI™ÛafÔýÃR"NB°§²¤€~`b"£~ÌàRN´û‚ÆÐ_î>Å 8lfÞÊðDÐ4ýC6¦¡ƒjomU9׺ñ5s †î"GV¸pG´‘¬õ ¯NìËȉE¡)ÞQ¹ãh ›…Ü‘?ÀµÎF²ý`Át¦°è*µÍˆÛB~¿÷ÿ©m>)è „ù÷ôxꢂn|8ÆV½n:²±Õ=™qØÕ²xÔdù9 ð¾¥cA°)Âo¹_¨}Lý*Ä5ÏÉ*Ï©]l_N>uƒïÊ.Ùd RËG{  Qlè¡èý,¥¤kÞ ãjæ/e¨[§i¶×Ùs/oæõ&çm-õb27Éóž£ÿîþüËU#åéÔoKOv'à´¢3ÇÁ^ ”F‡NUY*ÏÇQßmŠø«,ÙîV~‹5, âp9œ7 TˆhA–)îyY6ŠÛ_V¶¬=—fš±N]w:š&}¶¢·B7ÖPœ’õªI+aV'ŠÁ”d´·8žÿÁ.qðÂj†Bˆ¯™Œ]Ó9¿9«$Òt–WÛ¹™ö`6Í­‰ÓóV–6¬²õbíf®uBº>Ù‰öŠħ±ZÖÖtaù¾§f·Ÿ^àr ^¬äT1V¤n˜[L}iìf-Ç]œ.^ýcã'§!C†™ E×½KCT}ZÃÎÌJ;aAxo‘nl«P‡&·…é,wæ¹Â©´\(– «eђ¯€€,‹RÜõï 7&=tÿ tz–ì Ì‚}‰x.ƒ‚䩸‘mæBZ'áÂðø(ì`iDjùçÕéÝ6ÿ-M‹5̆¹§ò`É”Ø>ý†%zÑù*ððÒeÆ]hñ±PË”¸-´J} ;zÄ…åðPR1Ä¡§@Q,¿øQ±ÒÍß`¸FHabŒmº)¹O4ãïq¶@Îuĺ±üžr§ä·þ8 3Ë6u.«7~zb»)gmÒ;¤+¦ëPpœÉ1ÈRIé£îÀ©oLOAªƒwÅÍ 0”ÜQÔ.V} Ïy¨=%GŸ‹S\hñ笾*åµ.'±Š2Ö¦_wjÔc!ÁÈÉ®C.M 08݆)^{¡èo…fµº±ß€ ‡÷‡¬´†O~,5=½¼HÄq¿¹®˜Ä®«Ótt<Ç×Îo¨’žGÊðÈòéYmߟ™`‹>DµJÐkâuš—î8 '-ôߺȇ´ŽjUŸé.Št?!3< Ùß1n¤S^&Ó+vƒ$Ö‰ñ‡lÙ«UÕ¹?û¡:ЀÿS(ÝoÝ‘ªþ8¬ª{Ì}ÕÄjçx’]ùåŠCC\I¨¼à£««ª"ÌÈ]˜ŸýEî8Ø‘­@&֭覞3@o«J —1©˜H¥{*\"¹õ—¸X†£êèWFêQàsAWâ#±–Ó½¶¬¸_X¨cúÍI2kž…3$7º1÷ûƒ¯S¹{®;,oá°dÅ·/®D^õA]ñËŠ;¨X'ÐÇÝʺÌÔ³ñŠÜo\"—é€.|;­‰V‰Œ® Öű`4Æ'Ô®{[XÅÐI¸æëån¿oLCÔþ¥¡]oГV˜¡#>©ZŽóP5(éš*·|¢¼"ä«‹š_«,âdØ®Kï8ÛT%’j»©»–™$ =ÛþE>>ˆ&0rž_!œoz hBÐÜß«ÊóPèŽN=7ô%¥Oª™’˜«GÜ›álFìî‚y §ºWj ùÆmbìˆXÇB&´ó梞…à·ûßÑÈI´ýhe;çu dÀ›†gb满ÈM'[M†ÀÙÃh½È„jY&ÙLúÒ¶ì“{t’D/ò‡‘^ФîãY´.­£{·°3{ÁøžÉæt1¸(ËÐÜIOþ® ¹æ‹³oHnÀïšÊ‚'ÿ±qƒ2C­Åa_u6r©züÝtqÂâ)í¿t ÑS>=OÂú.(¾ÇApÚw¶+bE"³ãîÙìKì}‘Õl ”óCµÅF~®ª zðV…îxÉÖºMÖJ§6ËHò@㽃¡›1 > ÍbEÜä…’dÛ²Ã\kUàŒÅnÐѱ–…*ÞÐÜ·%—s4—pÕUÊÅjv60ÍgèP  ÓVmÀÏ\(b1býpr̾׵wÈxx½õ.{ÓÐN¢>Y -Ò/°.U)ѧ×ùQ(Æ­(å Xô·ìrï 'Q\õ–D]?ÑfÒ-0ÕîàþÜž |ËåW— ³ï6Šý¿÷O30E·¦˜:Là˜}…p_ò»éÝÍEI>`>:RìjC8aËå}W‹0l>^EÁ±h æÒðÀU·Üã ^Àgiöƒ&·Òç‡xK»fú#:Iøþ±côòk÷ë-Þºïlœ?«EàþnXåüf˜ ±ÑiÐpõ:/ø ïXr4N›¼ý´¿(ƒŠ¯âèe£ön‹-r0ë©Õ7Q«\$xín^O[ÖÞm 4ÓÃÄ…Éœr"»erPŒ¨´•òótÿ’”XOÍÈtP¼…“±ÚÝá‡a¶cµµ¬´¤µþ6ë“ï1¢È1›¯5‹!Õú»_dþ“= álDZ.~‘«'é|Êlƒì/1Ï/|M¨ò;S”ÊÒ!q©¦ý@Üö™‚ßñB7*^Åü¥7â‚ÇÐôD@îé!¡?"^sÄåz.C¤÷€[ Ê‹kãú”VOìq¶êEÆù&WVŽ»‡„ÂkwÒо¤A–rÅw>ÆÖ–ƒv•·gBÅc¼øè¤^ŒHÎÜÕ3:·i©%F*³K„Ò-Þ„‰%Û :Õ^¦чO,Âè/ሃŸAèQoRÓëŠè^x hœYÛê.ÜùUã. ñYšs|ÿ_âÂý•£[ºÿçqÁ©ó›¨nhÉÝcjz· c$ Ax 4D/Óá.ПZ確ÆéÙŒþŒ‘ÖƒÜeÌú³d„3²R5lÂô’u„ˆMLBš£†QÉ«ŠÜ,‘Äa5N„ŽÄÌñ/²õxä†Á \ØzI/|MxÁH„7Ò^˜$Xü‚9Avï5Àø†Þ¯ib¤³z•éÆBô,gTì9·¸ÏntW²òjG^[Í)rÌË\­ùüåÙœójUKý‘ŸëœMÏ”Í8Ü&3¼ï¡Ìü¡Ãg¼Õ¼—t¶Ìý+i5bz­sØ~°²Š—KÐ& K ó[.¤[)]NÏñqP½ß`]3 X[™YžÄY N¤«º!G꣋ÖcÁÅ&ØâYÅ÷!­¹Nîp¤È vŸ< Úë8 NUê1½ GÃÆ+YT`ØÑQh,_–œhö¢×÷ v£;´-lRÉe²8h£?™±Sµ!¶cêÝÑgñ&OÕ‰DHûÔoºéÕB Šv?ãro°L‰"_$¨:/Ì0E!hü¢~yØÓâhβ8Šl×-þ¨‘éêz×û®¦aíɼÇé³·kàŠ|4¬;졯#2"ÁÂý¶ jp)#y4ßS ûirÝÕÒOï!o˜×lGãxÉçêÔEúÅÜL%5aƒÕÅR5â³Ü÷˜¡TÓy’©EåìD’„±Vâ^ªlÂ~8Ö-¤b—±ý3 ú\èËmXh!5 µffê(Óåz‚×^bý‘æ±÷wð­Feu® v¶ÃåCÕí'ë‹0m>êã´BKÍú‹•ÜjÝ~ rr´rÒZŒ –RaC:!/Éïχ¸ì°G ïÀyÄ#81góge÷Р൓ißk*WÙ£ú…éôÁXú.`Þ~*÷øã…>štÒ†ä/ÀNi¶«YX%½‰æëþ‚aæ`ÓPu±Œ½«=›@X&¤t»ÃUÕD&Ëeû#–œ¶ŒÎ÷ÇŠ)÷T6ˆêƒË-+™´ð†§Ñ^—ÝúZâÃ@n Ö’Jª¿gÅ[G¦È[›+Ê•«¹ÎJfc/nú×ÀÊÊ÷ÉÔþx\bGXBÆB>Ü×k_}Èš5a“ýÈèØï왯€öD·¬‰,D僱õ{Fî%¶~¹è€HpòóT®qÉrÒ‹ñ¾ LJÛuDqêog;}Ö܂؊õ³ªÈ|D=àBÏô‰\WCÆŒñT ¹ÊœÕúüp´;„¦j/#X ¡´×pCöÝÆ…jálC÷ŠžË{òìÖ:"VYÄW4|´Ë.iWGhœ@ðHÞ"8sÙ…Ñ@ –´Òö“çøL _™þ¯h€0ÌÈåÓlL¨>~ƒå††ù8¥½¸A{S€BúA0ï¨VDÃæ Xsd;9) ï(6Z' s—ŠÎÚŒ9!Mð´)Tc¯¡¸Ä÷¯És¨r¶œ$Úذï=GP˜\|F®Õ~©(Mˆ£Oèžu®Új‡6Ò<¦vû¯èØC-_'ÿº&+•Aœc9ì(Æœù—ÎÕÇgè»QY=oOöi†¨ûððÁ‰¥œcQ%¥¸Õ–ÿ§ ¸¨ÑÊt5CúáXœ"“·…[¼_—`÷, (\¾ý sÏ>zQs&ÉåPÜè¼Æ…ù'ѪÕEãCt¢(“­ÌÃÿ ÅKJOÂ|OFõã¦8Þ¨8þÜ;ÝTäCt©Å‰“Q§if¨$µwèë=~±eùvc¸ &¨Ÿû<¨·M‘…ü­…Àí,ZíÛúO P0È(c…öáÑÐ숀¦ ÖË©•X±ŽÍ>ë²rOë ·ü.œü·lv>)µCÃkÛÎn_6«¾ › …(ïÌ]dÆTsœE¡UTbLõï"‡RÚ¸KüUå2Gx_ƒgwåúêê*<ãÁ²þ+dáÖÎ Úaé»ÓÍÈæÍ}¹’hÞpûDe#¯  öç¼c[Uj Qdy€÷Q~ÐZàÖ@µbMq¨šÚ(¢sþò³›håiµOÒ››a>^½õÊ–˜ɘW$ ”¾ÊEÂ}WNÆÁ©këÊ’3 FSÇ­aŠÇ€˜:5œÜ½HK0OG6ƒ4 X´ÒXê°Û64n^d<¨±ü_Ø‹U àYvIV„êivSΈb¸³PækZ?èâñP<Ý¥:bàòoº]vƒŸªƒ(Xºˆ¯bÑSl-ÿ¿ô2@<,‘¬cùËÛäZi²Ç®t’0†mô²CFª;çWê¿?kPî¿ú³éð$6KÙ\¡6°@Ÿ`©Û•kD¯Þ6cVE?Ž¿€³sr™ Ök<Çͯw\–3¤Q‘Ûp£ŽƒË.Ó~ŽÐx`³¶r¯y†ï/ÚÀèËìdÃ)D¤Y`¿ëd>Íç¿Àéñ 4¯jä„"±Žã׺t°.oÇï²€fîлÉ—6ÛS5ŸÌ]¸XŠgìW³˜±Uèæ~Eì® üºÊƒJ=š– ò°vY·³ô³ku Ü‹*d£lL@6€ä©Î¿¸ýÉOÛkëd_™íGKóïÎî¦õ’G¼šLLÑâ¥~qP$$Ù ÷>K/Üw¾Š£L_¤3ˆöóÍ€Y(®|GÔˆŽ"=z[ž—ý©ÙVÒcNõ4{DJ%Â2[r'a–úH/ý-u$ÿ$}èN€‡ÁD.ç°@å¸_¬ Q}]C̵BoçÿUEÕŠ™#s# Ùõší]˜\ž¨å6YηÓ[´)¼‘çh´Ž88‰W²MÔnʤy¬r†~vONcTƒGT²)ø&Ñì`‹Ö?’ûÊ’â<¦hTxy Z•8ÿ¡ú9Vx¹”ý¹,Ÿä­Q&)®Oeÿê/?!ú?b8Ú[¢ôØ)ºþ6»µÝt®2㮺sV|™ÆDf9{‡Ìº†ÍŠýMÓwTí¹°ɼ¡ÇfѹGKÄüotþÊGS …™¡2ÑêUj]!œ¶¢…û¼g†Ì- õ€‹÷ÌP!ˆ"»ÐÐL+³]½xÕV€Ú5·¶4u±À%Ý48_×÷-µE­È1)¤@GÌ4&Ð窰ۖP{ c3`)÷ë­Õ‹®´È_]ÿÅ«žÕ­²Ì5*1ÐÇ5¢Ñ1Ÿ|• VŸ EÇêý1ì§ì3(×è†Y1-$À©Šß»e¼ÛãB{SØúWà@óÍÜ®¯çµU-f ìñŸøŸ‰®(ãf’¤‚R øÊ@¼ åKêWwxèÚ(@Nb;úxft0¸Êdžð·ÇÕ ý/=>ÔðkÍ·¬¨Ûš»ê^ÐaU±z©ˆø$çþ-Ak !„îÞ‹…À§Ö%ŠôÆZé”ýT…ö“:é3«Ø_ün21åF–=zVÇ›Áz*ËzV'ÙÍHü¸#¡“h('øwÒ¥j$4øŠ"õÖpŒnֈĊ¥Bò}Û–L€rCåÍce€÷æÙ­úæ­²‹¶9l£,‰ooó«? ¾Ë‹1Ah}¼—Å'ŠÍ+õøÝ5À“Ò2en‚ù kÝÏ06ÇNyºÍ8˜–`ïâß‘R³R‚Dï9Dc×z*èÝw,·sâšglËOúwOÒq…0·I~û|ÿŠ'ÉGþ}V€w˜ÒÁ㤟DÒ‘­Î¼eØùîçP{ú)Á¶SƒÆb©/¿­Éll<`è®æi ŸÜ.™ÒÒT2Ÿ(éa L3ñTíéû›^ ug¹ÃÓŽ•f˜¯oºÄû²?G7:!vWë]ùçŒÄà`dfá-{Î.åÁŸ=Á“È 9A-ÄóÂÒ†Ý| /—ȯ¸€ÍI”y¦)zÃ×¶LŽøZMÒ‹R)2`ZA¬ÃöÂmQ3µ¯Gü$Ðqcxf4(ƒNŒ–ÙSÁ&+à'¨seivJš|p?¶¢RÏriôoÞCú‘UìÚš×…y¹ãâRwÀ4EãÐA ["<‹¬¸0V"Âþ›uÁT/j®Y¾Äâ:G>Ï£å;˜0{6èI€œ(¿˜ÍõémüNü¡M¢é@né2²1Í’"i™” ›®ÜŽKQSü‘&2©±TjÏê¤AÉn†TÒR.A*£ÃåVp=î$˜®ºÚúȽ\tNí„4ÂŲê+—Èã][Qø¼0ªw™²hȾ?~–òH\Ó‹|È,¶#„žÁ號›òwÁH!óoåPÁ‹!žªêVDœcø»)2¢]•«’—'AYØw·P7æpš‰Å¶È&òçÜ£=ê?s°‰&ÉÞºS0ntsÓ‚Á釛Jh¡/oòð“v…]®5¤¤¥#(vµc·/[‡6²|¯UzV UßÊe†Ýµ !¨h8‹Ù ÎÙýD,© ¸Ù IzÎÔðY úºOÿ¢ ¦Í'­€Ý[dT~]ïd®¬E÷hû„È|ádm Ú Æ­4èzÖ`¨îá„€¨9¬»ÓTv³mìÆ^^ååC@s¯Èké?¤EpªD#$O(±:ýš·m8÷µåZµ r$iî…fA3®¶¬›áSÆÏe§÷øF˜nç |nZÈ#ùÄ .noð6‚Ùî³p—kÓœ-¥DÖ0lÂH8~Ìߌ|¥l § mÄ[(=¸u^cwº§ÞzPÇl÷¼BCâ(ä(d‰ÃÚ°óZ»¤ÛTì)Ä ôÚå5X0Õ.ò†Û» ~Þ-ãXqéÆñN¼ïÐ_Ö%ó©¤åWX™A­ª©ÿظª³c[Å@äŽ,'ù¦ö8Õ¡/w·©´šRyÀ§Kù›jEÊu(vNÞEÕ`¼Ž¼ɆÅ"x,ÊÁÍ 8jd=õÔ³)û¹èލ2åËB4þW—ÏŠs‰Å<=Ô}°L°Z‘ƒx1µÙ‚,h†A“Mg}p ]ІñÃÞ%Eð¶MÝ¥†R«Ÿ˜{vƒ†çXŸ9a5x1€^¾yyz£×¾n–¾aEïM=ôÂ)ùmÑp¤˜ÎN¬aÝùeIþ7°q©¨œªßê#í á¯nPs´µ-Ê¢ÑõA½Yi¿×ÓÍC├i­ŽÓ×ðÙg_Hn½N–èhS+ô>AÙx¡þÚ‰¿ ß} 2ðåž³•åQ¢»7ÜIÀý+‡F7·IÐþxp[%Ø»_£'*[j!o*¥3TΓ87,<ì“ÛcBøw™GgQTµTáA³ñ 䢇ä×V2™ç“M'øVJì”÷M¬ïQöAxÊ~·[‰lúôVÌC]‰ À_ÊAUñãë}¦¤r-$†Ü“ùL1}äÑéq½¿š~3p|"HõÓ'‡¡ÛeGM£3Ñ„Ïq™USë” }ù@"çAæ®îýmÔv­Ôƒ•0ŒTRÈjÿïHçßOGq¬+~Ü:1難¨ÞAQ ‰¡4:h’߃A¯ÅÝ*‘úåY“ºc¢_ÍT*¹5¬ñèH9Ø 6àZ6»õçîª'VÇ;‹`Lk)‚PQIñŠÑûº§Ý§È`u¬ß¡ÀpQël“¤ýõ¥9S³È¦«¨Œ"¶øØ¼ªiÒ7J % †•O§q›QÀWèT–,ÿD­>Dt¾²¿÷ð×íU¿ 3„ €e ©Ø‚^Ê¿s Y+5xL !«‡ Þ"Ÿ·£ó=ryCxqÛ˜½+¬•çiÏœ1ðÀA÷&D-“ËË0Vxâ§ë„¥Þlƒ:å²&²Ý5- ªuc߈5»L5~«þ¼[®n]ƒ'“‹Œ·Y‡ŠÝôƒq™8iÙÌÞžªpND‹o 0U–¶ÌÏ:(ò‘B.’øh¹zJärI]a¦z3‰yºÖfͯEGÛ«;a â½¶3‡ ¿©á@ Kúü%4OûµÃùÞz ¯Œd‡ÒýSÚªh–Üq©Rá×Úöjè]üº *oý¨ûîå£sRÅ‹öwõhˆTccËÿIïËåZo0”B±™õLërW÷V‰Ú€”#ÌÅL¾ÆAÎ Ž [êÐFÑÃà~s‰’]4âs±†åÈ7_à^ã aCu%F3ýnªjDWðn6p3Nf&_tØØ]Êc´Ó3#.Œ$ïÂ8‡Ú B‚½(ã=cGëm©Z”ö±h›"˜Ã3Ð Eÿ$O­P«LÆP†3gjCDÊÿ@kÎQSÙ¯o‡‹Ù×tùY€}÷µÕkzúQi%÷ %u“²È&TÞ+:’Wš…ûÓ§âÄϺ6àŒ,+  À´×§‚ëo奓ý%KÙ™ó  wâ gk¯É%©Žtµ"i :uñ#ÇO.Sðtóðåg pY8¥2àÃâ¿!œÂ˜“v¨A(C`!ˆåð#÷Õ¢YÆ „ùH]¦,­©Å×ãÜ›³¾ùS8ŸH—íÃåJ"º¾kðbÝß™¿Ý‘!®V¸¤Ü•†zk}¥äš³"5Ìgl¿œý5á®ÆáÖÛ.š³¢£ê¨~šÉ÷ xœ¹äÉÊ_X¾ã£1:¨[7ø8ñŠtù¿zmTßõ“ÏMñÓú [Iî$ÅA9ߌ¸,)‹ìÇ$¢\ÂOö×aÅJôòuæ·A¼®R ’¾fpó’;O–Kôü"Îôw¬lŽØsÛ§¨‚9ÃU‰1˜<Ç`¨Â¤?e¥Piᔪ‹{ª²SÁ:Z¬c#D»õ¥Í>=«é`ªCZhRù[æ­‚îUšøl¼©îÕÚGÁªñS;V÷3€É&0F(„ñ\÷ ¢?૱ïwŸÑñô°[þÇøÒeèØïsæÛÓŸû% “îûåvð„÷9ÿzˆ”ždã ýQ;“D(‰\=éZhà^˜93õŸÌÍs¹›íç]£ê,iÏšøUöx¶o"â¬?Q! ’ÜqGÁ u+×ô‹KØ¡Qð[‚Ô”¦ó«O9øo–ãUÅX7.:H×¾=Ù†!Ã@Êí5ÚÍOEún*ω¾á’a?iÇŸS»£'éx#­_ìÚÊžH´Í˜vH.Œþå#”4Äëpý±ðó+s˜ š-¶V¼|r“àsÝw奯ˆ3Ò`Õ¸þ!KümÙ,ðõ×}ßÑmÝ íT`Ÿb¯Ÿè„‡Jþ©Š[‚Wf=LJcS À9© Ììè_ø ßô÷Șaúb…ô·ÇpXüÉbË Ûü± ‚½áh3H²POn‡ ×îå“KJ³ní½ ›Âá#¤µ ôÕå¦!‡vµßÀK"b•ð åq“—wYØÙ÷4S¼ 'X™ÙúLø ­Pm‹® $5|ˆñûÓ`(²–8®nñ;>©Û3ÐÖæªÙÆ]68ýåG¥RH^Ø©¡¬ùJ<²\ç`$6­y·uv£ñ,.Óý$•Á×Ì’Å”ÞÑ-…Pú·±Œƒ§UjêÏ\ž¦­“›w5n JY\M¾€‰d×Z{´ ìƒ7ó7ÔOöæDæ#ÎX‹y@àQT˜jÃ~~)“ì!qp×½ =ÌM@7•$¨eR@øª1¸ãĤ_I—TÔ{¤kØaN›ÀLª+nÂa‰élÜÝšVç’Äãž/N&¸Ónùºh‰®.X¨ø…m6[¯?´²$LǵI.þz'‰å˜c?ñ\¯ÎÚ{beò+ŠßïÒUX•þ¤°§™pºg’—ùFrD¥ñ-¡Dô0ìÊU3¢ q÷Úk…^ f|Oý๛‡Yç›êÄçiU¦{}×»Dâ#@ã—€ ¹†ÁGf<=²êÒ±{†£R<î½ì5VŠ*4”KÇ;ÐúÃd"cëfŸŸ¹~ÞÄÛn —ÐT¥flÿu •\B§–&«²Ë:g)½…N••.üoóÝTϲç8dq”©çT‚¿LÞЈ¥b™x½Uu1MlÌ?µ:$z¸§Ìíóv¨µ&AœÔ›ÎÜyzSò%gx"ÆÂ™tù…ý’5^T{óVoËâ²¼Γ¡ƒÕÝi(¤#b¿¨}¶/{O#”"JâÒ±]ŒÃI‰E2Igø$C¶Šjo±à{Àð®*¨¡<8ä×9˜RWŸN©û–ÂlaÙ&_!]W;¿ñiq¼­æ ÉÍ«˜¹Â«蘩“ד%°ô2ŠIU$h&kT4^܉ AW˜xæTS924±<ð¶_ÑËÆŠ´§q5<'eiF›]g-¯H™SçÍÄ^¹=¬º‰^¸ÍpÃÄú] ^ä5¡û8E3!‚ëôÀ&oC$æÄN €!UHþ›î>ª¤¤,NšSËbñ‚1°eËLj6JVþ¨þ}ïÖÖuìLV2$íªè]@Ñ=4Èœjºe¿Æq0 ƉY7¯A»¹9¸þyµ#¼!´£âF6Ž-ƒ“ÓøT¥òüš}UõÌ„Lb629jØë.2QÁS­A˜ˆ»”0›n~kÓ‹#;ˆ ‰!ÖzÞ¿òm j,̹sŒMÃíÖ[@°.„¢R;¿>3QÍfMÚd¦¬ìž¸ÔGÝê֪ؕ•¥†e‚&ûdvïpçï¼ÙÍËѧìeúªhº9Ÿ÷¥ ‹‘¶ïWÎŽN”£šEÌÐm#Ì`ôõ,¯(ÑqãfýyÀ7³ 6m}±Å^³ÔÀƒD³£n»Åå´#ŠÒ5ützœªÕ!Ž6´§ÀM¯º›¥ÚÔôx˜BÐGz}}ê,š;qCj^Z+M ûêD°_ïÞ< ô2žX}ãœÉ~ƒÛVcL›v¨T/YÇ!¤âœ!JϲâÈR¢rÌɵçFY#S䯞ڬ8ŽF˜òHI›>Èä3Ü+åŽc.…l5‰Löz¿õÒ¾éA£, SRßfp0T£’;Ö)¨Å…Ø­ÛxÇ;kn Mã¿Ëƒ  Õ"Ï–ŠÊÚ‡´÷Ä«¯(D½ñbr©ÒаȲÙe¹*bLþÇRÙ«Gûà`êüa`´cª²e%Ú ëwI‰ÁW—Róú4Eç|ZFÜq3éå°>R¬/èŒ*Ëb* r‡ ߬eðâž]Ç3Ù¬˜s·±{C›e§»«¡É©¼8»}f*iO)ù¯ |lµG1*•4p’ +íÛÞ[î.Çã¢ýiRŸY„ì'cëz1E_IWìzßa¹ÔòSkàh\÷"й² .lˆ—ºò))ó€DÙ&Q.ZÔ>î,‚¬c¤íšJ¹5ˆ»às¥NurÎô®n•û\u׸í¦TÖ»\¹4{M§”‚õ‡€eA¸Ewe@!mx‰Œ”cbäˆc}=l¡Gäß„¥.ñWïËŠDÄ×NÒÏ$T¤øi’§*%`Ø@Êyaã(Óž2òñÝÅòE4½JRL¤µÔ(®ÿr êÉÓY*U¡D(H·¨3¤}íˆQfѶÃäà‡j DÁ+ÝDïýžOKù""ßÄùÈŠ(ãsl»S±KÀÉŠrP­#½íŠ[Û(Æ(] AŽÇöð4ó}LLϹîv›ÓkZê»áÊ|Nˆ¨»•C’•Ð-j­íCÏÒv” þ†±Œ$5ïˆY¿9ºÎßÈíÁN£íì\Îe?‚ŸŠ—S¢K +CP›^¿û ý¡aª†9ƒ»ÜQEw¥g~ 6ü3T^M ‹é¥9œÖ**ÅŠ&ô{ï&¼Âè£ôèˆË«w†Ãþ±Öž&¸¨“¢qŒqXö]S@¬©ó2 HÝp ظÕûËÕɽªJ-ÞëW$¹îì¬rx:óu’#ˆw¤ê÷ÞØør7cƾê™2Ö{‹ ì¤¯û3è3ý“’ó×±ô¾ˆøUšÔ0=›émª—Š«z]FÊ ,‚LIàn²é4¬€ø˜Ú[SQÝž)¢Vèlq1jx(¶OÏúǾ7IšÎòî`œöîcù+Ï™PÌò7‡ÛªÃ§Aq·7u E  õÞQ¥d¸=~e$;Ó38[ÕƒW â"}Hb=Å‚ØÒˆ2£WðŶw" ’¤CR«¹$Ì>r?ÝV;å­½EK)âlî ëcÄ(FlÇCÒÍ«ï"P£3òÂ%I5$_–ÓÛ´p§%Å~‰'ºð;µå_1J­Ë·W¡­ÖàX@œíÈŠHÔ°ØÀBŸ`X}Ÿíæ}Ö¨6ñ?¨"bïyudæª5Ý*O”üÇ85X²ãœTû„Ë,l­\ÍË ¡^êéÒ¤V .ÏúÌxüEÒô. /Ô¨•üÄ9í)ôžÍú󟜶µZ&7ôOŒXª².œ@ì2·L*Ã=%Á[’4.\Íà¢\x4p\ ¶“þ—O.Hö|AO¹ÄÉóWx,c5{›â:õµèA§/«+–Ñ8füFB3¯“@Ç<ñbüø?ŸÅ (2øAlp±ö.ñÚ˜½’ÎåüFLùƒUƒaèù-WXoÇÕò–ÿ™Y,@Ž6ÍトýY@ÏGð Þ ÄP~º$ÜèìÓÿšô;ëcÙ<·w½ÈÃÅ­ÖŸnóÌWºAÍ#¸=¡sÙ€ØÌ I+Ó!ˆ]¥©Ø‡›ƒ”Ö¸@ϰ‡(W›íË?”± }6úØe±³„¬Ü¨-Ž/5ϘŠññþ™XWHÁYÃ|>OÒ¯=ŒÜójRÛ7 ô‡®àˆçÍ{<_“–%Š»:Vø=`Ñ[ß:™îö7.5Á2limkͦü ±/˜½1ÝyƒZã—ñ„.¢ ˜L{Sû¿.îÊC B}``ò-¢¶w‡%uÛekpz[Íå %ß SûQʧ¶¬¦ãÖù­ ¾ ÌD`Â@õ@YO×N¨‰¢pÖ•œ2_"iØ„SPJ+ù±(úyù-ÌR¹ô¸á3gòiÚ½æè‡|ؾð´x/³FëYp„CEA•¦6öci+WÆ,×›‹î|ÌV·nVFÇ_ðCt´v˜4áº({ŠÜ0ø*Ì­ÆA{¾±˜Ÿc†FjÚöÑžÜðœÓn׃ò嘷U‹gD¯ DºóOg`}ÑÛY¾BÔ•“*®iaÖ 3Žm@AÇôAÓœ@À@9L ØÜ\cõ~ ^] 8KuLŒÑ¨ˆ¬k€Öœ¶~Âo—eð:V*¶¯TjÍù£{yn$ªafr‡»=²g/(fªö‰ÍãüM`<{C4œ±ºvjö²svÞ] ÿªžÍAÓšˆ6?<ŽÕ)¿/4àM\M7t+û×®P×ñ›S÷=[k‹`´§ë ã a²¬Íãù6›àsÜ[Eö!á"ÇŒŽ˜x‰='õn~8àô²›îq¿ê‘X³Ë©ô~ƒ«/œ$oQCUN›<ÎÆB¬`~ÿ†6[}Ùž~µ…ú¨éî£ùM:"Ÿd¦'ö¤² >å/)†:¿e› èuªZë·zwgQÝP„WgÍxåäl[¹Bl%Æ3’®2B”A—0Ïó…PË‹ï>ƒcîþ ý$"9f¯T­™fäeee ˙Ȫí‹d®g£Â Ùx Tõ eòA ŠYàDÎ_îDê¿]®Ò3 6æº é@K…¦àÎÏ™[§W¥Òõ¯·AKAÒdpÇøAô(E^ùà@àŸ¶0U$”­£ƒ0HóG{jÝÉçÁü4+aþ'.dø;˜ ! |Å$àƒÌœyÄÕ™+è°Ê DÈÀ9tu¢û˜î½øQBK`c|¼­ Ïû³g’ú:3÷¾µéýLÆRGŸáÀó´ü±]ŸÔ98@©´ä3Å')ÏK'¯÷Š‚ÈoÖ®q²?ØYG÷]Ôò>'·ÐÆÅ. ˆjÚtŽ·Ü…C6LÒþÖÃÌR[–rÜ•g@Š¢já)QÑÝq[ÛœæÕÏìUwËC¨œibZй¶¿`vÖ)ŒÖñ;r ²Ë •ϱPC?ÛXë% o‹’þ@L¿Së*EÃWÑš-°ëÿÓÝÎlä=ýó`V*Ú)t,Ý5•Û{ú4Ô]Y&‹šÁ†‰ôͧ£F÷;뢥¤Opë.ëbºýÏún4Lo’Ì6‘ä©æî¥¶RÃÄ£L^6[A¶‘ RMˆê õ«\-§>Ú$GZ±3œP@TPÎ^33ÒßC“ Eº®éš¡NSæWÔÀxûù,ÇÞ{;cÎ …GW·M½dhÿš˜êÙ§&/!¾–$y—[>ƒùÒåKâ+*“v虯ÊOAEÌiûæ¾b€†@;îª0ϯZ’£×áY¨œZÚÌwtz+iš¼˜-n¸ªÕ,µ×ÿÝì—NÊ °Ÿ¼!!>ù¾]]„–XÁt¥žÔEkj:›Ÿ±LIùé>ŠgO6K$|\@£¿ oȃ9¨ª—¨„S'N;ÿó± œ¥ÅcƒH/Ã>o|½2Ï Ñ-zµ•†]…ö-Ï€ û*úYˆ6#~¤_NJi{‡¡€6)SðÈcÛ¢¿Ñ÷>W·°ÓIáµüWŸÛøîÞV^L¾þïò  0#X›ŽMÖÔÌjÉOrß6~#pXqz°>ÜÛ³†õ'¤÷!#€d\¥ßïÍ,´œC¸í ™€Ž6!¹®£Ä…$x`„gÁ<³*tpcg®f[1]—¬W{î ¢:ýQÚ™Y~ÊcÜÃX‹Ò=fÀt!!4¾j„ªÜ‰küì‘í^ R’Ž È%òñuž!œù*•€šÀ»ã%`OY¡›¹V-@Ò)}öa5އŒû=KÛzc`DÒÆ\ ?ÑÈ¿:ħfø€‚65p-'Èã*$)Œ¥wô£ZÉ,Õy‰TR YË5™ÐÀ-:Tx¾'ïy”F ô Vq8èöHòR •X‡x󛳜U.€$ôSÈAÛÈîvãŒÂË8_Å“üJÄ-¿ÅÚ]Lè‹/äùž¨UH³÷°šðå&ºêuRŠïH&ÇΉöŠ:Q×DYDJjh„°yÃ97Í©‚¿bƒ;A^t†UAxÌo懤ؘ%˜î8òíòÁÔ}X?Ös;äp)yŽÏ„&?ZîIÍTg‡T)ž=,Y‡Á*ðvuå†ÀÅ#ŒN®‰VdS ð§–ŸãÁ΋Ëý…—éß“6„§WÓHAZ¡dzWu”ß퉅ùˆÄ–Ú1Ñ¢-úÅ"¡µ2ÑIfºØ-ðuà”R¯£Ï¥‡+üR Ù½½‡ØKýzdçÈmKëYvPôÀ ÄÿI,§!êhÓhå2Åו9SÓšŽ_q ønê+~B·ü»‡<Úè¯È’ÔOf¥«Í?X¯Mà¯q)DŸ»?-lH_aÃ'ÔÛxÙkcl‡" °/qK²Ú]ý¤"nLtO¿'A?SÆì!òK*_RzHššè0GàÖwƒNŒê‰·ï~¡`;÷ÁÇ샬ÊöæìC)#Œ›­8ÏÀî§_‹ :r2Ôhˆ&5C¼Ã0*ñDøZŠœÜ …>\¹)´âº ²f» @Ë–N÷ÇïvJÀ¸Rx‡+u kžŠÄ.Úê&6î41ïŽG« ]QêŸhûý,8ú b«¶‡ETe‚HgüÄòŽ.€>š×w»°ÑNéhýܺÎëÌ.ô?Ðl9\‘!iÿþ®` âIÍgB8[RÇzÑs6pa®`>VÇ«úì3ë¶ b=×=Çrÿ´:ÄÜÛÆë¹ÞÍTÈg²i}õ lƒKèˆN?·â9,Ò ß@o¢öa±StRevÀÿN*”ò‘E¶¯¶``üLø¾Z$Ü´Ë‚¶ ¸Nr3ú5Ùe ö„ñ€Ûgg Œ²eøiÞc˜ÇÑ‘ûøîŸ²øiä×·h¡¯ùʾ:‹g ëŒeâ…µ+Ó‹]9–µNnþ›%(Ia V_~G¡‚HVò; Tm+ªH 3$ {é¿R/†ÙJ«œòò{Žq€¯Tz6þ:6ž—ÃTíê9o{æQ4zòiÔQçóL¾Š÷¨hMl“Õ³2»ÞUœýOO)IÅÁ·°“Êq­€8æ³Hà?ÙÍGŸûé÷7´†}C ßþ ôólàžutg®&…êÐ íNûpñ÷Îú**¸Âø'2ó¿sÖ ¿o->¢þL¼®ψÐßùÚ}yÀ÷èFDÿù±®~âÎl!­}]D0ÍÃ1Òo û2YÓ*ž½‘¾ê+à¿ñŽ5.—øÇ'Þ0”€¦!Uþ’wÛþŸNöjîrFÛ‹™ÄC¯„:$±¥œ.Êáš\L"\($eÿ×:)Áp¶b!4&DjDßЂØËeÒ:þ1¨ŸòAÓp é‚ÛNyJátVjªUòžÑ0Ô»Uð‰\Ѷ»“MLt׾ū¾•† »|ÈiÜ·ø©GH{ëµsŽ vE+M(°¿æï8àÍ$7ŒPþÿÄÆ¤·’ØLÌ{™±tÛÿuÎ`ÿ/„,5ˆsúNá‚qÕ,ö.;å ñ§ÏcôQå!:y¿#‰I³ì‡8.%~òóÊtàº8Û–(Søÿ"Ð¥òÒÜ!_û“`Pª]z¨Ú ˆË'ÿØqts^Èä$xšéÊ[×ödø²Eæ~%JØÈ$PuÿÒ±±QŒOO'éë­ó ?f}Ϲy lëá\ôýÄZ$§ã¦86,«¦­¢N·m‹¯/9DaíMÞ¦Çó¸’(Ö¨[ÀÒ_PO8:\¼¿R+®M¶ ‡ÎR€E…H9)æã³¿JôI£Í݃-GϨ`¼ ×M ¥,¹çýzò٥⛅q‰À`At$j=+ÍpðÒV ÌD‰3Îû’ 3àµEì"æÎ†}-?_Cì3ú$SÂØ…ËÃáGÏÐ2F1H7xµ:¾Ä‚vE£B,Ãø}iêݹ•¿ó"¨’Öš†“@d[Q“ku(®TÞØM94ãÑn…F¼ÿfÆ^ƒc°áf>à0m%òJweþ}ׯ—&¡0zÄ=þÞ$pñ-Y ‘ÄÆªäJcµ±·p£Èópb¡Þxf•Ô°Aõg‹i Ì‹if­Å¾XÒSÄ{‹óN4ìuB£i[$×DO¨x;fÅ`—wšåH˜²Ó0¶·E¡;ü^훽¨óDnðpÓ›Kh¶U=ñ`S}zÄ°Ž¿Ø"òæOË‘Ô1 ?‹ïüXôVL ÖÚO® ¯7¸J ®Q¡f²¬Å»•ü¨¢Î 1X3HÓð™ñcL÷ÁÚD²Ó1RßŃ}¥ þ¹aïSeöõÝñ’•¾ç6Ä‚ ñOµ©¾{A,_Ùd¶ñ‹‹ºÕêú‘c86¼‹¼tLXæ{³!óƒÕ9Õª1ánðSqnŸ| ²C0 Œ‹W­>¹j ûÖ‚])â\C®}^çùúM…»sf)ü.ÅÃóýM<Èћڡ5H쉂⮋k0 X„5ô¸¸ØÀqÑ9TåÉ„ÉÏmتÞZV/ ½SOûY žØÔÜð~œBÃëÅ w¥q¼i¶.É+ƒíx…¼X¸¬`¿-ðÉãÓV86jcçˆ:>è~'ž‰‰…ìëAœ¤¨ÌzÿçŽVå™Èc:¬(B>ýçlgIžKôÜéœeAhœ†gCHà]‘‹#%Q°@þÎ'‡÷Ù­0 p†ž|bØïñÔûä¦Ëgkx•â²Òªªà2¡²WêC£K#e¾Å(ÚͺÏEeãi”äÅ–™[‘Ã] ˜_óbÆÆ+þ?ÂA…iþOMß³BÀ‡}ñ6ÚvËD×LË3-’jÉn«à,/p¹†“E—‡dXèÂjˆO¿Of«žõlÚ}J»Î‰(ƒ†jAÁ-:·×P×ô½¥6rLq÷•2b¿“LŸf±Òý¤þYƒ¯ ,D¾×œdFÙœBDôLBïGâdyŒhevºV`ÝÁÃóÂn+XQÏ[, Ñ4I \[JÔ¿O†2ÝÃ'¹â“!(ÞtXÔ–ÆÖûød¿†è³럻+µf}Ò³¯‡è‚wÏ|‰_Ödæûø‘oÏ™×ì4ŸÜCÍË‘R¼×O°ô´[FÅüæD%»¦™žÓ¹¼„=Úg¼ªšÊ-¨ù£‚ÑÜ’f¸µ-T¥]Xã-«£*&×sÒ9ýÁ‹¦B0Q³#(áeDŠÜcéëĸˆ÷ОGkÌðclJ¤Úø 8”ÒGñŸ{`Ö³À“™éÝÊ( öÄñv(S“â^Ðs›cñnÍMùÃÓ÷ôVN‚sH â¢£,ÅßÑ"ÂõÓåòÀüÏ%» ý^¯×Í¿>:É”Ýñ«!Q›âÕ¶ôD|Í—ü÷Y~†õê¶àfïÓnhsBOÐÐø.‘p²» hrvŠ¡õÞïa 2° ð8ÿ†ÍÈ"G*í— CÚ–yÙ Öš ¨²Ð'¡CC„Hî)8ñf6‚_||Yé»ãw>«ñKÁÅݼ }zAšË÷&‰ï©§9ÑýQbÝM”Œ“™ìÓP$ürw:Ûë©b¬úÎ2€­M”1š_‹eAùøX³iΙ))¼`Ä`DD‰Ô¬y÷Ë S’nà B*el§§Ô!/?ß>ð q5tø[€ø>–:js,ø¼úO‚üü$AWoÐpÿ žVެ˜‰"Sn4ÂhÂLËù+:¤º 1sö]”5nEÏ4XQX Ý¿Jµ F„)Væ  FAßN—ó1¶u¬Ií•ZA>mz ˆµb.7b/Ê©H3 ÛÙ2’}Ó¹8´eÕ.©zó@]oÂzS=£ØÅî—Ûn‚ÇòÍK–žPµ3„”¾Á²¤ægµšÉ-²¾ vX?”ÂxÄÆµØï–ö͸I|†á822NC2I?RÁó‹vÜ,‚×n?Æý’ ó-œNA9 \{H‡ÑfÅ{¦â"˜Ð¢xv׉¦jjV?¤—![$¯ûvm£hðϨ7oÔ·Œàmø ýKRwJäyh¹§×Ý 8¶¿ª?Hy$XÀŸæ½*² ªc¹ªDû›H%îHNi™éRKéôê»Tä¾ÜCïÎÃð„eÒB%p„«¤3/àðg¸$½T¡`Ý'80^(ì—¶íéÏÁÐ DÖá;9µþ°Ç.Ï)9¶¯®ñE$‡@4Žø5¶¨U“àÔähõˆïÄÀ ,…9ºê‚Ç)ίO¹?·É(x_Lw­UPÌqž@k„ëh›9 Ë{çn”jnø6,LßoHïtofutâIðÏ"vŸ€®‰—¡. pášè¾‰8 $I‚8iÚuî&©ÚEG»E•l+ vš«ï=}¼7ßÒû<ç¸oÔ9nK#` ñÞèÑùeùª§Å2Úrâïå_ŒH+mð@£óö °wŒñSlFcîn¤6ìño¬bC;ÀF}ÆÅìûŠ”BÝ‚²-MC*ص"éø¢ÄÑþøðU½@KÄвÂäQž¹CãÌ—.΂»+¢I Á¤ý0å&BÆâõYµŸ«Æ>Ö{퇊öÀšÂ…Tªµˆ¢ZõîãîZ×U<Åé‘iéÀ,Iä-üæîpÁý‰žîCIâhE¬VZ‚Vôt㽨PƒÍ"Ä(öïI»`öðr£â€*ºÆ7¬è‘'œþûj]Ø ¹5§=-Œ´Ú‡Ýw‡Ó"ÕÕSÉ—Ú[ó…œã“Žc³úˆMM(®íºãq¸àŒehíËíhÒî)¯ßžž ¹fe¤jsº~¹#8RǸ²?h0êWgGEàF®Bšâ…îpΟŒ¡š"Ô6~°°ÇÙüŸÀMG¶ºÊ¼ü°¾XMjŠ"éœ}ôB)­ÓõëàT›X‰<"¾Ö)kùTßj<ÑΚ¶š\rìg ÅwBÔÏ»hÙç>)T ÚkH‚_É’`;ùÕ¼ULQZñÖŽ\náT©¢7ŠÄË…»Òµs½dš´e&^•%R†V·KÊΆs»óªm‘7ÄgÛ»|Å‹æÜÇ6ÔîkªŸy_Sù ë#ÛeuG9 ˬÙŒP@æý¹f”ìôÜIV ^d÷IÉøó]™ïôÃN•í…òy—e%%¿³u)vö––Í%fdeدÍ:ç±Rº§»ûm§Þ€kVyº»êRqÝ-QoËn#8vóo«E™¶ÛˆŸaJÐÝÐÂìS¥¶=l ŠÑ'ÏYát–¹F7Ô<ÑñCÜìfcU¬†Ð-’ßa.$6–ìºRHÛ?ÓÜ×-_ÝùÀÃmßðJ|ê||sع4X¥xêÍbâÀc_ùû%@B ‘‚I~-âf}f0ý‚8ë¯"l`âMI~µin1YØ9!µ<$¯h‰ëÉ>ËQ%ߢùáE'˜Ÿ.ñÆOaMÚd‰Ð‹¶ó3ÆñôL«Öø‚Èø‡PyF矬‹mÄPÒh‚äw1Ùˆ2ïjhÄÝ]56?EQL¯×T´fAÄú7¡WM㙘¬ÐKzƒs/(Z§k?jo.Üë(bÿ™öŠq›<~ÈuØüýäBÆ,ýÅNÄp`Bc-Áé¶¿èZlqÅ}ì6Õpå5pF °–°PŽE#¥ ?DÞTA@fGÚ¸æãêTö la ›m|¥â ¯³áp÷0ûìîñ©.Áüh»{ÁŠvê@óˆV3bƒèC_JîD¬¿³Üð ¥T&*­©Ù¡Ÿ?[‚'‘ÞZÞV‹k¹^£òîÚ•3Dð1Þ§‹íh›†Zlh óœÎði ½ª­6)(â•v2"…ª?|Ö«&“ŒûªÐ]ðmä7ä›zz…I½Å@.1†Êí¸UjêIðô€lP©A[wÕ Œ›†æwPQÖk›žùš¡¿è–l,=fÚå$¸@¿;’£rÃ^…k÷Ñ^“IèÒRiÔ¿s⿸Qd5t³%ä¤éÅö«êlqˆy¡—n™Mrbnu@ÊŽÁC1¯Ý мýç©&‚Ó;,–žJ÷çE½¶y ž‚pe/_—ÙćQ¾¯¨Ç˜´ÀÁÖŸ‚áx­èÎOi{¹ä mU¯Ín#±z­²…¸á¬cCÇT5‹äü„x ×x9Óß¼Û ž ÚPgdûª‚tú¥ÇòšÎª»8­KmÔrÎÒÜZQ¦Íûuð¹WæÙÕaŠ:³k‘—JVVOWj÷>IîËn䌼°¢L(Fúƒý!*W0¸z,âeê|Ëç­(·åžÊÃYógN²¸Éy¡~ä°˜ G΋Dmøf©kèëdXåvæyüTÛb4qÉýÂâõ¿÷¬Ã|#õO]“^ÃË7‡²ýP S²œ @@àåÌóXέ É,w*œñO÷X_ %+öñåèYÂy\Ä<÷‰P²!ø¶:_칚ް™µúâÒз/Z‚i— («^F÷ÔÐoh®ÿjC83qIåò6o‰Ø0ímŸ§3+|E$>Ù¶@ø2Ì5%?ŠDôªÅ0Î4\ŠY³d˜±£›Ù¼EÏM¢5”I,\t À@E›šAÚ(s-á‰ËÁþ’ayw!1¹*×@±JíÔ u€nÚ¾2ǧÙgñ×2+Ò ³¶¢Cç:Ý‹g {ÞÒEôÓÒîCOm”þ8?œÉ¿z×bz5EúX6háWy,.ìte5Ä‘Å!-‰œýÛà)ìWï6X|ÙBæ”ååó8_ŸöcЉÙ×k’×â$*¾ óO‡Goí Ö鯒qñAlMXÏ…Â.:LJ#xö7:4¢ßn™In¦êMÛ=fy—¹ËRˆãBÏåȆ“K:Ϋ¬ét5<$ußÁ#âz ïav|óÂEôyÖ›TÕeà§=s­Ú±€€xgàèIt‰­û°á±U±Cp‡®Ê©ð‰WFW¶yt_õ¸cùyˆF[.Öóû)¶æSN§Þ:%ª&Ò½w&IVIÀÃÒ~]QЏ¨,ÇšÏ6Š.XÌ) Tœ¦ê(û䵉T‹‘Ô«V_ 8ì¼ÿ‰5D̲3|S8A_XEÊçxBãOr˨˜`‘üÝ̵7&ž±Ë×ôý Š+™i(¼½ƒ’†ý¾ IyÊ‹¾W44IóWDëªüª)úÙþ˜”!)®A• Û¿í zpŒÊŸ¬ÛzX™ÓB5>ßÑÛÄ$gã–<èó—±p¦¦ÏÊô€òbR¢[á}äQn†0©?½îßšeY^Ôò5ô•óò*^lý…Ô謸I{¤¼¸©^er•¥/ÖÖ ÉÏø°í òYþœø¶yØF1z¸×¶…øL((Ì&æÂuëhù¼î˜A=TÎáñçæ§Ë=âé Dt,à§‘W HBpŒHǦ+ÓyZ“Ÿ'“ƒ>õ[¥÷ ë%7¦I´›¨‚ÈžÒ‰=@}W ,ìO‚pÓPi (ÜJy™µ–ŽÕˆ H1ÒK{™ðšœ \ÖZs;û[vÑZ‚Ù¥ò«tÆîâ™1ÛXn¬®rÃÿ.‚™M,’÷AT¤[þÅub‡F‡÷ †·Jh€æ¸=ìæÐj__«»U–N¥0JÞ`îÇ©ÏëB<+ñwVáë¡,æqPkÕTÂÕ’Î&k(C!®`Ñ3ôž #sŒŒãùòäÕ'¼˜ǨÔ•üu>>i>Q»§‡k†Óùî«Çh˜4Š÷Š›?Ýû§$RPØžlº §É[‹Èç~xj÷Q)$èî±%,Rzð³….aàÙ¿ÁŸ4ØQ ?°™D1†=‡XXw ã”úЂWöÎUHV1Ð×í܈uÒ%>s;°!€Ý3É•zY“W§óôúÐ}:í¥HTïÀu–¤o7,ºaÿl‰c–]%W Gl d÷ÓIœöã¾ žG¤ðÓ£tI<Ÿ" g†æT‡ûøžÞ¬™ÌLëÖî£*ñ7>MÓIcŒf¢aIÏÒ#Ø1ôJýÃÕ~LgÿÞîÍÚù&¿5€¬h®¢ÔÐÉ™£c­P;Ÿ+á–úÔá²xõÖ).ûS7« gŸÁÔÇ3|xpËвOš-AHÎŽBhVØ÷ÿ©ŠB‘ã2èÚ8T¿´¾f®‚š}ܦñ2y'¬Q’ÁnÌ „‰Õ¥Ý쎿¶´[ÿéoæ…´ô¨ÃýÚvÝц­yHò»®Ì€Ü/Pú) |¢á ë4ññaû d†o†á-Ý0üY5O*çÙ4i½,8yÝÍNEŠH¢~ eF<ãঠí;<¼gÈüôþzžÛå.ï×% Øþ Wã–ˆá‡Ü©VÎL½ó·Ž]¦‚RÕ—GejgžFõÉÝq>«åg ëé·d{ÚÄ×üUÿ%¿‘]›1[F¶¡Ù‚ÏkR.lCÌÉ¢ ˜{Üwg¼Ücgüðdó-äíÔdUy‰ ¦ïm*äœoLmˆÍá¥`DÆæ%è¡›çÞ÷]Õ.‘œ»Á ïôSaVšï`S¥q°ã£(˜Þõ_:"ߦ˜‡Åô’z‚+†ìÑR&yH"€”ç:g@Ûe×8-ï¦cÚº:ôÆûy'©ö‹É! O]ßó%ŠVõøç|³W ²?ã5aÖ‡˜ˆ\({º[ƒ×úBB}˜*(xKúø÷‡ÕA4ΓúkœQXúüÖ#‰#±oBOÖÙ*Ÿî ÓkJŠìL®% N­¬a.] ø é¸+uEŠå(96B”Ñ¢h•ûÇY¡R_«—î‚*¹æÍ!vÒäã““2]² ËÉìnâœU_¤Ÿ>ØÄ½·ÕŠ|¯­ÓUŽ›‚þ–¦}ØMeñ¾ÊÖÁEE@I¨åÉo¢ÜÑ*ü{¯H‘ØÊ öåß$oÂ}Õð pKý ò£R§¢ïbÔÅè¸<™›ÖøwÅÄ'ÁÓ~èXÄxbAéÜŠáà6]ù"çÓ,Ênëýo¹F8}*s6ú¯ZVkZêÕ^h(8úÒß?„áó[(šéGW÷fÓœædÎÀï>Þ<•¾Ë_6ê\\‹‹)Žåp&ÆuΩ‡”\#¿…ñ~š)ÇTïçåböu˜·¨þßÖ—Aˆ*WZæ¼ùs!È““IÓ' R•zúÇïÏ\F¡4EWÁò–P~„£Àe"hµj“b´ä³ú1×ÕyÃy‰Î/S'ùPwñRB®Ü-:]/Úz\˜ÒqÙo0MÍþŠðV£ƒ¨äÿó|_îŠJ`œzx¢Q igÇzCà›q®'µÏ€`% _:ªÌ¸Eø[öo{߉yðk·S y(ÿÞ4È•V§+±¥0@ ]äÞGv£0Õ»4~ ÍŒö_Ob1¥¥m㿯ydúœÎ"žé_¤=çú_ ·º†ŸÓ¨ˆ´÷ÆO=Ñe®¢=k;.«õ¹§’˜C…ÁbgÌ•ÛL)t#}“ƒFõARþ9 kŸRzJc°UÞsQ`ûwÄuîäx½ª žüàVšÄç‡ü]OcãH&,Pö®à¿áêb”ÂÓ==/úUTêIiÝŽzƨ÷Ÿ±µ¥ÕÍ…$æÓQ½VéÇSï­üŒ¸¯Uj? Æ%`\¯º?|¶Õˉië$ù-ð2µ6&AÍÒ7˜ß0ß;­Q:7‚®€+æ… ¢ÚW€ã üyr¬m&2bØ~8å×—ä?Ç­ëM² tä4ÒýXœ¿ÓŽ}$™¨¡ºF/C=Ì9W*Ë ,Ñ4iõ³@@þ’»AòÇ¡À¤íŒ»ÁÔè2Vò©c3âì@ü=ßü®ö5vÛ—”þ(:séí]aÓšè6XØ\þu)&ÅNïSXéÐ+mê,¥—ÜW=2öI-Cò@,b2רb Ыæ•j9 CòçÒYsRîv1Þ@Ÿ^¾še¢Õdr꬈[L=ù3ËçÀÌ]wÆ;dgâ—˜üñ'~ºoEcï:ðç[$=|³J)Š™kñG±{z¤f¿{kßíо$Ê«æG¼>²yڗ蹡àφ9dHñÄV$œäD(p FŠ“î¡Õ™ŠS…Ä¥»•!S@³b‡f‡¹{Á<÷û×Û,^ˆå[„žé5.þ"ÿuɺêX­1?æ]‚´²¢‚oá:‘ïy¶Òò8Ðò[ºª>c¸u úÇ’tÞ”Ö³.r’µ÷>Î91Zµ _>´94Ük¬c+îì$»ÃÀ ÿ?8¥Ï°Éá æÀ‰Ìœ@—Ŧ1jº•ÁÞ0«Ÿá>y80öÚâ”68qß°CÃ^–i2ÁfH.º­}‡2³~HD"2"îßÝuÊ`Þ ¸(™;–fj˜ëô÷â|há‰"OrKþúÂhr ĺ#ÁÙ“=¹ª–÷Ñ#,ÁW U½£2•ÜÏ”·æß÷óVF¤l(i8›%´µ”‹ÛB¿ð°6;…¦X6Î'Ÿ_M*§®™åc"Ñ%XýÜ%úzÛ˼PËï¾OƒcQ¤*d€¶T×Ò9D&'Õ¢Šº…ò Ø×KÁ…)Re,$jž*¸™Ú E[G©4BÕ@v•†³à1ëÕÕp‘7æÙ6¿Ä©ë;ë}†ÀÝÔT> endstream endobj 12 0 obj 43648 endobj 13 0 obj << /Length 14 0 R /Filter /FlateDecode >> stream `šþxÝ€¬A§±ÖÂvþ0fëÚ$>ó–9l§<ê…³ûÁ¿»¼‚Èáúü\¢@ï”Aî\>)÷«™‹ˆ¿t?IJm0`s2ÛEöt¼vv!:TÛ¬]VÄ[%â¬Ù™M&ØUû½üá›L…ä[¿-û{°«`zʨTý˹šÞ~·FýÀœ{l¦¯îÉd¯‰÷ K¸ºÀ¨ÌÒÇ&ö¼ZØhÛÁm_Ñ8¿»Õמåx¸«Îª¨‡—­•‰^§ïO©1îTãp#Éf€Õ¾Ìõ¿-!O†¦oKŒ¤Äed½ø¤§.#ÍÆÿ„¢‹\Ðú(ä‹“|,>›—ÎÅo¹³ãÂ!–3”‹ÓÇÄ‚º{N 4µTõ&Áý,0 Ê:Þ8¦Tè­~ï;Pì9ñ””U ŒLצ†ùù¥’íWE!¶í¢ OÈI¤ ‘RsÌæâ¿AQ«Ù<±;MPÞæ‹‚ÐIò )§¶¤?èvˆNsm«€{YÖÖl+ì_ËcýÉÀ¼1·{!C‡OZbv…eÝ&šÆBBB+!%Óù~˜5¿Œ%§Æ ú»ÈiÝx'ºDuåDÕ³™ …+äX1 žý¤³Ë¸€)cæîÈ7X­tí¢ “á &›d5ÌEef\p"$ýpìAÄ`|Y:‘Ã-Šöè´²¯;$ñjZÏŸýΠqOáæ•]+!×$ÉÆìr.s]Ÿ+Ó{ŒÄé')ë±y‘Žä&)> endobj 18 0 obj << /Length 19 0 R /Filter /FlateDecode >> stream @„›‰Z¢@v‡Æg‚9Ââù†3»1ó‚Ò‚…ÃhpÜRÄÇVŒy¹/šç!Õ †ÅT‡ÜhsJQÁÞÕ} ÑG§ËÿÊáæ 6=ç1²JÑ»uªY®­6ƒ4WÏ•9HRêv䣺ƒqNÏÉ' ÁK‘ Åtgÿ.•ð‡®Ý6À ¬?äíÏA'}ÙQoÅ>¶€ƒ43 ÝG€˜QMpÁ<–t6™û…ÿµ6ëáð”=}0ˆˆ·›œ„cÏ9ÈöŽYš¦ø8A¹R@ ½#H¾ZXŽ%½òSOF„^ã~z%'Ðè IJ´G¯U–2ò£ ÿn;PµF×rñR;’ZuÊÌnô‚“•ü?2M—·-ué\JùEGÏÔ‚fNí+F…Ès–&2ê)´¯´ÊvÑžZp¾½^1}î±—ϰ³i_!²À§ºäúäôêV2øùÙ¬Ö“ðU[ Yå¸FÂ=ªÎý[–(ù±Ò±½J®Nÿ+󇊂àÔfáÆô…Åظp5 RY‚ª\…¶GUGߨŸÜåAž2£ 󵚜ÝpˆÑ?Ž1é¥a¼ ÆÖtSînÍßYŒ:Û0¶Yü²{ F´ ³$Æ<&MÈ%)†ú™ZQ’ge,’¡ˆÉs âlX[uS3‹I8·ë]6I„Øå U«˜%2wx½@ÅÓ:40»¦„ EïÖ~ïÈå-,O«ˆjØÊ´þ6ñ”ŒëäH…R4ü–p~I×~î(-§¬î»­Õ[…(‰wGlH.“QŠÄ%œ›Pê„ïR½‹ùIø…ŠR*°é)rÔÛcÇYð#¬à7f÷…·H¶HXkJâ×´Içc"®¢ø£ô)'º%Ü* èÑâ¤8yÃ'Ü¡ÎÓŒ–P8§:SŒ8*„±Êbdh€ŠAASÙŽ>¤1ŸÅ¬‰ºm49s7½«¾HeÒºQfõ—á Õ(,Ü8çTTýÄ#±MQÀÔ·ò$!¼¾SëƒÎ'u‰+8ÈDá^?ÔhÿõýmçB¬ ±+º5h`øgrË…¨ÅgÛMˆˆ•d3¬PÂ6ÇÌZx˜«z\fJ3I$ð1·Wpÿ"þp!±JÏ‹ðÛt)ózðYcw‚¯ì¾PÖKþ]6î¦b;ÊøcøÆ˜™úr3•ËCæ×sÎ[¶Ñ&š|o8‹ùàȲŽ„5IG=F9T]Õ“™‡¥H ²ºýX’_Ÿ ÏiÂÁÁàºxµRÂ; @ʳ%…û|ú˜ÞHz®É”&š`mDÉX!W†rBL¡‰R·Æ8‚Ud¥nP£¬,«ç …^l¢áðJ–ÜðK à;ð¹¦o²sV?“á¦é$¹~4whªnü Êm§¼ÒEl ÛÊÄÊt¡ñ+÷(à»è±Æ#4 éMÜ–°ÄÈ×yžk°êÚÐõ°çdÖ! Ô7#s¼–)M9¹ðé9ùgu;~ «éÂÂÎ#Óžÿ¸—Ý[z~f>û¦I5õawûW­CuÊzÔfÝ‚yx†pßYÓ᡼Šô­zC¸<ƒ 5––©„Ä-Q[•ˆSÚ¾ŸÐœòYÀV¿Âä¯Abßrßò*1h%˜X1§3š†Ÿ ®+YÏQçß ÌÒ~/ô“ßu½$˜ÜMï–QjwÐý×9vG§E:»|Dš|§ªð…³.1ø—§Oü7=ýô™Â|ï~m ³©¨ÆºDËð›·{R ~Ôà‘8œ¸×©i/œÁ©jŸÊÖ[#BIû ¸™VxO‘¡Ä:Ì¡ùÖºÈÖD È¿.¦E¨œ—0ªØg׃æ™þ+^˜Ri'Môßaé©%Ë*rïX3øex‹©Tê7Å?Á9 mreãA£K× 9ɸ™ß‚ÑSÙt!\v´K~³dk.ºm õìéoˆçÞA>Ìèšn _1»awHŠ]à9Î4&ƒe]É(»ùˆV«àþ*%eûKfâ—~[yYËŠSÃ›Ä r=Q³éeU¶:þKÈ‘åéªè\-žÉh†¾&å{Ë #Z^“¶¹PM8p?ÀVëN´Œ9;·m –ÂAÑ= Z ëͼÌÛd°˜å,† Ž÷cð¸w&[Z¢ãð.W0½E·×që°›ÊÍöŠ´ÝÅÞsÆ-ÑJøWhŸˆ¥õ]0@¢×òtH`©“Hç¦$$…+„"ÔFHø‚/¸¼»ŒˆÛÎY9Å !›è!Y8ÒZ"Ê ´ö®OÛk ‚jIÓèÔy'ö0åW Ôìº ñxõgüG#ÉË^ˆÌ Š&¸=òðuŒRþEÌ…ýή× ›Öéóv-«ˆ"Ò+…;èÂrä.îè­%·£d]ò» N3à~.¦:ïJt’)O¯þ¨K„ó‘=äv9ì´Ø~-7x D à+¶È}‘iºª{‚ LúR3ñIèÜdk:=¼%ÅùQÏŒj•1)|æC,Å3ϸéØò7³¸JbXj0WÅë 8s¤ÝÃS.Q ˆáÒñY»…ïRîùcªK²—&I’í@‡ÎšÁsÕÓ&%·œÂÈçµ=sœàcè“òäq1aáoÜWšžáâWå½×‚G”CÚ+îÚÁÌ[qdo—ÎHX®¤sâ’ÚéïY MÝJ5Qð´ñ+Û `ØoD2xD„r>d˜þäT Êkû¦v`Â@vc² ñ[DTÔ$1^ÁV¡Ñß5uNÆHV“ W­¥aìGÃp°·Æïš ¡’za7|œ‰:L»¸¾è¡½à›.Q€~¿ƒ˜&ðv"ãÅ®ØïKÍü;Ð¹Š‰(‹|`‰¯ZÀæÕ$jÏ„¨páñº¨³_(W8„ŸI»6 ¸ÞnHÚzÅØÛ‚Ë_ó>¨ëö¿$’}ÏÜÒjtøî9·n Ô$Š#]?C¸JIÝ/ôÊǪËïiÜéó!‘Ûžâ‘=¾*DÉλЫ£Ç?&À°„o$íÃÇ~3qVäÛõÇ{ØËÓp⣮”<?È9LK”è ûI¥ðçf#âª-æìØ °ñtYüÙœi1Ì5©Ú¾T¤%?’þÄÇ2·[[º¯™‚\¢§Éòp¤ëhT>žZ(¼dî1Ê4‰•©üzËû¬¸Sç+…þ.Ô) &lö Üï¦~Û´;€kûvt9ÑE wý¡æ¨™D®Û€ßNdN?oT¨z ° ž²Š¢¾î„ùšéåÍà8OÈ_†öÜQ”P!3RAïb÷•9E)Kô5ÀÖÅWñ± u±Û*F |Ú÷üXÄW!D0BGøG$7&C´—-sÚâ¬ä¥º¸2S;]k"˜²õš¬;hGÉÇýVZŒ©È/‹è'IÁ–Âúݹß%;c3 lËßb8!e àŒSõ›T›ƒ©qýãÄH”,-*AÀ˜uene¬%Õ 8<´âƤë\€Š™=bý <õ=ê½@\p;)±Z½ÙÕV¬ÂÕÌ[ú}ꬪÆÀ–‘]‰öPmжuxIÀ¯ #-ž6'¨û9 žP”2¹ðéuR#´t‹"—å?õCŸ±fçY¯¶ÖvÔ v˜Ñ“fsxƒ§¯± ç~?ÌF]ãâb–îÌäXjwË]-Aæ#œø¨Ý`"ÈhV¤WLÜ@|Péþ Õ´nZ.11©Ü„(îK:;‚ÂeÃ=ûhËhœòfJðÐ2;GՃɢW´hblW endstream endobj 19 0 obj 2864 endobj 20 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 18 0 R >> endobj 21 0 obj << /URI /S /URI >> endobj 22 0 obj << /Type /Annot /Subtype /Link /Rect [ 76.534 541.228 106.586 551.546 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 21 0 R /H /I >> endobj 23 0 obj << /Type /Annot /Subtype /Link /Rect [ 325.235 125.257 355.287 135.575 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 21 0 R /H /I >> endobj 24 0 obj << /Length 25 0 R /Filter /FlateDecode >> stream â»(«…¤}:åùè¯sÃî´£äÈ t‚õ¢Š¥iÚ…Û|Ž oÙ.û²Äú¯íUînµ¦¤ðì!Êm¬p•Õp‹\´€èðA)d›¶Á€Ñbö’ŸËW™nÔ„–5G¶öVŽöA«€q%£˜çâ 7Ù)¤Ð\¡Ó´9A(Ì÷Qåxaw¾]iÔ׌±E²Ùù!ÿTëJŒ„H++?m{gÇ›ŠÑºQMQž’M’îK%~[•¨4ÁòH«h»—sÇ—ãÊ´7'ã ÐÝ A6Žñ~íVÄéâ19@sÖx3¼»ð¾ëeF[$NBæ’(á °;J ,8Bå‹Ý«¶f’ä(Y_)¼žÌýŠg s–q‘sk3nsnZò„µV‘÷&@²æXâçqÒ»ÑÕ"úZ&½8VÒ&t¢åú'بLe1'yx†¹ Q?° €ÃGýa¢_ßgûVJ©×îä8âì€êÅds ¼åçÚ0$Ñ‘ wz 2WÉ>üIr°•Ûî·ŒãÈÈ{1±û䡀¡ª_q{ƒ,ѵ«%éa$ÌC‡ºî!¼¿ð„.iQ#›/×gz¥†j‰ºÿ€ÑP+~äHž7*cO~LÚçZ PþÈNXÃa«ºù£%«þÂ!ü]3ÜE ËÆáÚ°uíV²Ò 5 ­½åµ¦¥MËÆÅÞÈXΞJÿ÷l‡r†ë$Ûˆ¢Åzv²èÖâÖú9Lh&+ýù‘S]£ºNnšùCpÕóš›L að]ÍÄ*êT+?0±œb`Ù O#,“ é LJÛÏõQ@ RW•ºÛåòRäKtZÆÌ™E2}{68ÿâê&?¼¾üýT†GÓò)Ë›"–ÊHG‘Z@Uêkx8BÜÁc·†·Û£éHë@Ï’˜¤”*«^6›pJ–] æ"óUû®&0ØýVð ᔚçôë5[Ái±ýR/¶xw)3·ÂkÒÏg o_+¥GµaqŸ)žµRE}T´ý5X-Ð ²y3&M0²üòZ¡ù¶äpg#–×flv ˜H‚Û!"»E°_Þ®?×sЉgô„ûÅíUavÁ•÷Ô•è¡æ¯CŸ[8]›ž:P‹dàq/k¹«aB°Up¹ÈïwSqß¡©'ÇÁ8?ËîkîèÒE¤¤F_Ý¾Ž¥ï[Y}ÏñÞ1Êj"ÃIÛaê„æiNNmN&`õ~í#H‘_l¸°\u³Â ,å/Èn³2,û ƒq{?dâpeöŸ‹Ó•1`Æ-ýÔK §óVHÓt+¶Ž áE‡5IçŸÝoW0fMbs1ðªï>(,y^Éçú ‘qì2'há»àÕ ®Š&ë‰Á?°=4 }ðžu_ùŽI¼Ÿ …8¡‚%¤RÙ?rm÷£z¬.j¶œ"Êg摇I–×f yþئR³ šB´ ,ŠYœƒ¹8¸Úº´=·®Zªp˜ü)–ql5'Éb†rä=Ò†sq³lO˜ =vS‰ÁbNÃWøKÜúø¤ ³$f_$NTZoµMߪ/:Z>‘œàPí}í~Í Ò[®¨ãȆñ….b®âêO£Í{a'ók² jVH=!=ÈÁ%µDÓüeÑᷰŲV$}]˜^˜bnJ\hñed&Ôegs¿ýÖj>†Ølk¡yJy k`‘vâÏ9Ú]\>ù¨{þ¤÷¦ŸyR¤a­Ä1ÍÅýnåzÕ 2¬úS äõµQMÒÙ%=;A-e"ƒ•Úã6f‡ h-2$§µ?f¤4HŒf|…€¸ÈèrµŒ/WµJ8ìîb¿,•#cuüYgÎÉçtÈ¥¢î§J]}fZ±B¹!›Fˆ¢þ¾ï­¤ ¿dÐ0¸o‡‚@ô^÷¸Å™ÿé!RrN^a²„]R•%_²¡× •„ÇIŽàŒ@x¶)EG`p=çãúñz\$U.HMjf=ü£(dž²{<íä\PÆn H@„ˆ;Í0ÊS>î: OZQ„ZJ:C«¾13†‚TÕ#–dät@~„d4^¸(UD§m¤å¸õ—rýv äñÇ]luju¸«˜fqaÙU°Ùz·eà¨6åͺÏ=KG—kÓLaS˜ùf r˜ñ(³<­·EZ‘,$g3”×¥ƒe4"ÑQ9švsïT˜=Pê³±@ìwÉCùµž:Â)¨rP-SûCÆ»ÚØZó¾mf§9^[½¢cãЃ>T[.ƒO·}Èão›¢ã"¥ÃÖñ‚í<hî;p4ߎ¬,%ˆr™¹KÁ‘;›i´…}Ö¶õ.ëCe(ú |`ÝPëiC°æÜË×CN/ߺ¬2Y®æ›CuÞßx»¤½…éRQ$…Y&#¹:y§–‹ n`/[þäÆÁG­S‘c,jÎJ®Ã_¹3.6>5Ñù>‡3þÕÆÜì8!Ü%¡‘Ç>¾­òR÷(•|ó{$óÉË= S‡¯ÙÞ²+=:òÌʵ˜4EEU"f67 ЍÐõÛåa5]˜ûKÍŸ|㇡¡$K“Zº{ó_´—4 ûŠ&}onº'±’ÉúˆÆN;¿DÂÃo{+# ©R4}½?/ƒd§áSoQ{í.õ\˜ LœÊZq(º»]©Ž‹Þ˜úNLac'`.JÄ×Av}Ó.I8†õ †±ZÛhÌÀz5Ã[pUö£ endstream endobj 25 0 obj 2720 endobj 26 0 obj [ 22 0 R 23 0 R ] endobj 27 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 26 0 R /Contents 24 0 R >> endobj 28 0 obj << /Type /Annot /Subtype /Link /Rect [ 411.285 722.519 416.301 732.837 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 29 0 R /H /I >> endobj 30 0 obj << /Type /Annot /Subtype /Link /Rect [ 389.725 700.52 394.741 710.838 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 31 0 R /H /I >> endobj 32 0 obj << /Length 33 0 R /Filter /FlateDecode >> stream AÁÞнÿ½ ×ýxv—&¶V÷<ÁL•† páqâǦPh*+°óÔzSIk_ž*Ú y]¥W IõÀrÄ!œª•ØYx͹g¯Ùe²¹T„ÞUv*Þ«?¼ÊÚðþÎ×ú‚å—Ñ^¯|‹y˜S gŒJa­L9÷ø•z,¡ÝÕ»àuh_(EJì[CÌX,¯ÿ!ûÇ1lÙÅCí©p<(‡‚ÒÇì0ƒm×}­¬Ä2HØ‹û\[+Ñ[G±å›ÞKyÜÉ1ÐÀXSî?²’$IéFÁö1º’¨aòû¥ÚÔ {Ÿn·IUÖTž]iÚÈDçÉwD"CG0Ð2CÔ7ºýl7Û~¸Eß»ô\tÌG]>âx¦ ò¦Bz`ÇËŽô·¯ß>«HjøÚÕ¶Ú5_B髨 ò«„êÄnk|aÀÇu’gÒšVM­™Jà}ô^|j:³â 3¬ýˆò%oW•î0,JmŸ†îtÚ|ø1vìèöŽl `Lw"ˆŒ3t1³ãÈÒ‡ÃøÍùA}`àB¹¢vôŽQùS¶B%êä²ë^óÆkVj¾¥r9ÕØ³„Ød-º_Xt£V& Né5&’™A…5À&±¼eö"U„`ßE£Ð ˆˆà‰¡ 4 ¼MV†ƒ@šzóº>ö#Óš"Ø<„ܼµÅ¥nÎǸ­•E9ß?ù²aàÀô€t)äýô^^O¤ã ' ŠL*ЈO¸¾XލžªŠC±k²ã qE/JãP×ÿ©°á4Ȉ¾ÇvµªýÉ`”*‡=.xjoöVm]LDnoTüóaLÔSÇ ¡]œOb kåë.6»ò^s<Ë+Yc^‰“hð:o¸h€¢„ƒûøîÝœ´Ul5f›×Tç`~NbhçWO­*p†I¨;™Úé`W{Øññ (ÈR#,ð"¹äXýÇXG¨Y6ª6…«YÄÄ ’š%v[X)&³Ý˜[w8B‰ƒ¡S2wLQEÌkæóíq3ÛbK蕱 ¨ßyl^äí¾%p¨x#’Éü½þ¾VÙ†õ“¦X(«À¿-XÁ#ès0‘)Ïc&{ ¯ñV &`‰¤ÿòúúsvAb%]¤ÉÌkÞǦVá]‰MòÃÄ·À0±u˜¸ü|ŠÏµèƸ^†`ÿ%Ìq…ž=yLªQ€•¸’Æÿ¡áí‡|-Ã&º«Œœ^:…±[ÍÿLWöçñ ÛÑE‚¦D¹_Û¾]ÄÄ~-@÷cjÅR;–†HyÂÿM¯0wÊGX ÜRŽœq!ïŠÝAØ¥•j ©hk¦Üî‘W‚AnK˜ÿ4Ýä“ÄP¤¡qè½\™åÊŽ~ûÏ7µ˜×‘Ãôg”‰eì¿åÔÌÐÈÆÐ²M_'AŒÎ°|Á¥·›´AJm£l}8gÔ;Ë$ÿì|äbÉ5ߨ~žÍ¤éí5<œÖ[)G"»ÕŸÂp'"ÆÜŠ- _o><ÒQF㯶ì7i äc[¢ÌÁsY” Oü[›WËÌϨ°¡4‹¬iï+ \+YhãYY&¢ÿ¥£ÞŠ-àÌýÌózÄßzÜâŸ8nqëAwzÆêWLù äNÙÜvN)±Ì‚< 9Â*(ºŽ™wm_6„ˆuÌ_ Šåœ|­4Í,•²þÖ JóÌùò(¸ëeÓÒ¼¥óÒ«[G®‚m6bö—€nõw³í?ÁNÈ^˜2ŽX{ ‰¹‰ð 7½ño«­àST˜…yŠsdËæÁlÄÞÔµÜñø×ç^Óß&n“"_%ÜýœŒ™`U|0ˆ/j ³„œá‚ìWÞ£Ñmd•åë?sÎZöõ˜r#áe†™vUw^[X“]÷íbÁœï§WÂ#¶Ÿø³Êœ‹%œÎìýdˆé«XtÛ £ž{zA`„ò̘ϼ¾ô¿­Øv.;¢ JVžån«¥#°rõ endstream endobj 33 0 obj 1728 endobj 34 0 obj [ 28 0 R 30 0 R ] endobj 35 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 34 0 R /Contents 32 0 R >> endobj 36 0 obj << /Length 37 0 R /Filter /FlateDecode >> stream õï`VÛ;sFÒ£æð˜‹2ÊÀv,È3c±¥[1< Â;Y@âö9©fÀ{} JÌû®:x…/h:BzÅ(qîx¶JJ¢·"V ‚H|PvžNZÑœ°ª:(2Zuåç‚aÕǘa :¨£¯ÃY½™¡¡©ƒþƒáö0K<4÷Dø )Œ.©Õzw6€ŸXr[áx)xk \ŒOœÙ¦½§ë³ÔXÕ&ÌÒT/éÖ¼‘í­—}s·"`è´Ü2ƾFSPŽšãHèMo4ChƒwBw –k§õÛ¼ÉæËÛ×`Ί!fÒJº]òÇFì-Õh^éÞõ‹ÄÎÂ>¹Açao\:þ_P(¹ ’$ˆGi=ZÓ~U„˜‡K(NØÀžžþÄßë™ùŠk '’Rxé/2]‹¸Æ¾¨¹²«§0–Q†`Mã: éüáy®ƒÆiõÕs¼°Ž<óŒí¡œÐp^‡j‹%ÙwƵ×ÕXi˜ÑâÞ™±mòy ¿cÛ²y¬¶hí0®½'0Í˜ÚÆmjšØ~Íbè'ÕäÏVý0DI[¼!dÂܼ>¸Öî˜õ~…IÞWÈVˆ“´È£%â+Öî6 (DÏ„Š§+¨ÊÐ#^Ï©’ÀhÔ¯Ö=!?>,>Ö¦@˜2ØØARƒ@¼ùyñÈÐläpø‘9ץɈ뉥º#2À¶,š‹¢Ónh2äfÊúcuÜÖ§OK£ Ï•”äð÷Zµ™Ô¢öo ÂÎÿÐÐ_Èí Üí»å:§'¡$Q¢[ 3£q.]ÏÚO ù ¡.MbÊèJ½:N ‰¹ãe[å°.±DÈ"¿žæT(;Þ&t"}gçÁìÜ3ìua„Øì%†#ËŒ2ÿf f8$¤‘aÓƒ@ä[­T3,BÎÌ0{ ‡g ;VçÕ(,çÏ’ƒØý)ÏZ©„"Cˆ²û ÜÆ¤´Â×çJ†t²¡!…%>›®±ƒprÚ¼gG¬SWâXTìë®Â š1b½?š]®Ðâ¬6¯f”TÒÿ¤Ü?å}i°­Û•¨ÿm:ˆ¦f¸9lRo7è›àçaB4îýMØ®8z#vŸ€ñƒ§½B%Z8RÌ( Þˆµù[K6¡9a4Æ/¦Þ¨ê­nè cЦ_9BZû2Þl«;Î$GÿñÌÚæUu†öô³Èzí± ‰<®2õPU.æ1úáÖêw/Ê­ø¯±|i_›L è…o{öÄšŒ7 ,"Éä«jÅ÷™ü³æÔÝ8yýô(4q^% qÒ¼áÔ<¾oßèIëMã2Ã>êÄYµ(†›Ÿ)$¿”ÀÒÊÃpnD¤ïA ÇS1˜,–W-±0ºËwÁÑ' dßQ?›pí5~Vkj]åbpF‹ÐÍcÌPà8áKQ|©ŸüŽzËË ÀåºðU•؃sǼßÜ»OUIŸ/*'•~³ZBRœÁÑÑääŸ\ÔÒBNÝD¶<©îó:c)Õ¿²nZëm݉ࣄnÀà6ºÿä?e±|e$‹; ¥órNBЮƹ³¦6¹©Ó“Å*!lv3:¥#¸œ8Øø„º0ß©TêD¼v» B ¥ü2 (õõE\Žní·‘_RCªi 7t€ YcÛ—w‰],ËW.Ü€²ëôlì(v‹M¡wK0Ñ€±¸)wñ|ó¿œf÷ÍèœPF"@%é‘Ê:„6ŸÃ±Þ¨µjŸ'›ã®–ç7‡äj’:á‚Ê3¥š[5T«|í3¼°m¢I=R¯+Ò `Dhż¡. 1ÚÒBÝÍàRuÔä äÞbùŸý…·îA—4ä{¥iü|z—)ÝÍ[´jÊgŒ¯bEÿÆò̹•¡GpØÛ®´÷ýöêÚGž4Ëòµþ¹£öÁ ^ îIˆ¶š¾‰,–¢L€J¹å¡é†7RNì­uzü¼Â()^FöÁ×ôôe({¼qß<|j¶®ÁD˜2»ø‘°"\GKMæ’ëÓy•¬S¸!IªXnÞ÷6m=Ò;¹eÂßDᷜūä€yN…ðF=ÖÜ刂MršCM> XOä(‚æg…}šœï:ÂN·z©3°€êu±¯u¹½9¡>” ˆ z°ÞR뼃€ƒÈ•€à{­Þ©gßz)´×Šbþ÷OãNÿgjgþ+„MæðŽ;Ãyƒ?’‘û·'«@“àÀCEŠ«ÿ&؉úþz‡íÃþ¶¯žŸž³HHoµÉ*(Ô—è*Ù¶Ê U"*ÉP½þf<%=¾&†‚îÐÕð¦¸À3® îÍ÷*ùb°æ§fcu ¢•ŒU¡~N2%”îdA·’2Ü×Ìò5™AÞðªû¾H¥áÅ{þ`ã«fÆ>MX¦ .€Æà? lLº„pGu.Ü(Oh_† {Æ\ÖBÀMi âk†ÛpYñirƒ2[¿[®ÜžÂû–[ ±—àQ*`'U]b>*âÏØ¶ê@6?í¾wœ·]°è¢%ÈE™^WG‰ µzÝ)!F ˆdèÑßdݨ ?ª£ŽÖêQ®ñEÌî˜lÊMy"±µçÖx ½ÕüËÄÒ4lHhonlú¹^¾ 1o†¼ ÷£käZ0r‘ß6QLùžÏˆ«·¾‚·26—s ºLt LxµmkC‹Ïù²Œ¶ž”õd«…#¦K±V´<Šq ©E0Kƒ±É Ù!ÑØ s'®ÿðzõl£‰‰ˆ fÓhJìÅf(B~F>;…þz‹ýæftkç‡ù™á+u=€t`{ì1 ³WrvWï¤"F`ç+=âü.Þ½üÄÔ¯àNcÜsíúBϦŒáõèÞ§Ç6œæÂl½¯{*<Ô5!³› endstream endobj 37 0 obj 2368 endobj 38 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 36 0 R >> endobj 39 0 obj << /Name /Im4 /Type /XObject /Length 40 0 R /Filter /FlateDecode /Subtype /Image /Width 51 /Height 51 /BitsPerComponent 8 /ColorSpace /DeviceGray >> stream t[¤š—ýF'j;Ú±=ðjgxñµ–÷Bö+½ÄÑØQ÷±ŽÇ4³°³Ñ;ÒÖßøêKx¡T[®Ÿñ¦”P5lÓ€¬P´º‰$µ¼ÈØ.€’±OÏýŠËÇ?[I—^Ð_òïûH¶‰$ÞˉE£‘M †9Ï áU“¥äí\>ã¢G•Òâ>10‹"é ä<î¾µ}-Ÿ”:ºÔÕ;z ö@BΦvýlå­ÿN·GÚ‡x¥ö²pIÝcTÐ=täÍ> Ý g0†;¢àpEGw%NæÑÜànï¾”õÿk?^¯*f·µ÷A÷Î>Ñߦªª|Ǹ5zHšwwhC;¼¡ÍÎv‘$NpœÌxŒ¨Ë Bf7&#ín>Ì#C ‰|r©iG† ´§ µ¹PÕîK@݃RÖ‘§»‘QÞ!5U§î¬ Úsam?4|¨åᣠ…ê“z1AÐ?gb™Ùå“Ó9¹ò5 ÿæ@þ (#9ˆ MYX¾Á9©B Îû½’žò>–˜ °ÇDR+K@» gÕ<“·gôù.}ÿQ!€½dÌ„ÑiÚäkÒ°ÃεŸûs”Æ 3ֈئ–µ®%q„é4G £O‡ÈQER«“çûï_Õ÷ef?hå9à~¬óƒv˜.¥âäÐ:êìTtkr€#½)Gg"¬,r¬ ¿,–µNÚ¥øO& hc_(Û¨‚³êß²/q25èÎÜ +coØÚ_:©ãà’PCîvŽRGöùÌm9^)¸2ˆê£y~÷i†ö‚4j$*ØÏò€&épUµ¾ «–i‚¬ü 9nÖüõúKz 'ÿpd»ñ€w åÓ4Ðw \ïzí3Co>B3«!›7ˆ D8/×ö´žÅP‹ÆÞF:œá, ½ñ<ù4 ý¾Ìkææ¢Êu'_Hל‹ JÞz_Kãñs#LŽ“>ËÇÙ½©{¿Unõ?„úÖu¤£¢ÑN]U†êÄ|ü;õ£ZýÄ $ÒQꯕ{cSz5(ÛÚ]Ÿ;.u…J[ …¿}Què壔f¹;ÛÁPµ"“YRkyZщP­žˆ9Í ±¿ªVŠL.y!š5V#LéÃI\itœ¡¶ÓÍÝ×»POaK'Á@”%çþ7¦¯Æ‚€– endstream endobj 40 0 obj 1040 endobj 41 0 obj << /Name /Im5 /Type /XObject /Length 42 0 R /Filter /FlateDecode /Subtype /Image /Width 51 /Height 51 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] /SMask 39 0 R >> stream /ï;®b'ö¶b€õ¯‡“* éƒz‘3i¿@¬ëŸ$–ª'ÇC` Ž4ö¼ä¦ï ¾näqAU¨4­. 'y“E@û\å TZ{uíaÏ çØS,lÃh!L^`A|ýøßßòg%v/ êñTŒ3ü DZ•àÓÏÔ€a6ËãÆ( …êù°®ºÔxyq¬t BïùpÇá 5)×Cåø¡ê·=lbï÷‘¾b;Šé}Þé]ƒ›8MÔª‹Ÿßè0@öÝÑóñKWä“W+ã¹<›“’W_º( ,dƒõP‰U-[uÛP›]H¤ûïšu¾!«RTtõ¯#ŒñëVY”P(*z¼= àÏ'ôØÄ¾ð–u_@!3ýÄ?Æ/êdubê~ª®Î8|#=G;qó&šŒjzy@mx@4Ý™„[‡qÔeúÕÇÂÌÄ.ºg-Ð&£¸[å}Ã,CôGŠ{úqèÖ‹ðNÉrLÜöþVrGt‰É£LðUMTW7 øƒ¿–~_S±ëLª“µ‰:k±–VI·#i5?D_ó•Y›;öÇÊ(n¼oLƒ¦¸•3 *‡tüm|Ž?]=­fXP«’è¦qŒÝ:Ÿ8íû·Ðœxç¬AôR˜ÑÞ]éã}g¿U2 Óè³lù™`,‚HÕ\lö;¥ï3ò8~„„U¨*êl‡ÝvF ¼ŽJ¨·Ø35*ýáÙɇ/1ðq|ã­qŠìTn2÷÷ È8r—_ZOP­ç5àçW†’/#µ¹õêpüíéËr±uÐ9œçr¯_QI3í?œ´÷^”©›¬XÙSä²Íõ«ýå –ÖVÈC®W. èðnMý¦{ÞPûVW©1§|МÛC7$¼¾–/êºÖ+j4‡î!ѸsEÆ¡.ÕFzq„‹]f9’}³Ð‹j Ëøq.™ÙryÝ@ð^¢*g~4Þ ¢ô´ 'ÛW²ÔËŠt±úHñAÀ¿ñkìè]ØœâŠ6fì½WÑì²h,ø}}‘bCÉ\ˆdÿ)«ýoþ]"‚Í_ÞzÄœn $–B‡k€Èë°êR'[³d† /S /URI >> endobj 44 0 obj << /Type /Annot /Subtype /Link /Rect [ 238.544 703.506 451.57 713.824 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 43 0 R /H /I >> endobj 45 0 obj << /Length 46 0 R /Filter /FlateDecode >> stream î¢K¬z¤j¦èwOBy&C•…pÜ×2@ÍÈRÀºÑôoçI¯ô‚P"+&çξ俈+0éö<ªŸ˜q/ÌFMy¡²ÊúQ€f"ä4|ñH)ã:ô[ f»9^ö¹3r"‹zAIÌU 5Õkñœ´@‚èÑ3ŠúÑ銻¼n\Üù ¶ëœ+CÚ(Ó$­CŠâú‹ù-RÉHóÑAÂ\E¥jü’ü·íuz»­Æo^iG)Q6¿ûÞd‡›&QÒ7ûÚi X'±D +­>w†Mz^”Ö³®iÑ›þ”ÛÉàM;o6 `[5ÝÌb(‰×ܾûªQý,o“ ª*ˆR%ƒG!-LÂ;©Ÿ Ó#²Éú£ „'ofMÑš˜:ñh£cb|"åªKð¾µh•—ð[,ñún›¾˜4=·P ÕAKÎx3.„6Ĉ+«[É}e0`4¥‡ƒÜ)§ 4ÊœJ3ÅçŽÍœ'ó@GFßÁ²#¼t¦ÍgIƈ²Òº%ý"ŸîfQë©níóÊnŽy‘I&€S~×’_ÂòÛiÛä´ªëößz}S¯xC–]ˆœ¸sþuº¸S!z6¦‘ºø¤áÑ%{ØA¹½=÷#Èð­{ö¯ äûµóÑ\»~ 95ÇGù%¸_²'Qžàs˜ dŽ&¡°Ö\Øéù9f¯«•Ð)§KGÌíÏpTöÇ⬠^fg¬gP*>»ÛÁPÔ¡nhôù¿k…掩Vów<'œlGNz^ƒPà«> b¶ÝJɽ،Åj¸„<94IŒéŸ´‚¡Š·GSàé¨!¼rn×䣢ÿÌ'D7 0á)K4GrÙÍœMÐÁf.ùd˜3.O¿³ŠvÁÚ¦Î),‡~«œ<•Ð>Ìb&“ž Vò—ý¸vÇ%äëûõêl(NýOmžð™uáíÀ„FNÃ¥Q…sw¨UÅxwÞ¯lKãz1bcl† 4b+]÷–÷«¹¿.;E&µÂ|¨B½ñͤêfV:² –C©_ª—2Qû"\CáüwË/^å2"ºyñÅ·s ê¤m`ÁˆýiŒGÛ¸€ÅÈø5§]ÔúPnl~úñä=–üZíæ_ä™™{c5™ýyÑbßbô€µsÒAx_sz‡!FjîóPHÇ"S¬È«šOiô¶Îý6¯­•ÀËh¡äžUL‘Zý{ú£ø7¬ë ë:Š]HÜŒÿ#x$Ý•f•c…à¯Qf¤¯t{ª ÙZë¨`ULí{z6äOa#ÀøÒ¬=t¸)¹GG²1¼É Ýëû¿ˆVBÝ™&ç0¦ßwï==먽„Ós›ÐÉFògÞ®aR›€ÞÔô b#ÒÀÕX±(XêW™@G%ÂFæ…ð@³óHÿáX×T3® Ÿ3tˆ¯>LwSü½$KdÃóV²¹|{óõ“Ð ”O ðteÀ«Î/w5ÜãÇ“Üê6ÔÕú ߦ‡WAw23¶k‰^G&s±²¶žÉ Ä×û“Ç1 j}Ѥ:‹Ø—µ¨b‹`‡Ú0q9ÀÀÂ-…~Bž›±/@ë¿_¶\öƒ `µXµP»ãC8qüÓ!°ÔÅ-‰Ëxb_YvÂwmú9 Äø+7,ÚdÝNAvóÑÖ—¹9Œåç ú xmÒ¶«-˃í|ˆäà–,i;sµz‡‚§;»y1ÝèÿoE7"tàœz’äN«ãš=…dsº”¨ô{Ù°´G;à²åCKSÁ‡GnÊ0´7f°Ì¾ÍAàXMá¢rYÏé O]‡^æP´'ŽæyrÚlBŠLiRpøý‡Œ/ <‰ É=úÏcå ÓX_hQSC̘}ÛÂTÄšÇá›vS?EŒÛj endstream endobj 46 0 obj 1424 endobj 47 0 obj [ 44 0 R ] endobj 48 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 47 0 R /Contents 45 0 R >> endobj 49 0 obj << /Length 50 0 R /Filter /FlateDecode >> stream +%üvFž¹jÝ-Ɇ¦D‚²%ÕýŽðw×$»  <ím30ûbc *÷0R{k|U ×p Ȫc;6ÜT¡Æ]±pmz’‹—Ü(\q=×·Šôÿº\t#(ØïÝMq¥hÖýîÓÌþŸµWõx„Ïé5••“÷>J¢tþÎ¥x-ißèÖBNösœ§ƒÉ?Úòî*ß\×”ç5êYމ樨õ7ÈŒ|·ó ÄôöxWz$6Œ|zÏa»×äS€g,Ã!0‚•õ. Ó=<öö%„;«¹aé‘Fˆ¿?h‘©ÿ Z›wÄH°X› šºÕñlF©zÊÔ“X{;ݯòÄC2HÔU±â5™€»Ï•è7€­ü=qÖôŠ-–âtÔ§9Ÿc3³°tq_dRÀhœ5ýΤIqk™.°”zà^¡G%óò׀ƞd߯B–;m¼©l‡|*Þ`7<*÷Ñ`þÚ©ÖÜ&XÞ\/gX£,bÀ#,hÚjÀ»*`Ì "goq;{$0Ãÿ0ÔAOHÁ~î!î1¾QbåB#ÌÉy^YA†Môîò ˜<@.ü‚´JžÙ2< ©Øƒð‚.ÜîÛÁ£ë¬{Í­¶ßÿǰ±H=p4´Ç©@JÄ·…֙婿ä÷¡›Bûû îCÅO Ÿå”|‘ 7ñµÇ*nŸ¦W~ŸR^AÀ±åBýNv˜T9ËDÂòÐlœÒ­wp$–¹½Ý¾»”ª÷°_c¿Úúïàù´Šû‚,„Á“JуŒ¢¦¶‰0FrÙÓ’,Yz¿šÒr"A¢}5¤·ŒTϤÈRh¹PüAš¥å?þNíÙ¹j­IÐÄE©”WË?o”¥G‡žŒ’®_/‘¦ÚAv‹`–BIͼ,7â2áüô âm7ã}¥d:Í0”êš5œŽ{Vå\-sÔ!*&>—Tsຸ¼?o5·œLAmñ ¢²pœJm¼ ÷†ÕežŸü¸Å}…ÑgÈù¶r|‘: ÂªŽ’çà­]Þ¬†÷º¬Jë{pSí÷k}rdãJ2Ot ys™ ¼aíUAµñ'.yò.W  ïÛŽéûÀ` GÁ?¶¬r÷TÀÕÍ™Vìúf±Ä@lËÓC(8ƒÔ—L€2a…)8ô~ñs…ܕԢ*¾K{dðÆ:B*"âJÚõwyÄÿ».ÈÝMÌO,žÿpËpÚ£ÒÉΧéÍ•]Ó•ÊžlˆþfxM{ÖSY‹`<·+|81åF}…ë³ùO*½×¦µÀóº'À¢s¢R™Øõ3Ùj§&ÈŸ±`˜íe…­Wlƒ=Aì-ΊT“Ú½ùÙ”L4Y³éy߉GBVþ´ƒt%ŒLAZ ¶Îtò¢^-'ŸF‰Óss-jø]õîóly±RÎxJ‹XÝSÀOæ »ŠCFgÅ…yO}ë0<Ìô7¸˜)•ßþ(zH†æÂÛ;{»ä}r¯Å„ÃîHŒ÷ÜÍ¥ÁÃR|dšàu ’ÙówæDné˜,u¾?¾î*.‡Ó[,æ}‡iÕƒÎÉÀ ¬¥ÕMÏ_hYcÕ(Ú¸Ó“2gƒ("F\`šXåÞB®æöœmEúc‰ ‰qŒo®¬„4A-u| •·p×äëe>ÔäfdmqÈÞí…ê ˬô›AÊ\¼0ã‰f'•DÿѤÿéàî²(Žè‚:‡C€t8i/]{Ïæa6Â}ª6”Zc:™>Æöé/Æ V='í~w)–ºÞÏÏœÿܯ„Ýv$éHðÌN¥ù# WôæÔvWBèppðϰµÿ'µY=çS{+PƒÃùgÁµx"sãxú¯l̘ËdjþÙsÞP«%RÉJƒThÁd¸Aeƒh+mn¾‡›MPŸ~´Õ!«bÿ™o¬MrA\Ìb±ÍYÜ·`X^ÔL|izZOg B V$ oâóÒ³$&;B)ñÉïQôî¶`jÔõòæ“Þ³ý&3î æ{¦ñ'^ AícwÁx {ÄbryІj9•«Ò£Ó«ý§º§}û7u¥ý»1rû ^S”–÷W|ûú̬-ôgo×CÚªº¡;gýÜ—höð…1‚ÔÔ¡u¹'£àrééÓÉÔ¸ µ,®µ^ï‘B–ïÏ<”øH‰PH,йíw³}*ÿ|Wµsô$cë râ‘»±Óó÷ê!B¦ÒƒKS"^ðßmÛ<²"{„‰4y)W¯ƒŠüƒ8&…vÿU;ƒ—Œ¬ÁÃá=jzjB»v)a R ¨½D^ùëˆÿöÜIµÒ/ãXïM½];Ô›ö1ÿmà0Pd{¸˜¹Ü–Ø•žÚ[(`A´*ß‹R¤:ÅrágA÷"‚Ó•5P’atuî¹HäûÏ‘`Œa:Õ|²ƒ÷*íR’ÿ3Òx$²©ý§Ç5ÒÝV€Ñdï›e‘LÒ3l Ζ 7Bƺmá'Örèf nÊ;4×h>‡úqaÓºh™$ãyíiš4ž0 pç ÄHì9ú…·ÅÂð ÐV5°7ƒ 0ûwC壢ø)_Eg|ó„Z˜n‹ ‡cÁ+…RbEóÚ•'[R‡MÍsa¹ŒAŒä,jr;¡AÊÉ«ÊÛ––û³ Z4^C7‰<ù´‰;ø²60Ñ]W¶W/+¥GÅ,p<ò[;*‹X¸qÝG3ìR4Ï*Ë8]x¾¾nÃið®Vê>âVp¢× ‚ðÆBÖ{TòÏ{Ç|2¥Pþ&cŠ û±‹ÖN¯ebm%°ÙÕÁc·‹šÿsnÞõ4¯h<64bÁ¨ó½L„ɱÎË@} ;žQb4² ɺ¦ÿÙÁÒ€zV…Š8GsF2H’ !XÞÊ*’ c©#8y¦q›•`ÖPÃbÑ|¨ÿt[EU›ñ’ñ¯R‚Ó„#ñ5þ–fIºmØ.¸0hùV©MÞÊW‹WÓµpÄ-©4Ókvª¿ZÂÿý¼ÔL­|­Ž"ÐG6“ÖnéúŠf3è÷㨥`Î Hq^C\‘vÏÛu?®%¢cáNÞ9¾–ëí;ýÊ>‘hÎòãøì~ÓS¾°ýéôu®åúé3ÍCKTQ«+^#FUáŸÿF™Zæ™_ÕÐ2 ÕZ»YË?Bœ´zl:î¦Xt¢b•µ³`£¡ƒ_HØùôH«ü„è¦,¿4€WžY¡.v)k[lÛü&ÖåìxL+‚²î5Ì~¬’šÄã0à÷†ú±1¾åxÖ)Á¿M”Tôhä]9½!ž¶Ž§MT98Í'…Îε«Œ4±O–éQš+mn|\n·ÀÃô–Y0šìxqatW 'ì?Æ„— ?´ûÂo{ÍÖÀI,GiwÆÚÿK+/¶»HÁ˜ÍÇÀ†«Ûá<´âu³Î6F]<àEê&=}£ú@‡š ÚÖ„ò1¼~ô„%ÌGâ›påsÚ£Tstu-¸¬”þ%"v³˜•Š$¦¿¢k-éKŠ´¼W,‡—ÞØEµ3£ÑÃ51ái±4ÏóB²G¸åvìy™Q²[Þ0Y«sƒ «F§?Úk _ÖÕj­ âç¿—w:D\3ÈrÄÇÌ J'ù¼Å’9a’!«s$#ß”…¢ff¢ü2y£/~ÉeÁ>ôdr½+N‰vÎ7Öâ©…³€Hñxás p)ß/,šÄÍ1ÆÞÞ­ÒôËè_ >9[Q z¾° A`.À»Þ­à>>v&—í2ö•j÷Ùi-ÀØÌO55ë¾îøŒbÕiܨ­æÞ%¹+P¥L}Âoï%Há­âx†òÂÐl§º@;ÜM_Dë\d¤ˆ27'º“°¥s‹½.e0n¶èRéVQ¨lŠR”ÚvGce‰#<$zŒÄÖ裛®|JËç4[]ÈìT±xU?`<Ø-êZ縠ü4Y•…¢ÆåÙ=¶NóÄšá¼+Äz'@lÍ0°þ7)ñk1±&éïë3áßVn+«³ó•r€#ôNÍ­™„lnö ƒUD …Yp“ºøÊB+ëûtu˜b“b%ãx6‚&ïÕà}¤§;òPO±kxBo‚›Ùa ?™w8„ e¼ˆNu üœÛBê«™ yÓÌòI•“ÍQXSt·HÐ6ß³î3èyÆ/»y‹W#%jÉÅNó‹µ×ÛS¤B¶Rã‘cø³]¢z£pä½­–D4xû_dýÐ\ÖHT…ƒV…áÁ×xèÇbÙ å£Gª¿€ˆ3˜Š’£ÓsIµµ6”¼ÃUshÿ¡Ãÿùó(u*v³agÃh™ÇHE§ñƒpI+¦ñ˜³Èž’êÃŽOÆ=> endobj 52 0 obj << /URI <950EB5474FADEA423324B566F07F547B85506A7A0D57FADBDD74FB36CEC08972CF9C83E9FC4E4628E048B1FEECC989B614D386DB23E41A01A430AD934B15CBDA1EACC1A9BB96BBD8625637F0ABBE31A7CDC4163064F7F0605413B617D83DBF94> /S /URI >> endobj 53 0 obj << /Type /Annot /Subtype /Link /Rect [ 115.786 143.113 165.418 153.431 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 52 0 R /H /I >> endobj 54 0 obj << /Length 55 0 R /Filter /FlateDecode >> stream =áß*R‘µè"U_ã“®ï™Ì»¶v¸ç팜«{aþ€Ì1r5‚®Âã—Á[Â~3c]p„¿±½*y심ûå>1«4Ǧ§&±»ùbõÓ·3š¬,–LÊÓZ%ioùͽ¥Þ0çÎ1W¤´Pt€ú3q,d ÃY­2•—Â| óµèÿà°Å·¤âñœíÀØl%ãÍJgèKЧC0à´ŒµO ¾Æ·)6ÙiÏŠA¼¾sõ'áE±ÊôÉ›‰9Mmª@ĶvZl?HâiöĬÂuòªÒÅú‹ûdäåßÚë'ÆY)cò¥FÅy)Ð,§Ô9 žf_ÍÞ¬_ó.µì;>½ØÍNwðÙV²ž²ábÝA’¾Wø/ÔXâ°}ì °ºÉÕ¥µüæn‡OšLnˆä~çdú2Ý–Ì×¾÷Ø¢Fi¹V£Ó«B5=Tfé±E4Šœ³§pJ'P´ßÿdËé9°g¡»ˆ#Ë —ˆ¹°êŸ¼Ãdl8Bú@` ’ @ÖdJÈrE­À¬óœw%€bå/;…f(.­I²ŽøÄgÆÇžBá¯1ØB+×(xu†mmº«‚ VrÇÌÿ1sOhÖ5e[·ÉsfOª3upkŒ 3xÁƒÆ‚”ãƒÄÊ`vŠÃ˜<)Uñ¶Ë××m‹«ñ,Ž`ÿ 2Xüˆàu™{Ò•’;ˆžÁí´_ó©p*ùÀµ‘ÙÑÅZ-X ™–aè²ÁíE¶¿îÈ`‚ëûhŸTsËÑ`¿œ€ß¼N´üË?¾ôÎãùkîA0ÿ-¸~æd’¼]vËm°”ü¨’`üú´=ÎÓøëÆžàç/Ÿ¡U<8ŸÈ$ÊKÃ\âCù rék' [³«ïçTþ…Ø`~ºè‰ÐRer©‹œ ³0^sJs÷Ž“Ÿ-£,ƒ8³7ÈÞFæ(gi¬ŸIï F”R_o2 ´guoV¸LÔúp8p·ÕËÒ“ÿ«©_+Q<—·sô@ëýDVðŠr SJLiN£Oµ%L<ù¨wMÎLOÙ VçJì=VÅÔøÝmC«z¦J“õ{›K1Ÿ¹‘‹éEzð1ˆ:YO³ÍØ%³©I9Kx@[ ¹6£V4éYa&àL­e‡ˆ«òÓä,VNoo‘qfsìÏZdj…r‡‘KD8îœ#2fñn90U ODÕ%ë,ÙœêÎüÑßI±Boà÷sÅ–ûÄ”oÂî®O@ äÁ¾Õü0vG8}äbúåY„Ðlk\pWng~ oDç¶•@¡±àqLÀmÿ qüžX•V R°ËüMçi| 6è€'c÷µÔŽ:hFÀ#>Üf¡¸B¨ Ê`¿ÈZ6íÓÖÆ:S(k’â%DMŸ–%šhÐG‘°·î¼e#ˆjMº¦¯g*&·T75…¼wg0ï9NV8k7`·éÜ,ßr{«ƒ«UV¤Õ”9ö\´_DƒaòRŽÊÄ,Å"k†}ºR¢ƒ9îŸZv`3Ñsaü|J_4Ýzc* AåZ)ç’œ¾“f@ËKþpË*·ð©i®[ÉlA1{*¨ñÇÀqÝ»~•íMëã…paËÊßpf *Ùfo‹“?ýlßع&sÄpµŠ¬‡¶Ô’hM†æ ¼Øhçt¯u X68z׿/3ñ#p°%Ñ1…¼ûu‚c«à!O]´ýþê/1=L8ºˆ½¯˜yÇÚB¹¾z¿$…Ë¨à³Æù\tr3»ÄoµØ©Èó¬Àlè'ÿ¼_øJ1§® B9žÓº ž½°Ä}uh^‹Kïç¸ù7+l¾ N%fE’”y©·yZ°cÚ½¼{WÛe#Ú•^AQtÏJ‡³$ow8¤Æˆ-l*,kíö*rýˆ6Õð÷8ƹnþV×$²Jr@º  !êת°ý™šœâë3½ ÙX+=’Ϟ⹩⽳)ÑÛýúd0ö“!ÕÚR“³^ú¼nÏJ ö_dú;„Kâ ‰ÄINþ¬IM`p”ào+迟“~Í!Ö›‚G©î2™÷ks¼˜fZrT®ýÐ1F;Å C­¸Ã6<ßæ7O©\gcúÎ0‡zN4µU^°†Ó$gr'Þþ z­¿áÂkÄßì¬}Pç‰NOì`¶‹Hh7“Ób‹‡õûÒù"+3c3r^ÛÕ‡ÖGç' ÛH¶1âÚ¤G_C? “ÓäM7Åžï_ô[Tªºjn(Ü›‰›Ór€¤EܬŽÇ}÷òÙç’ `U¸3fƒ ¢¨ëctV×’[´uˆd”0¼û2Ú¼´W$¿ z@®9áöª²ò×aH"áMåp¨Sf7ɲՓнÅÚïÞe• ¥P´9Ψ‰{¯*·áŸN„#hÖúà.•,M oÚH¢#N{í ¡G7šñv‰6¥ìEÁãOݲ´î>?­7FÖ®«ÊÝŒ[`9@%Âå‚’VœB‹lØ—~5þÙÌÃàDîÂ}¹®(ªFÇÚ•ý 3í™Z—1ÐÞGWÒñÒ<2*îÆF£aK«eN{þ/iÙ:h@_:{œcstŒÊ‘Ô…‘‡ÔŒ¦«Õ¥åî#ÎóÝ÷+ÜbËñ(~‡Ü‘'íê%lçì<ƒÝè©M6âí’&¹€¢Êø09R¸ÍÃÒ •½Qø°Q‘Še‘ö8ßí0T@½T|ûÊÅLÙ=DצQ—?¨ÄsI©­O„Éf«éëTQƒ(xÂÏÓÀœÜ‡ןZ/"VÞºâ­ñÀ-ßœ£z Üq’ÖܨÐ4–,–±Õ" +„nH=P„Vñ¼t¢Yu2+c¾¿`€çì‘×/Ï ½9ï ØÿqÎTA‡|Ešîø?0-I±“AƒôcJ£È¤!ÆuOÎ"Û0÷/?]14ÍXj43Óù†O6Gš¤ ívõ©¹à%cF3)Í_üL˜fÿÙηŠËÈÌ9FÔ/Ør Ù–3¢¢&(CŒµ–Èð$Æ#µ0Õ8ý0å5¸I¤úÆn¸t1 Øc_ ãú¤ šöÃó’áÉO–)ì4»¼µGnó.$ÿYQ“8ƒ€`ú<”†ï½Ðü¨µ¦ï5vÚ¼¸}¢Êø9ä’gÄ ®Xµ gŸ)Wå…bÈoêã  °ñ¿Úû[Ÿß§RJ[9Š–®2¾|¿ª)ôaa‡¦?qƒ·×ÂVKÜi° &Qþ=*%û«_þÉ”ÉÅÆ±¹5 ŒóÚ<ƒñž¸Gr[ÎãDU9³&Ôa¶ç¯—T ®;N½ñ§ò~µ±¦:å½GëØ<„ŽDÚÅ=ˆ’ú¸pœhaŒ”º…Pšßì&™­bñWÊã ô<}j¸É„¹ÝS\ðj÷ç’Ö4ýõž¸ì<û7"P£« Ú:âåv ðõ¨¬Á¢ƒ´Ë¢òZL§Ž5ìûúŸaз•(†ƒ±¦k=ª´ŒŠ‚_Fþ1„zSÒÐè%¥¿²J7•Ç›é^ …»PœÚ|*²Ë•üw!'}Gr¤^÷,GWoH+¿÷„bŒû°½J¾\'é‘“jj4BÆr2,XÛL¹jÑ<>k½íç[an|¯²ÅqYz1›˜[ò]6³´¢…踰0ˆœq–*$÷Ãäk{P5AÒ}E@£&ñcn墚< ԆšÀaVM¸ò˲ad)7fÈ䘛¶+qüîs%»—}ÌËòö99ñç­DT•~Ã’ã&[[26ÌìXs%Î,óèU´°Ô“ éø…Á‹ü+Eqbb¬^œãl5}TW,x”±ǂSjAuqöy¼¦s²5-¨ÝÙð_ùMe}­ŸeÇ–@Î* 5|òNœ´þ¡C²!Ì©Ë00s*Ÿ'>Ÿ¡ÌÎ;ƒçÅB×IJ»7õŸšn«EìˆA'~·`P»b€†+D Ml£ã—“©´jW”‘€µ2 dí¸’ÌÖòg)Ò%ìQ()Vfõ=_Ø +< äøS¢ã'õ©m“'€%F#£veînÁ£ŸCt_Õjwâº%ž-ÀNÔˆÖ3±c`ÁF„CƒÙ\á :jÎ9¹±yH0TÕtxí7íV.ŠþÊ2‘œ¨ªüVþ-²~Ì`¬.%9nØ \Œá®Ç†%ï“ܹ0E[º›¢l`Êa´:cÌÓ OÑöžèY5ŠpTÉqo-ß, ²< éÌ1[á¾àIîÈ9‚¹H {v±±Ùr ÿjÿÉä>ë_êê~ –DV8Õ*uëÀßQ¥‚‘çoì8Òx±’­§lŽíð@: ÉX)Á¼„~~ !Dòž šŠ‡™;°¤Æs+ `v£r\èåfâ'5ZIËWNµ!&Áð€n'fëœÀ@Ùëü(p÷÷Ç>ûÕÇ&ú‰³O£ ½â{z^>…òà7û@f‘üàÁCU²Äv'.¡¡ ·¶ q­¥€ŠO?[@múFëä·2É8wµŽ¬4–j=ÆñHOù-]0æKÙ™4ÙAçç¼sþ=ʼüµÎTè?´„C¡jd¥˜šÒÝç¯)°,wÿ*,+§¼_ØF2™è ô¸»ã£7î¬çÕtÛògPÝ•“OZÔÉ.?ÎÕÓs·ç–qæînu× I&œÍ§ÂçírO%L*@d—äc <˜¨š¿/¢}{ÃYÏ×èƒ}¶¤ÊPPc]µ;ò†F=OQE8¤‰â`$M¯DÔëúã0Œ‰º4X r·rP)Wþ¨ù€ô¦ÏFž`Æiù¥{IuåÖùÊŠË!MÔäër–z7/nLÙ¶c ¤I endstream endobj 55 0 obj 4592 endobj 56 0 obj [ 53 0 R ] endobj 57 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 56 0 R /Contents 54 0 R >> endobj 58 0 obj << /Name /Im6 /Type /XObject /Length 59 0 R /Filter /FlateDecode /Subtype /Image /Width 1337 /Height 115 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream Ì@P2¹?yfq%Âî|s@Gç(ý{›OÍÕ[³þVñZ¡Ü`¹ ƒCŠèå.UCcI.zú1‰DzŸ³÷-‚ÌÿÑ/çq—¾8 Ö}[`Ä@,×#„./•lI×§GFŸy¡û÷jÙ9”8eR`ŠT­U® XTR*€Ã@™(s¥4›Zù+R àÿµfXÜÐEÀlRŒmÜŒt_ôw>W¿ ˜ NÊä†Jû( ¿:3çDU¹a'ÞV>ì€SO?ޤ£úlŠ {Ë‘ŸMI\ˆrÅ<¶õðäÏY^p®¯ÒXéÖl‹VGÜ“ìEÀ¹È^M.NãNÏH‰’RDƹ3CºúÙ¡`fÙpVv6´™~¿7ïeè8öŠLÒ-¾¤›”‚o‡xª°qs>=Dœ@áæy¹ÀuYlÂZC]#Ý‘”95hm‰Ò­Ùº}Ý'ݬ<ËN†È“ wX&:Lí%—¥¨êkœÎ B‡4yÑakL¤Çï£]ºá²*ý`1ýó°6Œ{Íúå:‚£Þe錗ûcAü`˙ϖ•ܽœ°ÖM5‹‡.͆6á®È~¢&˵£Ü&1‘eºrá á$GÇꊴ F-l…)zFûü®³çbSDţijÕWr‚q‡, SI܉²qQ• >Ñõqtµ7²>MQÂ~ ,ñÆ€=63Ñ]^é.“H¢)¯×}c§Ëù-JTŠC|ÝAëàt€iWö/¤ÙûsË!qéçðÌèg„oÛЇ…'sØ×ƒöŒò¼3WåË]š¤È¯ày%}cËe¦Žx݆*/ABZÀð õú}CÿÚYÄçófà‹ì×›ê¿Î°šÑ~JÜ­…]»Uqü÷òe•ñ²XRÃP¾æ_.À`‹Kycî@’-Ùj2ÎFu5c¯%úÑY¶ÿþÒ[äß·¹Ê¨±°¼“³»?wiñ±ø_.ïÎw×móÑ#Ô³©§’ÒL;²ß¦a›&¢ù‹¶Xh¯CÙãxs>àTY¤ù #¹tÿ¡XQo `‰‘˜z5™Ø|T,«>“$áƒÔ'f„ZLì¹RÙn½¥Ó¢Í#„’¨É/ ´!ùäeDýXã­’›áUšv†—²qsE¦­¦J”–äСŽvéÚ‹tÖJÑ.:¤8cƒK(W‡lŽW»¤¥¬oÒ±@”5âL™,FSÁ©=\%o`¬s$ þÆ*Üâª^ÉFßëôu:¬ÄJP ¦¹:=ˆ`é¤kTÿ4ÇXˆv)—I™=zþÀÛETÌùÜXÇ [­ýKÑ>ÇÏóóÄj6B±§&œ$•U)Øà¢Ã zu/µÑuò«&ŠÇ'¯HS ÕÈ“QB`ê鬨:c/}=k¾,¡³?ßÄΓåJ!ŽöÓ°QD¢û÷­TeK«ò»Ésluá¦ÉÂÇ7 îaÏÆXiy‹Òl9½'ü„ß…bà÷€Ù–§¿›ó'Û%ò…>f„ð²µ9Ÿ5v¦^|½¢ÅKüË\ L?Ûßú¨$KîŸÉ’ƒEFY@!\"™rœ¦ô+šß<¨VG>Ž~~N¯,}A“žv–8éN ñ¹ 7zL9ßœ²‡LY̹OÞ¸¹9wú â±2°ÃJ£šd69gÛbšvÓ¸mcwf³~´Ë—·È*˜?õiÞ¿í^„K ( Öô}Z÷ExU56ÅâWŸÀ•cüW“ÛT èbX=’hÆx]¢CHà<®—ˈìn¦;j Š F5ݾ¨ÅÇ}?½þÆ ý²}*^}‹Þ@p¾wG’ÜfcÒòž¾<ÆBt—Ó°(½%½-é=´:ÿÂೕƒ ìÿ øô4G«yµËu‰TdÌE‹ääx¡RˆÂüÐ!) êÀ´ËA±¬gÀÏtzÚ˜f,ÉÄ“P¯@Ö† 2>1ºÝFîlêsœÒáhˆo*J\ä«m%.o*ÆÅ:¶ÓbJçž!Ûš2∿Æ×â­I«V–4ùyËSõkõÈE¶iÉ+õs~m¦Ïr¼ž¶8Ï‘%Ìú÷†«=«Vñ‚AW†Ê’ÁùÊcPþízo‘*¼"— QÙS?`œÎ©bfc/怅»’rÉÀhÒnÕ¨75€ˆ‹p«ˆ^Ƥ‹»7Žq.rŸ¿æQ ÎÔ·^öžlcûý\ÿјLQ~žkµy  !l‰•îÏ‚¬ã­ëÜÚ)hó7òUª}ç¨çõ’Òã+£ÿ„8N|2¬—kQ¡<­¤¹Ÿ³/…ìp^ó7¹Züœ8J‡”ÂPkE»ûÌá!Ððk/^Ìnà‹<†÷¼L³«¾Õî™4åfÂf‰ñ$÷ùñ2üÞ+t]!×»åèâðËï?VJÌÑM#@§;0$¸½:ã¡ET>¦dš./±+óÙyïúh0¾˜ú0Mx:…ưm°tµ‘û¿Ù¥â5|^BgSŽò™Hé?*YÙ"ì/ ëV”í±U:ÅpP¼ñ3Oã}9YÀ77Šï5F—>ýÝÒâwñš mo¹Ô‚¬â³Ë°F^îo]ª¦.ș݄¼϶a÷TéÐÓnLÀçWJµyrÍ׿¼ìÇÊ-¯`vq"¬:z…ŠÜ œ<ùªô’ë)SÖ7ô¹°Í],]œ&©Ël€îìYØ‘µ…YAÞ ¬£~‰ç]b •Ϩ(À»£éçÌÖžPNC¤¿¯ÖïÏ‘ìù¹Î·˜á¯é‹åÃj¶º M6§®k&c<žµL 6yÊQ{@Ôfˆ„f‹,ðbçö×Ñg$éÀÜrœ¡AVRq)nVŽJð¾)ßÝò [Ž>ª€‚7!œPºä¿—¨öq¿¬)ëjïMæã¢Q”Ì_«ç¦ M:Q18#·›b…¨ååÊ MzÅA3®&]1˜~ç[9%T½Þ„Õ9G9jù«„\‘Úqp/Åu~I¥íãˆS®i¹È5v: ÆûBÌ^MbMqÎí‰xâV9ϱAé0‚ù >StÙsòœ8˜ɹù‘2T Õ¬Ÿ(‹þ»…Å6™aæ¿‘¬%$«;^ =b¥eMe@9˜7Ê|“oDL•ÊkŠÖ±J]J…N-3o­•´õ ÔÒ1• Ç_æî”¿wywH‚C¤¥P²ó\aè¨~Ï<¥]7ÛN´ïßöÚü4¦×nôÙCܼcFèoßU97ËÞÕÇ¡Ó[î-þÁ’ûÀàÀAÌÄ–ôU6¼Dé«[z¼úB ›ûàÉGŠzs‰Ö"XÌêtŸ¼H‚Ûœ JÜô»_«6”ŽNÿ§bÁ8cÎyårà…¶à\E* b²Ë¿Ùé¿ÃŸ(L(ç +æ±x!X9ögæDhðR=•0ùÈ ŽcÒ"]íä]ERוQ¦Tä'†>žç·“<›—ë!ìšOÄžh­XäÕUÌÁ2,µÊïXFŸ]Uv“j)bÅ £\mþW%_ÆPùæ÷àE®©£º½ø‹Á$Šol£˜¼Ò@¬¨TéYt;ûóc&uLiíÈ©Z£vS‰ÓKÅ~µ':ü7r{;æŠÞXÊýP—ö“r}v“ï€tÐ$­—¾ôøu§IûO‚¦ ’7;ÜÎR ¿í›*þÜ\Ð'°Cýã'|#ÝZ´²ÉÓÈ·i:øÒ«uã^©vXxíì>ä…³&´ S«Fçr©eéxc’2Ï NK#ç\eåÍî`nÁã/` t†ŸKÓeìÚ@³©8·²‰«ÓjŒjΉÇp„,ò9.$ÿÁÕ^yvýE ÈéCâH­Ÿ‡à_Ë‚ƒ0:Elje”kÎæN&x5Ë)³K¯(I©\zÝ-]ÓUnÕÙr]/¯2Ⱦ+Ы׀cVæKwÏ+@÷8Å}ÜÊö ΪébiÒ®-þvjžžIAÍÆìm¾©ô„Ûˆ‹û²#g%ÑžS¨á‹„U5W”ˆ°)Sé^Dª–ߦWëð86{‹\¾œ\jÆ'[á.›ÝÂn‹çÌ`>kw‚ªñçŸQóZWŸ¸ ­›0}ºuk`ÍŠü0s¨ñ†’ò„tйñM9(@R§Á㥳½É$¶Y’FPsa÷Ë`ôá(¢ƒVŽ›Ä÷Ç-mÖˆ¡qÁ <µ wxÒÔ=nPñãݾõHéc¯¤“iŧÂ"ŽWŠ…ùpL~e¹±ŠãwåÖ–à\Œ?â¢O’»â¿ "èœ ž6øÝ¶jhâZ²œÍ¹ j9Šb•.ð`^^dËxÁŠLÀÃzm)`¥¦0›ÖVqŒxáuBäÕ<… zY o‘ !}Ðmb”Äz²óщô(.˜QÙÏbé”Ñ^‡i>T£\Û3kPYfa»á”çSE! i í¨ •=Áf±ãõ™éà $o…Nx¤©•(x—¶”ÎÌ>ð̯õ䀯ì*‰î!BG¤e“·ÃVÜŒw<Ëá¿'ŽðÏòjv¦2Ôµ¢%lÁ—Ešô©”þÔ¢ÞÉìa¼‡CGWlÞCå¥ñ×c³ œ¨3“³‹q4Y×¾@ ͼ ×68Ü‹æ™c™jSÂF8®ÁÃÆ2¡yET\†û¸¯ªœJŸžáÿÖM¬Ñ-„R*5?…ð§À§*¿«Š¬-Mþ7½í(lG¶NÝtíºË1eYiq%™îôU¥TUìÔ{t¡ ù¯Dßa1CUˆp —¥V¤OLßq;kÜ[É~Àò¼ïOO(”ðT4qO‚÷7Ræ0m™LÙ£BåÚ­FMžëÒStð9ëJÃù•¹€z¯ÈßJÖzieç2Ç™ØÎxå9ôç-ºÅ;_u/Fø8‰ñèøÙjêZ~µúm?'ùuVøšò{7ÊÜì˜O{XX&ŒþŸ~ `Oñ ¡‘q̃’Pdž5 Ð%€™5SÂMÞ½ák ï ÁDOˆx,¹ñmásJ;#î¹'˜Ês»¾),nD*ŽzÓ¾‚¤¡âÈ ä¹\9Ãò’úß]¥ûa¶¶‰D7·Aç öjôhsR‰ œ`Y˜{jàénÀ\»1Ð<ŸÔr^Óo­ãXó†ÍÅ&Ùk±µ°¦l†ËmI“â)ÿf’IþìƒUµyÉ$7†¦ôÈ­ä4Yœ’€ºôñ‚µ²Wælû%Ù̼y'6ÕØc.ì4âWìó…Ñ?Ñ=¬.÷ª^¤~Ã>ôçg)!¹‰aa°M/©ÝxB/"¢ÔI“Ø@öá–N¼tªÛòmb)ë ÅAóÓAWÊ9¿ =¤4Äb[â€Y¶y[Ä>ë FÍ?Bí‡wR/baÒ iߨ\ÃÛ# 7‚“Ánúqÿ{~' š^8j<‡ZÝ?«yd%Û(2@â/#_ м°G'ÜȾ›l×Ü2,™†4ds I\Ô=÷òð½N"ÀƒAø¶oÅ‘ØòD¢@€‘pnXjRú-…‚_'=Ø[Û–Å—úJ]œÎµs2-gdÙà¼Çk4Ÿ¾?Pû4Vi$ý?e]-M5РØ4”k›vd,‘ÉÍ¿ }eE$˜ŒZ5núü7ü._: Xjܤ¿.‰ú$Ûø»!!NLô8kŒ{6[æQ°\JïÆ…ì0WLtî.ï·B‘â16§çP½¼X«®±èO¦YXö!‚F*ââ¿‘G+s¢„NÓÜ)µôÿ¦5›Á¾KÄÀþgØ¿½p[È9>ãQ¨a»\ U ø„mIH=Œ»’ÒCr¬Û!Ül%ÕÜt°ô{`$«ŸôNÝH‡o"€5ŽöI©7*•ž‹ó"8!¯ÚíU`j©©Ûö|Ar^€Émõé‰Ôùh¦S'6vÖcIêãN 3)2“AƒÊ÷Z~0D‰Ì©„(¯íU˜-Lå\µŒV²‰t]b°ltÐ1¹ÀŽÄbßüÂâo¿Æ0¾«®g÷:5^Œ< âþþ#¡+A1·¸ ïepWªÐç?IŠ·Sx’Nu¶G0S-?PÁ]žk!ˆÔ£FÄ’’QW¢›jÑBƼ>S'\¹ ELl¬ë¶Ø `šôÙ¡NB‹X-•£žvì ™×²Ðí‘ÃMeøâ!›ÅÊOQ[û¥OA²ýÜ2öRØ·q›’÷E“ g– DãnE(ðá}VÖA¾Mú?º%M߸À†Vdt!Ñ:’˜I¼feŸpÜÅÞ%®¥B¼·=·ê_^‡…§°Ù{•{›¡DÊ&…ó¤®0¹ykFouíO ïÉØ”àÅ„U0¿µmxðøG |ÙJ_…"”xûÒ´¿Îz»ÛÙJÇ>ïÎRÀ c1E 'u½w@Â-»jßì·¢†¸ûÖlùHÓýÖ³Ò«¬¯Ò~‘;Ð(R¯a€#Kœ€ˆì]éÒ³kŠ©š|[¢UÙ³Ô× ¢FŒîÇcS`¾Mn§èÉï7ªã¹°%FæR®Dhꉖ1æŠÿÚ5Å/vµ2ÃxÿÚÅnÌryžr»ä¬SÔ˜Œ@}Y:Uû¡Kp€®UÀN/ 9[HIÉt&<8>««ÁÝtБ! CÇw‹egBrÓ2±”½ÜÐd²ßÝoUNÊœö,ŽYÞà«ÈÉ¿vµþH½»:…Ó» MtÛ¥,Æ‹J-Šö2ööò]м[¶Z¹`áë ì,?Tæ ¡–À·;æV¡@Æ÷O1ŒÆâ=÷¹Ìí™cG\ÍùÔ|­éûO‘‹Ü¶í·ÔÍ.m\º¨u,…°e`lmÛ5JÁéÅëù˜h«;³Ÿ£ô”F´¸aôô43æfÎ9ŠÂ“ o*S¥!bôäYH9Dìë)÷5ÂR¨t“”Øg/¥£ŽáGôš OæJ˜ G‚d`w5íýÖÀ(] eaÆcC6Š«Mõf}טü›œ\|  úŸÌ;‹Œ%æÑê°ÍÎ]H’ê €÷ª3¶v£zT`vÀm!æý8Ëï•ð IŒÞB–ˆÐŸúF243«ZúÖá+‡“-â37{©£kÔg$"Áþ JS°¡s“yܼó’$|QB¥¯/¶,¦aðTˆ’)¬ÍÝ6{ôNÔ`?4£¨rë:g¡#©ômMG rI6~BQ=é›q¬©U’!¯žï‘ñ`Y÷WŸ¾È¦¨5Ùe²ò EX¡±ÌçØ‹¬Ô —|?êÚÜ΂ºmf°ùöéæà3r^CYcH‚?•Ÿœ=ñLØBM3i}\0óX[þtމê_ Ð8ø«f]ÏeGÌß¾¦ŽÈ´ß蚪+|¢¡íµ!~Ä¿ªV¼„{ÅÖ•ÝÊKí"¾ÚÐnä rׇ>26á%ßK3Er¹UiBWúTê²I-Z.cõ˜ìLFýÍS8tGÊßmm?ø†J*|–ô\0¯ÿ!l€ßMïpüäÏ “¡6}ÖÐe ¹Ïa®ÎR"Ý|ÝU*é‹B×îwò·aMö°À•9æ‹‘·È+@Hï¬0Ã!'4z=¬ ”1|³Œàÿî‚&Ü‘PíNèL8+$ïÎoæõ£ÌóAb`&`¡Y¹È±EŸœ¸nš±ÒÒcf"Ý9Yæÿ«éÇ.-añ¡‰2¤#it¾MSNçåœ}YEÍt®¡y+. ]ˆ==Wø]e¨.'Áà¨ECëfàú¨4å9¬Çò¿9ÏßÎÊKŠ,¸¨®ZÎý„+ìæcvTdŸf‹¤jJ{M½î¼åÈ«y͉¶C*·œÉÎÞ.Óóæ³L2Ó 2Š «¹Ÿ­Sºz|»ß·K×[ ~¢ø ÞM€Ú|ß0@ÇÝ‘5&¹lgkÔÿoÝü_oÓ¼â=F¹kÞS]UPìvœ'pò玆;2vêA†¥ž ñ·ëcÅÜÄE¤ŒbH Q•€ð±~>„ªÍqÁ8Æ9²³¿$“ù(Ü §L×Iöö$§pR\îÿ ì`~ùί2ƒƒ—V«´h7ÇšÒ:$ç½U¥$I,½;&¯]›WÂx“šCá6·¨/ùÞ€è^»_ÈtkèR>˜CphuR[mƒì‰fzgbo •Æ$åW=ÐWùœ*bâ;ó2—Õ£xUCwTˆlY¨8¢ y F6+ð©`Ѱg-­pòê‚¶þäœ]WItY¤D,{:±MwS扨†Ú~^&á‰îlšœ½yuHPD÷½j+‡q¡îÇÅBÌäšÚ½úÊeÛÌëÙˆÄ€ŠžÑåìRpkÕarêÈ{æk?÷®‡Ekšpýâ$Ý…&ÙØ5R7*Wâ[Qø¥¾Áê¶›_OüJa5°?¥Å\%2„1„D{Z|ºt¼ ÐpÑöOœ™· ”.ʇĆ Íá¥ï»û«w5_[ëû;¼í9§ä%v—û­,4ñ)ë % ¯Ë8—Ä(iM|Â Š¦ã'àOú6׊¿ëƒÖ–ö†#_è.ðlè4qæ/€ëë²ô`7.²~Sý´¾—Ð"-.±Iå/BÑ‚–ãL¶ÀÁAÎõ[qLjFy[e<Ƴ|¯{û¹ráᆂZØé±]lÉÈB÷ÄöŽî“Œ@U7¦”%îÅeõABVYŸƒ÷®»·œnÔA~ቷáÉB@Z©ãCŸ´K¡Û¾„-Ÿ*ã.9QmÓ+†îü a0OZµ«­€ÔS{¶ì¹(¶1Ô /Î"µ÷ûq¿ ½IwˆY¾þþ(ëz¦}1µƒ¹ž?®¾Bé-‡n:¹Jþ0&`Ê]FÐJÙšìÒx܃E>Ðlô´¤k+ Æx ÌCÆø;yL UúÎ­Ž E¬ˆfƒ@ Cɼµè¾„ĺžïK’Ý7ÏÀ¬kDë90£ù+Xޤ§ÁówT™â(ŸŠ×(hXñbÛUUU‚$²‘U!K78a§ëÔ0Üá£`¥•ïîmŒå'§[FO$Äîb×ÀëIØIôª³*f™!Ý¿ñÐ¥vÁþ£QQ'Ç”Œ3°Ønð7o•JÔ1Ÿ>k¼ö ƒ\YŒN¢¦ãç¡w€SI4®4ÉT¼Ä!ps) ºÇ5®¤¤@c˜ÿ «´ô¼¾’‰ˆ¥ÜpO}EöÙAUeÈ0ÇR¶¸E¡P†ß±4p ³IÊD\ß4h6Æô#öØîèú û‹Vºoú'¸%‹®®„a[+¦¶ˆ}©bâèiÝ×Pô€ŠÔ/+3¾žÁºU_ ^>Ís¨‡yQݘpü`]¡Ãœq6®~Ìž?~âDmÍ\¦MÑ)”È0°YÞ;¯}æþ——ëi»È&5ÕÀ€ÜÙØlŠGT… 3Ú8æ2 ¬ìÂí#º¯âV‹?^ÐÆ1 ÿR“jóh„i¦bóÂÈw.4æGî”Ê€ È…ÆŸã>·wºJÍ´ UÍu~Ûm /“¿Ñ"ÂFüÉô»JÛYÃb’eBc J(_‘¯¦<6î\ ¨¬:pÎL™¹CÈ®ÆM¦W<šð‹àÖüP¾ ìÇßÝ’G¯¡™›+ùô%Üßtƒð@] svôÑcÒ—î·¥6qgLì~’@†bI*!h!7ÎÃ¥DM£Xm\|ghIÈ5Ç@Úèa(V¼ºb[^¼T¹¥S£+¦4Ú=øØŸŠüzÝÑöK(ˆ´FÂi°½íogàVÌ~=õÉ^ÐÏIݵ‘?´6HFl£˜ÝʆK(½äÊÝB»\™6„½-M!(ìÿÈ"Žkâ .½Ñ”­Ê§ÂOœã{Ð[U•'¼í`$nDðiòÀq2g7¥çÅ›Qlî3íÐy'¢ÿch(`k–بA„ ‚DòÔŒ"§à?—tðœÇ öIÐäGKt3³×@—†+.ö³X43+ÜYɱó£Ó6Žyãc&¾âœµ4ĵa‹u9M=X¸õµÈÝ@S o=Çÿ˜†ÎMÕ­‹‡Ãäú唞­WÞÛÅæZáÞ'5†uÐýM85C‡\HúÄN!à¥^ΈÇ5\fmWuΜ>×¢´úF –=%#Ø7étïwÓ€”,¨™6ϱ,³€¢3™’WMyž)ì£Új³IQuë){%É¢’’&ÞzLë]¶aqeµæÏ½1» 0a‡ð'håqY½UÓ nS—À.C…õ ÍeJúùOX”r‘™M'óD& ùMeµÎþwÍž ¯õꉌ#V§WG /Šôìbvå„Ã'»»KÁÊ<›Ål”4ÚQ äþÈ¢ý›ÃM6qr9¾«cI¬<×5‰Ih­Û¯¤,‡^¡örʨ¶ˆpQ§}K”/±z$fAòëg¸ÿ{æµÈWöâoÎ gºóŠ|ÍD½rJevš½83úêŒä;˜ðþ[ôTÏ·`ꮨX¶IæþƒÁyßµ¡—\qV ±Q ¹3!1Ь›¸rY¶(®5“uO»!GzÑþÕÌΓO²1ÝAÁrI=e#”ÆÛæ·_Mþ<Ë1Û‰é²Óó÷DZ`ÛK¾F˜ ©ÖêcÇ*‹øYè3S!G×_–ÕýaÇW»©24Ë¢õ = ¸¡©Ë9Xþ¡3N…I+°¼Ò¸a—wç³a=˜_² |#Îûau‹ö¬¸‘h,Î\šsœD»³ŽÁÁef.… ¨³3Pí%Nnõ’K+qjù™%&á*¤Rý@èÛpÑ?È—÷ÔFÎɧœBº)=»µA¯`Ä£ÐÞ‰á÷¥­•÷íÝùë4ìFSÊjæt_Aì:ºÎÐöiÎHcZŸzä4S0 övJÛŸ¥Û$`ÃY.»OäŸ ÏR™/¡k½íBfåc9óľA²ŸÒ/Ê×£3¦ë’Ÿ@œÕ´*«·öüòão”ó¡e;˜{ æ“kh/U²úé¹p³;,°33‡¨}Û±IŒÚ ÊAÀü•ßÒ!Ћm4fÕ¥|xѲŒW _Þ*I 7ðf(¹_ã’äÈÅ¿íMÞŠûübö¹ArUÂù §È¾m€8þÝ…üޤ§7H«UV}v¿†ÅëÞâ}—RÐ˰BÖñ ÓEªß|õ=X¡—µé}éןÒG(Å÷byî¥htfóUxaí‘B}Õ&ÙÔ’6ﬢ&'I êdßmiÄE·•z„™GÇöýºµNÃ~BþªÈ6¡Œ«æÿZ-¼›1·ËÉï>¤`[3r¤£Wÿþ)§…Jjv¾w{R9RŸ ¨¥Ð‚-ª•Ò-•"áÍÑëqwsç+@…!ÁÙˆ*Ân¦ªøá3¥©¼É çßâߌ_„×aöH¹éÔß+Wï4-fw <“Nÿj­oÉ6q°²» }J­—Wk.|¢]2Ï Ô×øþ^êH‚Ö ü!O-—”Ì-djœÁÆ(cr¥ô0y’]ö)Ió?Ëvïjé·KbMëã[E^‰›SÆV^äqò7æt$~õ°ÇLJütà'£ñÔåd~bÓ&Ò†û¬§¥Ü'´ÈcÓ˜‡¢)ëÊPÕµ_ñI‚»„U˜Ôß0oAí|Ù:ì“XF¼ÖV”†{ ÷1ùLð˜nž„ؼ%± rzüV¡nÝà¡ñæŒê­ˆædqï“çîÜýÇR·qÚ_xæScŸÉn´1ôªe¹é;TE2WçyaK•¼”ŸqT0ÓÍ­1˜…`Í{ ŠØÖ§i­À~©ߊGí‰_\#\ˆޢ˘Ü m’;,‡ÅrÇF}ÏŽžà0k£=¢bÎŒBm«*ë±¶9`ý5žŒ6|Úz4ÇOä¯QTÖèµÜƒ(qÕšj‘šùYuXRQó¼X-”Ñl>:Ò'ÆåF uÿvý‹V>"ÔäžéÊuì9\]´(§Õ ¨U½º ý)ñÕ®#‡½¡Àu‘ì£*)rOFFd` ¢dU‰vóGm9óÐó]Mò±y ™¦%FB³80:¢Ã¶fôs’aú§ ¡Ùû)¨èMIkÐÆ}£Uóù£ÚL¸{ r{„gZ9c2ÎNBÅⲓQÏæÆ±Œ:ß. ‰WŽÒç6Ü}¹‹Îs³Õ%ÝßG–í¾ï‚t„• ®X;>€é[mVÏÑÇ  KV©ºìºúýôþfzÐuñט¢£÷ƒÂJÊ€ƒÉ¬ëöiêìe²Ø¾m×^RO—~@Ë®Â?³4²i$Âã­ýD3yIŠn8¨QM"E©`@j&ø@^q¢ók1^‚Ý¿"lI=æZfôGi-Œ²æ“pIêÑ›ÈÅ/&£fwœž«ÏUoƒÖKÁ­ÇõPÓ S: ’¸Sˆ¢ž$äÏÝ7wÑ*$pœ•û½kâáH˾‹—{y®ôeŠ˜ì|XÞ0¶Júpσ ¦ õ>¥°ÐJZfFëâç­²ìR¨}öè#Â0¹üMI¸Lïwö%Žê’yXI÷q?Ôt“²0 âtÿŸ[§`£PÐVÚ b€Æ»¿7¬Q/ˆ°Åx Íýɼ‘›ŠÑÃÅ%‚Gå°Þ[³ÌŽn)ŠÔy398“–ðªÞ s…JÔŸé$uaß(´ÀRèØF›{î¯hÙ- ·‰ÜìUê–r–c_ èŸ09¥µüŸd¦„‰Q£ [çO°?í¨ûÒ:aî971ê.­¦T>U¥Þ\—¸ KB.€¯h|Ò«ÌD)wr¶¡{‹¼þ`E'{¬™ É?ijâÚûVóÝ= Ål£'äà]Œñ%£'Â+¶is¿òy#qªÈwë†ÆÔ?£=.œ§Ý÷¾°i±êwÎ35³o’B7Ž:Ê“E©™¬ÎºJÂwùŠDµfö^ ’-LŠ’o%VZѾÌÜqèG}Ã? ÖX»ß)6 ù©0òËgðÀB.®ÄÝ Ìÿ ‹|Ö5Øð-\ÁÜÙõž_Ì oËIzðl ɪ¤ôµ§RÓîu—ÏZú›Ë5¸~ µ¦©ÇfRC ‘¶!IÞ„/Éç zŠg䄆°»Ô‚>7°vL²#/3‹qWfœ„g%ƒâÅÊF$dn1ùÅE_¬NOv˜…³LùªÃ™ºxy§XmÆ *ÔOÀ}5By@CkÆ•œø¤öD žAŽf÷E6Ð(µvÊ?>î F; °´Áçžf*äo¡Óô§  HI=`(Ui{ùÑ2ê·Ü±Ä5´ô¶ÕÖwó_-v-0vG/j‡X]߬¡ˆލbTû¢Iw€‚q™Õ:¬?œ ÀÞ³Gà#(Ms Ö(§¼·;’ìëí™2[õWÉšC&-DÃf µà’N:–½pGÑ{@=§qk¶‚„LÎ&ÛûÁ(™/Íݺæ¹TNÒ Éŧ»p{Þ+ô³.<ç„âÞW¬RaÏoª…?Ñ l¦ Ôÿ &å Fá/þûgþŽp¤\s1*±g¬œASç{žoFH¿Zê=î„ï쪅¬›Aö Íb-Ѝa±>Ÿ%Š77 a Õ¾ƒb¢›ä߈å ô;Ñ£ ’–,ÇymÓ2bð¶I[ Qß#áÜ]ÓÕ{Àîüí‡uòRY÷R÷M¯Û’ðF\.³¯%ùZV"u@T_ã¾¢ûÍÁµ 2GÇ3_Þ7,ß5>ÇI©]üêYõ¢Ï{œ&€7;ßQæ7"†tœ?[rLìû@‘¹²t«,ý8È´|pÝ-±1WúÇ£„l)ŒÕ|^}\Ê 0Ž#»i;a*Kˆ7ôcZŒòÿêái¡¤Õ/bfçÏs†¾Ø*6cMèeck<´03àq艩4s >‚%u¥&Ú7Ú-0àt¼£€fÑÉôI<€“È`œL<Ê~=¥Î¾-ùÚƒï-%dtåô[oX²¯ÐX‘ñE6„.)lÐ?/z×? ]LiPæ¢ †_EjPnnLºÇ­v“ñH;e‹1+sãA÷f]›×h¨dVbáçͧ‘ÿ¿ÁéRÂ®Š»ø~Nd³ñ4¤âC·!×!$|£{/Ã*´ÂàUa³¿’¡Oá¹â ¬€õd>èõž‰vdå3 C¯ç]ÑB½Ëó\ 1Ï:ŠOíÆÜpšeó{b÷Ì@tÏ4j®ñë¥6ô˜9 òÿZâæ;Gz”ÙYÄSAæÉë­A¨‹¬t/?ÿö©DõKIHÁãïJ½!àpÝêñ/eñXŠ}º= ­°J_[FHÔæc7™Íl‰ûq ʵGL¢t-³ñG˜†[r†ï¨ñ|ê"[K³%l 5óa°E,ášl]Õ‡$Cˆr?3¼|`nJF={)ì+D„Yq'Y!žKmÞáÒÊ.2jÙ“½˜]ØÊ@Öí}B!Énœ $‡ñ ¸Ë WsÇÊB&•†ÿš9\\ô2ÍðLU7vÕrB#ão“ RŸš‚Bz›ß›ÁL”Ù­é¼ã½É)ŽNäxê^U­rF®ú+]¶$F´ ‡>žŠÜçªiHÅbêí'íðôê‡Þ0S pó&ÒY«EÓf ‡-Ó»™riÔH!*?eY³óH¢K£¡Ó/\zÅÔUÒÔ/EO7øY‡EsÄ ¼_Ö¤äÞuî\à-íXŒ°þ»Ÿ”Œ±qñN·,óëÊgð.P'¯,°iŽs^­ø)Ð56dS z%ñ«oÕDáB¯†\$ ²M‚EÅºŽæŒ›=|‰}²¤vOL[Õö»ó²™ xÄnÏtÜvýD/ªM%»â"-¿hH¡ÛÜ J3[{”:0¶Ùåh‹±ávÁ­â@¨ù÷]Ê{ññ ôy 9Þ¯åôÈ}D,7Z`ÄÊøx¦AEii%X­ÈÜa¹~ÔQÃ?¾» ¹ÅŒfÂÉÃjõŒ9#œÁ÷‹©¸ÉþP8e·1+†FÔVG¦þ}þ•ç?›oV'…0î×Ù´7NUL`³¥ìncúŒÃŒˆ îíâ~ÀB˜š9¼w20˰YÏHó×fŽçäcü•b“ð9‘+m Êd÷5¤Mh Öñ_8`‡ZV`7¿¨FÄ‚wíCRå¹Ñb3¬*Ââ M[`H.P'€»ì>Ó‰‘!‘ F[l!žO$0]~…¦ Žü(½œÑ{ÎH Hn& pö†çSœùï*9Ç¿÷YèP›Qñ>®PN§–,”nöŸÈ”ƒkª˃ỷä+NZ* à5Î1i-O©CÍ8DfûI',Çú“¨´«¡„’3‰Ã Ý@UVíîBæ3ÍèZˆÞ`ù)™ù1QX˜µ*Ê]øa¤““qR‰µÍ­‚þJ=¾äÄ嘉Ó®rMš·QïÊç3És¤ªÊ£që‘èYÅ2[ù&f«{뜾C´Ìñ™:~<D±Îl¾ñkIšDY‡Ž;,Uæ¦ÅaÉZ3Ãf(Új+)vØãŸ‚ÿËʤKó9qÙVô)pàë³…óÕg„Àש8²:¦ÝmZÁ‡0äöÀO•tz),e";hì¹@Ù•o§äjÏ’ 9W1ªR öíhQó d¶.õ0ßÎZ\™—(Z—ðeÜ9|Ẏi­dÐõk¥„Ð~ x ÷Òña5J'§í¯»WKÑn ¿îÀÂûA+1¸gÑ!œêg$$θfKd§*nÁ÷¡8“‡.:åS–X‹÷tkY6¾_Ƭ è¿=WTC ;ÿ¸ï. ù´ˆÖЃÎÉ*kÇ7Eh€ˆjX q “Äk¥O1ŽË7ÖiÄ¡‘Uá%ÅjúHÅ›án &Áʼn|¾Ubd8Y‘ > ËdûØßÿ ë¶é2-ð53ÞIm‘jØÆ@°Pq²ýO*&îÕ¥ËÁø©‘ªànm¾ˆüÑ`EX»[ðT5H„¶/1¯l0×Äƪ1ÂÆ4ÇRÒ\8Æ' Ëçø×¾qwµ3<ìÌ÷gšUKõÉÆžÉõ÷Õdº#sfJ8©–Ó.óv¸Õ#¦‹_Xmœ*®uò2ξ¨{ ¿¤×hGfÖ½ÞšÞV·ß&kÛ1Æ æuXqïì@ð»·]pEgvÓ›%HÈ‘YÏ]”Á%‘ލð­BÆÓšØ$µY¹÷ùáËa¡³jÿͿ¡1n[uôÕ]!lhsAþГ¸œŽ”œó`%¢A0«\ÓDy›ÃÓéÿ_ç¢w-ÃÕúýWêú“Íï8EKyEï~Ïy ¯Ú…âѦósËd9 ‘ò+YZäÑKë !Ÿnsœãò„gøÁõzIŠ*~M==Œ:;Uɦ“ÂÈCIr¸KÝBpÖ¥½-µ`rÑæ€˜LVõÒÓÕŠi.ÂmŽ 9°ÎZ§?H6OoÆœ~E©/Ð Ø9#Jþ£„……âztÊ™IZVlÒ½r m¢s‹ÆÁf}-xBžÐê‰áåI­ö-²þœPž¿ ÚšåÈK(lfàQ »£‰Ú4ú•ЈñˆÓ‘Y¦{œÆNÕ‡spK¯f—À%š§í¼î1`_é}Ô ì¡¡££w9;UIÀ¹mvBò#qÎU¨¶.“ÙØY‚6\„€·?ÙR(ƒGé+—ÌyÊß´ØpwÌó˜í® ”6F8ËU¸U¿ÝG½°PQš¸,×›*g}žQ i]) ê,ÏãWŒú2»ßæ‡öá›þ²½ždkw.cšOaëÈÍþý1%g\ÜûƒDJ–N²åQ㥠‘@ ?„<¦xnë+M[î˜z§¢à6=*ŠÓ»—Fù¨9úŒz1¸Å^ŒI»8U¼4Ì^éÄvC¨šAŸã=unÆôL6cïi‡Þ1f¶{ÝÄ»‚\²¼‘¥‘B¦èÏ/OÙÇCaªDÝ·e©¹gï4ømíhc¨èDÚì/3A;[ð]þWå$m[˜G;wÛ˜OO›I–d…ʪ:ñi‹pdå°hÅB%£ÜЙa±ê´Mæêüè”!¯‚4Ôö2媊@?ò~7Äî0u“¼$ ë€å=l&äÒ)×-ÜÖé1€%Ê{uÑO$âoÌò˜ô¦²Öᇄ›‰ÅÛ¾µM«RÞîX%Ú”Ö­©)¢»h5QWòe™‚êeyÎà¡{Dl–­©Ä'þË…ÉÉ{R“V=QË)1…p¤K¥”MÄ];œŸË„0§¥¹*ïvvàQñÚ‘æ¶‘"1.zŸW[(-›LDG7*‚iÀ¨ñü¢ü•8•8ÌÈ"M-äþ[þòï8pŽó~TŒè·ùdá¹¶•tÙeF Óºw¯ý/Ý’+…5B7Žx˜Àå[‹Uu('“½É³€´2Vª9áæô+Kýõ!ƒ'š§ïTäffWô“`qÏíḙೈB[º§ZØNBi£øC® í,‡yZm;Ä_¯ˆëΉ-\JMªBCaÀYJ¯1i¼Æ÷䆒8Îþ6èÿåhaÔ´f¬Ew{Lx`8—œ;õ ¢ˆël7tº“hµß²ˆ/4ßNZã¡3ן­XÂô 3[N!;®ØUí"t‰ ÞQûÈg¡ëí™,ô_×ÿ{½Vý}õ¡ÿ tÆ…‹¸xwò=Ô::6l »,ø|äŠmú²¦¼4kÜû)†" ˆl;È M’»y¶~ÒDaöw(Ê(ãÌ:ùZïO¹²–Ý™ZFxT Œ¤¼•³J™Ð`²‰¢ÌˆLh ;ûÍ™yÁ`áüáö³jU“PÄ;mNs Ó`Dްçjb_Ò³oã]abHï3mË?»p¸é`<¹§ŸZbß8ízdx¥iÄAaÿC®zLÔ ú5#%ØÂa97k Xuûóm¯¸&ù3r&„Œ×½%씃Dû˜%ÁÂÛzÖ ¤ï†"Üz¨.ÇËÚ>=]Q§Ø)q˲ÕpS-_rW¶Â+Rïü™@ÎÉd—cŽØ,ˆÓ CÂtò?,±x·;ÐëÞóÖñ‡®ke|¢~5 žÓLç/4´+'„·¹ÏìôANÉÕxÆAÐ#Õ©àB‘r:9§ ³¿ÇŠ"¥ïÑÜçî®]O.ƒdå"Êà¿“KÀ¥ ÈêF|Ràhõ#A ÐÒîc˜´`OéŽ Q¦ ·vyÊ ÑÒ!‰ƒ¼AnçJcËÿDÖìÔû¡õ¢oŒ¥’âIºùQù@$®Òtü‰€ È}õŠ‹UÖVaçúñ:–ΞëY¡>*Ìuwоm÷Èÿ[b ¾aWì=ªGßdzß+ʯ„IÝ(í;6Sùê½áK ܉ݔ-ËÀ Å2ÓpµpÕìÎ5‚Ü´ÍÙèu7Þþ;ι8Ìu}W÷&ŒÞ=CÕ¡?ÔÛÑñqözR_”†W]Â88~ ¤§]ÇV0EÛ¯¼ÈÀ 9¦ÍýÅïr TýJ'˜®À`b/òI{œr;äÁM©5Fˆ3Û{“éiºÙmìçmǦ=ÅK‹Åœ èÉéÖ¦CM×ðÊÔ\{°ùýyºdâå`ø}ÂÛb®šk0ìÔ m¸H· q¾TrF"BÔ^J”¾Î'DB滌Þ}ÄÄj Vc¢uµ<@4Ͳ–¢hHkÀK¼î1ob$Pÿ`Çץə‰•&A«w0IÍå1”öŸdPà“Éð‰âïmòþ=uݰ‚AØ=óe†4ôÌúAäó†t-¶SíP?„°ô/û PwJÚB]Î51•*l#7­Ÿ ®ÊÔÔquSÖƒ}ôÿ=M[ÎA—¢URDÑúëržBðšK8¬É–) ”+…,ì`$óŸüm,äNÿah“†D!£ñXPt÷Ód‹ü)6¾V=µ5™Ù–Ò39 #“ `3^!ÿôóá“évÛ  Ü2RŽ¢µ›ó.%l&*oôrœ·†^-}üÁ=eÍ;–/¾ÚíI ‚™_üüBEÇ \®ŸÔ8³ûõ/^Îf¯@ñûçyÚdµÓá¬2+¦-qª~µ@`aøöT*?/OB2Ö“õIP09á|²Êá(ýéP›EÍÔ­W¹Ö„4aµ’ʦ¬ðT ]KÖVˆ_Ï~s•R¸×xID—¬#b®—oSœûšgRut9K¢#“ ›«a£îÙafÐÖ‡„·¶íêÕ[7YºËÆ7Ð\ó9°!V§˜,Ü™‚ñŠB6€G­Š¶ôÄÔør}9I^äímL:¨"Žá`S´2Û’ûM ‹Qo¢ÔÛîýÿÂ37i+Œß‹éëÑRº.šq?(eÞ~î>¯šPSØ7mBÃ{†|ÒÌÄ ¬]Â(X^ÙŽù&€¥~®OP ?íxU¬KW rýÄše1Œ ö€Æ«™â|rÙðž&¬ãU¼¾¼¸˜ïÀ€njp·ˆÃÒå®Bô!Øœº†£ï·¾~%­ïرH,³‚4ãìŸë•q´Å”£w)É^n®¿6%›½<=8Kë`dTöÀ\Fó ×ÛKsK\ÒU7Û`ªÇ¸7Zi3U‘ýj4„ê¼Ò14ä(}w 6Ôˆž€N^Ðid$Þ0©Üø4^;Ña œå„÷¢æ©Já衹ô²¤A»&[ÜIcÎÅ%ÐÚá€Ãb8ÉV%ö H£Œoóª}07O ë<‰êö‹³8ZVHD¿1ÌF5ZöšGÄ=Úǡڽ!‰[5ŽáV±œ´ø`ÑÐwx>CÎXÍã'ΚÔ⺠>TÉV9Y:ü»Årº|BÇ9ïÄ+Ùd»-Îã•퀘úH—ÂírXôJv¬˜Ãˆïÿ²&J™F›Q?Åû9ˆA:©ïù#“Í)KöóÔ“)WÄ@F¤¦u7ðÓ~—^ûz@…à¶§Q ‡&??rŸP鯯n‚¼ â |~z¡ðÀÀ‹ÑI«V$€…®a‹9}dÕÇTcX’ß82„ÎýZlù^ÖdºôãP3ŽÃYöÛD æsöÉ4îáx²yvR¡R·GÈå 2°…è‡üäØÂ,ÌUÂ"ªN­òˆÁõº†O­‚Àó£K Œ™'‚rÓ d¢óuE¥YŸây°R´¯/E¾åYâ”íã]]C¶» 86» ;@ÈÉtJ$#ɤ?¤¶gtV¾L†‹–2Ú¶â t%"^fÐ íG¥1ó£°"Ÿ¶Î?‹Ÿ¦t"S5Ðå'4¼›ÊíëX.Î>~vO{ €Ê”3Ñ$ÁkYÂí\oܽiTÿé”’]GNüV´À#)#ÝY&‹ËõN{ŽŒÑï‰GžBuÝò%Š6qõ |À!ÎhC` ÙH6¶.ÖnÚ½Ê×* ø–áâ÷¨F¦š#Ìí•ä±ó–,ócpÛç²²™28P§hK‹ÔChAÝfË3sJ×P*ìO‚ ç"pe ea`¢DÝú«ãÉò|ªèˆ_–×Ò‚¤e ™ñË ¢ìÓ ؆ÐÙYƒ0Wgà‘öŒH9×å É£\Mï°º>Ïú¸xÍD-qÛ+·²²`¯Ýd“O‚öu™[®ÏD•ðç¹ß“á.Ã3TéIÙo‡\âs‹gƒ•÷»]Yè" åß:{*ÃêÍÚðÆ-åÐ(ƺ鈩‘¬×\ïß‘ Õϯ!›1*å#l Gw~ïŽC;J…Wöº[Š)Ï–1‚‹4½1r€îZƒÀ “½¢HHí/>õ™L£tô¤”Æ„õã©^5 Ìã£Ù'nü‰sÌTµƒgÕä©‚5*›A—­¡”z4|97¡ñI$Äl³¬áÙOo¯“$o¹/A]d—vLúãú´M˜3ï-˜fæX>jÜOž"Ò—©š˜1 ˆêÜiHóÔ0‘¤ixV'Å®/¢?@í#®]ÛjÏþ¾Az„VÄèQ-†wYð‘šqrâ;\ŒSÃéÙºŒ¨~dãÝdÂA¸=oÊfwЀáõµ¦³,¼é›ºÌØÖ˜Ò·Åê–{ŒbÚ c3wø86°µç†*Ñr>Œ¼>³{På6À¯÷7FÛûò%ž>Ù‰kúa’[mW‰ªÛažRTÈQ¤©¦Ñ õC´wpÎ*_»¨l/m•UÜ<èòjŠx“*Ý‹ä1xì×Ûçó…ØlLá8@ ³l s½Û7 œ¦^„ÌR¢è“@[=JZÑ;’yi$º·îò+0§\aúO¯ß Í•—ø|ûÞ9/ÍØ£©^Jt¼b®"fzWtÉ*&B¢g{$aE9 Ó|F¥ÖfPǧ%Ãå„v…ú2G×Ü@÷M áU'C×%Ü£O•H¹º¡P]€ ´}qdË _87ηգ%‚órª 2TUn7W5Šl[¯Ew¾ûñT˜ÇÛMÓÚkpƒP씆fˆŸ%.‚W©yO.T"Kƒ·Ôõš«Šœ"­h*‘µïIÿéSpš3;É%VpüDWE˜J¹üB'¼?Íó›3mÙ6 â¦O,ü».óc÷2iÓb×á/ehiG”-é,€Šsœ™7[ðk<ÑÓ¾ÏeW„✢ÆÒõ…¡âLkc±>—AIa]*Ä`ÝC•Á *ø¿“:íÏÑGRáÛV}Þ¢QM"rËBމ]z‰ÓX@t×BbAy^ÁÓã©\ã4†q v½O½…ºC3Ëû°%ƒT¦«dƒðfrËCåèP!–>éþ¡Ðë?JIǦâÍ ºÌùîV Yq&sÂöƒ Þ7ª‚Q¶’Ê}¤]ºé|ºEˆ¯)E¹z#2m¿ócƒÏe`›¿\Òq¾·CÀÍc }b÷ºõÚr#‚üÿú\]ý¸òæ&:Ü5€§½;%Þ+±LÖ L¡Ñ!Žž£’'ræ |5—Øp_‰ŽÏíÿÀFt¯Îë¼oÃS;2ÔWv‹UéôKªÁHc…ãU¢3{Óï® AGy…¥AL³S…»Žw@‰”¨‡“yPŒeûveζST£;KÐø.—×½‰1^K®R‰ˆ-SsfñY¯fxé’Sd¿à; ;v3kÜve=Ó¬¹6ˆÁ+ÑÐÕêŸ$Ѝþæ¡´œ¿²ýØ\„-ûFÖç\nçf¶îêƒðµØúHì霵çw&L‘ È1HÊœi‹üýbÛh6%g)ñ²(p'Í®~Fš|+’×*7X¸Û€J(€S ÷Ƀ‰3×cúlë ¶ûÓ}n“pê$3íýïí€PņE ÙµjT0çÉ”÷µ´LKN®9Ò±‚hǾìÇ=:nN>òïaæí`³QÌ9ãÙÊÅëN˯qúíZë®è»âÖ ^<ÀdP©Šaƒ¬ø²É«l3¥_2ãÔÓ^F¯$b‘ŸäæÜ¢bT¸â-Kø¹õLuhf¡¤bþðô`çÂzkß‘¤A.rÞÍ m·|¬" Vƒ½8\ûó[$5É- x1‹ŽjÈqôgGÿ€€qlTòá~~×ïÓéÛ ¾KER Âúü?×oßµùïØ3.ë†(¿=“û@»6„NÛt‰5®J¨Åyþ^wJ°"âe Öþ›ˆ—¡T•è¬&„¸^D¿ß!ꜫ±¹> ¾É\ö#8þÚ·þµžE©>lýÔaŵqÆe+1¥WÑT¦Mvk›¾ Ö›ª€“Eœ{+µ,l—£žQL£ÖÍ•®s–HòµÆÃë¾å]É„›)õÕÔ^’VcCI°j‹[8®â™Cë¹L*6 3·—Æàö‹ÝÓ~9Ëu— Ä4ryHd¯º»¡<)ð|¯‡§É m P\L2lñ‘–š·éëªùŽ»Ð9€BPÐuå𦌻Æ'SÓ÷üZâ¸ÚyI«ˆ” endstream endobj 59 0 obj 20336 endobj 60 0 obj << /Name /Im7 /Type /XObject /Length 61 0 R /Filter /FlateDecode /Subtype /Image /Width 1314 /Height 113 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream O 6Vm׫„§)£ÞeE4vê§ÍR‹ o«°ás~˜n5:QåÐÃðNmþX4¯¹yç]íY“ë»ö6Ž»Y²ËÈÔM¿Ybu»`‰çÏgÅS‹xé·Y ˜SX'yÒüÉËÕ°Ñ [Qh/ÑÐàÐþÖ¥*¿ºgÉþ,ÇÔœÞF‰SÙ-·ŒGéŸkº~k+5¨ûJq×AXCb ôĦSRA¶*ÎÅí£kN|cïˆyŒŒ±›Ž8|týW:FY¥‚ÒË©·¤¯®ì' ¶R,¦5:ÓÓ"/íà1t™Ó–v÷GÔKeïú’ÐòáÁp ¥ÙÎBë¬ð S:ëþ±î~LA•x*Ùgfù“¨ˆl›žQmõ¢þ³óÝýÊ»žÿ¨é·r}ó¿P½=InÈÈéM#.[ýÕ†ºF_\•cgÓ»)5#OÏ} v Ó¢Li§î ww‡ÒpÅ6ò6í’¤+ÊDÀ%u¿!þ˜whÉ)ézœÀæáØ[~?fŽYÚjT•Tqµ¸A3â£iÒ†äoˆ7kK¢äåjlö ¶kUbš 6q«ífzs"Ñ­”W½Ê/EP»×ÇÝ_h.ÙUÁó¯Z°bQÀ6rkUð¥TuZ=Ŷ›4‡-Í—bÂï«GuÖàR¹Ú4bõ-‚}êðn[΄wö]°\Äž…ö§ï*e À­Vh‰\Ï*Ìj °¶¼÷ t8É(»ôR™œ^âQ<ºÄÍÙ'-½W¾¯ñ ´§}/aÎü\œ½ ”Ó˜7f–ã‚Tbjˆšzõ0D¾YŽ:y,“bØ6°(íxE—RwC˜Hw£<øÜÉ7¬hê•Û˜/4†Rœåßó÷}ôW¹U5˜olx+¡&îa ã±ëJL0±ŒÇ.¸ÅW6W·ÞOº1%“”>Â+£…š¨ÍöÒé~«Â 3i¯u¥x¬{}˜[Ù,æ?zŽ=eµ–-‰VŽsið×¼àJ©›#0N%´”áî|ö¹U~¢bì—ˆƒ‘ê-Aí‘WjsCûñ¨Dõ’%_%G㢾Õ¸Žù3:vϯ^†{<}Ù+9¤[EQ‡Ë©š¦nÇ›CèüÎ< i¼yõ`ÂÓ0É1ׂÎë$÷ÈZ¥M5´”ÞµÛåF4›3ÔoÁŸ` 4Áä÷ûè®~1–]×3hl/-”CÆ‘l/o,ïÄ yÕ’¨MvR1Ô¹­Á4Ùßý㆛iÙÂ&]á­ªØÍ¤àÓb翎ˆ½Êqû_…L´*Þ´c[g«,íGÊ„“ÉèC­¸Òàþ•xvÊ»úxhv.ZÇ ŸÁµ)çò¬ÿGQ;o%î—Gö~Ê~‰ÔØÍ'‰Z°?×OZw¯ªaoÎeS ÷9Ž £wºPz]tdl¨uEýt®§*(X¾ž8côràIH=:²¬}6àƒ âqçÿôE°P·XÇS‘¤(s÷>í°Êûû¥2áRu cV€®TçÜ¡¸DbWXÿFÎ¥AÞϵŸ‘­r±ƒ¾×D7#kZ†ŽŒ+Ô‹[Em»—Ø>»É! §x©ºƒ§ óñZEª Íåì€7K2ÓósfîWn}!¿¤ÍhV|ûý ‘@ɽwá80F˜Q‡«éñ*(ÿwÛy…· g+7^ÎöT‚=Ûdo#ŽåŸ ¥oQº\ZQøÉxôMm– A˜í‰ û!8 %ÌÊ%û’W÷ÖÉm®ìÇ;ýýðiƒ½:dÂv­Ö€ò„š¹Œ ‹Ùøa(‘]DNbWæ’2Z®K/ój“ôÔõ 5’Ó„Ç—Î.\3F’z¦ˆttõ=Ò§ÖügÛëë~&JG^*Ç9(‘ITx[Šà¦8¡HU—®ìb²å‘‹÷%¯ZoË3È×|ÏôVú²³×E ‹p¶fú,€ůBàsÇ­­‰ÒxFê{µ,Tf×|(Xªm²”~çr¡c^lÅ©0YÖì¬cq‰ÓM¾³¬ ¡ýb¦rå\.AJ*äbåiÁˆ¦0ðÇöX»# û~7 ïô‚7Ôô­àMó‰öpˉ寸dä+2u•]±ˆ“@ÁI-Kß1Î i¹•VÚÓeÍn/? (î ÜÜ)Ÿ©°ö†Çw4ž¥’]¹^òÃ\3-8tu³"ÇõÁfެ&i‚0‚y7G2oŽÈ‘­P1A`ögƾJƒ±û!«©&ó Xm3²+£bü+½ÕÀ?!‰êà'ÈÓŠÐî„«¯´vivÿl<ÍÀÃkæì>23ñ¡1âÇ[€‰„'®ö¢(Â[¶µèØHrÝë GØ€êÏüì-&ˆxãryÇ£‹*y\?†®BA*þý<Ø_ÉŽï”®Pw—”ðæí¯Ñˆ™u¦Þ£hÜtȳäwblÅG'ý¨ÝE©fë#Û+÷¾öÕª_ÊL|ÂÏcjøvÊ'”ûÞU*# ñS’¥Å4@=Û "Hª!xâµH*•¾ïƒK©qGc•щÁ¥¸¯ >ksíãìq©)W†~ƒ }K-¹Mïô:g;cǧÊ-(MïÞ¤Ë;Ã[ ´¤©$LÇórO§Ï*òå]—³SP…ÍÿäÏ&œ)öÞ µ·¯à'>žÎ›-L—žz=]o`-bÚ>Ó¼UX§ÍÉ+`=²K?z}ƒÞø³R˜:‡£ë-rà„ÎøöKϹG”žo¥ï›Ÿ’ürª˜wiÏmÎŒ‚¹\ê{Qð’dQrÁØY’ÖÈí;¶Äæ·N¨Úª ª{ú2£Šc!8?Xw`ÂcNÊÀf—|™¸mSÇìGNmG:>wûí h‡¶· åœ&¹Mk¡ã}žþ„§ŸûÍ·áZ:Ú¦xª,´@í¢SÀÖÑüY ‹¡Õ¿]Gh'£þñ0 h$â”9;ƒ\aœ"jÿ`zxá|†ôPŸ®²N“8XÅìSÈ,ÔŠP šq,#nËì2æÛ2rw.÷#]€µIºjOíbm…g7cJ¸ß®…35·EiöywÀ{í ZcÅ+\>˜m»puÛ¤JÚÐO§ŒŠ;uOŒE$R§ø£@’;céƒ=sÿk•`{Ç[?†!S‘Æþ,hÚYô9*erVh®¤¾ÿÎÑq÷ÿTÓËò*RµCºí&µbG‘.þ­GSÍmAH »±†5ï$Y¹ÖÞÿ_X3Á©ë­B4ŽÐƒÐ]V[ò}ÒQÀ˜\pg€ŠüaC` 183?¿D³$Ô¯]Üñ¾ÆÁìö{p÷ò³!eyÒç!§Î†jóýßü]QÝ\Ê÷ä„oR™”éehWéó\Å[¹ž©èÐÆ*ø‹±·O¼5/¯‚&‚.KÒüÔ# D„±Qq «®AGây¾1Ô~~NºU@i§4Þï‹ã—-`â¹+Uê zqE«²BÒ»Kn©´hGˉŠkD-»¡*ƒ8»{–ñv´’¾ˆM”þ´HA~3SÚW.]¥ ŒÁ»‹~©Ÿ8¢ ¿ýkfi–äò ˆok¢W/v»XE†€¬árm·šYŽ ³Qsñ`ëwí}âbÆ‹µvúæÏó<–? âS„7*‘&I.ϰ´fý0Šc“L1{àøN.¥“k*Kr üw¯Ún=5 ]îÁ ûà|½)ƒè±¿y.‹Îµ½*p¬á=:ßB‰HZjïGøŒ2ï÷ª¡÷ƒ)Ûé}_4ü‰ë)T `ÃY&Óõ×ä¦}<‘NDy$jƒ—=ã $¦‚ABdzÐ{aÅh3º0g4÷òD¡»éú6¨Ü&…Ä 4gÝeOšc+Tdï; ?ºÙK£$ ­°åšº1O\Å Üšš>Ôx“X¼-¡3ì&z” ÊÝú@Úw„é[&¡w. ½_Ð%wÙúDv4&MÂ$xÏ¢ý„aÕ_€¢‰gÒ9êä|L“ª‚e³àP<.¤~v2‡Õå›6q¾//Å’JÄÝô³Hiº¼ˆk¨œN¥‡Ý^ˆ$$În^ø¼ÒÅg‘2›&‰Ü ­.a¤"ê‘‚{z 2 ,^t®Þ‘Ÿ”NÆ&›kLÁˆ–póÏbÚüz× At˜f`…ÿ&55Ìf˜ Ë€M¶‡˜4‰MgÈ@IG#ÄÙ¶v@d³H—ÔÃû(³£ŠØÄ[¸ª‹u&QSu®1G€4kÓÛac& ÁUêüݺˆn+[±ó*¡TŽpô8!Ù÷+ÁOø˜áúÌÌ0#ˆ~éºv·2ã“7ü›-O·¼ÿâs¡í·,%^²@T2Í~âáï{<)õwCrŸ\ÞìwÒÃÄe¦ø²NvLê º….Ε°òá6ßKm¨%ž°”+nÄ*ýjÖT^‹èR´ ÊC¹ÃK aâÊÉóËiÚF*¬æî¼Œ\8¥™|°4þôçdåüäø$Ã}Vƒuÿ‘2“[Î' 4·)ß¾ ý«èdŽíýE/OjàmïC”ØKý½a²£ü-E%É9¾T[Òƒ¤Òà ò±úAÔùœ›xï$yg¼æfž£-þ] Ž ´w¡æ[®=;N«ð5Z(M~!äwGyÚ‚6Qó^jBÆ¥3$h:”'–9¡b/û)à5 Õטpƺï/éÌ:”@ï7\ +äÅ jáPeG¼@§É;ÄÆµ~Ïé‘ååfùØj÷ðLŽ\nOÞÏ‹©w`¶P—e¥É½Q”R;TŒàw©³^pz¥ñU·m*§SWg¾-Â]Î9kæo|’EÛYÜ—–HÞ4¦¤M[q4¦qÙ34¸È$º+¬þÜ+)«:näà-–‘|÷€óϰL.!‡%”±Þשm¯ê›è \x‚ârwTQ¿"Z È—¹óÒæÄE±JNÒGQÆÿIô.\pÏ#ÁçZv rÜ”€=êc î4ükN}µ¬©ÞSHFYaÄ&£yaÞÄ7â¶B b”eL÷ÐÑZ~.óÓ}ØùÉ¿Y1õä¿ïᦸì’|†[0ï5*\ ƒ›²èÁp8›œR„Ãlj(S .iHH=gd#֌ҢWzÚMÊl7áiÿÕ3D¡* §¶éJ¸`Œa|ël%˲–Ù9ž‰‘ÊûhÃ3åj†Íܬ›vêdp4ðÒ5­׃Žpo Õv¯ &ÿÛ1‹€1*\C[‰ÀK>jÀçSžØ•*½Pià$I (-x¾Ë4Ð0ɹRCõZà:XÇúè_K_‹B¢òPðfÂkR”èê“ÏQX%Ï%‰´H’ƒé<ê\AC eö?Žë^õ¡*ò~VKw9ùÕáׇã¿^r”K~ µ „ùð/uÚ>HÂÚ@ZʸgëÁ F€¨gkº¥Å¼–COF8v^Ö›0üV$œš(KÖoYr¬Å!ÅÓõ!r~U­¬¯T“m゙9xÄ h´­:‹ˆÞ8k¥8X“D—2oÇ>žqX#QkŸ£HáÃÕEþžÁŠBŸJJvÏæÌy^©§Ô{ßînjë^ýw˜'ºfÝä_A<›¶#íÓDÃ2¼"øUó¹6tÿÄƵä¿Ü¬ÏÖ5´BÆÌkAÂÕ±ÿ„í©­ó¤•è˜ ý®Ä „o1Š?‡7Õ ’KºÞùh»TÊØÉbú0FH úÚ–÷Ë# ©å$ XTÞ ,=\422m*P¡Vºñæ uH E¡t¡òÚPL8ÁÓà´S~»Ø5”Ž| o±&[D©ã^&ç" sX ‘r¨'c¦É¸© ¼‹^mp`°wúñaA–IO?ç+¸Þ¸UЬîpÝ.ÜnFŒ"i„ éÊ~Ês@é”„ÔÆñÍ3ÎÝðúúyd­\c™øBÎõ¥„œ;XychÆNDÌŠéäÄYÌ“…¹$Ü~=ÇÒéìݸ.gˆ!lÏcì•÷HC°r·/:»3Zå›O£„:Š4®¦§" úW~чä;5Æ/Œׇm÷V!d éÔ?ÊåóÁS7Zþ$7èóëO¾Ÿ¿ÁJÅ,cwØù1ó½ílá›c¢WB¦?_Ë&¿3x G_]»TCª®à×xd”ƒæ29‰ÂxªŒÍ•™‰ƒ±¿+#1¬30™šÒ§®…,PŠ=&V„†n@lÛûyzy1¬ì5cGß\nÁmÀ¦ {"0Ü»¹ulÿÕ ˜ï3ÚQj;6;¹’ÃL$Ä¢u—óÁ†ñ^åO.æ¡á¹ÒZ¦ôáZîlFuI8 2^À‘u§s´´\^¥ZÌP šÑðålC¸ÖVÏÛ”#´Ìa +Ï©¥Ibh#¾­f’ÄÑ·eeäšäY'céíÞ†Ù‹_QˆiîòØ‘`W^:Ý0m 3BP!´çYŶÆÞŠãNJ‚0ŠC‘¦Í³A¸s¹ð“Z }‘r¥Z¸üU†üƒ£m,iõ R<6öÃqs&ì4`3”6ä@çÿ>î¼±T¤ý¤E5ï$ó Ø¿TCí¤·Pªë”ùþÙlX;i¸¨ÔšýCÛZƒ£e¤þ‘~ÑHáõ»‰½€âuN Šq.æÔ[rfYjH?ÌÃl~„ðð«‹î ÆAḠ.¦©åáõ/4éžhu+±zyFø(oïš<¿Ç‹AÓEÄ?Vpî׃U…\én@¾JFMAF~}° h|ûª"£”[2«¤ßVu}Þô½aµDö¬gòRåL!_+î«ÎÊq÷œg½iÔ¼{U•ï(+»Œ_Í\ôºšç®ËñË¢ùüñÉþ•f Hti ÈÊ"I[n±¼ÙÌx.(,ï•é”î.óÑ#¢Hé*ÎÎ'nñÄ^0^0­r:Óçß\¿4‰õŠy¡%Ùn‚ý|É^NêÉ1­åô6, PJ£µw´0{V´±›øÇNgz°‹oö:“[ ¬@_8ž.Â-:Bý–Q’ÓôÙÊÕÿ$¯Yq*÷¢7`㻓FÀõ–2½Š@MIô݇b3`t÷1ê¯áR´´L;ï»+|â©õ¹“1i÷>¸ ÿ!Îo¾æ%;ôÓRi̓»µz±öÞÁ?‡m Ȉ&Aå·à$N~âLßÿè\׃C‚àÇga’¨ï÷*Ó]· ðùnÅr`9ÊÁÀÊEÂî=75W䟴’ã,fÚD'–ÞB"áK³¯¾qiPjPȪ‹Å‰[­¹fš§†ãþ½¤ì‰î¾ô¼«êGûËkŽõÄ)¦sv¸kÌ>ð—‹»FÕPI¾i\ã¦äÈÌFÍOï#â›Â#vG¹p‰Ä¾†Z¿ÉÚFT ŠƒòTm5ç|÷5ç¹G©wÂ*„R¢*ž*0þJ/—[‰µÍ Án©¨í%SÿÂו,³°Ê—<¾,ª’{ ÔW~Oô®]7»4gi˜çóˆ÷soÆŠ¼š¤~Å¿ýYwë·´ 5eUVÃ>7ÊIÓ#p§_ÜÑi4¥ÊIöéÙ4X Ô%„Q<+©|lézºÄÌŽ?©¨V¡ŒœIedæ>ÛõÖÆ+O[˜K}KTÖ* …µ KŽßS_ìjXÀ˜¶ÈÏkÞÇÛräiò?©3¼ÿΦœ¡ébIÕ¶yöLb7o·Æ5-7ÛŽØÐ≬ÅJ×ÕZ^¯¿æ2T7gp»Ð?p œFôªßâhY ãÏ.KãÏ_çÖødÁYsIRÕ¢‚ÂD' Ü6PÃZI‘ODžÎk·¹ƒ¼Ñù€êéòð±Â¬0šïè‘Dÿoå}÷&/›Vh1ÒPÂ7Þ¢Ü×> ó÷ޮω%gçú×gj?½zÆk‹dN.«È=». ¨@£„ßX0¶©<34úç(ßd+¥-­'T”讘Lck.¶m¿ð2+Xõ ð–²6~‹Ltr²½7·qD“êâ'¨Æ˜|Jy HŒÊoZÓ"2«›[c%Í•9!WTäb‰§ ›´ÜùFGVK’韴t\à†ä8ÓîíwËó<á¦nñ\[÷€š·¥'×û}øá·ÚМڎ9ö?=©ªÊµjHß™%£ÜÔ#¾²á&ô¹™LRºóYŒpÕÙ%ì1Ëäˆ ÎÃ?#»Gû%ª¶ëbÅUéÐdýÓ+ük+9Ëé‰ÌoѸËèàïT#ttÈdÀImŽ~‡VUž+-Ðva¹^c+ÕÊJž&€0|#¦IÛÐYt¶kt0Bä–Ö¨få;wŠzûÐcÀ±¢Éfü¹ÍÛ$“¹n†ÿhaÑÛÐ…c…‚)¹rm(ý÷¶#~–ç­•i¾l¢& ×èe&W¨¤ùøÔªúI…w+ž×ׂ,· â;Œ9UÐ’Â妮ަö_)¨QîŸ Ó‹ÜZáñ´_³ÞùœV «[5=Þòèñø»¥ÌBÛ3\\¢I²ž qX2A_šd ôùH#[ÔÒðÚ)®SXF“êBP8Btñ+„FØ&0¡Š2‰ÀZŽˆ•”§süÃNgb~—4”s‘':ÇËÝ­üõ ÂKÖymÙ\ˆÞqš…yG[$'%yBGŒZh¡µB0%AO’ʦͱ$$lCQY¦f£·ÍŠv'n¢ƒb±'Á⃪pP–‚„&æTõ¹¾kb2‚ñ!­-céc^9hщlPyÖQ`(lç<#e§Ú¹cq×!–æi;“oÖPl¬îŽ$ƶ‚ˆÈ=ãᣓϒÖx£Ñl•—Œê§Ýd.9_ …T‰-ê~P|íe~n!¸ŒÍÕª;­›Lœc*³ô«l’ñ Ä£ÅbØÑ›sâž3V Vb»6œ$”iõ/r½\\|sdPý:³•a4-¡Ì©$Œä<Þ†æQdDŸŠŸ„£…#V÷¿DULY?o¢åCñ¥ãÄ¥S§jiû È’þçì‡ÜÇLõ+žm)ÒQÌH¥û‰5úQðჩüc¨œ™ÑB4A·ãZžnqå ˜qÃOΈxˆq™—™~ÝÖqc|‹T¤‘sê"_èWŒ"@û‹?lÛqR**¦[jK#êk»ay““a Ô6r‹\pÈ«3 ýkJ1¨S–z­tQ—ÞÖvPàqM&˜‰ÙC[Tì{z;Úz<h6ï1}ó3ƒ“p]¶6{;Üö/·$KªÁŸÞ*…Žwÿþâ€ê ÍÌ-]¬NŽnk÷ M`_›};¹E jqz¯HaÃ¥²uc-­‹•1â®ò|r"U‚j³¤4ÄÇ>"º|dÛ0âÃ`¢W%<ÜFmPÅ=ð°X < Ï4o³Ãš¨³)Qø¦grk‘HÖÈZ ~qÄ ´ß‹ÊìüåÅ™1´´iK›PZÚýÔ7ó-”Z˜T*cç¯l¿€'d¤ûF¤G˜€éå$ØóqÕ]%¶¼sÃzG®¤!bè°i—Ϋý÷®.âˆ&£\§rüi‘™Ñx [¢@~ªÊf¥Š½ú€dø_ôTJŽÈ_  9«ÉT½ÍêÞ)xC¨±1ÑÆüö9pµÌ´;¤5K„>Ö Ö/q™£GX”¹t†*@Tö…¬¸}3ÃGý¸Í’Ô°òÊÀ2NRÅô”иHlÅ5rý-¿û|'ÔYÌ푨rpp®‹¿]5#ÏO-ò%8¾M| ü­„ H10k9™œUç¹ý¶ €ËÒГuÑ»ñ åºÎ5g%Fí N¯à+˜}$½D{=éÖpØY™%IÃõæõ]ò§!FxònÝê–Ê'&†ayQ2–Jb›Ç·<ÏxîwÇý ¡UTL:ˆ)B²tÀ Ò|»¨>ÛÏk½È^ªÒŠJØXÇ´^£ê)È„_pÁ"Ôë¶ø Rº_ˆ·«bkà6'“Ëz™<İ1³Ÿ×)ßeø`æ¨Q*LíçÐñ–ý8¾ ä<ݽcê¦Çµ#[ü!1+íßqC3ˆñvTî¢JçDç\¯+¢H8Ç?ôk£~dÐ;à[†—wëM8/2 s‚ý”'“ØX_¶âÆXýÃz[˜µõW‰‡¨1ÆÕu®<ÞÝæ¥~*2€¤QÐň*Ñäú/Qu˜Æ=]"’BDaªVôäU4ñº gšþH“¶=û_†«ˆ?hÒ*ÉŽ‹ ÒL‘LÌØ8çÎ(*ÙRwÀdnØûë@lLÖlŒSšvCjÅ"l®z´"  ›‹Ý ¿·KötÚ9æ]TŸuzruìÚ5X“2Ý‹‡ˆ˵‰.Øp0°6xêÀÿÙ$›7§®ÃÔòQqâFn«*`"ýŒ4ânìZ¼+3¢ç×ül5yåÇRšó F9³ÙΥЈe#MÇz)ø'/r09|XOO&ötŸ'ì¸õ~uøŸ Ÿ¢+²%°ùD6òÊl;µ–Ç„Ñí&?×yê÷P4hŠÑ§Ž8%S²9:k)¯²Ë}q —ûë+)Z=²ç€þŸ`£wáU÷@V‡;¢Ogßr ³KÛÚÿ¢@©#©|-ªgqð‰6îW^@*5^÷ãÍÜSí60ªMQ¯Jˆ×72& šåÇŠ€Fyp˜Ý ¶ê¿ì+ šE|T™‰1æ•&ƨ ž=¼Æ¸5÷û¹p$ k"ä%Š#^fí£Ö§0Ri †Ý”PÚvJãݳa˜NRãÈ(­rúf)šô¨èÎú­L³Íj“/èy«†£Ú¿6Ñp!Oª»?5à!:HúòS‹øÂ]£ÒŽ÷¾‚v±ÛïêÍ8µ-WÔp«¯ºt6†wRò…Ãf½T­ÔN0QXzñ‚icb]à¯Njsœ3ˆ™OéC×à ½èhƤó¹9+¿’:ùØ{q^_˜mqôÃ@éÃ& »O\;Ó>9öP°È,(Ÿ'ˆ“ÍYO]!“a©>R ®4’·5Ç5‚PG"ÓG?”5ˆ½¸1;ðt|Œç=’ö|¿lƽt§(è¯Ø0^b{9YsNJ§¦Þˆïrø¬Ú2€žøù8/wš@[‘*}žˆ¾¹B¬·Ädn4>U–˜i>7.n–?!˜¹¦dqŒ%Äc³ñ¬1wÅ–9­³Ë¾ükvŽçµÔ™ç#}þ¦}ÜEBŒÜ_¶Zª¸lôi€äruÂq¯vQó®:ƒï€$h`‰ŽaK@{¥#yª$›ØSϪÆŽÔ×QÔŽ¡×ÒÜÅÒ‘ˆüÈd¡%jCËæEww‚tè}§ÁÿÓ€+fÕážðÔ@kå’O}“JÆÉõˆý‹'É6ŸE ÞŠVœN…“¶Á®)aógÔ*„G#İztB3z.Ms£¥8éäB_´iGƇ»ì‹¶ìg;:èL<ÖÁ6tgef#hk–¦ùdŠPú#1 kV‡ûƕ܂dR-¬¿îâô®5{›#u»Q,ÌFþ‡ï³“¨µE ’Æ–lO©ÃÙq4ޱ¾;^Qʤ7÷I¼uóX|úÇg4/Ù*xxØÓ` žÇ«l.‰%Èø‚¾¡Ô;sˆ"hdÕTwü…Š)?ŒžK…©¦ëúÐýb}) Ô‚Û#’ñ·;þ‡ØÅe9Ùõ¿ ,²Ž¢•~`ËqïѲÔ2Q:匤´§ßëmc[ö`Q él¤¬”hmåÍsVþ`]óš´GÒ¡$ ï„_/2D"9¿žû€:tÈbÑ—ëCrê`õqD/•¡rˆ^µÔC¨{–g9ƒ¹G‹ð|tCR1PIÀ,þ¤´&aä?È~ˆï_e1<üy" 3ÊT¤\Ð-¬R-ë_LtjËá1ò²¼¿;à⹜C¶AŠìzëKÎba€Z¾‡wFJóÈ'b!&öÂxÖiš—üÅWLB}Ç7@êÊи—S1Ê…•r7‚³?úµ Ú‡4MO]†Î4#¼fßÄ5°u;£Ìä.ñ{onw7Œ{“6§tX«h×â©#§3ËšÜ@Ø ýàd P""„ކÒ!aµÙyRä“ U.õÕ@?]ü®=  ¶ßˆïqÓ.6×q†Ïb uK²òÆeŽËº¼Ec¥Î…P$4J~÷®½ÅÞ"ò$)úVb;1çñÈžH®ûEÕòs¥>Oo0²¯ %Ö´XíÆüƒ[{©鸙ù¡Å¶i½µ,ú^ã~¬3$‹Ÿg*‰…{ÀéX<ïÔ{Á†Z޳4°Õº *ºOSj|q£‹­*M*ËEMSŽ÷ç´°mT„’4m¡s×c“!¬6•ĉ§ž+‰p`@·. oXt»â¯ÏÙyÜ_­ egŽM³Aâ¨äDŸÌ<Œ[’û „/OË®ƒ!s.[¡,å"ÌzZL! £®~æòQGÆ}@¢Ÿ|“®—Юë°?s `•>„_øÖ«Þ!ë2‘&‰@›4‰6ô.tÆkÀF1›ôv³~Tkm΃6”×¶¶iÉüÖ÷g…d$êV‚~NkPÊ¥qÊf.Éüïýp‘+âø';¥â|ÝæŸSÕî²ü±>†G,0‡Y4¦Ó‹¡‘LXùég$e<½\¸«M ­¨%QÓ‡yaô<êDà;B¾’ådh—•u,ï3ÒÓ …ÎcjÝò[ïò(”ãÛÏàí¢¹‚“–R6ŽŠ°}K@ 9±UB\EkŒºnͰ¾“¢è…¬jtvù Ïýz±ÉCc½ina¡˜PtäöPÜBâú]!2Wì go<®í;mŒWË­ëä5Ãxûƒí¿÷j½¾&½&ŒqTHs‡ÜÁ"“K’…ÊÍ΀"îU¸‰?›·H¹$g9!NqÆž„S6…¹˜õ†âý‚‚ð'¸Ü.Œ¼W—m›ÐKœÍT<Ï˵P<xÿ{ÈŸhzl~–m†—þ¢…À6Oì7‘»üèá»òùÏG'ÄîLïŠÇûª±«Î×±‡>¿—cÙŠªÈ.ÛÝW:07vÖ –%±Î³*yó™AÇÔ²» Kgyk`±|>ŠAìÐn˜|ÒWÚ€"U¥¨Vœ®A³Ç"Þÿ@¸Þ)§ø {WQ%Í“¢ÀAz^Nµ*êd.I@ìžeÈIÇ÷eŒ³#/†ÁLy‹nJ³nðÐæòŸÃK{¯c~lj¢b3:¬`—4Xb[a«qvy-õ ”\“ÎŽˆ¿HÛôUþeSœzÐTÒ ¢w.P,~žÁ fHÑQX1ÆŒÍséDVÈ‘J_‘?œ,‡ò]hë5"¤šLX-½}L4Ý}x6(|%ùNý_÷b(!‘ÖÚ ½ vC?β‹ç¯{©F¦^i¡ï&$ÜngFŘÐwXtµˆ'›ú?¯ékpȨ)&é8*Ž‹Â}(×u‹ËÖk*Sÿ<ˆˆ'@P©ú)úÙÕnŸÊ<{‹=—<ß0ÑŠ'+*öœøÖÝB™Mœ›Ã›ë‰Ý,ˆ\ìR_6b8ã½1X‡ëŸÜÖ¦÷Em‰xsHW‡>qß߇\“§ôŠÝN4ú=XoäÓ+¢”$)tBžD2àà g•ïIoW‚†Ñ)h­Kd\›.‚ëÊ`?…¥ÔçMya~fß½ôxumÀñïWOÃÿeæú+hùÑxãô–ƒ}.®sï2d%¬‡E7kC åšK£˜MÓ¦%ÅÔˆôáƒ+4tk_4±þÐ[yÉñlÏUŸé‚eh!Çð0’ÈQ‡í¡JIt¾_Ï|}#$%ÜÑxA8β¯DÂ5§k°êÅHßø +Zž*{&î'Cd×/q,dË¡xTÀüé:%ê"UÿØÒ„:)&¢ŒSþQ–(m³½ò¥¿á€Ò‘:0ôÛλ+*^ Fëºy«y[‚|ØkèqÉo ³È)ÜMÊZ«3—ñÊÄ—†®¼6»P•€‹Ú»v]óDÜÁϽٓ8P ,ÀSO½ßËie||#Á¿'’í.¶äµ£²k$‡"/²Èf–&&82i½BŸ¶e?[Æ¡!‡™¼â?¿ÍC¬šPdËL˜dÜñëÏ^E »ÇH6ÇÚÆ‹«ëÚ&‰5ÖDë½ðM/(¾Œ©Ÿ÷æ‰.À¹–ü€o\³C;ˆ²ƒnû¬,jüãü˜n |÷•ÆU{¸¡jÜäÐMK žö ¬·3<`œüúÇ¡s7Ó__ôèìºwàHuh ž‡”¾ŒG‹wê3{a]_‚&éUqw`L]"Ú¶b`$;¢ç1õ&Û›IWçi£¸³™{ëø˜–q£\¶Á]ç‡ZÔË%^ø%:ZÏ’`½·K†Fi†¹Ãb·5Þû0¾H³áÔ m\sb—šÜ¡ȡعu*»Ø|»fé_f'ËÑ´³ ¨¢n 4Pû|t’˜ 6–õᬚ»r3Š<ƨöÙñ±Þ0ûUr —iŽÞßË®–,D·ªèþQNõæîþ›±mÂ镸Ù1ZPÐI™дêg[^BW†7‰[‡:Q¨0¶7¥ºƒˆügi£]Tן UÓ"<²ï£ÀÈ Õðïÿ2%¢„濜S¦Çad÷w]Wó•¿rÿ\”7fhÇ_dUáß­83 )Bùò"ÊówDgŒ'¾‘º‚gB=`"ÄÉêP øÕíÿ×°‚¸÷Õ@Éj_à> «¿Vý;'’µD9>éù#‰ú$:'ï!¢óöé<±fB›Ë+‘¨ö‡ HuAD¾ƒ„ <ˆ̸{`Q¤JÜè”Ì FGЅƈ·ô˜já‡m`´RáèÕzÇâvnE®ÚÒ/¯å :5^JÙ·›þÐúŽ,j6EU‡v›¼þˆ‘]Ûò¶3à²F¬Ú¹œ1Vüûxðמ¤¾6P¼ènîŨø¸8ÆÏè_½£™~¼ËUâSÒ¨*~ÿ‚õ€2¸2¥vº–%H1¥¿C÷-À²ØÝæ´±,ñ3T”œ‚èæê—ÌöÙá§ei.eÛW¨$5KDͳI¶ŠüAaém¸)*îbÿijˆ¶ç›P_,Lö ©NÔƒ«…3úàå-Úšê]7ÅÝ•:-ðM}éÁwÝèE­cã±A³§‘v•åc j͵•êKÃäÊáiwÕ¡tÐ^!â¬Õv@`¬ÐÂfGtÖ–Iø@>íNǿձ|zX®˜sý>&õRßD¢La…¹hz¦xO ¯Ð’—iŽ·í«Rƒì—;<5m—T(o nPÖ)¹õšË(µ?:ÿ‘ÓÝ^ñQËÙnŠEáùkñNûÂx¦ó316H"ößUgÍ­—ŧ–þ |÷Ð7OÈ96BÛ« ⸻¢½½0]ºƒVÂ5|ÌíÚ¼ö@CÂQ¯ûú¦w‹xË0¯Ù3j0„ˆS‹MyÅE¹#ׯq>¾g}©ÏmL{ùü£áLÿnÂY=a™Z±47 sz°6+ ÜÀÌç—œ|l…&®N–ã¿$,?å¾Àë¹ÍùD.—¿>±=23Wîó èÐjJá–§Díäp'cXøQÛ0‘'o"ÂùD7wZ½ÝŠÜ¤ÞÚéí¿‹I‡~QÜ[.¸°ÞêÓ3Æ;®[?êÆ.Qé3wÇc»ÞC_5&ˆÅx >LæÃñ­Æ;B…\Ûõ`8S!f^àO¿èÒÍlƒ¬Nls)Ëïm`/²ËSwLZoO_qö2zÄéÕS~>Ä>}˜“eý¯5ÀËVÅ)þYº-÷󯡱,)>c{3Ð…B‰äž~Rú¾øAñ´¶Æø‡üøn„žA¸Ü£ iÏ™û¶Çá“7UzÒÊó3X³½LÛ©³(ò}¨"“ùj-î)§‘žµÞ&Fúkÿ’ÀÆôSµzEá&ë‹YkçÞúîîoK[¨ÄK¥SÙåðo’¬>’‰’ÒÖ "m¤ùˆÂ©-+‚$†Õ±;yuâúÐA(voÉuyÑlygoÿ€Ôã³7zŒþ§e$¯9®a!ñLÂÄÆ6yà#¶ ï0%Oœ;a]ÿ©È/Í<ëT󋡦mê5UÊ×TªÃy¼‘už˜0µô«)ÓÕ¿ŽÈ3WZ5õ[¸öO¨GÌmC¼oƒŽªŒ<Ò¢ œ¡ ¥àÉâH–]ÆÅ#‚vÔpÈ~•P)µ4¹lÅlT(:¸´¢þ)Å‹žã,ûž¬ÂÛBÞÛmì¯óžâ’í–Î,áü{¹ÓyqT~µF‚ö*=ñ{CßÓ »ÈÞ‹‡1ÞŠ¾>´úÞ¤HûÖTëœvÚríNŸ¾ËŠ\ÔÆ_V{Íö_ðÄ¿û r«?×è{®ÚÓŒö±© ªøézë‚pPn#¹: ùÒµ1å•M~{º^XÆ¢’φß/‘$í`<ÿñ;¥Ûï¯6ibRUâY ßË DTÐ…I±¶ Õ1° <þàØˆÌãe&Ò¼lý™y1 q(Æíw Ô¡ŸßËДôîô°fi8I»ÚöYHL©+™ÙêM^P1µP"®ÕÝj–“™öƒ´yF°§-©íTYltìé*+<û h@”1’Å–™CÇNPÜv£¬N5KšáeÒq¥Œ´`ù™¦z¼zqɦC”s¯ï"•ìdIW *ï~0̤ªz“:2æýÁß'àXÅÉp>þºþ… -f;¤`ÿwVÂKN•±ÿLÿíó+Ñ Q)9F¬ô¤ÌotàV¤l‹n&*C;Hî*—´ùml O'¾¥Å0C¨V¬¾[æH2K­³¼{XMvOS630æ0ågVRS¼ìéúQõƒIðê—ä,÷’°^ÀšÙÔÿ<7ÍlCõÇ*>tÝ|Ï"LNAΚŸ­ÃK…ŽË¥ëm4C^²ŒI|~Y¥äXÿqµÐŸeV[ ZhZkq±¥SU¬9³œ 9¢àRÌЇ„óUµQfØehYZ9­iS] ªa6çÌ;N|eSä™ùĵ»Ûȉ«ÖÄ¢pòÔSócÓ  (jéIE)qƒÑ5Gk “î<Ò’';;%ÐáGá8·Ô§Õñ9Ø›ØðtÉ€âêÍCþS¾Q 9äÞ¿CUå÷SuI¶¦!:z9BISËæyÅc0ÚÚPr)ÇA˜¿œØÅ9 ”ž…HSËǾ…#[<ÿ>]Üc]“Ô_gé]ÒÑ uÞ»¥ ÍýªOÌZ}1â|@¾¯µ dtR~Á Ò$éß„†,Hûl*ºž€»^!äÌl↛ó„Xäl²·ôU¸i~ pUSJËÝ©wRV;8Ï")[KÝ=µÃÒ2ÝBu¾{®ÅEö²K#%âÏ‚Êu&ýªË:>ÕÜÝúÀáø·¹þÙk-ƒ#ÓÙÜöS¯â¬ÏÞ\_HbØH‡+VW¥.‘h˜DMg-#09¥¾ØÍ3ÿÀ¦,Ö[$׸EwhLœ«ðÂD`øÙ\[ˆ‡gÜ(Ó„hUŒÈntæb£˜IDVãyÙ‚ö ”ì;&dŸ»ï iÝ$)?Å•CU,ª +…³@JUdK±I±<«7‹ás®í_w Œù,1ˆ¯ÿ^6Õø0‡È”ž®ýݼðé='«Æá(y¹Î·«ý ¯AÌ–ËèÁ°¢I ‹¶ù‘D½Ó€X:‘¸÷Žt-Ll%|s+K:˜¶ü¶ áCU(ieý¸{ë&syÊÃò,øn”c¾IOÒ¤ÛTêe>0dÐKkYkh—Pñ¨î¯/î'E‡ ´j¶EõŽbÍŠR'SÒ£ƒîözÇs?Ÿ>kXˆ/µõ :©sï|±×³ÞµMgx-.ÊÎÁ(÷mÑm›j¿²L ó°ûK$x„¢Žû4>O$)—®8xÙ䣋¢‹ß;îX×…;*“šÕ‡ºçú±ÔÕ×΄‰rÎ$)x˜É¬6Q9@ûž¯4IjyPù¼`·eÅÍKÑ,­mh*ɼýãNc;.Û¶"ZH¬Zñ(Tг’‹H,Þ‡tÁÔùè•ßÇ¥=r¬Çç!qAcfûùrž¯ MG‹ØÑþ¾>Õ¾~‘ g»Ù@/ŠŒ΀öÁ7Ë;t ×Ò˜µ‹¥;ñs¸23}šãw•ùswú‰;šP¶ðRÅŸçXÙïßçpùÅÃð "€°£¼Øœ²*Ä>²íÊÄb;? 5¬T¢ˆ¾ZA¹¿j´xý7…âj 2æ#[”ÓfÑË›fYëýzð¸v ôºíNCæ^WÕ®àãOöÏmqo·Z_©»üž¨YÛüüî7|„Ö×x¿ñ¨êˆ…ùÂXq(œA¶&;â0Ûp¢¨ªšðóý|p‹ `QþBcu¿'/MÛðe^k2ƒùDï ê½ß# …©zŸ øX¯ç$ÕE«R­™ZÉÝËt^µÓæµÁ¢Ì‹mÁ«ÈÞ­ÞÐ"˜ûtBÎ&Ô+â‹ìUG£ý'›5û÷¨Ópá«ê8ÚÄ\K`Ùâö^µ8ÅrIÒôÏbÉ­ÑOìj>0„®¨¨/ãM[³æÖt¢($fçhfµ]V,à½7 ýfbú-ÃnøˆÏµ~U³ŸüRZÅ|4„ÇÒa‹Ïãpl"œ}uŸ ´©Ñø9ƒx–YøŒúî¦J6ÜÉ@‘ÄôíõZÏ(|2÷ÕœëÓ¾)ÛþúÞù-ü0{¥RËêÑl¤¦×G}ÍS#Âhž^þåËQgôïù¬Xd$acæ²\žh%.Ìðœ÷NÀÊ9¨²‰ó½¥*âŒÂ½Â!xÊG µ,˜¹Fňžõœ]s e<(›k}õãä°ß @¿3öc`Ô³ê@E¨’Änxʈ‚,¬>ͺKÜÆó´t–5Ûd§ciôA£¥9–BS$=>Ã6İ\+¬olP™86ˆ›Öƒ‚·]í-n¥³~âáæ¼aK£ú¤g,ŽpÈ­+¯bCÊšNä)Y¬µl›3iDÄë%ƒƒ!š¶Y‡=YɆe#ÙO ¹Bá‘oå×]5Øö´JyæWYÓþ0î¤C<™öF)p±ž‚îž$°‘Ý{ú:b`õ2w]mWD’Xí^Bœ×TÞg7NøF£pðŠ£Â1¼C鯙Ñþ¼-EZþuhö<èmƒ¼?LÙJRÖ“íñˆ¯ä‰! TÏ/l?ÑïZ{õÝg$ü6‰Ãí°ŒéÓ>!’‚F 7>ä5žJâwŽˆ4ÖÜ‹­åº_A=‹ÀdO÷%ƒj¿âí<‰^·QÉÌé a“Š9ä¥vèQqŽ*ƒS1³­+ÔlP*Ù»[5f+õpHÚ’ÁÊFU=ý×–*‘R#é”C!(:2¨‰*"|‚zsM{n‰$¢ (ãx¸T·‘Û 0‰äb„ìUJV8¦ÍÚ©¶l E·Qr7~ûmzöD¦¦[^å™ øk¢cœaão–ûLÿŽÝ)Í#Ù4’ȾUõ¶E°—¬(ßfÿð¹G0œ§ä K^iÜ/4a»îéç|3#õăp0蔑™+G^#Ñ´åEi:ÇFv£ÞV¶7«G‚ ¨}HÅ2š/’H*ÖÛüݘs×ð`bb7Àò’ÃwzHO¯™]BF7‘ÎluO<ˆu®'Ð*”kQ‘Y¯׈‹8ÅhÚ4:æ³üwÛ ê»ÖãXŸ·°ÐÊrÉ#7Hãµ`Å T*#^Ž‹pIw2|C j¥ …pSø@°]¦–+äã_ˆw=§uɬ° pO““qì?¢`>ñÑ,î@v=ä”`œÕ#™#v&;Ävî¹lóBÔÅÊöô½$ËgP?^G}çísÐàÅUg+ïHsIm7K¯Y nJ-·o¤ÿçï:gz@¹Çõƒ1 çæH’Ñ(i“~+5uœR‡‹3[Ì»“‹ÕÙŠ$ØP¨ŽéˆrÅR… åˆ?V[[Árõ WòÊýÞö)þÐxqÓ÷îõ#‘«¯ün<¡>~L|g•&G-¹Û{Œ”V‡ÎÄÞiݰUõ~ëËPµhÎì‘XˆöÂæ¡þ˜ [¥ÓEE5’NòM—s¾ÆüÜ© 8o†bÕ^M jçmÓÒɈjnòB=Ó0'si ×ä+¤o£êg\xÍÂÄQÈð~¾1Jo‰,ÁsÕ²j âͧP³[†hRöe¶'ÏgaÆ0ágÔĽÊ%}³ÀÏmÏQÔ"ß°²ïõL4dŸj‹´´ËI¿ÒËj@á‹ìý¶N…à³:{EUÛÐeªúôˆƒôâàMÃSò#áÞ#œ©¶Š‰¸åZül<Þû7ºä†÷> ë˜:Pë$uÓÄ|UÁO‚â­é@¸ä¨ HŸ§X½áŠ… •E{ªëYX J½éPþTéŠz iª°ÂT²s„Õ§)Ö6aùÅr&à~·`b*êÂpDGIAoË^zÍSzùƒ,H®o_މ÷îo~Æ9…ØoØÿ€ßÝ»õ KXF·íŸÐª«“›Š}ë†éD’p¿ª/±³@â%Mù«)óÇDv‚?lÊ4¹_J»[+átþe^žº[YbøéËï~ÊÅZå®vQÞ %Õ×W’!ãT®Ÿy”ú(Æt%Ó.º"_G·óU%½u2Ç.OSê ò'>Õã ܼ½ª<Ës9ÊœÉ[ñw½÷Ž»!M#\ ßcT.bš½[« „RBL»+UD˜ð†Ú; ÚTôÝTÉV²Ö5T ¾H²Ü6÷GªûuÍSrýW¡}þ®gž_f8| ¼™J¬y¼Kq~KvþGÆpÏÈ5 Ãy„ɽÕÂy3Ú ´Ü»6sÌb8ˆ°ÏØù ølÆìÈÀIìÜý6Šª”eO”ÅÁæÆé‚ÅŠ™Ãê“´¸Od!@¾?Ý`þÚ;û†;Íúêeí˜jcWMÈuOÅgʾ'só¤ñŸ®.oàE`©ï-1¶³»Ü»ŠO ÿIÈ^Ù:¨0ó@ò(°y'º{M\[}&”3ÇàÛÓÒ**Ay«K$eR³xyt€öì¥q%1Ø Ó2Ó+ÉLð¿ºUÅèuÚ÷dwÔ=I6ïí7Ö­C°„²”^œÙ[~§n±·Ù/¨ïgÎhý5;;C¹€!tQ#Bhý d‚‰L¦¥éÐ_ QÁë:ÆDhÞŸ<´ÇVÊXé4ŽÈ‡i?KP‚.vMbYÛÿá&ÉȈ„mvi¦¦I@/µ{5üH¨F7 uôµâ㼡ßׯ¾¯vg/ešéÒ5ø¯Aý%}Õ{ ñøø•¥Z_nþoÁ3  ƒ´€q&H2dÓgÏ:2ÜåxžHbîÏý`Ü5)/Ý_܃¦Ö¥ñîÜyñX¤Òð©õOúbú.÷”á‚ùZGäMÇ;gÅ“odmq[¢ü”‹£®© ñ_Ë3‘¡dÎÔuH°>°ŸÔÉÅüw()wܘÏgÄt!&OÙXÑ„Ëiy¬+ŽªäÉi5ëž_ µ˜aÊ«üŒµôˆø“D­Â‘Éqû•ýïÒÇ«ºÀˤÔìT¤é¢½Œ 5ay‚ö¡Ž‡îy¨sj?*>eF@táæìi?ñ+_ àÓ#jliº¬ãÜÇôYÐa5@k uNcãL‡»£ó`ý´·xX«Ë˜Ê…êhב}EÒŠÑèdåµp‹¨®íIr2/îCzJèBÜE­S¿»/@Ù(Õ¢Rwu†Oþ²ânÑLfPa,uŒ|±På O ÆŸjÞhB¤kÈ5‰ÛqÛñà´j‰2ÅL¶~ KO 1Ÿ†hÈZrÕ(?÷e*ý9PFÞA¼›«_ÚóHü±[õ7@ÁR‹­ZQòµ|Í…}=ÅÆÿbªj¤þÓóFœÿw^øvÔrßðë«×îå’J¯žQÕ{+D| L °/:Õºîªð*t+vÚó¹èîÈ U›ì}ÿB•s$ ëéåC×9 å ×T·FûZ’§ßï2–3xËäZ4[AèGÜc~+êµV`E;ý¸¬®]k@k¦yEo$Ptä¦õö÷L;˜Æ¿]úfþÁ¿Äkä¯ajÝÝ + 9Rj¦@3–™ ÓÉ)Yåaº>µ^ëÒpI¯>á•«IÍA œö²ÿ ãoÛ·z=›r15`ÊëéF›^Tj JˆK2ÖíÀƒM0_0éΡ>õÛ)›LDQ´}×Åö×ÀßÍ*øHÿÚÌy17B[û\¨8þ¢RÀn=ÊrÕã“­Ÿ3 ïÈÚ Í´j>\¿ö;=wüZH­Êî«•¬íÉôZLÝ:7þ`8½sNή3ÞGAHÞ9TÑA¼.øE?€jóN‘U9)ˆW$„ù]»A›ç-¯ú?@+“laÖžõ&J§ô¯},ï ØœÇÍ /S /URI >> endobj 63 0 obj << /Type /Annot /Subtype /Link /Rect [ 470.292 372.172 507.89 382.49 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 62 0 R /H /I >> endobj 64 0 obj << /Length 65 0 R /Filter /FlateDecode >> stream )Ø} y+7Ã2L!þ™Ëéìð¬…ãÓ¶ûâjÜÕ1c}9 —`!\?‹×‚?;ݺ%µoó’â]H#6' ›=üvævq1í׊ã H&pUg56{«xÜP¾_’?p_ò¥©ãM ÓGÿÜÕîR(Ïq1Æ ¢J‚,Øàžß Y Ÿý1‘ú<ûî±³´1µÉ?ÝFÑ’-Xç÷QT=¨³’Í÷3µL­ “8P(óZäÔü_ðÔs&“ć%t‰¡çÔÌ;q9H·½¸­ˆÙHÞä(Ø%TöÅ|úÿêð‘+aÁë$”¢âÞ­óWýQVLæN“J?CvIå^ Õîý 6„56#!ÝÖ³»wR‘ð[ð¸¾?š£g{wÕØþêáì­+­¨ûv>œÝß ˜»¶+àÞΔùùFÈ/2YÞhµ‘@Hï@DÂS èhé"ܾù‰âí4ŸÃm‹EDXâɽ@{S4 3Á&.V<¤Ó0RéHBRvx¦ÿF„ùY œfn­½N¥R™ºé¾kNõøÇžƒJcÆ=]ã°ª[’Ï-·¦ÁN_ù½³Å?ðclnã“Áண[ ÷2ÖóÔì‹=â•\vqäú>jŒÅÚá'ƒ¶Õõð•m&uèÄV >™ݨD¥>Ú™-§1TB#™¾Lù•?¶a®ÆaV¬]$Èì²µ®WøKoÉΨ «}ê«·"–á¾ýl#ÁYYû(Û€õÍÁ.À½X½SA—³Š"×FáÞc‹áï G˜*É׬ ¡rçýJ[ªS0ô^Ñ#åשØÌ'5È'AöÆ¿!ñ>E‹ÉŠKso ©& ï sb÷³­·Œ­É¦sQ?˜™¢XjÍ.G¹<ÈR–Ê`•ïáTÇà«Ðö 52Èøì‡"üï¥Þ?qc¹ø5*bvKEÚ0œO‘ôo(ç¾Ù¢6kï‚ï”~?VkNÃÙ‹Ê»÷WVôåMeæo_T«½”ø{¶±Ë&¾ŸÅ1y›áì¿e¨óÜì)?ô œÐÅÁtÎ \«Z+8\ÊÅCwÍ>ýkl oÅ:ärÈ/ ^kñ}²ÚBã`ûé^ûAÒ@)6ï¡&Bq«¨\¤&ö$ÁR#ྐྵÿŠwÚÙëÕ °‹›õe­¬È¥×+âAŸS.Â"ßYjˆÓþõ‡—…zsöû[2¿‹5ºØ!0ùþìÖØ>ú–¶¿¤Ðh!è‹®E/eö7&6.­ì¸ûq"å®|;—âV¯÷Re#åÇ$Ï€F¯^úØE]îc‘üWûnÕbiʼŽ/?w¨¨@í-JCð®nC«‰ù³zRH`À°ôéFé_BÛåTotbq°ˆ2b.{nŽTU5z U¨¡ŸdŽ¨Â¶6/„(éÈ=~½aÛÉ)ç™]ci÷a G6©Ž?  ê!à„–xÊ0»Ja~÷´Ž*üÕÔ©G V¡‡éåØêý è¼e°ùÚV^:Ä)1é]hâ  R…‘+Kv9_ ª‚Fþué¢.>OF$ çÔ•¯a_Ʀ¢›3Ya -Òú1 ³+°ýXd³}¼ïvý"A[QTGk´ôà5«“{SqÒ.zµpR:¯Á}ÕÒ¯oâ³]¼¯}ŸrMwPÀÝAeqž°Ù×Åð·@Ð輇ÅSÆ”" §“Lt`t®Á¦®÷o2<ÑúõêÜžõ”ÜÕ·Ç)ÏÎ Ž)¡ÚÒý‘ð× z†ài5|º¸¦Ni¡/'ÿ󦮄²ÃË.f;xÈÚ³ëI¸8'·¡Ñ á\ å ,G¼VBxæ·^ôo à!I½€nj;;nãíxR÷™ú¡¡‡ÈÏóæK¹*/†¶~§Goh[G$£OÙÔj71EÌ,‰Dò¯ € ›n1RÕ#pb ë¶®úÿ7C9¼ŸÁ@„ `o„ñƽ´T¥-pâ§7ñjäY¬5žÀ¡Ý½Mئîlj& £´|òª3ƒŒ<›;(QÆþAṅhy´_ž%²¢_Øj9gôŸXô»då½8ä(+.ïú3û™1Jµ~^b"7±ýV;“B »²V‘TÒ\c:¯/­bÇÇâ¥j3Ü&˃à­ÿ€æa+ •êvùã”⋘t’–Þeà N ¿ÍžÞ®§Au½-úì¿áè • ”Sñtöbeó¤¹2Ég‚V1~[ «Ò“]9[zÌIC{Ö%ÍÍvõ3UUåtc¤ŸäIú²ëΰ£Aë¤8ýÚ5s–ëø‚cO‚‰9aš?qΜ”4#ÖZ7ßÿ¡ü©”\|¤ƒl#L4n"S‚`Lš©‹³Øšüo€G*á »4Ÿ'WñŒ"Õ+å7’hÍPÚ‹ô™ 4N(%â´ÒŽ©i¾Þ¼O«ìƒGòÍ—Áí»oKÕ¸z¢D\)…Õ¬ˆ­D¼yøgÝôâ^“‹ xˆ ŒÚÚÛ‘èIÂÄxFwÝ€üR–»ZLïáP«’hh½CçÕwJ[åSWN”û1Rmœã©±ÆY!fü Çq¬Ío@T¤s(qÌs¨µcîÞr·°„Å:Ðà4Ô§G‘·\¼¤¨[qðpÎ%E ðf§îž~À¦þ¢üëNl0»Ê0ÈÜ`ú¹Ž;:%`ŠVƒC¼…ª÷!Œþ¢¯Y"Uy8aÚЯáç$>ëH1O”МàgkN?'_ÓyWÁL“Ðþà°‹6(Rg gm`ó8ÌiÕP9jngÐÞ5¾.}8ó`#Ä» ëh÷·î`È+ì´AíÉ^c_¬=~¶ñ|Éä&šJ·ºÂZõ½b ,d“Å)QC…72—9Yþæ2*ß?Â0úáµx+”üuWM^˜‡Ü‹Ô›h|Þ¯ ²í|ÌÊÌE-{‚¬GOÇ$è¿Òã3R?2)<ÊçÆ§t;uiž™vöP7*½®äº`u&Ü ªâ$fvJ¥¥Þ•Ã=ô¨ïN¾¿Ÿçl¥\—ö4_̧ï×ÃQ7·!"õˆ¨äʯùvR”zMøùƆ„€Xóúb°C4?›·q¬e¦ÇÍFÈÄóò«þä+ endstream endobj 65 0 obj 2784 endobj 66 0 obj [ 63 0 R ] endobj 67 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 66 0 R /Contents 64 0 R >> endobj 68 0 obj << /Length 69 0 R /Filter /FlateDecode >> stream ŠÄ‰·|äÅ-¬ÿŠ{ÛqØIû1V˜Ã„ï³Íû¢á“ǯ%ðœÜIHü@žUSX×ÐöëÀ¡Ï1sWó¢‡í ì ãwdÜŠj³;¤?ñŠýƒ™„Û1t¾ù;­G³0"œÍõ›û(Ô–·|‚ÖÉY¯T•œËj…F„JÁWæ&Ö48N}5ÅBd$Ÿ:9v\`öçQ£TwKžz¨ÎG«Ž =kÃI`Úø06À«£BjcÒÆù5¨¿¡óÝÑÄëÊÈÄ…1/'þ™ÇåcR"¾¤ÄóŽ ¨6Ö1‘;Ýò%’‹ |У5¡…,0qsø¬¸ä6ŠìÕ;`£#Å ,Œ„Ò4]žF/§„šoôUXcH9Ýâ‡4ˆf1gIü¿²äv"Z:éaWeS-.mÊs”œ«P–p–¯Š™ŽcßT—…RhqH-‡ô‘Vóžî_K|5Jˆ‹ !“z´”¸x±%šš'ÿ bç0ã³1Üïy†XV6³/ÅœJXåfÏ)0dV•L–H—† ‡Ù¸X¯ºÕ¸aˆ#µïw ½êƒ‹2¬Ÿ@YŒ£và—çáVSgU8r‡+rkw‘‘%vX¯y›Õõú (ï1w¾±"ŒïÓû'ˤ‘œYåª|v®¬a ‰ZÕlÎ(Ñ·aHGNót4 ÈÛïmÓìToWu4Æ-b# Q‹wƒÐ±“,¶3.Oî#ß\X0˜¯g—cð] EƒM!d¤ ՌڶéFVGÁ;Ú9av‚ ôV÷ÀZYŽøªõ…ÄWtœsËMüýz‘’˜cP ×H‰àsŽ«ªSNGs‹oúÇÎ Ÿ>·êiLLñ Ölçk˜±'!ü)ôÀ¬‰7 eÚÈÂýíb|Z1ÈÖ¥,cç?'Bþw&ºF*ç_ð¨à-ÏÚ5²“OŒQ}œCìýÚú_ô4öh{2® WèKÞ ýtÐî;2Ôë7‰e36(jæ>UªQ A PÚ’´ ä`}ɶdýgl—,’eè¤Êd5z‡A)¾$ÀÎ(Ü?YîCÙáOhùàÒ%N6N @5,–)ª¬^m锌AÓ/GŽoGž¹õú寅¼¨s’sê&/Ôd¢–°,IUÅb¡é”²YîS(xÉD»ÁwM°dr¾Ài|ϹGåJäy¡fÁ¥»2HkNéƒôœ»µgPätKç©>?oVæ"c´ûs+’uËœo–6—Âós*öÈ 8FSé†×šŠ}§S0'Íèæ ö¯‹”lwô÷P žŒL–é}œé…>ψ½a`ÊÆä ]I¸e´`ݼnç³t§²ÁBÊèq o§º“ôïµ#FU„†´)ÃRð¶/tB™¦ÜwaQl‹JÒ§o®N!;„w…©yn_ÏÜ;¤è§Á²vÿ"žfe¶rXJô'•øµ–ÝcYéèÙñ‹æ{ß¶¹Q.ÚèÂsˆ‚™ØÙn ;K€C÷þÓ‚ßKKô¶AœûŠH³Kÿoò20¼ØP,7l^ y~Â'A$,Q*Ï=.÷N;V5Í4tž' ÂèÚ”:ô&5ñ”q=é¬ìÃ3f÷5)oèQ”©ÛßH<!˜O1ø¾ÆjælQØ"òa 勞 Ìot†kÉ-F’5´vˆ>BÔäÖdœÌÀY›På¤rﺙ—®ééÐ=9ä%Þ í²7jÏü‘±íR/Y„à&o>QhÇÿ§ûº~wÞ'œl¬vu4Ì)N`ÑÅÕeO/[ù¹MÅYF$ÕQ¦ÅÀp›¨‚ô‹¼£ÒpUn‹Md]:w\b¾M/pž·GÓ gŒÁwV‘Ù¤F Q‰ˆe§ø×ÂéY’Ý8Zö8ôbµSö[D5جV‡Ö;½Õ‹™ˆû:³Ì‰EÅQØÍ„B‰Å LÜW5²3ºÍ†ï‚Õ€n•uùeêì%ª/ `[j”åñõl}3Œ~W%7C ¿Èî«Îv0±—ÍÒ9OSGˆ¡Ú øùS·–ÊR<}µ_u©eXñG€Ì*s§þûßù®ogKÆí­>'zèßù'‹Ø¬b_ƒ…·a1¡—úª«`Ïy–¨ ¿.Y´ÜÆz‰¶´[A€‹=›îS£_N`ˆ é6òŒÄ7xãÝo«)~ärQïA «I>µ¸2мži, &›mÁúP¤Æ<×m–[¡öžaÉ]zlt' _Å7±tDý‰<)e$T rѾI•Zó3ËT¢ß/ëݧœßÇÓõÇ=Nßë`¸¢mêI=FHÉEÙÀÂêv,“­~ˆ#{ôø¡¦)½1jXº%¸‰’è$'&ýúF¹n¹*I¼ïPÕOS¬ÃèG÷aŽ‚ðæK x!-pWcÌW‡é²‹ãK!ˆb'MÅ(ºä¬f§^Uœí/½m&#šjÛQ"ÊÌX‚‘Í(`p.Æ?W´å®,,Bµ>Ý  ½ßœ‡Ù\ﮦêïDŒ&~Uß5øB5ñÈ kE"-r!}I?‹%CÐ:fo9¯€oW/>zÌ(‹ €l1%.€8ÆìVÒ=Èšè]IØüNe!箜’÷}=õ©z8Bí8xÆž}vѹh7ÏÇŽIš5ƒ£ñã°Ã ©‹ÓôøtŠ´Žyüf&ä…µC!?A&ü\z)€€CLÐÖœ¿Sáñ”•y¢8Òy¾´)úJö‰`V‹úÓ"ÎÆwþÄØçì÷EÀžúĶN©ä†6.=ŒÏIœVεÀèqèj&çBv±e»t0bø¾ËˆvŽA`WLŽÂgv&Ì„À'ÒcoR?ä£(IÉ8îŒÜT/CÃ^xßþOZÆ%—~¹ÉdažSh@íI­¡§&…ÂÊgH™±d†¨­ý¿éçG]t›Ù*Lq€il,%¿r‘w's«jkÂú‹û>7W5é8ñè0’NV#çÂV&Vf‚æˆGYíI7i’,'' „š#ñUõš ³ Íœô‹ÙúBD9üIgI®t²n ï’ÖÒß+@¾÷Ò’ö•ÒUš¶˜ñK¤¬&S”>0X.¼%ô(Äâ!ÐYÍrs§¯œÆ4©~Fd ÆÐú[vÎÉ ¿HVWÜ¥,°µ†nC# à¬sZ{oÎ[b0…гb„vg¥ÂÔ«Èuwì<6÷‡Á¸Þ¶ï¨:Xû~Öìó» ¥Æ%ÖmÞmÂXøÕQ.'Qé•­ (ÆÐoCýk–,DÈðÇñ#ça·–YÝñ\Ö­ÿžD}Mè´­ãhí"~¡C‚âœ%Ï7ÕáÎï—¢®–/™NžúŸÕ”tø~¶T¶Býí‰ë¬AEL¾²µ„×ë“ú×ÊqïáY‚A± 5[x™&ÖS§¶ží2çô„  ‡"³«Gv2…r6C^dYDJ]dû©3z‹À Ï;(ÕõûÈn¯Q6œxîÔkI6?D½ÀÍ/ñ£›f?¶µÍªç"J&H}òËÌÑzó7Ëç ¡˜¥j½3˜+eü-WO¶« Ü-ÑÞÿ¾m ›ÿ`W¦wî 7W¦9Ûß•,µþITÊsr™ðê‚Ô<…õJ«²\n‘¹~µáz^’È‚ïÀ‰/·eË´cÅ=¸ä¿l[2Û$ÜBhŽ |›H\„gÌ{G…d2ïá·ÕŽõ±®‡¡[ÚŒž.}9i? ¶»f8¢¹4¡ç}cÅc {*ݳ.$¹cC²uÅÉK/Ñ> endobj 29 0 obj << /Type /Action /S /GoTo /D [71 0 R /XYZ 61.192 484.713 null] >> endobj 31 0 obj << /Type /Action /S /GoTo /D [71 0 R /XYZ 61.192 452.313 null] >> endobj 72 0 obj << /Type /Action /S /GoTo /D [51 0 R /XYZ 56.692 680.317 null] >> endobj 73 0 obj << /Type /Annot /Subtype /Link /Rect [ 221.945 666.657 258.003 676.975 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 72 0 R /H /I >> endobj 74 0 obj << /Type /Action /S /GoTo /D [67 0 R /XYZ 56.692 700.159 null] >> endobj 75 0 obj << /Type /Annot /Subtype /Link /Rect [ 239.974 592.96 276.032 603.278 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 74 0 R /H /I >> endobj 76 0 obj << /Type /Action /S /GoTo /D [70 0 R /XYZ 56.692 627.572 null] >> endobj 77 0 obj << /Type /Annot /Subtype /Link /Rect [ 337.192 592.96 373.25 603.278 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 76 0 R /H /I >> endobj 78 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 472.954 97.25 483.272 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 72 0 R /H /I >> endobj 79 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 440.554 97.25 450.872 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 74 0 R /H /I >> endobj 80 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 408.154 97.25 418.472 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 76 0 R /H /I >> endobj 81 0 obj << /Length 82 0 R /Filter /FlateDecode >> stream Ô˜ëñK³ŒX¢1_ñD9ÿ‚ËÍöÈvÈ ðûÒÞgDgñÒŽ©ÃMùŽÇajç9Kú.ÀÒ‰ÿFÅÎå¡p!¿Ã^B/F¸aj“ vñj{#tk¡#ƒƒâ(g©BªÙÚn57>id§ËÞÒSG’IKÜ0‚CÛƒyw¶ár¼¬Zï:f>—äjÕjÊ'~aHz £7BaL¯Â ñƒƒ5ÛÙY·'JŽ…ÏwdÄ/"Ì—”­„6òb×KŸöAú߸osÕ{>z}€'åñ «8G2½9WåÊäê)IÖéW&,;Ñ£™>LNœ3¦…ý8ŒoÓ¥b:֞ݧ¯õÙ½$Ÿ`e»5Ú]D>”¬ ¯mª…¤‘EÚQ, 7šŠÛÎfehiTµöIJmP¶Å$GD™½EFù‡‘žLÙ$çR¤b8ëþ³¿W`_òOP‡ñ²ÌË8æ·Œ\q»ãF"ôF cá›=ØDò'%aFP¿iŽÑ…bp@´bá]2ÎKˆµ© Lÿ5à/Å̤b±ÓŒ¢¶°àzÌÐzz™Qçl`8P«ODÌ´ ¤ÞBlG¥‘”<‹Ú¬pDÄ5®éü%ª}ôY²ê\٬΀®*·ÚDê·_¾öOËCõk»ç¤;Wzˆ,ͽ²ÎæÈ‡# ¿}¸RNËTƒŽr(h¡ÐmÝaã,ÿ!]6>æë}Êâ·Ó*Dö_~TdÅ™‘!²ˆä…î7¯¿q*¡JŠað ý‚Š1IÍÃ+š'~¿'ÆìÊE*:­3a5TÏ&ì1ȉ" Ñk£]*.éêr¤|)(áG­7®¹‡†ûSl[o¤µo6ǽÝô“Ó¯é/ìœÑ èJŽ¢SÓÄn– «ú—Ž:Ðáñâ¶XbãÔ»ñ÷hMÀÌlé½ .Íê¯{¯q/·«CI »@Ò71ÅïíU6áß%~ÖPÖi„#Î¥äüú›I“®4+?¯éª7Xq¯ç÷E¥äã^ôƒ’p÷|çR:~5¬÷GiD¼’%g±/ßjG‹¥FhÖùƒ·ÿ¦¹ „Ø—ò©®‡ŸÞ•nÐégŹê¥ïêÆ B/¢gBlnÊ% ‹ýTÖáƒg·ö¯™¥ïd@ýž€ ¶ òmÇ? øÇCÂÆŒ’ã,Äëtz^8Oa”·"2$1áxã"/ Ô±¢¡yø$S±?Èv´Ò›"ÊúÎ wÉ¿ÎF"‡’ßçÛ2iäÜÜnÌêöèÜ gõM°” 2æ*E)⌬Ÿ°ŠP;„ïèÁ 9¹å* ‡¡”küB¦=ó?§ùÐqöEó5–RÊ ÌÊ{CõzN²ˆÊŽfó!œs®t$gS›l!A*ßÛsX.A_¾Ìã}rѯ¶{6Òö‡Ÿ—Ÿ‰«†Î†ÏÓR327ï×ë’Šë;ÝIˆ^JËTPïækfÏÐ] —- ¢Ñ3Ç€%jkÑUß`„Õˆã™_Ù¾±‘qÛ\fqzhíD‡›mV.çÜ>˜O€›Ç2Fp­÷ɪç9î¾¹û×. î5gi̵g“‰zÁúåy7ÎÀ·˜ç‹Ô¡Z•2 AÕã9hû¸ß`ã~™–3Œü~"Î 02CW'j3üß{٦˿uî¡Üd·9R2«n>Ú·ú¿vÁ!œdµ¯¿ˆ…Œ•Éë$Ü™ 58š'Ñt`emË»ç.°å=Î hxØ)vñ\½ …=ÓÎ c5Qr’ôœtx;6’¤é±«;÷¸¾èÝž8CŒ ‘Úq"ÕÂq/þ”|èçø$ŒÎˆÛ¦qÊ;´S’ôõÕKæ°s­ò)ÅF]i/À‘*”ÈÄã £Ãxü‚„E~—ŽW÷²WWÙ’â±òëëæŸ:à8EùáIKXpb›Q€Rj0>WúíÃûí“gëI‹z›¢ß23d‘ÆÇš?N ¥¢…ÚzeñNy®sþß÷æLÛØ?ˆ7E äÑS:)°Ж è)ÝÔæ¿,HÁ1Êp‚p.ñ[£Iô$› –'¸²†õŒ\¸P|0\=ð*š•ü°Ê×6‰™}€KÂê9Zi•ð`\of¬Ù 3`[ƒ´Fdøú}Žedhñ,‡âÄÀŒ0þH2³S¾…ù‰»m"ök… 9­ þ\U™¹š8~úÈbÇ—œçDB†‘Íz©£¨ZdÁ‰al|’ÞØpyâÃÝ-’Ãã'Ôb€ÔƒÓºgµ¢ÅrÊi¤œj*%7ÿx‡tÚî3×Ïè~«þÅ»-³ö°RBÕÔgòÆ«nBʯôI!£à8Âαy¾Éf{gã¿ö,»U¹eiÁ´vÀ›+\¸î³¼®~«%mpÕ.Li-m¹†Èå ¶ OwwT@eE¶ª¡•òtb%ò½Ù7ýÐDJ³Aœ{¼Yšb ”—ä|Âä°í$Ýyá`qû:.ê dhì6ÍYï;v‚ÎYÀ¯Å”‰`Égª£¿J«fzÕ˜ÇÊ ´O<;³k>нHgoª¦g“3;•×?V„,©ì¬&/æÐ{íßzzáâN«€¤x=mzÐæ]f²LÍq‚©(ù~$«A¾nK›þ «ñ¥šŽ[ÆIó×®e{GU0A[É >å†EG=«ÏrÝlº²~ty)“2Òº2æ]“²d¥.{ú/ƒÀž[U°®M HÄäá÷C•Љï?i‚–hÐbÊÕ÷ÄŠ^ñ›™Õ\v| DÜÜtâlUÇùä7ÂiF‡ŽÛ•AG›ÆRÅ-`”/ã öôõÁiÄGÀR6=jŸE=‰}޵ì}ÃeáDÉ¥½ÖÒÏ[¸òq3åB¨|kz«‡Iñm·S §ÏïZ ²-Óöá(î* t/çôäGP ò÷%÷skØõ>¶ZJ®ù£–vEJZÇ¡=Fµ(OLFªœ h{+”šO·Ž*7¥¯‹‚jâ÷ÏYi|vž:›…eüÕ̤Á8:¸5Q›6È¡å`öùPÿ$œÍ*XÍØ†X mfxp}Ρ­LÒúÐ;P¸ÒØ2§rO@¯P³5‚ã´6­íbAÖùY6®€ê%}Ç'¤ÿz•^w²=ažM=Òb)I¾ù'»>¡(sª×öØîõt%6~>×KQ¥2›á1ËÏÖÊO_åüoÑw2ö´­§qDüîpI uMHÜÕ$bhe]£\HÆR‚%£w\åì)G+öA»Œ™ï[Ø9TB=!š¾n(@ÊÝ)©Ù#-RWÏrn$tÇ ¥¯"°J?ËüØÀÛíŠÈ*KE¨Þ1¸à#©"%,º+¸„ç¨àp7¾©4®¢>éDÀÓ”kÈE^×9€þRÉ/š´ÿ¤°õéÒ›à‡vúö&ߢñ±5l ä@Ü»áïb^ÝàÒã’ˆÔ‡ãÞèRÛ¡\6åFýgÃgym±ìì¹ÀžèÚwujpã1D.¾Ì;1ÑNA¤ óÃÚs†œà© (,ôR×`6æiêK×™™zÞqˆ‡¤G3ø‹r´›åÄŒbÛ©…Y†Hƽ ID›”ö0Õ\@ ”4µÂ±!Öþ6Š”'!Iýê9ˆaÄ´Bò«f[sð±ôW»À¶>®ó Nÿ¡ë׿—4Ó‡õâ$£ë!lƒóìä¾0†c²#‚¶äiºñØxX­ î;ÉÍÄr\QÃ?­ º;Šü­U  ÕV˜õ°×6p|Á[EŒAhÕEÑ á´¬ó]a@Љ§nÅd¸ /&ÛkWíMC /\U°Rä€Ðšÿa2%8n*Y™BÝôcÉö!‹U@ZÙi:¼pϬ£ˆ4uDÔW3&õ šT Ôׯr÷”¾8Æts&PÃ/òàITË\¶­íäÑ]OG üt\Æ@,h(•€Á•œ2º¼ïM3‚E½®ü’„Ū=ã.LáG¿€SØe­–â>°ÛŠ JMÀ) …].ïïÌÆƒüމb_’T¹l2’8mª¦­Ô£üØ ó À`‹:}·¨¾¤¶ÈŘG¥“)NÂ]#_C×Vê(]WÎ9„¬ #(§Ð¯:øu¯$Òm=Ž@È÷ÁäCAüŽÖ»àÎj5Iîåßݧ¿Í$yÉe0÷‡ U¹ФÚên‚ªúQó=.™0Ö\æ ¤S.D›š*Ã1ó˜ùü¨âœ endstream endobj 82 0 obj 3632 endobj 83 0 obj [ 73 0 R 75 0 R 77 0 R 78 0 R 79 0 R 80 0 R ] endobj 71 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 83 0 R /Contents 81 0 R >> endobj 84 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 699.716 97.25 710.034 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 72 0 R /H /I >> endobj 85 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 588.116 97.25 598.434 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 74 0 R /H /I >> endobj 86 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 555.716 97.25 566.034 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 76 0 R /H /I >> endobj 87 0 obj << /Length 88 0 R /Filter /FlateDecode >> stream Ëü¨“¬EÉ»^½‰Ù[rîG·Xd@٘Ŭ¾*FÒ;h08}AZ|0"ÝŠŽ+äQ(ãw»©ˆ†ôÃjïÈãOU ¬@I™ÉC&bØòKßîâ÷¦€FJ«J3ÔfF^­^‘›Ì÷æÔíþÝÚ—zù×{eÅ¥øŠ ˆâˆ CÏè–¹K‚”üÉÈö€Mf´óD{.’Yþ±`Ðñzž|æ}îÐøe{8Æ*²¨r2ˆÃn ¬–óE+SVôÂëÐìÛ ÖT8H=VÒŒu.ߤïtTf9–Ó¢›4+=[ÚîšZ¾‚2åLT«bêø]X|ð˜%s{6e­Ë³ÓÞ9\v=º3ì\“"Ù–Ûeãüšß.nóÖ4ÓT—¨ÍJºÒÖ5¤íÅ¡—›­8uh|*Œã´p²++úd‹£¯ž,T@׿i° j!òï’­aUjÑ v±Äf·Ü@×]:SðÀŸ¶çCj¦*’¦ÓË—Õ‘ ÓlÜ4jöþIv4vÈÒ©¡%ŽzÕõDÆ€ï+9´Þ¤J˜[ ¦3#¶Kž$®õ˜¼(ÛY/“ÄÅÿ"%ùa³–—Ü[¸±Æì9¯ÀÑÔ±÷B½7{‘ yð]Fõ>½#£ª”øx6Œëããë­[èXo;Éx’¾ÄІj.²YºL0ˆ6Eb(0Š·Á\@–«™_÷_gBÇ⺪a{‚þÅÒÐðâÓÄtý™Ð`‡Ð™~ÜÖPûþÝ6ø”V^ŽÒß1Á)?åÆw| –g‘"6Ššó‚[®Æ0eù’Û’Ó‹‘’érS;áGöŠºJ x6ø8 +µs´=J«¹.Ýþòàá?¹¨Úòí˜Æs²±§‰C^ÖöCa°wrhM•—Ñ„îó5²äжú©ø€ aE Ë:Œû[LÅâÜ Ë^¬*FätÌ$8IPñÝ"nˆÙ £ÅÛ5ýñ)3ÞŸ°Š¯fi\*­]’Ö!¶Eòd^Lk?ÀNHl­ûŽˆ~üÎGîè^~Ü8 þ–oú¢Ë®|M=;ãq9*¥í=O¤n47]t“ n)u»Ñ96oø”›!0ëȨ¯¥±cüû)Ý‚£Š„\n<ØžâHøúÚ´• ¼@@ø-yxÈhÀÁ¸¿Zå)Hž†6Ìo¾Ã)2†*- Ñð‚ü€ôrHË!ÎôNöÇV’X#ZQ@ê_²ý·,–÷žgV„,&œEüNB8³ž?OØy¨š}aû¡à3nÞ©~oøeŒ.ÐùqØ&µcWÞŸÒBý—JbÌldp jaÆ‘WDëz ®Ý¢6Ô’sª7ý¹RvPiU‚g‡ºŸMYÈÆ× PŽ›—D8yÑÎ š ¼¸÷ŽÕ)¬Äì,…_L‚<„5źu}€æ5+ë>Ðm5räÑx9¡vNe|wpª šÂþå¸&÷\§ÝÚ ƒÚ`“áµÎ°˜Y é䪇ñËR°÷ë"gÄJ«O°屺M5µHè‘I*†‰Î%#âßß«‚ zñUß[ðÈCò‹>›Iˆ‹71Ü“DìÊp•=»±Í¬[軆•)œ¸¹XQr<Ô“Ù”ÇqÏï—Fç㥲{CL~ްo‡eW±”GgÑ»‰`éë$ƒOm‚*âÒ¾#Þîò4ᬼ/Px>‰¬_fDÄÕçbGSÑéÒD»Kú®÷½¤ñ϶>;×$Ù}%¼1áX5Ìøi÷¤>{ð÷9bðˆH(|Ù"57öÚmÝVb1’ Í®~•ã뱂ãïh£"9‰÷ÕçWÌQ …´-€†ˆF†>o¦DŸo˜ù8iÀŸ îâÀóŽäX¡®“³žã^­zöówÃRZ"ÇVdº Eóï›xóéßí%oŒ¨im©ábqߣŽo-BO<DdT¸} -™º!aÃ; ;T?ŸL?>u`†ûÛ]4P†J†>æ%æI,%è„sà ѯ–¶¢˜”ß…+ݵH7ÔÆÎ¨Ì…<¡ó„VÃ@Ìü„™“~ljYcvε٧+âÇFÿ«œÍtÕ›â/º C¬"Åx¤_;±ÏeÖ‰áõ£|W©7í±ê­Hò‹ߡd¯²oÃÛôdžtlÆÙ*‘-ø&lÔê'ž»4XLX”(™IÙÏß4¯_"=Ò[žù ñ'bát 4fÉdƒ4 9 í šŽÂ´p;ZÖïãí O䲢鹒!¹¨o3Ñl7?5ƾÀï5„Ëh#ìÊÏ¥½’§:°—0·Å¼¥Å yÆxŸšY9Ž&C'\oTÑŸÀ† ¾“TåX½/DèeÊ¾Ž ̤’"ºëST5þÆßÁ,®®²ºÂ ?°@ŒÚw‰¨¤²¬FîÔš†YÂWdüýë ÄÏò¡à€ñöùÎn$g—H‰ÖéÔ뜯ǚîúD™ ¤ºMñÚk~]ŸìV°RDP!lÚÑ׈å™+ĈꮴäÝR‹[ož]RÒÌe¡9‰:˜Œñª”Y‹µf˹+ßÅyV̶_ë¢ œU÷ôůcÙˆ’ÞÆl’­ÜÞCÝŒŒ_Uò(º+y<ðøOÖÐOKlöŸ¦ôp?ï-Ѓ[“h1µ³T&%¥ 祖¡SdîG^CÀ@ˆ¥çÌcÖR½ñþURu()ÌEKýìºÆÏýI•¢OS0À*r endstream endobj 88 0 obj 2560 endobj 89 0 obj [ 84 0 R 85 0 R 86 0 R ] endobj 90 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 89 0 R /Contents 87 0 R >> endobj 91 0 obj << /Length 92 0 R /Filter /FlateDecode >> stream †Ç#†|ØµÜ TD&¾g³ t–"” »Ývq¯Á%£ÅAŽÜú/¶"µOÎûæ á¢ø$Δ­Ã&ä yÿ ; ƒÑW’ÿù{ë¥,²I‡?jŸ’î>ðˆ\ìÀÈIz"§¡Y……ÿ"ÆE1Èaxø1‚1Ì Åú5>Dþëé‡û&ù^ÉtÔü.&îN5G·œ½µöïÞ(€‚?ñ·ë,LS§nДºCž:œŒb½ýð6ͯëõ‗[Ocs]ƖÑ- ‹ñlÕÞ3ʈÀQØäÛÔqò6îc‘jìû>H h~ nŠ×&)ü õxÐ|$Ecfqð…¤%ô^íÙ Yo¢Ëíd'“¶î“ob•ÙòÏׇ#‚ÓG;4iAb‚Ý«hËFôB4ôãR¥¢sÿtk€ ÊìÂLˆçÛ/.«OL﬘5Õx­¸Dù¨~fo\¥~ATjàVø#a80¾úØ-C„´ì‰½Œï$à?'Ù‘iŒ:ýG KpÿÊLÇ™ài¤:—eÈ_ñ2|Ÿz|&—)s4ô¬”„ê» ‹8ûåÀM€õŹœOÕG#½n«/Ľ ŽÇOrô¶Ù`°b¸äì*ßÅ®Êxe¥’Rj ‘zîÁv’fÈ>PTí›z¾˜>7m3#»:_’á«|çûÁ(À°&BùîQ*ûŠ…Ÿ¢D(A1òxò’tSÁ³*¾[–òý5Ê¥·ÈâËi7(^}Ê8ÝT镯ƒ}”é’ó=e6Óð =?¨÷Ÿ©D54,b¨SëA“>o¾È^ÖüCÜ4ظ?$ÔóÕÒY$k‚èÕîÌ$¯½Ôi—ÄÝaœM˜âyÄVÇov5yJxkjä Xî0¤À­’4»z úñÝÑ_äÙa õí!ÎNnæQÂ;€ t)nÿ£¨§“2A è\2- åAØ/††4paIE`¬]}£†áWcÇ9fØÐgx ‚"îk]:Ž'©ÔŽÅC¡Î™˜%ˤ9rw)·šfú þÂg;g"íÓT,¢˜Þ°ÖŒ1[ò{…”88²¨jMä0P3É}î†Êˆ+ƒµúm{¤æG™l,èÕq#ºá±+¬ÒÃxåHóyÿƒptÐþÓåFñ Î7:‘ò_Kƒ-³òâ©s¢¬#• ‹Ô-i÷½ aŸ.þ†Õ6ávpNS{ÛØ‰,{Dì@¼ˆ°ÄÂ\/x òƒQš]Þ7M>íDЃ Ö_#A¯LK×Ö!§¸ U½»·³­5j©býà­¹èãßqCþÖ‰jñß|ûãñi Ib(Ta8–›û€¡R endstream endobj 92 0 obj 1216 endobj 93 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 91 0 R >> endobj 94 0 obj << /URI <9BED5D471FF2171AF9F35EEB05E8E6BF672517FCD318CBC2E91B65972DC8EF01D9B8F29968079C4DD56C8ABB69EDFC6606FB028B2BB6E6490D858C163034CADC> /S /URI >> endobj 95 0 obj << /Type /Annot /Subtype /Link /Rect [ 187.537 564.613 229.623 574.931 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 94 0 R /H /I >> endobj 96 0 obj << /URI <7FACF98E786476097887FD3180F9809918114A2FEDD9F13E7C331321FD32FB62C39F371724242820A6237BF8A34E1803C16A68A9F88DAC0446B8994BE5048498> /S /URI >> endobj 97 0 obj << /Type /Annot /Subtype /Link /Rect [ 249.687 564.613 284.755 574.931 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 96 0 R /H /I >> endobj 98 0 obj << /Length 99 0 R /Filter /FlateDecode >> stream Äõ¼DI±r)ÏqRUÞÏJ£ê*øF®^Ä®‚ë®Þ¹â³Z£F*½½å–Ólvã¦à˜ë´òÈüŸSå¹¾o—š”^¡°ž ýŽÌƒZH£w),>cAtxÊ@·?ü¸ÅF³k%—dKÖyëhÕêNVKÆÂ0ª×¨ùH%_ö7jk'9Çæ•„ â (6ÃÃ7KƒõcŠÝ¶ƒûŠ\ ´_=/­U8œ!)Ï{°9aP¼scbEXïD©úG›:¼éÐŒváÒì瘨á*‡‰Dä¦oÑ*×& ºŽOžåé‰xztí¼råËuo°0‡1¹·ÃðLºÍÂioI|ln3 LA®-[\%jw¥Ý7µcϯ’%À†íA!]ù 8ç²Gdª~¯Vkdjã±ÒYpXÌãÔZËýŽÐw|¦×˜½(8§Ã%¨ E,¶ÂI¨qß^Ð|‰¯ü”ÀSÇ!ú«ÁÚæ—2,jæ²6ðÊúÙg b=•1gŒ‰¤ûÈň|fû›?ÿßâ 5¶&(™ÄŒÑškBéúuWÜ/~4O39 ¢,Iì‘U,yþà²Ø+Ym  ø­õêÙZ{^áì]Á¸¬ŒO¸ åo{"2≽ ƒt™‚Ü„ýPÅôšévÓ}äýIàð&ÌØ „ìº6@ÃÄ‚ˆ“´9PUB¾°vˆ{|µKçv‡Æä¾ùÝ2‚XLnР̰Šù`ÓÒ…Æ^TNGöFïØ—Ûwf²#`ùˆW;2S¬´˜w/*Xª´cá¾Èé-V ÖÊoæ‘ï^2›d&·cä_Ý£žMUõUNªRé 'ƒò_X(‘6JˆÂ¡ï»:‘ÀQë 8ÂIH6B­TI‰¢"‘}xqÔ›ìŸACÒÖy¢@ª~•ñûnE]6E‰/mÀ´åáè% ‰!Zà…á#¸ÉŽ3r¾?§ÍÖáíUZTz%W@ï·+Là×0ºk¶ç»Mb„3@ÍÜr ŒJ:ywRŸ9;IÇÝí®CZËÝÿ¸ÿÏÅec&M†…’˜‹RЧb¸­Hº&”­øû@‹Õ’m=Æv¤í-G‘Ê!'Ò Š!ÎûÖù½?]õŠÝF]ÞÚÓB¿ƒ·z•åuÁýFj»Ü?…}]s£ SÌ~Þã¿Já¿»Õv¦9qõG_9FÉö¡ ÉGòAÐõRå<@óÈÿlòÁOnò;©'úr0»šª#99Ê»ÇÔXâDWèJ"¿+‚aÂÂAipÿM=Þ`´X5û}¤Ã¸]‰D…äþ'×~itìÕ]àþ³I|eÝ?¼›÷»ª‘Ö16_}Ie­n»Éë¿ËîXí`›%\zšd¼íý ÌÅ>©MIŨÎvÀ§gšóÒeø}7Õ%²YÛ0PpJ\QS‚Q7½^L™6|† Û"ßP¥<÷šžŠ§ Óî&§CoÀ]³O®Œ_U Æ[è¸!è‰ü&¾z¸ßs‚É߀?t`_à7ÚäI@Ï6 ª]J‚ZH¼¯©¨)t€Ø7¢ËSmµÇ$5ÑÉœbÏ$[_ÎE‘MGˆõÖ×Eð î’˜Á4Ôñ#×aÏ“Ž mê¬Üuï axbïßím¢çY!d@8‚:HÎ䘔ù|²ñ…Þ Ô£°œŒ¦¨Ô¢&èErôªrdÍý؃£õFp®s‰ÓŽlyq5—]}Œú*¿v¾ }Äó ØYê ÊÕhÆ %ÜA7cxOª€ñy?Ù™óôà£ýÕÍ™×ýúë4S•{½iá5ÑÌ®6ìcŠÕ«õ¼Þ³¥ßzݲW_Ãu¶Ü }^¢êáA8Xg˜ä»M`ÐUF¹+%úó²Yï}¥=ªu£Z´ç„˃RHœ¿i¿E­õ!wù<žñZŸñ¢¤|Çq%eÅYz‘\’/»4(O~ù‹™ª[2vÂ+‘L0 czÀêw^Õœ—ÚlÏ´ùÛÂÃI«œîo¹Ýßiͤ,§‹"—J;,W:IESøeªÖ„|yÿýAFr'¢ÆÓHSÍ©%Ýé@ (Œ.çWhщ7´ŠšQRÚºaEr â5^;wâ–Úx÷JÎ%–­®ÐèÏžN¨òRÚ‡–óeÓ5æ¹3æ¢Õ`’p)û¿$2Ã5¦è C Ï¡øGˆUø¦}µ­«¸kIb¨Ê_Eä§Ç€ŠôÜ[}T­!áà;Ö«í=ùŽ™ún“ЬRÑôO•lý`®¤›<ìª70Ä(ƒ†àuŽA¢¥Óbçø—nõÚ$îv¯ˆ)­ÑµG­E¹SìµÇ»Ú/ ƒpñkNQkï†Ìcɩ¸U#5ö;­T$ñ‰ OŽ´#Z%*q.ée¹•¦1 ÔÌÖ©#ÊÍ|ß{-0š»9cMiK‹Z7=_Äz“Æ¡'Íóâ*4ƒŽé˜Jõæ Yþ–¾Va*.¤yñëý.dñ?M˜e]V°§k.Ê57øŸBSî€ Üº|>ísWû Ø¥§óHS´y#˜ø"Óš¯ù¤[õÉ€ç5/´˜HæTwÁÐã‚åS³ X¥¦™~±û‘ EÝœ¾Ç»; ÇXÀDÛVÑ!h¢~A˜,ø¾àc”÷´^™¢§{ò4ï°,ÖüIz¼¦lÔÆ¾Èù-BTÿnJSv¸íÏ»ñ¸­p•¬C_7uLɲDs"‡éÔ„€…BkÒGl€ZžéKy‚^¯Ïo`UÝdÓùD¼ñDá#ëÒSÍbrO„p,—"p¨ì»¬çn_q¸ùžrÔÑ̲|‡¿!ÙX6A¶$€4·n3DJHYþ›¦†Ém¬·@øzézÏBß#„CsTÌ@ÙIŠÜrf4É šàãdYÊÒáê2ÑÛ%)ú…ƒa«Ž°^;·»fC•»¿0eoW Åb/l¥ 2¬åÎs½Ø0ZöYÜ€ßÏ8·lþȽù~Êbukf*Ø=ßɘC±ãof¡M%`íw5Ô‘5ÈJÈÕ[9wMB°•œ>Æ9áýõ¬ã à@:ÙA*´‡l4@[~E¯m‡¢Msaw‘•ÿgòšU)ƒÃÙò¤®Ç´§‘µ„9!fþ”Ê/•HJYµ+!ý¢v¡ß&ñMx:œ3-ï=n]õËN½@4¥‚zÄü™Ï€BKøn¿œÓz¡» ÝlÛ~úÝ$ÁäÙ"Ø\_ »\ ””BѰj9þgm¨¦Q:#¯£\´ß ¸Ï2=|ÝP¶y¾ÃM²_ùˆ×´è!lN¨Ø''Éc*VŒØ›šéUùÚ·~j!ÀËÿÿ˜Ýx/¹"Ë ÑWÅ]ÇÝ&߬MxZ¦Aæ$ÃÛ×7ü/Á쟈»ŒuæÝ„U4ƒ‚{*§¬š„Ъ×húΟòb3ÇÓÍÖÓ1˜ÕŠ!Kpƒúªl~Æ›­P—Tz‚«é¼Ý5¹ÂE&¾`1žHh¯!‡<+?1 &¬jƒÚ]1D†1-ÏÎ^!A7u’;¿-©Ù`>ºwúƒEÁ†ï­t§ffÁºUæÆêâO»rœ­p™:uâv&rkízƯÓ{ò€¼ìJÛ¬$ø[r¨~ïvn‡± endstream endobj 99 0 obj 2896 endobj 100 0 obj [ 95 0 R 97 0 R ] endobj 101 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 100 0 R /Contents 98 0 R >> endobj 102 0 obj << /Length 103 0 R /Filter /FlateDecode >> stream 8‚ô*#g_H ÁJˆ Ëëc%q?è š2Ë9Ëÿw">,v%«.Â=¯íŒJ¿ n¼v½–. ¡“9œÉ6f¥\DHQ àUTî[£…íê×q Œõo³ÃÁjºÁ“¾ö~„£êYÜæCgùqFÂ,Þ?l =ÀNPãÛ%XMÍWZ‚ÈYÃ{bf«~dÓè›É\h¬ÿi+ HÉù#»-U":fª#wîsf–¤^(šê´ÕmVþ}iL‘Á›"V(TÎws˜Õ“ëRÜû- ¸BHä¢Eb¦˜JÅYÛ­q#Lrj0 ÷þ…3§4fíªR •ŽB‰ÝÌìÅ ÍpoÝòu_+•´¢Ää6^ïrzEPkß”ýr¢k™!1+—jAþMLeºÅH2ö ‚›ð¸æ ?2d.ïAzK…ŽÄA¼@ùšoòQV¿äHCÏXƒ?ÇÉãPÚ1‚þ™Þ¾ÍX­Ø'.®³xãÇ×@Úþ¿ äT¯(FùºN®=Ó˜AÏ[µ”{Ã.)µ¡4;1P?àp<=„«ø6·_×¾%ƒ£Š µrÒ$Ÿ«&é.Éó>^S;×|šÝ¥î¾?ÐwÁ®C¼J˜9^ 9£/(Mà˜l)w% óoŸiëŽ5¤w Šh]¿zFS×ñŠËèvæ²1ÿ(ÞÑ•¿@x\,‹Íˆóïj$»A~ýA¹Ûߪ‘’«Ë…2Œ*½äª ]QUÂäxõ³¤".Ei¯¨»ê×bÎÁƒg¹÷èP~â–º†+¡X›ûÛ(w<#Óœ ECNêÚ¸ÏΉùUU=[ë@yš%0ì€i ÜÍ­D¬Öu€ï`U¶í¸²K¢4橵€Ö¤›ë‹€RZ&«fÞîü M5uÀìà|Y(ä#YšÞ4¿$ß}w¦y~l æ»+¶yl™–ƒX›¡ìýÌ:>•>9™÷C_À;¹Fÿ"Žú8?¢úb"öymO‚B™ú@Ú"Ùg+ÍòoH¹Ê)QÃæ¤°ÞºáúßT™ÐÆË;<E±úQïºÄèÏêšÊßöšÀ}µâCÌ ²v^+m Y'ê—È.‰Xîþñ8y¢Ñ9Ó·ZjÚ°\) ²ªHódiá°R‡³çÝ+vD?µÈ%úÍsiýE~Èeˆì’' –—ßÛ֟ܪð«¶ò}ÉPƒŠÁI¯¬ ²5¤Ûpùž$Çg4 Gfî}m¬$ª]JðÛeEb öîç—ìm|ÔÚyL™§]?N’†i÷[¯ &d¬ÞÉålûjJ¸ZùØ®ÎF!¯zn¬ Ž\2œhÙàãü— dcÉÌ)0#¯I<±÷Î(…UÝ))ÅÔŽï®:'â °°Íÿ2a’<:`Æ79߉™b.-å ‚¯w™)AÜå}P~ʸgM,5cpÇÀ£ذ‹ Ô`ù]®Ù»ÕnIéŠä+ƒRÌR%ÞïÝSØQ‰nqEÚm >w§â”qó‹ÓºÑ2ã¨7@¢®C”©Œ£# qzôOTŠ–/6ä$µ\bŠBÙ9YˆHd»]α°>ªéšÔ\·’åšžºEÀˆÝÿgŒœob $”¬2 k No3c¦‚Gÿök7:ÆW(‹ÝLŸ‚å¼Péæü‹!«ržÄÊd"¶}¦¸…Æv ö ®­îjsnèF‰½n4´ƒ]Aÿ¤Ò"òÜ^Íž!’<…@ERUüÅÌô¥6~nŸŸœ[1ÚNNêI3óCù’VóZDð±®èÍ.mBÎV’´k›éÝ/.ÏJrï…”íÁõbdØä óãkK²øupò™l¶µÆsâÕk(Ób“FË ÓÆ iøf¤þM6C"Ø"¯·Yuç›òáÅfêva-ŠÈLNV„XHÂáebvŒV0-ï#Õ,6´Ç|ÒÐÆ„3Wà‚%³ìa©þœ1M¨›Tãfà·•Úµ.­øžDc¼UöÏl÷ o$¶¸ƒ±ä§SÎ`ë(ÒwÐ 8­­¹¶ò¢z÷WÈ1r6ÐWMdñPtjsh‚çæá*CÙ<¦lE^ÿë(G„B[Sùø"ì5· (—ügŽž¹DhB÷ã1Š ¡ üÞ{ÄBÇ.áéüȹûÙº%óÏT²Bé½›øhWw141€E?,á.©E‘g‡0’{É%~ Ð1Îð¯9¸"ï Žú endstream endobj 103 0 obj 1680 endobj 104 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 102 0 R >> endobj 105 0 obj << /Length 106 0 R /Filter /FlateDecode >> stream nâ=j¡æÌÜ[‹üÑÁò×Å{\ÑHTKÛìM…óÉסëÏø›ßJ¬,b**¿ŽWf‹ˆ=z)Lupuƒ«r³IùˆÅÍJGoáö„´›Ä`œF-©ÈØI§º›£֕óBÑ9; ˆ;i¬¸Å¬t§GRËôÓ1³”µcI™(ô˜Ž D.ˆØ3I$ÌàT|<Û—Ë߃Îô£j98çvk¡¶4ݼáLË:ô®t8h9˜6E¹it^–âô þ[­S¯«ÚÞ‹nÄÂy£S}ÈÊ§à‹–‹B±Ð¡„w/ù£sèÁ~˜CMªºË2Ý68ŠÏ5Ü“YVëD4pkÎNm_BC)ÁG:“Z,¨jÝš˜²n%li]$žÿ+´ lÇ ,$ø’â;aíhUr¢ï›b4¨&¾Xw„÷Sà8Ñf¾Õõh4’õnirÔm‹°» A¯¿.g‹¦Ïã F–‡_òœ%Æ` P¡§^—ýÙ˜ÿеp9ê•òÒoK«K‡³ˆ8–Ì‘>1.ÀÜ’†DUî‡Ø+´—S щýÞO4!.r˜¢MË({Ê/ÈãL'2°ð«sLX,»o…ò¹Åà§( «Õhv§À['ˆØÍÒ ¢,v^«ç"ð…¦}þœpýN§?½æ>hÍP‚&}xrÅnôǫ՘+…eºa+´Ì%j{c$ô›X&c½‘×ì;é€gÛ“=ÄlÒ‚àIRì—_¨|Ô²ov&e”ZµIަù†m%*Ãîô_Uh;OóáùÌ -À% Œ*oyóÔ{ ]ßèm¿ÄÚ5w¿€†ÞÛ_ßëjêUI×x'HUé-³¥e€\3ˆ=HŶf| Fiæ}ëdof0,d§*·¿G?íLs6dmtÀ³³n]Q,E„ÐîÞm7â¨!bL‚ÍLßµ$rcÆ©]bÍ® Ú„/ÀtÉL«štñJ&GÜÅc–´xmÓ£7‚*¥›J¶ šé’f f)§”·(¸„¹…Œ×6tzÀ‘Ž…ÅàìÖ‰X‰…Fdw¢d×+p[-Ì™ÀÀ0;O®ZìXê”™#(ÑÉδÂÏÄ Ëm‚:vëRn'„í ¤W8c s“C\H 9½ðRôËgÛÙ|xZT@¿Ì.? ¿F7_¾#=íeZä/n×XËïOÚ“Æ|–bû‰ ÎϾ9H¢Fñ¸À¼ñô7JoãL«2¬øà_þƒwªï?‘iqûd麫ÁÞcü;[ÌŽ‘Ù—‰¾B§4…ð&ªÂÝ::ÁxPÁ3¥F¡áToÂߚϜšCÇ\ÕAF³@Àãád+.™8u3 ¹Á¸ÿ‘TGÏ£Š´X:­ö.ªšlðSN`ŒËØ‘îŒòàÚ¦ña€É_vϘD¶ "èÙèsuz÷f„±ýac –¼tóø°÷ª+<®¶5Ôï2JœhÚJ‚á, ¬óFÝÕ„æe">L? endstream endobj 106 0 obj 1168 endobj 107 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 105 0 R >> endobj 108 0 obj << /Length 109 0 R /Filter /FlateDecode >> stream \ žsä|+ß0j[̧x {é£v7&އN™4鯄®Þbg–°pƒoš*AÑÜ0¼a.Ùj*³uvÇC¾ÉŽL*D(×Z£2§E˜ÉIZø›³ô£˜ÍæòÑ2úYúéoò·÷DR}}Ü„kç³…F¹¹Ïucª¿šc‹ ­2MYú}RßÅ- 0Z{žœp{¡mpüÒ¼ÂXޱñòËCÒ_5©.á—™;khx¤JW«I~#i.§HäôóªöBt dô)ôcl±ajtúíÈœéo7Ÿþ„Ežvä®´>[žm½ŽYê7Àñá‹E•/­~ˆõûD²q|NóõŽ«ŠÍû¼s}X³‡—aý€ Ayjf®HøûK¡ )êè¶ñ´¶»–ÂPp¡{‘诇¾ÿ@` ³¦èVƒ {ÕË?6YjÐ*‹}q±:ðÒ$݆DQ§šÆñ‹œ-TÞ>UT<œÈ).-ôƒI»rÈ©~²&h£Ç¤@-Òî/…_X¯‚ºwb!ˆÈU‡€KMÖ¬AºŸ*§5p„ó9«Ïóótï­;w_ØIËÓ\…¿éÁÝ ¨áRE€¬ñëV)¿út9AäŠÁVG–0Zq²Çf€öj´7ÅqlYOqhÀVøÍ) qQØ P&`aNÃËù[ª†Î3A 8ÒÚ•-0¼Uj9EƒåÍúGÆ„™VܲäMo_ <ÀÀëÔ(­‘MM´}ÚÏ7†MWÛ9puäYáÆžG_6‰¡:#å“R³{1GZíì&»Ý üq³¸u%Àñ ~„"@¹ ø‡0q–ÒIÍÌß‹|›½ªðsf|”žáú. Õ4bp£[˜c38” úxÏâÉ?zžô"7 ¼ ·Ø SܘF×IôÍä—X&ð+Ñ›\›»é=øt]œ‚§¤@ª²øŠYfy)Vëï^’³˜DÍšùù*¶ËHþp{ÉÎI¹hÓí…ÅöÚ¼úÇduwwÀS»o©Û–½‰ªñUÚñ™.Ë<è ôS~MÿØŽCš™_—ç!=ã#q¢'áÉH Í“‹ò¥#tìŒ6k4ÿÔ@ùÞ¨wÄ!SQø„¤ ñ;ÓFCb®”ðm½ÚÏggT»”Ñ£«èçÒx 7¤¿Èœ6Ë ¦£b¬çͤT‰…J•©nëg_˜X1jj´t¯c\2…r°?N)7ÙˆÒ.– …Yí"³á³ˆdAX ÔÔ‹£çš‰x[iìdûxψÈ#ÜJp'¦¾FmÅZ'c¸øÅŸ’t†ªÜêç|̕ǔ ÉbÝý±iÝI_é6IŸVR§ˆŸÄßžOAS„] …釂}pCu¾b\a”úâÑ«ÑíÆgSÀ]þá~‡&sÝð™Q7¯¨µ:.Ó$a±sRuð½ð–_ö!Ó6L´ ŒÌÔ¡ŽÿÎ൙mÇj0ö*ê]p«c.h»å<ápvÈvŒÒ¨r.ß]«¯’\yâoÌÞN\YÃȇãd©Í±hzkŽçn¯lOuc]óÊ›£S*¯§›Vé.t؇£Ý ­FìÉ<†å[8Kÿñ“Ò‚õ†`Ô+±òv¶[ö…õ9•žùœ&‡*é〆aè×—ŒU­ÝV›P,W‰¬õMóÓ€üæ{»•VÞ½p)šZM¿<÷"ö ¯]šw³Ü=H[áåÏæ• ßî÷Þ·"µšõÐMB.Ž/Æ­ iеjSW6ÈÅ8¿^®¦Tp ÖH‡ÑÒ}SïZâA‚5sf„ ‹qÂS÷g…ø‹n¿„†ª›‡¯èåÁƒs!©‹5k–G‰¯> endobj 111 0 obj << /Length 112 0 R /Filter /FlateDecode >> stream ðŽ̼4yl·®q{¸r74›Ü Ç^5íq,bgß–ËÁJ¨þÌ}—xåãäT¦‚•R×xÇœ€‘Ã; ¡ìoOyMøùïø U7[õt¼L:ú0 ¡òP-É_±A÷ªäxe|#ÖÊÌr® ï1²BÖP«ù²fzhÎ •qàªWX„ñâ`r»y)½p¨(zûêÍäÀCTÛ Ök?ÜހРûsG*Ô-ý ÎE2#mögákî#M!Œ.GHe°ß Žž‰ÿØl%7j¢ôÜ$èsÀÉ>c½+Äœ­Hö“Ño¤!1!ܬ¾çzð¬eƒ aÀªëEl“È¿o»L®ê4¿üÙXáïű¸`û–ˆ«¦'$Kÿ”váÂ_.a`*…¼1´‘¨–{"=×9ÔÄ|ælç2¦¬vÛ£¼ÃX'©„…é|TmË/ û1VªœÆ¢ÊK b Þìx¢°ìçaÇÞaö­²¿tB» ý»€X°-ž´í¿Á^c\{6'‘< k‚krÕ½&Ü„Fù[ÏܯÂhË kï> 9)‚–&k9&ªœµRM$¼R£´i””·‰$¨ä3S¹¹tG\Êã4ÞKࢋJp[$ ¤0áèÉÿ¾.v¿>­üÀ°|Ð6KþN±˜W\l¦v9¥±~7“ b„áo5—Q•ñéìVH@EÂ7×àÁ3Á“¡¸ÓÑ^] _ö³6¿>$«§T¸hM¸s_?çaT)¢õ”\ýAá× H!›TºÛ”SPS°§‡iœî¦7*¬èµ‹ï˜r¨§=JBFÑŠMÅIéž¹šÙ’åÈ¥TòØ$Ê,…J$“b #ºx¹¶CLG@¨™: €¾ZwfÜÆ»ö˜Í¯gL z)šL\ç.̶äåé¨ocm[M?&™} 'ÛÈ”i0ßÞÞq噤×ßl‰'¢pø’U/2rÈz™šÅ›XoYÛùÍ7ô©£ûÚ‚¹HkÃil½4Ÿ ëÖ'ckk°ÈV),ÀÖbع)c ¢©¶ØS䤔ÀÇ2&<þ­Ézï©^ÿ—˜°ƒé.Ëß%dG”dNQÙøÌ6½m^Ÿ!½ø-²þ”éˆéÜ^:I«…—®TmÿOðl­o˜\ì¹ây·0·BØWêt.Õ@÷Á¤¢õ”OžÈ‡\V{ê’@œôJ%†ÐQÍØN(<€pw´‰=­ §j;΢±_ï#ï\{ÃM¬QèYž8!— §ÕR=BQÌ€¨-eôÙ¨äG‡‹Šn>7þÞÎmË*.ÄHŒ¾L—¹£b2¹—ùÿÙT\ÿ‘”!×yœ#8º³Ø˜­‚2oH$—ÜÃ&²ìý'Rt¨ö^a„‘®Š[‹)If\U’Ïåh†½Sˆßâ&`¿iJdp©Á=L,‡å1´È=ÔÀ2Âð1”»Ìþ*-í\ me5"9Ô¸3„–ý?¨ÍVúMÿgôÃ/ŒëìÇâ‘ôðSÙ^:È*p>ít ‘| ;¸û·¦|Ük´fMñS×HíÄ?jd\l6!æNE6Ã&7ÀkšY¼ÙØžÛˆ™ñ·îuçic2(`nâÑW†"Ô§ð»Å»,\çq™ŠÌÊYfH:íüAuÅäÓ2ûwí-¦Pvq[ÂD<*Ïeõciï¼ÔhÊhc脆bê1v …Ç9ÄLâW9ÍU~…0GÍ:­é'¾¢ïyè9Í#¶~}Ÿ\ ÿIÏwÎÖm¯™+2 endstream endobj 112 0 obj 1376 endobj 113 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 111 0 R >> endobj 114 0 obj << /Type /Action /S /GoTo /D [27 0 R /XYZ 56.692 771.023 null] >> endobj 115 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 706.484 61.708 716.802 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 114 0 R /H /I >> endobj 116 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 706.484 209.812 716.802 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 114 0 R /H /I >> endobj 117 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 706.484 535.492 716.802 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 114 0 R /H /I >> endobj 118 0 obj << /Type /Action /S /GoTo /D [27 0 R /XYZ 56.692 734.174 null] >> endobj 119 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 689.033 69.232 699.351 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 118 0 R /H /I >> endobj 120 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 689.033 171.268 699.351 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 118 0 R /H /I >> endobj 121 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 689.033 535.492 699.351 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 118 0 R /H /I >> endobj 122 0 obj << /Type /Action /S /GoTo /D [27 0 R /XYZ 56.692 612.289 null] >> endobj 123 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 671.582 69.232 681.9 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 122 0 R /H /I >> endobj 124 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 671.582 181.267 681.9 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 122 0 R /H /I >> endobj 125 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 671.582 535.492 681.9 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 122 0 R /H /I >> endobj 126 0 obj << /Type /Action /S /GoTo /D [27 0 R /XYZ 56.692 388.358 null] >> endobj 127 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 654.131 69.232 664.449 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 126 0 R /H /I >> endobj 128 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 654.131 194.808 664.449 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 126 0 R /H /I >> endobj 129 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 654.131 535.492 664.449 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 126 0 R /H /I >> endobj 130 0 obj << /Type /Action /S /GoTo /D [27 0 R /XYZ 56.692 272.142 null] >> endobj 131 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 636.68 69.232 646.998 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 130 0 R /H /I >> endobj 132 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 636.68 158.013 646.998 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 130 0 R /H /I >> endobj 133 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 636.68 535.492 646.998 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 130 0 R /H /I >> endobj 134 0 obj << /Type /Action /S /GoTo /D [27 0 R /XYZ 56.692 189.94 null] >> endobj 135 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 619.229 69.232 629.547 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 134 0 R /H /I >> endobj 136 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 619.229 207.832 629.547 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 134 0 R /H /I >> endobj 137 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 619.229 535.492 629.547 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 134 0 R /H /I >> endobj 138 0 obj << /Type /Action /S /GoTo /D [71 0 R /XYZ 56.692 535.758 null] >> endobj 139 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 601.778 69.232 612.096 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 138 0 R /H /I >> endobj 140 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 601.778 209.328 612.096 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 138 0 R /H /I >> endobj 141 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 601.778 535.492 612.096 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 138 0 R /H /I >> endobj 142 0 obj << /Type /Action /S /GoTo /D [35 0 R /XYZ 56.692 771.023 null] >> endobj 143 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 584.327 76.756 594.645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 142 0 R /H /I >> endobj 144 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 584.327 222.88 594.645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 142 0 R /H /I >> endobj 145 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 584.327 535.492 594.645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 142 0 R /H /I >> endobj 146 0 obj << /Type /Action /S /GoTo /D [35 0 R /XYZ 56.692 459.332 null] >> endobj 147 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 566.876 76.756 577.194 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 146 0 R /H /I >> endobj 148 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 566.876 192.322 577.194 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 146 0 R /H /I >> endobj 149 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 566.876 535.492 577.194 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 146 0 R /H /I >> endobj 150 0 obj << /Type /Action /S /GoTo /D [90 0 R /XYZ 56.692 771.023 null] >> endobj 151 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 549.425 69.232 559.743 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 150 0 R /H /I >> endobj 152 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 549.425 250.435 559.743 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 150 0 R /H /I >> endobj 153 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 549.425 535.492 559.743 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 150 0 R /H /I >> endobj 154 0 obj << /Type /Action /S /GoTo /D [38 0 R /XYZ 56.692 771.023 null] >> endobj 155 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 520.636 61.708 530.954 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 154 0 R /H /I >> endobj 156 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 520.636 180.189 530.954 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 154 0 R /H /I >> endobj 157 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 520.636 535.492 530.954 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 154 0 R /H /I >> endobj 158 0 obj << /Type /Action /S /GoTo /D [38 0 R /XYZ 56.692 734.174 null] >> endobj 159 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 503.185 69.232 513.503 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 158 0 R /H /I >> endobj 160 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 503.185 159.234 513.503 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 158 0 R /H /I >> endobj 161 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 503.185 535.492 513.503 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 158 0 R /H /I >> endobj 162 0 obj << /Type /Action /S /GoTo /D [48 0 R /XYZ 56.692 771.023 null] >> endobj 163 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 485.734 69.232 496.052 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 162 0 R /H /I >> endobj 164 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 485.734 197.294 496.052 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 162 0 R /H /I >> endobj 165 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 485.734 535.492 496.052 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 162 0 R /H /I >> endobj 166 0 obj << /Type /Action /S /GoTo /D [51 0 R /XYZ 56.692 771.023 null] >> endobj 167 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 456.945 61.708 467.263 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 166 0 R /H /I >> endobj 168 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 456.945 161.676 467.263 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 166 0 R /H /I >> endobj 169 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 456.945 535.492 467.263 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 166 0 R /H /I >> endobj 170 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 439.494 69.232 449.812 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 72 0 R /H /I >> endobj 171 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 439.494 277.473 449.812 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 72 0 R /H /I >> endobj 172 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 439.494 535.492 449.812 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 72 0 R /H /I >> endobj 173 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 422.043 69.232 432.361 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 74 0 R /H /I >> endobj 174 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 422.043 321.099 432.361 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 74 0 R /H /I >> endobj 175 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 422.043 535.492 432.361 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 74 0 R /H /I >> endobj 176 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 404.592 69.232 414.91 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 76 0 R /H /I >> endobj 177 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 404.592 265.945 414.91 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 76 0 R /H /I >> endobj 178 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 404.592 535.492 414.91 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 76 0 R /H /I >> endobj 179 0 obj << /Type /Action /S /GoTo /D [101 0 R /XYZ 56.692 771.023 null] >> endobj 180 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 375.803 61.708 386.121 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 179 0 R /H /I >> endobj 181 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 375.803 182.169 386.121 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 179 0 R /H /I >> endobj 182 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 375.803 535.492 386.121 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 179 0 R /H /I >> endobj 183 0 obj << /Type /Action /S /GoTo /D [101 0 R /XYZ 56.692 680.317 null] >> endobj 184 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 358.352 69.232 368.67 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 183 0 R /H /I >> endobj 185 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 358.352 294.82 368.67 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 183 0 R /H /I >> endobj 186 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 358.352 535.492 368.67 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 183 0 R /H /I >> endobj 187 0 obj << /Type /Action /S /GoTo /D [107 0 R /XYZ 56.692 771.023 null] >> endobj 188 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 329.563 61.708 339.881 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 187 0 R /H /I >> endobj 189 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 329.563 177.021 339.881 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 187 0 R /H /I >> endobj 190 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 329.563 535.492 339.881 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 187 0 R /H /I >> endobj 191 0 obj << /Type /Action /S /GoTo /D [110 0 R /XYZ 56.692 771.023 null] >> endobj 192 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 300.774 61.708 311.092 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 191 0 R /H /I >> endobj 193 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 300.774 173.193 311.092 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 191 0 R /H /I >> endobj 194 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 300.774 535.492 311.092 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 191 0 R /H /I >> endobj 195 0 obj << /Type /Action /S /GoTo /D [113 0 R /XYZ 56.692 771.023 null] >> endobj 196 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 271.985 105.257 282.303 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 195 0 R /H /I >> endobj 197 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 271.985 179.067 282.303 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 195 0 R /H /I >> endobj 198 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 271.985 535.492 282.303 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 195 0 R /H /I >> endobj 199 0 obj << /Length 200 0 R /Filter /FlateDecode >> stream †º‘ÜÕ‹VÀ“)ôþIRÛ¬pU3ç-…×v·¨Š-º-v»èæ·D&ÅÌco ðŽ¿YyŠ+ä`Z'ðs$mMw†é,ÖšNDñ6‚ýã ”‰% Ì–²=^¡X~šÅÂü|¹¹í/Ê &0ÏÀ}KƒBþó팖vùS’GÜÈ?wÕ#ºr?¸Ð–ßjÂäËVÚ4f%f’™¯ ‹Äzºµ›­Nà˜‹ö¬t@D$ˆ‡áŸEÃpZÛ°‰¦n‹L‡³ô-×üIÙº«MÕ­G‰1Râ/4‡œ‹B¨®{D±¬dç„;ý¿ªQE‚w–îtLX˜N…(&ïÙÄó³ÖW“Œ¾â`™cn=?NšÍËfÖ§nF¸"ͱ†å’^h5Ð~é2“Ü€AÃrºÃ«ÿÔ}{!±hã'Ú¸3gh4qóªoxBÒ ýK÷w êBˆ‘”Þ}¸GŠ–Ǻñçt…}òÏ K}‹•öáj'þ¯ÜèVßlQj<3ÑtÝλi¼nÑ€o—å«ßƒ>HµmïGC"X¢Ñ$ ïFiÅÐNÃ*èIÂ[>*X c5WmÉ™ç(:®ôÌ œè¬L )D¯ùŸsá·æÃ°öøÛtÜ%¾c™¸×Ü™HÀ›IY´§H-Ê”}`][G8¦u6›²scÁ^hÛŸ¤&é9V&hqlý–)-Œ·„±ø+égÞÈÊ BMÜuB†ÆtÞm@M‘àMÃ,`"š·y?õjBÌšš5àŽ&¼ßJób%<@·£÷—Á‹#¤€‰tbCIæ³N>q¼µø©OBpîQ»í&‚JÓhäxYÚçÊ U[2ž§p².ØiéÕâB¨óx z óQ!^žçþ‚#fLK‘ÔöxUöxÌ œ ©ÌAe‹Ïk›ß¬â—²º›¿NÝi“^J0›Ø÷/«ˆË_Áz·³ôÚ®;pA&¶ýùÖö°*ØáÚ Áº Öñ´ƒ«d­8{`nqÁ2õ±Ï½Y¨OœSß—Ÿ]ÙðŸfªà5-¿gÙÅ_‚°0è-w¾“Ý4):…¬¤½,åCèPÒA§ý?9äš“ºspBÁïÔEâ.€¡±Çùç«Ò“ˆÿs5Ï»ó:PR«ìD¥ÿjrOÇÌqãþ‚°ª€†V]¿ ×q"žŠw€`«VGGXñ«Ç›žÈê=ÍÜ\£HèŠýî|¸—‘$rE"gí´ŌèlE[€DaŠ`ZíX^a×Pèçv¢†åæX0)c„-A_Pœ£ã°¿S<'«y Œuå]êºâéj<_juV¨s³9c êæJlD°)hxš{Bj„v! ˆKX²$ó(K?$Ó‹ Ö)€ÄÇëaØØòÁð¡\l Ò‹¥t©ó} t±œˆîç¬Q$MR`¡:? endstream endobj 200 0 obj 1472 endobj 201 0 obj [ 115 0 R 116 0 R 117 0 R 119 0 R 120 0 R 121 0 R 123 0 R 124 0 R 125 0 R 127 0 R 128 0 R 129 0 R 131 0 R 132 0 R 133 0 R 135 0 R 136 0 R 137 0 R 139 0 R 140 0 R 141 0 R 143 0 R 144 0 R 145 0 R 147 0 R 148 0 R 149 0 R 151 0 R 152 0 R 153 0 R 155 0 R 156 0 R 157 0 R 159 0 R 160 0 R 161 0 R 163 0 R 164 0 R 165 0 R 167 0 R 168 0 R 169 0 R 170 0 R 171 0 R 172 0 R 173 0 R 174 0 R 175 0 R 176 0 R 177 0 R 178 0 R 180 0 R 181 0 R 182 0 R 184 0 R 185 0 R 186 0 R 188 0 R 189 0 R 190 0 R 192 0 R 193 0 R 194 0 R 196 0 R 197 0 R 198 0 R ] endobj 202 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 201 0 R /Contents 199 0 R >> endobj 203 0 obj << /Type /FontDescriptor /FontName /EAAAAA+OpenSans /FontBBox [-549 -270 1204 1047] /Flags 33 /CapHeight 713 /Ascent 1047 /Descent -270 /ItalicAngle 0 /StemV 0 /MissingWidth 500 /FontFile2 204 0 R /CIDSet 205 0 R >> endobj 204 0 obj << /Length1 11428 /Length 206 0 R /Filter /FlateDecode >> stream xJq=£}ƒMª·ê¡Â“ºÝCØóP[^;I›$p¦ŒÏ"PôzPÀÊu"ÛûÂfª¡¢¡¶ßö7ÉÎJ™%cq‡ã…mÞ’C?Á"8u†êÍþHÛ[{”ºëŽÇõͨøVB Æó¦ ‘L:¤÷ûon¼ü  bàFjv›¨u«ÛþÍr?í;JºNФFP@™´Ö×E™®­Ø•kE¥T;éÜWÛ˜r\€­áfÁo˜Ö.Ã}4r3+Ìo‡º>ßè^w ×…ŠâýêEì¶û¿P—;óû"‰¦µ£üΫ5!¼÷@, ”ÂVÈÚߣŽçèÿÌw—ƒù9¨ÈýúÌ?£ 'Ó7¡°ŒoÁ²_Xß—YlR /éõ¹¤¥í(•Ç­‰±– v×±ò·wóâëõ¿k«9ÅhæF±šìÕD"ù|+{t¸[ 2(ãQ<›Ñ<•§™]ø‡;4ªËú{‡ dï«®®7KèO¹à…)¥ÙОQ¾—ðâ÷pVøã":‡öU¥Ž"èó †h‡ƒßvÆ"ý¿53MCæbË£!RâDUŒµ=ºûÁa…ãÞÕÑmT"Ý"ôA*ÑJò¶-@¢u£@÷„‘霒‘ŒŒJÖyï+> ”Ý`—ñmЇ[+,ør uúÿkõOb4Ïv~Å›üwà$&šÐñ3À3Ô@uzµ*Uhù¼±Tk BuG ^ïX¦|…-ý!5Þf¢Å86”|BlëcÖå'J{ïÔU™ð–½Ç5&纰êZkPæ;N·ÆùÎ2ŒÈš\̮Ϯ§„ИLZcB@bíõiﲜ§ §›‡—Aëíò5YQ‰æ_ccpø?ìÏsë£nUÎ à*(³­Ô4Õ½Hâ4Þ¸ý]²ÙzÒ3+ÿŽÑn±Àò¯ÆWLŠÌ=ˆaì8•/ØþI0OØ—{àåî8ñJ@•ؾ- ¡ÍGÄ\Õ‹¬ÀD#>P}”¾²(°¤!SX+hôWj3ÛX™ð’ââÑÕÂäȫʢÚî)ˆãËU-SkÍœýk3TfŸó£ ®IŸvÜG¿Õ† s/Þ⟓Å}ˆûÒÀœM˜uÑkmÛÜÙ\¶ˆø`¢’ð÷Úvk}kÚ." MÓ‡R¡â*jyóºØ0îª"Nô‘\]¨O~™QÎäSrþ­±êÒù¾ö¾¥±¯¢ÂIønj¼:Â’3ràf¢ãRDÚY—éhVݵYnì'Áy¸óffÁÉÆ› _Kê. §A= ˆJ|Ó˜E·"¸Ÿ†vùë¯ÄûÛiÍÁ¼—âPlɨ4 »XT¿‚’È}Š*Œ À¯ æš’Ù°}vîÞf¾ê»¯í®÷îàìÕ¬ Dª~œ|Â{•]ñɉY¨ë•$²ÆE’û(̹/…g9ÕŒVúw‰?Ëœ§-¯/™WMŽ÷¬Ã¶·Ýì˜ÔkCGNÂã­©ªåY–uÒ IÅøÌ÷S`%y\æmÁrjdB§N´fP¼”ŸILNEý!U\š)5} ’ÇîÏpCµÂ*˜QfaÄçÂ?±E8¥!Å8µÖxä¡ kÆSo‰µq_j6©ÍñJ³·#¯72i&l¹Öo<…ñã€(()ãÝ¿ßÆ—Ùè÷KNu­ nÜwÈÔ±ÚnM G¦ÅÆ¡¾< -È Ø‚€®¹þ¸vn˜ç46p´@ŒÅßú‚¶È 1¿2’:¨ÿ 4ÿ´µæ)9¡›þq›/ÛK ½”;ž¶²Ëm Úàaaäí²Êÿ'èÐ_‘^"ÛNÖç,ÝÉ Šp0µ¶YøJŸ Œ ¡CxœÞi‰N ãµfÞâùãýÿ»ø&ú±ª .*ÇÓ¥8Ùhãgš´ \Ç×8^Ø:]®OÍæ°L”Œç {Qõñ>]‘U¸ Œ)æ¢Ã Šœœ³#ðÿhºªŠªÂ²m<¾±G»ÀÑÉ}¡!–'çgÛìÔµmÚÃü+qŒE"¢º|ˆ7ÚLL”'PèwOà7ø4´qÿn£ã‘GuÆP[z¢ˆâ`¸{Ôà¯^èu¦o5˜¸g»™ L¸ÝIäV°À5%ZÈŽ ¿²u7.0m¸œ®n>wÔ\âDC»lÞÄ¡+ˆ ìaûßà(ÂÆŽ“¡úùùN)a(:Üþ×ʸÇ©û5 À9ÖMˆLÕgñ×R¹úãÏ)ŠßÈ’HXä›=ÜêÓWlÀ=÷˜’v®æ… o–nüì?';ˆÙ“: æ€E@»ý©þ“Šª=>ºó/—ûUÁ° ?Q6.°õ.~I!MÙ•Ë`†ª^ëHØ©{ÜÇ̦Óë×ë Hp¸á?—“Ž'|ÕÐÃ*Þq-Mæ;O8W¾·›|R‰?$:õÄÚ Ÿç{‰_×[€ll¾nnh7So@¡ÔÝt‹tËZï'ÐubGŠ`qB<'¸-–-η†b8°Rj»HÈ›¤…H™)@5‰£lP\C¬.ÂBA jLÍIeÜ!ƒt‰æÛdñÅð#pYùCS™fWËøï¦•F ë"/Qt ÁTtRUhßt*Ÿ›¤øI)ÆÌNÎ|J€}™únÀ"2½V¡FDãé£-‹>v º¯_1YGeê·¨æ|ÊmãM«¿­c`ÇJë€I扨cÀ],gÄ]r2”* “̨:-1>Œ€¡‚½ §AÚm\õÇæíC4ˆ±ó×2½°™ªà²^à¤ÅÈY‡£±Xù6Âï„vµ_h!S&PîCp÷á%‘}ýmœ(wW'0°] _½_ŸŽÏÉ™úC‘ÑÚÃâJñ*ÀžøüÛVU«æÕÏ¿'õ02xÄ-IWh(gµ·Meê.þi7è`Ëœ–ûèøÍf>b¯¯ç÷MøüdW«ë%Œnsu2ñØOrùçMâÕ(kSo¿p®Þà¼~h–%ìmæÒ¬á½¶7w`·ç„]XP!órÜ”è‚@ûAq qm(w\­ß}d‘±g.£ž”¨ÔͤJ³é#Cr+ÿ³GQ¨ \Ü‹âøJÖ×ûØŠ…::zžÔŸÍ¸ž%¾³P‰¶ŒhÜ%t·"Ψ·WO< x—3xp·Ø»»„èoÃÑÛÜ3\3–ñÍDÖÿàD—ƒãT»E•¿)RGW2«mô¡âwÙtè¼ûe$¦6·€ÿ1“‰ µ/îlóß~Ëxj…P8ªÉ§P›Ù‡¸˜K!w³ËŽ˜)õÛ縳gÝŠa‘ר¼ü˜ ëá—@”˜&بb’ë$0ÐYØ×à"k—‡>èLz°ƒžÝY([uk÷Îg¢Uˆ–¡!¹ !JÕ/”¸8¯5ÿñWöŸ~Sö)U¾ú¢Úë}öIŽÌ£ôGú¹gÈÂ28Æ*ûÎQÁ%ÉÆÁ ý—EFchox¿Ö+¼py¿ zyr2nÿ¯ (âFZÛ9Õͦ–÷ÞâBo ©|];ÀCÇc²·p¤bn”j& ¾é™ªŒ6“9Ò€ª/p†Í¾wر.Ëþ FÌ_¦WÖëSá -—åu®tPåÐK=(˜qvT•n¶ÌKž`¢(ò…–HÓyUX÷‰– ýM^¸ÿPå}49ìÀp]_ÞJç=–[„U4HeèÑIvÍ+¦#Õ%Ï{‚L`QL •Ž™O¹—uÍV™ÿª¬Ž0•Z¬`~õ/ ±X¡Óöj,iÖ˜w”/m4àÏ6LËRËѶc@$l±Ê;Ê;›€0à îìe±Z‹wœl½Àå ù „îƒä£Èû‡#Qèó´) <í7սĜ7Üt§e›-”Ó´ûØgAâ¨W}±k`‘þðŠÁ‚÷ófçJ ã<ž©Á1¦y¨9„§ ·h¥Q]ð¦È ú}ï%ž™5œ™Än¸Cáùzl™€BYËTR?qð4ò±øæÖ#^„DK ‡æ#濲+FÏïÓ\Q{Ä¢ÉïXSû0«ñxi/BØŽS >Ö«·Zxœ€×˜NC® XLÜìÈ“dݘ ´8;¯g[©ãÒÚ´!Z²Š6&åu…Ú˜ùíÔ(µb _TT­â.iî¦m£iÞµø3ÛŽUåwaýåï« Y‹ü"}GlmÁçÇë×&Úª¯t5ÖO¬ÑÖ ƒ{Ì’Ý7!hÅI^ Ï.Ô›ð†i4ã9¾ú°¡ª$2ÕãmÀ¬×DÃôv‹(nI@mÅ5i’17× ê°°},Ý!Mn6±`ÀAL®6fNpñòÉ®paV¦J!×V56t:Á±x¼±k*e íSë7‰ÄXÛ'0>i^ì?j7)Ä8m¾n"t|m¢JBDÈ•X–²ûS« 6%¼ø=<0:‡&¬ënxž5Ò ²Ú’`–€#q¡éLF(hÌ­ù›8³pÜ)Wšï¥#B?i|_ßR¤ Zð™!¤`KÃ>û¸* [BÁ ~«u‹‘y‹¨,ü–IºìHu„EcÌ^/]Ùþþ"WgŸÜNÀ•ˆ¯¶ ßú£Àêט”›¶,üEãì7jêªì-@Ý•(Ší Á ¹„ewÉ|ßÞ•;ÐËWIT^¥…G¢—»æ®ãwÀ*ˆ†@Ç”©*ÈýlÆI1ÙÊrÔ@¾t'bÌŸÃ ¹ë2ÀQvÜE¢%æ¸4Ty¡²j¦|03ÞÔ˜?¾¥©(–šfU 3þ‚}V²Û•™ÎK'£<̯ jÛöMoÌyÑíßuñj:_›ß­[ Ï&è"T‡V˜ôÿP% &å‚FQ èוlO£È~eJƒ5Ïp¼ÿ²µ‡P`¶pÚØqihnË"Z²9ô…-£›[â„ïçï™{›e”ø*¿jF¬è/GÌÔ3;Ò‹÷v “T6¨ùK¨©(NÌÖ0¢ ù‡Frkƒ+;ªå·H_åì$GPò Òlù5£H»ÀÔp_—3oØb ØùjW±nL×Ô*i—!-𥊻ŒHõˆÌŠ!œNM28ëá£2#4–”õ/tô²E +íE¿Ð±„3–}ý½)þÄZoaß±û¸· Kþ1þ?:‡FFËo²ÁÁ“4ŶZv'˜GÈüçd˜´m¾¤1ZÜJc¥Ñ Àw‡fvMň)qÅæJã¯ÈÇ‚ÆlÇ·‹xBöˆÀ”ºÔ¡ðqj °« ùZHþC†j0Tá‚râÞϯêc¶‰»êÃÌWB.–³6*DPJuRN›4:+˨% Ö-¿<㬥|“Ô`Õ׫P'ö[ašÍVÔèœ»×ØA~ĺvÂMâÛ.’žûxà ¹wJ¾tG&®©ÿüÝ×Ð6q‡ë_\?V€Á$}§^`Ýbª860MC@Wé," †üuÍ`•/¥|@ú¿-u¦± ¦§>SkÕcl¢7 ëF´ä"E-z¼•¢âòÿp͸šáø0ÏAÿ[N©†»Ñ”ó{#¸LõjØ“QÌ–zè„ä<5寶ÕèHN6tˆmòæ#a~™³ÿÚ[¼€Ç+³E‰ìc궉èkõtÜC"‹0rä&\dwìí©åöÊŸC¼,ç†o@”F>!ÔÍ —›¸¢ËùKa\g£væÊZÝ%«®ÿ\ÒǨg/¸?uëf„Lüæ¦ü·S4ôe\™ªZPc~ÁÍ~®ý*÷ç6¤¬Ld|µÑGL©4¥K–¹†ÙQž‚ü"“aŒ¿xj4€R4Y¨\&Ç«ƒšFE³ÜÄ}]Ó8¯.ïžbHÖQ©BP¡"”$y›{ÛÍÚqôœ1ž¶o¹4²l]|u”‚¿ÁÐæ+,ÏòiIàœýd8‚Ÿw­ÙûÏ<ÊfÀ‡¯É˜aP4!ω xé’&'ÅV3ùÎ!.ÈÒ0÷B]ƈ§<ÑtàÓR38µå@ªzº(?ÐK\ãÅ´,K¼×ôH…XñY»tÁJ³šœ­ë ñx䈺ÄMÖÜá#é’ 4Noî²ÑÔûÇ2Z<ú{…n÷Ó¥–±d ½¸¨Ï{ê0xU£ÓO#Þì9áÔñ|ŽäF¾rùŠD½]@ú÷h»LUÐ8P{.›|¶{Þ—SÆ>ü*vNÇÌЙœA u§@åÔt¥©=ÂîYnάÑйEnEÿD8U$L?âtᆌÔj^rœ Õ‰VŒOã`EÜùí¤¸aÑÈè2HáÐg¸±ñ#œË* ïùܰänàÈ”plç·ÙŒhtÅÙä™L2|Ƥ1z}”õ”ElAwÓ2uå–•vË%‰ÝX*©¡’"f ¾”Û— ­*\ífý†1¼™É`Z^™ÞC8tñŠ•gNKë&òSb¨%7´Ê–™Â†×ž4_æ/™p†… ðÝÌLŒ„PÖ­8ˆ„œ¿Æ:û5Q&(%hÔY /ôÖ܉S ÝSbé©y:‹€g¥rN˲§ YÃV(¢XŒwñ}ÉÛŒ³£SE¥›¨>BùxCaV°ÒPл´BšÅº,›,>%`}äý8qf’ Äö3pO[•Åy‚nêèq Wªæi2çâ8‡ñ±þ·C¢c³QÒo:¼w‹„ódæ“/á Õ‹Z¹sÌ3m™ÙöУçu¢2ðˆ#¨† iOVÊ $m;܇a¢TuS9ªó{fús–Jé÷7tÏÏ¢7tgô.öÈL>y×¶<‹Ü4¥Qß{’ضTWíkà68í\ñ5¸pe}p›’;£%©ç"YL ©Â¹¸£òùìúGû|'bã²碌5Æ÷Oë†d´‚ý"iÿvª  ÂàÊÄÅýû¬Bs…Í”RÖ²Po¸ô ðãTH¹À )8חО2äòÉÖ/¼ï¸þžŽ8-™ L§[Ñôåcö“6Eq»²=9‘+|6q@9ü’©¤YÊ÷ª„4aþm£¾–E¼“¶¾Âû«=;ëî!,ÐmDÐàñTü¬‚¶a2G5Ô[•®+3¡ÓÅàYhµOo7ç}ý7· k¶"4Á¾Ó:?Ïq°‡÷»‰å‘ø S£Bœln½Î¹©Å¢Ò«R+bO“«Ùî¬Ðx1í&&&V­øQó âuIúdWâkÑôŠ6"‘mðû~£@ˆ~ Óû2lפ°õÖ›V¨ÄÈøš kY?¤—B "Ëe$æòcºm¹ÊnÂ2ÑÌD’ïƒpØïSí£Tàü¡i |F <±w°#Úé”Þ³À ÷ËÕZ/تühï×P³a8)¦4ûXŒ¨Þz‹{lÌ{‡õÿ9V\»’»>(I½Úl/qØÆ¶M$›Álž“Ñ€ñã)K—¥Lþÿ¼ˆØR!Ý£nšTöeô0¨ËàBéuáfr%[·HI@¸‚¹wÝ÷1ïÍ=a„Å—,n|ƒO7D‰2©`{e HÅ`@gW³¢Èœy¼ŽXùs[_~€•á ë?ßæ¤e¬’ë…ö[ËQëÓ¸ sß,Š¤Æ¾Ê04~“µ`b­…6ïî˜ÚB¯å´E:ú~ÚóȸGXÂ4E±ä%Óûž¦]¥„i=Ô6vüžú½w×BÅ´1]Á£pî¦0ß/þ"ƒdðjK³…’¥8(ÙbY¦ç$"0{5fErCGÏ‘³JÛÀÕ># v4-=ôJKƒ®ìW»–þÊ«Ûåm&:žÈ¯,šN4N*?¶}Ñnlf˜ýNtM¾!bÀÄ¢‚¨j—ä¤L<,øØTVò@ yHÈusTß=cðŠÞ5ÓežÕ–àì=Î'«ì–pé_þ@<œ%7[¤8ÝÆÐÈ hqâI17íé ‘ï:£Ë«:³Tôä+2irÂ1^¤9¨š¦>ôog Àñ"ቾ>øÊŸ†Ø±kž3»ѯ,(o„mÏ^_b$ðë|Ãq]_"Ãê1jvÊïE"V’ ù1ÓÎb|ýx¸±X¦~˜d=g~ùzü­¶ŽÕ™ÏÛ&qùOq JöœÎn¶üëËPÅ7ò=%ä©Íóý¯¢"4þK¾&d "¤ßAš´àú섬@o8GÀÅ]­7}ÈÎoçæ3%×~©/T¬úVÜêÜcÜÈŨ8^1§6bë«–¿4wiYhst0ýX89àHèZ‹åŠ+¯ZM÷ÍÞ×ùùªƒl®G¨ôÞÿXa`‡þÉö„lJ㜽ã]8Ì ýÂå•8¦[û>66 ” èo8Û"y¥ÿÉ"ü°…o vW pxÝââåï×Öeãy Áªôbûéªväh1Ý4–á#z g}F)f[hÍkkÍðéâÒî]JQ‚‘Ù²ÚÀˆ?‡¨xÒ4<ʱžÍñöœ’£ÓSWƒ‘‡#Yr}¥U ¤Wݺƒc¯Pð¸{´ajzÔFUˆ'«ZUÞÚ¼ò¿þ•m¾TCôJðH‹R­9 Œ|šˆî. _ð]¾]4TB7:]Ê( x˜föyš°sÕ{r»¼ãÞ·!£ç±PÖTô.¥»ÏºáŨŒIýü‹ëõ±t MdÅ`†@PnÕk"ú¾ÞÉ]Ö˜£ò©\í{D²G•¬×{»Dê™ä˜ƒ;”%¦ÝNç0X~Vö_ZñHŠóñ˜:í \t¨1ŒÇuô^(™y"¨f‹t…ên‘{5)(h}“x…8Îâ®ë:lèð÷¸VÄu¡t&1¤ Ðöù[ÛKû`ª†”]G‚e¢pdýAf|wö¨F endstream endobj 206 0 obj 7728 endobj 205 0 obj << /Length 207 0 R /Filter /FlateDecode >> stream ¾Næ"Oêü>ä±5ݪYàÅñÍ…Çë? î{ÇÔãù§AUŠÇ¹û–ó{̨ܲ· ³Òêç Ò«Ëà endstream endobj 207 0 obj 64 endobj 208 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAA+OpenSans /Encoding /Identity-H /ToUnicode 209 0 R /DescendantFonts [210 0 R] >> endobj 210 0 obj << /Type /Font /BaseFont /EAAAAA+OpenSans /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (ËîÌ=˜›N#ÙRƒ8Ž_\)±]J§¶‰W#ïJ]¯qÔ) /Ordering (kÈ`{ìrXe}©ÿªM?Ïa+UO2%ésOÈ;ã€p) /Supplement 0 >> /FontDescriptor 203 0 R /DW 0 /W [ 0 [600 259 321 548 561 476 613 408 630 604 612 618 500 252 777 612 353 753 519 613 500 553 728 252 338 729 930 602 477 595 556 612 571 556 523 503 266 278 571 524 571 252 571 571 632 613 571 516 547 571 902 591 571 1000 266 467 925 259 ] ] >> endobj 209 0 obj << /Length 211 0 R /Filter /FlateDecode >> stream Düßñ©hæ² :ádbêAŸø¿Tƒ“Sœf¼´oª'Ѳ…ÝŠõôñ<ÃgÖbÆdaQgY'„¾ð\vK‹]uK€Æê¹ÀêbSÂLÐr„¥À8vrÃÔ\äTÏ:PÐ7‚‹«»€–È|1”´äï¯W°¢àW—6j ̸bU¶íy%LÊ]àºA£(¬J ¸«ü¹Š|×mA÷mÿK£%§ðò)Ï1ø÷ûÔ•­úÜË\áHrÿ •ŒX¹ZÁò£É‚É5$Õ[:+ütòº/ÝØž¶vŽ¬ï••«¶Îu ƒ(±6²ZÛA¾énèŠs³GiÓ­Tõ[ÁG²åü±â¨Ì%X*Åý´À-“ži&7žæ‰q I¯RŠâÆekxAøZg·È£¿é]>µ&Ëlui!•¦c‰&÷ò°Ùì0¼[ùòü_ …´Ûó4 ú Üy`™3[® ©H™N³š¾ðl>È Ï¥–/Ó²/ÆÒN C¤}ÃÀd3ÇÆžl©7Ží#ÉÞ;¹ˆë#å* £7[V9ä÷@Ã^z?íƒ-8UïÎL91p#ÊËdzèàFtu·Z=OŽ~%þ!½¬K’ ƒ,ÒRVýD@JtÏ÷£qˆê÷tÕžì*•Õ‡®³#áüczp2Á²MÖîx- endstream endobj 211 0 obj 512 endobj 212 0 obj << /Type /FontDescriptor /FontName /EAAAAB+LiberationSansNarrow /FontBBox [-166 -303 1000 910] /Flags 33 /CapHeight 687 /Ascent 910 /Descent -303 /ItalicAngle 0 /StemV 0 /MissingWidth 500 /FontFile2 213 0 R /CIDSet 214 0 R >> endobj 213 0 obj << /Length1 19612 /Length 215 0 R /Filter /FlateDecode >> stream Ýöyo b13ñ(¢HC¼C’¿IA×?Ò{ã íËþ°¦îY»r~*äëp ÙZjÈYRž„Þâñª*²‡…Ì&¾8Þ¥g9TŽ`_B€Mؽ˜îH¦"/ ¼ãƒ–t;ÁHx¾šZ÷šsÚ2šþJkeäùÓ×ôÂ]ð:ù5‘÷/õEh p0$R¶æÀtÙzwèk“&ìp©ŒjÜh¤)ëÿijÓ<®Üذ¡ñ(§>6HÓxÅ—¬Å„ºïÈYÎ~P@bwß½¼v§37³¼iSC4b˜iGt抸ˆÔ¡©ÆÚ‹ 4ÉìN&ûêú|ÔZÁÖ¥3¨ÊOǧöSE¬IBG¢JÆá”'^õ© ³ß~üÎQ Õ4íJ.½¦k¸noÕWçÊÏg¾H5j‚ž€ß ¥‰”O¢“8J*tƒÔ˜€K΂ pdö`^mĉ[òÒ.…öËžÐ[~ò"e0û'?=i¤<]úß…ýêBD”\E'¼>аu¤Î‘(ü¥]Éøa°éNª¼îqvîkCî–¾ÌĹ›w'u±ol…Å_k›T½‰sS^OÌãy4^xè÷7bІ.>ØJ¬÷⋆œS—`×ÅDÊœ‹¸à2<ѾãÿLE²æªröªŒu€±6ùÖ ¸“õ÷}Ÿ¤#&2‚بÏþ¢VÖ¦·°— Ëö™9¾±·é’FØ¥H¡•ú^=’°ðl[¤Ü–°!Ë~ÚleEßc™µS¾T*npP!`åùgãÕ‰+cân?Z†*3+M&÷>SEÄÁb Ç4Þžìxv5íHqrl¾VÀAÿ:¿"?³\'Ó^™&r¦à>)iPá!H#"°Ä6VÂ0Tl.ްW/›h«¢#¡ ,b­k¼Ò¿ÕîÀ@¥¶)̈!äæ¡¿ð1ßéS»êGg9u6ÕØë§AÂõCuyT¤¸µ§å$'ƒÛóD§Úòi ‡¬Ýgeì×Ðd¥ÞŽ5=&1ä?YÍA €"­E,Ã'ž„O½J_Fl( ¦2ºS3NÝLùmï=ãê›ú{!§«íIáç§Ž  ´ ¤WÜBÄæå§gkyg:8D÷&C=ÚY»uXÊÆ&yêΣ7Æ.Õ~Ãá*$ánLþ»411òSŠ£,ÔÍÆÐ¬ÉEµ‹ Ã׎ ª/bŒø™L‹Ý¡‘«Öœú\£ZMÕP˜2ÚˆeŦ ìœÉ-‚Ñv½tŽÇÑ8wÿÿhühH´3éH{;¾d˜eß5áVœ´´ü¨»bA²z$n¿ w0A¦=”ÐñŽ÷Õ@-‹8ÍÊà q—øgœeÈ]¶kj"fƒä‰Ú¸leù´|öªEñ>ß5ìÇ0¢¥çp8P„<¤%Wqm€Ñ© ±Kµwz“°üÕԅ€JIú{?÷tpÞ„v> eK¯û] 9u×zÖ#7‰ÚÕê†%Øêë4?³°j ˜®øãéÞŒMãð‰ž,S™Ô}mXY€Nœ÷_ Iù+iuå\Äá}1Ï»ãů(Á˜ó£jÀ‹<<ó}|ªsµ²ƒk¤4y.ÕQ¾iÄw É(íÔʾ߲E;D† ’PQ/G\«%y”påÉ_YÍL½"èÁàê^'$o”~…ȨŠ`íd†÷ÒY%.PE¸Þ»çæMéå5ýµùPtl÷™Ï.,O{Ÿèº¢¥ ,^ãŸ+oœ`L¦cÔ$¢…äŒJ§@¥‘*ã¶4³¹6ðéo¾(Öq¯BNtðe ×/éž4§¹ ­¹&Ë’Kžáa`ùŒšþ°²†oÊÄþØe‡» ISÕ;½ï¨®GÆ›(ðb®"®+s$X¥}…>’»P^ËÖ'…ãÇkwŠ2¡ Tqd%tTçÒr¸['¹èˆ¶¡Ì×î4Û˜;`ݯ(üE2óç@ è<¾¼ßÖpu ™XÜHoÊ Q˜°–Âef’ÿ§y-zx õ’­ZNÿW®öÕ-ɶ‡¬7Zè&è¡csÐÿ›6«J;ˆ Oz¤ƒK@yÎXÇϺLCïÁî'JÙ£3ëÏ©ãŒÛ’ÿš3µ¸KÎÐ*iN‹M“X•ÖÖÔÚ,¸Ël»†:Ú3¯TGRÑ´;ï Zöa´È<_ýä§N1…6a>hÌÔ¸Ã${–öÞ]ûDržXø¿ìż+f˜Ê={¡Ê=¹ÁÐÈèj‹õ™„8ÔáoêÝYv¥5>Ùf‚zyáC®4µ‚b«d¬Ûrë& S¤hŽ‘¶b5ñÀOhÊýi“Û`½Úä¦ZÊËÖV½®b*ÿQÍâØ˜¶Ëøqû¿†D2á A×û0‡ÆßÍçUi“.¢ùù«”r®¢ÿ Kتbä®N|+üÝ­âüú:$™uî?±™Ê=Þ…Õ*…˜úqÙî8°ñ椲҈Šs’àI\D¨­Ò¤Oýrf„@H¡À{úFv•Iq` è Y©g÷:‡9;úãnÐ0UÐL7†ÐH®T›‹Vàc欩ԯ쉸%Ñ|o¹âVCù¼!™°2Rñ ÇÉkHY)Þê)¼'Z9–Ê¢¾.ˆ‹´Å|ÀQÝ}g~Ëî*áyáð‘Ø½‚–·,R¾¸ãØëÚ —+~ ÇÛú¤Rö#¶,öÉÄ,€œ¯ “N]¯ Æ¢Žrj!Gèø9Ic‚Ü"¦ì ¢t^x{S\Ýw";E¶éÝóàÖ÷iš]ËRéP“­F3µ,y}fýÀÜKî²é \ ¹5¨jíë\eÑÓOV]:¹»Í‡Éí/,£ÝuHBt\¦èÿ+%›/–ÇÃU4IdkÕB:œq¼ d±K—²œ9厑M‚,ëNÒ1j~ãu@¬{÷àtŸÓx±µ«6Òzz•7K ¯#ÿsE‰[:Ûœ0 VÌlh·Ã‹ÀDÞ™§oÞfÒëljv|‚‘I*Ÿ7Õ™Ò%¦ ºõì馭ðÉ£â)§K5 î°¹ÍÒõí1F€•ú'8„Ïi‡Ý„¾IzœÅµM‘¯rÈœr™¡0CãO¶X­¾¢òöµ–Ù JøËx¢‰ LÌš8„|g{‹¿¯n?ñ »k¹¢ñÀê-ûæ-stýùN–E-y ÙôàÊ…•âè&F[Hän ¨6µÆìæþúŠÞÃúGTãÌpszÓ$†µ5JÛ§)´”¬rYÐ`)€ÖJ@çMßß »Ûœ B=N†övm‚À"C-îÎÇð»q”¸¾pú¡;ÖàhýµÌXÉ3cJ/º!]< Ÿý‰E$X¡S:|ƒ0cöÝæªæj bãy¼Òˆ$ý8{ÄÉÅ@ã•~¹]_Ž?Qº‘ú 3‹Õåù#?Óݽ¸¹_y˜Ûu.‡ÍEw0óžnÒè½á¤âæ&?‚èáh«Gªœ*­ã¶O‚@íìQÇñ‡üq 㣖}ü¼;ÄÛO”n4¬é1¥ãÝ®dóG(sþÑHëѬd~ΧIL:µ‘‚œÌU%[ çe2Ô 6U´Xù}^³™¿U¥'Èoƒo°xÑ‚é½ÓBJDˆÎayð¤ Xî;5ÁH3 Dâ ×^}ÙÙŒP\'ý㱎3¹i1"D1ÌŽÜu‰%ÕG ú¡Wþ¸-ß4:ìÃë;猡‰aî ÿ׃5t†‹ö`ƒ‡kb?vBðs*(^ʾ׮T;vïßÏËkÿÝ›s4µUn%æ~ô‰,ñ¡Þ»-æ>$W2•Êœ-÷n]ß8kOŠè4í™KVé}eбx?ʱˆ—êæjrðÓi lÑ”Q”Q‘› óØ33·ü_éäµ8|„ì¼Åâfå+Ën^§"’~ê·¾dƒ zñã iÏ'Ý^Tpž_ã[ w¨çɆÙ7®Ÿ¡HQˆ$I«æª|¡n3 Ða]Ì3FÚp 9Õ5ÓÉí/IÂo‘XÕ,ʱ‰£ÆßRèÿËcÝT0ð0û“寀(æCûñ-äN0íc´Âú„ˆQ”чÊL NŒ}Ä[‰SD¯ñZ˜MʽÅq+?#åIË4ëºö¤r7[¹ˆDl°mª¨½X¬Zò¼«£#Þk3ýÐíÑJ:ˆýt%[l*{{XÅ®†»ÂŸÖøõJÚeÆÂ_ÊøjƒŪ:¬)çÌ,^Ðr Ë&ÇÌ¢…UIÔ‰!öÊ1ñ{*Û œ”?²gjõ"OÌæÏ)_N3fؾ@ýaÇóž6}0*&Ñô &qy]ðÔµW²ŒÇÅÄE£ÜÓÓÞö³o¶c®µü6ŒƒjÅsë±þëòWé»­¬(ý?¹ûQS%ê,¹>x1Ï©Æ\/+ ÊÓOe±oè&çšž ¦¥F—ðCÓ0Æ|ÆÈ»ÅUÜ6À÷²ñ¾6X3n«™Nˆê¾ÈþŠd¶>Gÿƒm?¯YC[DŠÊ±ÐßP2áˆ[ó(˜É5Öb€.”W¹Ôx¥gÄ6ù”c_70±”«–mScÎÞ+ŠE>Ó„¨S<²†ù»ºÂ—JÅcäB|:&&?i[»>ž¤è³ô•,®BÎs#×vöy»É gI½yÞN¼\3 p¡†iÕkS·&i 5‡(ý{Ù>öœ¨¬t¡¾·å»¦*ØA7öÍýÚæ’›3‚3Žƒ$íΙÚ-$|<⊠`™\~À—O‰þ1¹ °¡ÞHŒúËç×ÿÝœšîK^]íÍ€qHsß([BjñÈ—‚(¯"=srkN1>†Êy£&l¨#¼#P?‰+ôªŸ;r[Ÿî é£H«ƒ[hŠ’‘BÍÚ®H_D.92º‡‰@¤äSþ\Ь^E©BÓRf­Ôì=ÁºòÎ\À C ’Y•f‘ ˜Yò×a€ 'ÝAø"R¨º€ß\bÁ•v¸ÿPDйŸéíŽ#ÂPõøÏ‰Xq•ðô0€ »åU´ÑV͂°“6[8çû©n³âg»¼Ð=µ|4l+áQx|ÑÊW›ïY7ÎÄÉN6 X^Ñ{óxǧ9£¼M7¯“&ê…'“ß0PÂ9Âúß”ÇgËõp=N$½ôÏ$˜ÍZO” ‚à¯ý ÞD¯6?jÛ¡@ç²ò0Û~òGfi쿉®pšh'Ði.Ðf6&¬¶O×Ý©£m0?©Ò­ ªEõÈ¢kHÊׇSwÚ DÞ„6yÃ5(?éKÿI¿‚úƒùÅ0 r¦«Ý—"”»;Åþ5„¸?n‡ÛÙ×UNöä#-Ö$q0ÂcF±*æÆrÉrÈ1T¼]œ–n¤Zg\bYÒAØö˜J­#¶~‹ dGWRJû›ñ€æé“K yu²IG(ó\•z3m¤—aÉ35c© ¿øûÔ|>¬qð+ö’NÅôÀV¸~¬Yü#½øù0̹Ò$Ã~û—ñxž¸Ÿ?ÃÌ DÇ…º”’z¶Ò¸KìcáO¡­`x Ñ_WÇð?a7¨§x9hð~ÑBþwïTh7ÂÀ0DF„Œ´Fº†SÑiŸQæý&9Fö7~rØ<ÿj"Û%”Û8ÍÊÉJK{"vC} v­¨2¨2š=Â$ï¡p$(êëÆî®qÚÜäläu7© C‰}yîĹRòM` Îò,v­0ôøcKTpFèñëÓ:V©Ow¿d;z âø‹½r·k“ÀAÎYõ®ôž°W2LÍd¾Ã›Ô‡•C ˆÿéZ’‚GæÈ†@ìå#G(]Üár4Ffòüy}=×èõ¿ý¤MÏT¶hH™í³O³ GÏÍ/ ¿6êÏ]jÊC\ ”Ô Â-.в*Œï¬…‘›\­*Zœ±óY±ÈÃÝÇrM²x„-hª‰¶®çö²bø¦‡Ö~RüÁxn}4,¯™ÇP¾G%Z4œ›º‹dc”/œƒÈ>crœf ºöÞR®< ú’‹rè>ã•Õ,‡Eƒ¯µWÑ1.û¡J#wt‹Ö:AÆ #?´aå+C½ži'–IÚ;²_˜žë: çÿÑ_†ö¥Î”Ĺ}¢/-–`YÖ9^rÈG_ëò°)Ùt–>_t£{¯¶KèWtÞM.ñýR¼y $ÖD2~oCK¡#0ý #Qu͈PÃ…]ßç1'ßKж&bÕ;³Ž¼l¤9+få)eýcy™/ZÉ!‹ä ¡óDõøáÍdámÖvì‡\m“³˜NF)Í‹„c7•ߢ¼¸êbî‹ÚÉ®àa>±ŸOä²Õ溥9ø`aýÔšÖz°Õ­,8~JùMº«¹Á•RPqh ‘õî‹d>ß²“UXnØ'W@ç2\Rb·'Üja”kɯ‡hŒé>u";”"Hêªiÿ䙜áÏ.´ÊQ”û¾q:1TÊ3g °nþ—â%ÝÚ›7ŒÊP­¬J¡,÷ßfº—ÃëîD`Š{éåâŸz[EIóà—{LÀ’?á/Kk1¼­¹g¯œ·Î:wÂ/0|!(( I…‡¿Û9´£ÜFKdÝþÖ ½úËV †6(6Ýn6A€“íu˜?$šü+ËÖO¹9øó7Þ’¡IýN4ßîõœ¬挭‚'úº/ånÞ±yìˆ@Mêñå°ò,|E8 m˜úçܹ„Ê}"Í®òáŸmà>§ñíöýXò²|¢KWj§2ÄïÙwÞúGþ;ßX2g4pGÝøŠ•.?É/kYWwïôpYLÀè!V­ç¬}JaÏ#½—pϸ:¼a{yÞYb»>WTVg·ÐÁæ² TOçǶæBÂÖºcDQün6·$pöÖjŸº¶JÒ|Gï`/•s˜†+wä-—3âQ‚ßœJ$‹õ°aõb=I|éÍ—õ«ŸZL›rñèÄ"QS}{µohß® qE}$Û~œ^°'Þ}ž%G³ÑÞ77J«K}WôÜfÎoû¤Á¹ŽÈ¾iêãÂw)¢´³¯/£´'‹¨Ôa‹QóYºY–%ÛJªœ#ëB¦)ÿÇ»×~þ3DÈš u‰g¹wJÇ£ \ëÕFÒå2%ÅÌÉû©­BÝö¤Ê¯"è‰hü0fYàsB-¥ÌcÒHˆþ/†Ñ:ô¸D§"Sœ¾«I4"(Ë@ƒ"].4}\“š==®;¬6Ö·ÛJJþ#+š{¤^)#U½‰Q®z%©v5,‡¸ "ž )‹QØÃ¹Om ûÌbF<ž²GÜÕ#qr?¥ ³¾aã wâ{?Ÿ·=° ¾Ã=Úù~Ÿ‰……"ú‘âÎÃú“öKЭøÊ7Ç6(aL†ò¾]ðÿÎ{8`³æ#6{‘C-{³!ÛùaÌÝ |U1òø2}'”;“ú ¦³ð_Õ;œ%±ä+0À^ OýAþm;§¸ø_Bv€mòű@<‚’¶ŒR·Âñ%2¨™6ùæ ²WSugLå3€T® = s"¦3Ò¼;ˆ7@LœØ'O…abý{½¦ÜíÀ¶a ÞE÷ÿú—lÐW[ çÏh©@ mµjÆ>;]¥Wo…qyÉrbBWÛ"J¯ft$ì/Úb`•å(=@>é)Ñ->t‹ý|xŒx²Ð¼-n :Y'yO9IÐÉjkmË" f§{î’ÌVí{8./ƒ¥£AçoŠðF!Á F ‡”<è¿™a•±ZµnàO1÷ÓÎ|.͡t…*™ g·¥¢ÕI•úË#‰W4½ßÌÒ•ÂÒéx´Ý¬kù þÊ ðv%… A z -Pí¹€Jò>œ)‹zËEœ1”¬ h6³;¢“Ï·Í…$Âÿ˜¤ ‚ÿs£9D.ƒ¦k‘hÚXGò"«ª½–™]Öd%q£†æPqTD©ßáøë­ßеáyÄd¤¤µB=-êPøoØÝ2¥$ ×°µÝÃ8+¾Zèn%}EKYÅ<=¹§J4†ŠXau5™T ^—ø§óV­¬ò¶M6òéèÓC÷ß àg, 5ùôpé]Ú/O±û7ˆhL;SApÈßÛkÔüZZ÷VÝ Š¯sk…Îz¡ÏÕíŒ*8 iëZBÛMúY}ƒ3¸¿JLDJ¹B<âüƒKÃíñåÑb©ý õÕ|\ Œ –c‘x°Ââ…'Ô¼Ôš’òDT8w„%6(n†\õ­áîb‰³¾ Çx,¢®··¤àí„,xV¥ä˜cÞY n³tÙXÛæP°‹ÿúíÁm 8Õ×1öï228»¡Å !]*øoK>…”ñ[7ÏÚû£s½Š1Sà±Í›ºéÊæ)ÖX\7 åQ EüÛrÝ™{Ä|ÕÉ;k-óùË=JJu—ذP‡<"PEKjµv…„j ‹åÖ/#;åFÆ­ÍvE:—˜o\ ñPÏNûÆ×Å' `™‰%%VÃï™÷GöŒ0O9IfL»"cDz*öŸ %/ãÚÞ9-3 $ ßoÚ]|0°ÍZõ°Û¼!–ïµù$5̪©rr0¼yÐuñ®OàÕ¬<࿾R'ÿ#ÌÝ‘ñÌÅõ«v´u“‡è B+gž¸ÃÜuRöÚŒbžÊJMýH€j_FšøÞcpÇCš 7'ªÛŽK$B íé hì—azèÇ$Η” y³,%P;ÉÚÇÐì…«•» çPX˶SÜak#爊˜{!3w ëçoËxøzN°¿°Ùœ •Iíªi¬äJ܉ Y&×}€’ûsñn˜»Ã˜È3ç(¼y©ñ$DÔ;€ÍIm¯ÕHú 4†.ËW?ÉuG¸9Á×täïðܹ0kEr Ò=‰ñ!ýwp8ûޅ㥰˜í›òžë@ñgvKué…ðWÕòóò\£iœéV/8â‘*Pšúœu1¨l~8ef+ä%<æÚ­dwÑ”óÎ}U :©Žî@#(ÞDŒ¬ìô8†Zà#À¸-q–žwS¬¸¯gX|)þ«QÝÂÂçq¤D7(ágb¿“†¬jûÌÐI¹!ï“Â'ã‚ïÏüäÔ}-Ž«&à*<ÙIo^š‘éF’øŸÍÆô CšêפõÝ'³Setšš„šR£óAÝÁÐ ÐI•ìáprËnM'J¹Uæ`ëĨæÄ¦—*G—å³}!9¹Ñ_ß]nÐ I²~k3ô–äx^×é¾]ÎâÅn'¸ÛD˜ù¼¬ÏEϘtˆíB|ó'µù½¶Œmd’È?X]·‡ÒÒìþâ­/Ô”ìNþŽ9·Àùþ…H·©ÕïúÁ(è¤(±Ç>pdŠ)?€"à3(ÓUŸ=¯"9s¾YR’‡†ÇÄqå Æÿ‡¤à³>†ÀN%%î6…µIÑO›$µO1ô9×ÄBöBõ†Ü4»_Á K4ÑzßYVCZ~ç:&è“쌌Ý9öq¢Ø_°á™}Õ1!Ýo*$$…fe쨵¿ ï³Ywü-gx;q5MQÚ) AQf$%|9×¼‘HÖ™ŽÃO„£þ¤J÷þ£—&S)©žŸ=H“c˜6ÇãäÃ•г ¦|÷Ý,f"AüR³ d;!nÚµ÷Mã5sì¬äP¡j ×›ÂEMB8,u”»n[™0#y™åz¦ç,@Z/ß:x9J:õ#éX/虚þ(­¥oÞ0Åyrº •ŒÄ‚”p† l%‚{²ž‰Ç ¶ qœºc˜+‡Æ9å²´ÖUxÝx®T¤wý¿L1Ö”yÂã@a¸FÿšMàFæ¿Q›ëL!‡!‘ [§„*»i f!ޝ²rõä†y {_EŽDÃÏz‹ý"e4;øîÊÁ'‡€×=´Q¡½Z¯Þ”Џ,afhL,}[§Ê½vÇJg³%½ÕgS\,ö†æ)è¥n£[ˆôð;I:ea·„ª$Æ®“H±³£¬b‰½ÆŸ¿6¹íïøÃ) ·éP'OJÏxéÐú8þ}‡¨·ŒŽp¼|Õ¯QÍZm‰‰>ãVhð24m¦|œÀD9ËaƒÆ]5ÕZ¦Ù™‡¬-éD¨'öàà5£u#¤Hž&G£ñ€Æ`Æ~î–i®¶ýeòQ–L-vÀy'ë(DÔ§bâ*‚M’çü¿J‹J]­Ö+}‰X+zgöE!6¬?Kß©îä ”—¦Â¿Üò餺­ÐdÛÒþ²«E`nOÒ “jy(ckFc³­íO(6Šì›™‘vH;è+¥9¢ÎuÁ@Þï”0þ,±<~ÅÈçºDèo±ÍÍ+oZ!špö˜Ã=B½ð¹‚¬ö‰÷v’Ë MaÍL¼(î.½•Å<ÉwÅ*ˆ©tž¼—ªLÌ›õêoß: œ[ß“ sGnbî‚eP¹LÓ•Úl¦õrëzh.C –ã%À7 žÙ½’ DÅÿú³´åø–HGÜ_²ör“õ(ðáßÓ?¬D´oó>/²’6Öä?¨C‰[½†’R$“Ç_í¿‘Fa5+1˯íêu7y4ýóxrÐd<#/E‡‰D1ǃJÝ-oØI‹3)×+QÂKòÅwË=n3€)Íăp[uö±|ì±µ&¶tïE·IOxß0·sɼšÑÔ66æÈ‚FAôÛA‚æÄ«¤ÓL/“T5ú‹2•ã/S¼‡üÿ ôb‘æb3êcŸqÍLË옧[­- L„D! „”—çè Xew¯Õ©¤BÀ­ªð×\µÓHªàŠfÎnG aâÕ6ÂR7•~¼þý(u+Ó]ìÈœ#LÞYB×SéC|EšD‘—â&Y”à‚iÜH]iعå„On€ÞÊçZM$Ô†~dв¾|¡†O«°ºfN ÅOaܨ¯ø£—t¶±™ PŒŠ£z4vÒ–7ÁS(©HæÝšÊߊX„šsh<qɇLÈÞ&Ǿ¡CÒ‰•Énõ ” ÿ³…áÁ•kS¤:È‘l&6Éã‡;·@^e+”š00Q}—¬)ŸsŒ§+:Ë]IÍ#»ìFgJÀà«z´y•ÑVlå§.¶,xt¡½¤ˆ:ð4Ó½±G@wvÒí{!Î+8Çpî¬.cHja¤Ó¶ù8Ó¼ç2 ^.¿z×É„îgß.1· A1N÷tÕö=¨!Û¨uñÛØËg¡ˆ%¶6¡2.ÚU:#]vŸØP¼i|‹àU|GI)‡4uDI†&šiI£˜r¡c/@J]™ätwçN}P'‘b=—lŸ!‡ë‰Ïy ‹ç2Cù|L0 ¦£3’%6qÕ¦6­¡f]@¥ÐŒb@71°£r›rø¨d,ŽU+]mÑ sµ£WVð¥Š$î–Z4E”¹q—ö[ˆ|¾³l¸¬Ð»´Ïò˜¬y$¼¦¬0@ª»¥rhxË€¤8\µXµý3? ZTŠòÀBÈ, éúÂ’V•ògužÐ¹NLZJòÚ™à6\D½–˜«ÅÓmÔ¤Œˆ]‹$!¨%\ÓŒ·Dþa]=¡ÍM“HžÃ( ÂPõÉñùÓÑQ7Џï£Åkä0vs.oî|»Ò .ÃÖ_B„éƒg!“_cl^hxõQ2CXê-F~xüö¬Và|!LwÖ¡xíÛ\ô¡ã}‘׋bÓÖäŠV&ätkó’Ü4·Ga¡í@¼•- ¼ÕÇŸW ²Áúö`ÛDZÍa­hà YnmjUh½ÈÉrÌZáWuiSEí9=ûÅÙèy„²mΦ>¨˜¾ðÔ÷‘4Ì&ÿuë§YÚÜBw(‹Ry£>®¡§éf)šk‘ ³$0ÝR„ <€hÒ ” n½¸›’‹f Ts$´©ï‘} eœn³œ„,-§”«’V#x¹ß¤xö°öRãàW%>*«ªPÇy0@ë EDr­›KÞ¾—Þ56cˆO«¹O(‚Šz§:\Ü{ ˆŸÅKI@0àô9BæÊ%Üc¾lü(ò~Ôþ¢Ë¿glȉڎ“±Eõ‚O Ö~XSÇÄDv{6uñ6Á§çsgC5g’›w?ê´/œ9´´e”1Áë{ï~( ”ûÙgźÀP0ûñÕ³êâ{,‹ð‰(ñ 7­Ú@jãâë*¯ñêʤC”R?^ºPHíŸçííȪ!qâý¾«–±m ˜Jöbrr¦Pͤ/°©ñÛŽs_Ø}¨É§ ìº ÷³—ÄÎßj˜?¬áã,2 KÜ_öÔâNu·Ý@¦]*­áÉ£…Õéý1Ñ¿Q8ê{¥×­Í~Öb𠯔éŒ$7lLƒ×Êm(4øqøãÁ±¬D”-±º3{EZ#fÍâÐl +½¥a`‰‰Ë¨ü8À\XÒ>)SÆã„1ÊIÇã Œ«Æi@ûì?»¸™>Ï*ý› ~ÒWŸ!~€T€¿¶v‰#¤²òØ r˜ê·˜ÇñÝNíLTR0…½ªÑæ8èãWÑ]ð»Œau/”Éÿ“×å_ÞÕ7M4…«cÍÖááhDÐtXX rütqƒÿd¤e`Ó\9a””ÞJvéæÜÍêâÅ©šenkÑŽÕ„ˆWñ¸%耹$êô(x=8 €|5«¡È¥AH:}$Ÿ‡ cªÕ—LýÕ¨~^W¨”ë0¤,-ñ8ò¥Ja' „6i¶È¶æ»FûZ'G{QïÇ£å/UY€ÇÁNÔ4„d€ä˜Ù㈕аÞQ””×â§í&YÑRs™a0_;G ,N| Fl” q÷=¢ùæì¹ZoÙwâÀ[c-Àt˜¿†]æAî&¾_vK$Â?Èà;ù›%Øí,ŸbÚ³n;e¯DwZLäÅÃqÄÿ24φ«0˜äÎDB51¤íE§0ÀK\~»¡..Íf½ý5Y÷ ‚_iÖß»ßÇî|}ÍbIþ_;Ý"à-Å»×ÿê©Ã•Scæ-f0të±Í„‡–àɬˆÐý˜CsòÁ²êÊŽÞ“YÑë°½†ló¬Y¥ñ•4•C¶Æj8u‚¨¤}| K9€ÒI±><™ èÝ8%“—„ûyEïl`f¥‡b?”cízµ™/ÇàÚ“€ŸLå¸ò†kµ´ò‘Ïg¼¼åÚ‰.—~Î ¾÷ù”€aüK?>*Î|úðÝ,×3ìܹpX¾êÏCa¨€K;p&WoÍu®éÇŒ5ýE>; ÓíÞx;–[^…f+Ǫøøìì¸k˜!|Åïʰlù³}…™¬çŸºn(ŽªËšF4c‘û\°‰M¼¾2êß—ÿ ¡UM6)Ï€ÜHÀÎÔê?ŸËŸtÃŽzñ ŠŽZ0&y»å0að˜ûÙWDaéü*¹éP¹z"€:mú’²Q)¦ëÝAcÄ´R®­ª{ªjÞ àãÂÅÁ3;ÃX É1¥=T«J»Úo®lz·k€/ª{Ú8ÞÍ,4Ç‚ô&þ½è9&ià×ìWôþš·Òønˆ†Â>ZTXò¶À¡wêÝü]qO)ÎðꙞ9çyÝXûqNoÖ÷¼hÍ ‰/þ^ÐãÚäÕ”˜§­€?—Г9¢] 6„0­aGó{®(¯!{ ·Gò@óa 2ƒå9¤`²f¨mÑúÉÝ+±+.ç‰ÇÍBf÷¤ßCDÃæ}>Z˜UfÖúê8Ð7 šLoŒ­Ý­À Å/ãó_"âaïÝ]ƒâmÛ³'ô/MË •„& ”Á°ÒDŽØSðž¤køHZx„ÝUƽ³!áJ Bâ½Ô |Iä;õ}ô>` TìYL?'s^¾*¼Ç+wDe»œ´§î1Ég‹Èï5Ò˜"©¶rÈ}‹LX endstream endobj 215 0 obj 12864 endobj 214 0 obj << /Length 216 0 R /Filter /FlateDecode >> stream 'ºé|¬GsÅx Ð¡¦£A?Ö!GϬÜ\ÇÓ Ö»€·rš÷®¯ØâŠ1 Uß endstream endobj 216 0 obj 48 endobj 217 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAB+LiberationSansNarrow /Encoding /Identity-H /ToUnicode 218 0 R /DescendantFonts [219 0 R] >> endobj 219 0 obj << /Type /Font /BaseFont /EAAAAB+LiberationSansNarrow /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (äVô¨‚Rœ^ZiŒc€¤OägÐ,ëeœøöÙaYâ) /Ordering (ÈEŸ»}“æ‚zÐ^e[É[´˜IîÌâÔ69) /Supplement 0 >> /FontDescriptor 212 0 R /DW 0 /W [ 0 [228 228 272 546 456 228 456 546 683 410 228 456 272 456 456 228 546 456 456 456 456 456 456 546 456 182 182 410 591 456 591 456 456 500 591 638 456 228 591 410 456 272 456 456 456 456 456 272 410 683 546 410 591 228 500 638 410 546 591 479 546 832 182 819 410 410 228 287 773 228 456 591 157 729 228 291 ] ] >> endobj 218 0 obj << /Length 220 0 R /Filter /FlateDecode >> stream ß+tÞ¡ðœ`ûbz€f$[終ŒCTU£Ò%?Ä–i &5ïp¿+ø$Àé¢Ì=«þ ŠÉÒ’o›áƒ&]BßÕ I´ ßÅûÚáÙ*ðµìF±BÛQ‚%(Ü÷P’R™ÿŒ¿™tÇÑpâcC}baJׯɮ'ŒpñÃZ¨ÿkªjÀ ÇÃu‚%$ûŸ¿àöˆZ§‡?IÇ»h€ù|?町ó1ÐÙŽuyvÅø:¸Ó½aØT—£ôÒùÓ qÞ/îÕ{Î(¯Ã´V÷˜û¨îþ?¡&6‚)!&ºì§ùèä[‡1´YhxÂÿÙ§Q<"_l\'{ ¼/ÊQu±ˆ2UËÙ])l–£È€hŽm³ Нç#ïÊ7{8?Yí,³­“Öâb±¡Gî‡3ëùoÈOb¢?ÜŠŸ./i®Ì²Á©Ì£O×}6;-ç?7Ñ¡O7ÕJÁRò¡ï¾Ò×%àT­÷Õ´’— Ù7Õ€±yJO5ß i‘×Ö ãûÁA{GDiß]ȤÞ?’Q³Ê¢sÉc°É’x.»bç ä¨JÖi¼ìH=Lþí‘‘ºú.§§CK S tCCjžPŸ% '†Ï®:x(rôŒü™0æÁ[0zuëÉ÷M©´šW‡FÉ››´{ÝàiÚåIù¥ZL3ÕôTȾM7çs‚ 2eˆÆÄ‹àÌB)䀘îzýGòÏw endstream endobj 220 0 obj 576 endobj 221 0 obj << /Type /FontDescriptor /FontName /EAAAAC+LiberationSansNarrow-Italic /FontBBox [-223 -303 1000 1014] /Flags 97 /CapHeight 687 /Ascent 1014 /Descent -303 /ItalicAngle -12 /StemV 0 /MissingWidth 500 /FontFile2 222 0 R /CIDSet 223 0 R >> endobj 222 0 obj << /Length1 7732 /Length 224 0 R /Filter /FlateDecode >> stream ¹Þºòëaÿ\ÐQêªùÌúíàÆ1XŸ$ ¸Æ)%·bKUåŒÔx¿‡/ƒ«…¼C¾ÊâÓÊàû7ó_tÿSÌ'ÆâÑ÷(ƒ‘+6ƒèj„V¡€Õ†Án’ìÊ“-2„-R²E¤åʥ深´É]w2‘ÒL@˜1–p(Š)}¹D"˯r!ÏždÈ3`µºõa!K$•lµÐÛvû¾8A&+¤‚Ž 2ʯ3ƒÈ™†ñöó=Ýü—ËñÓÐÒ›ÖñYò+|ÁàÈÉÆ ó8¾F4Œ·ÇºvÏ÷Q9ËVdÁ«$Ή·èSA/„-²ÿK-EÑ‚„2¬…’µhG ° €í¯#{èÃVļG­Å#{›š’UþAÉ4*ðà›Pv›å¦¾J}.ò®õï˜ÚdD?\ÑKÒ£‡ô)!Dõw†í vÈ~cÇg/u*I‰äøe‰:Þqìó¢ØÿmYc‰+qr<âÒS=Q¾ßÞñgW^ŸJQÒ/là퇋˜yäNlM-¢4"‚R¹ïÄe âAtº#`‰¬×‰Xª-Ì9¸©ˆø†¾3aQ³[œ5jB}š«® ðíý§V|”!ËN‡Ú†äL§ªþ~zQÁzÊô¦¯r)ýêõB³T3²"&˜:¦ïïXtÂ/ä€6œmÏ øi™Šd¾Ð2¯N‘Ž Â…x¡3k’v:çO5ãGŸo½Òb5¦™‹-ª1Í>·%CšSõm*ÿŸdïÜGPBõß-ßiç¶3:–‰'ZšÄ߸[†Ì•Æ–k›ôà,[K|íÅú´%}±¸'ÒpGït )´™k¤!92Õ„êÇqkðšíb]vï;Üá§z4 Óµ¶b‡xb<¶˜âÿ¿E›øŽ Õ:©ÐÓÏ£ò^7bï\…$Ç5¼ãU{PiYÄ»1Ò„s‡=¾ W¹¢†ò®êyôXj°ò4"sý]"ZÚ"5–Kþ•ÔJiFv“¬É ˆQûÜ; arú•Á>/²tt]ºbÚ‘i &±¼@ô¦ OëUÍZoîãû¿9c[ØŸÏ £ëö#·}‚§‰@¹ÏâÝûŸ Ï’œÖQÔ%2Dx;#%=Hì!Í£™k4ã¢~{Ô½æœ%Cš/O<^c…ʧÿsRD:¿%ë?º#þ@,@àÜêæªÊÖÌÊ-Sý‚Ÿ«0ueç° fõLºá¼«›ü 5ËÀmÆ«â,ã,ÜÕNò…$†EO6b¸¦V#(̳æêÐ_ÆVî—Ù8îöëê{„a˜E­ç¦dô@åÎ>û3¶¼ÿ¥Lݽ­ šÚø¡µ=C’y#_ÝA§g˜¢¡½FŽZÊëoclME…'aÿù †Ê.YfϰŸÇ\†`åð÷‹[Rôת¬ ´²ûŸs)<®/OTÏå7T Ûe¨;üÇX&òùW_E<¸ÙRß­æáþ*ni•v1vÕÖçôù™¶"Ù \Á5‚d°ÖKgÝ÷—} X7£2ì—çéKy¯õÙ[ÖŸ„Ú ~k:œ€M]¾îí¡ªïQ|4¤‹<å±"ZT'OABÜ,FI&¶²ŸZXäsÇ}3>=,Ãô[8(ú¯ùžRì:RÑØYÏ‹ÆÙŸÌi¥ðt xµ-4y,XêÑÞý9 ˆŒ~:t'Þ;  3.M® ˜rSö ×ÉüOnŽ Qs´8ñå1.Õx¤›šÎº`Â@ŠfƒŠ¦nReŒ•|”ú¡´™³ë­ ÿÓÖ‹Ú€3 €„ýºGøsØÔïP&K½Ù›gi4zðOþ˜˜ó0ìÞ¦ÊW´ %®bE€L1ÖHCË„ñ]I¡òá‰×M©xhX<‡3’3­„¡]¦F XìØP c bµ©UÕ9”àù[p×?&åý¶4fw©¤çºùc/úò ¨,!°¥9WócïÌÖ)dv÷fñ„±Ê‰?º 3ìl&½ü‰Õ_4,¥pÛuæ†;°%ø ¾§Ü.Öy”iJ=¬Mx®íò¥Ù|;%VrLêøÖ¯øpéoiA%†¸‘ÎŒlíŒ!›_˜ÔKjŸÞrÒ3ÕÛ } öŠ"ƒ¨§ïP„ú "m‹ï\Ú§6²réù|Ö/B¬nð4‚Ám`}aþ;œ¢L +vVîØ(™ƒ*ùbQKcP…hÉkë— Y­F·¥-Êà£W)z)þ³\”<š]jJZ¥ˆûÐè¬Õ1w´¥Bš0’ê#cc›®Ißȶpú´$ááFö3>²×@ .Ø]Öüõ­Ü?þƒìm‰®pâôì$½?te†bå¢{³P ÆcL<üzÄf¶O‹1É(;Q–°ÞÙÚ×¢?ûv^/°wÞþ“RxCšy9‚߀Ö©‹íéiRíõGþnÆýˆÏAxëó‹Ñõ2NØN¶cûï£TÅ%OºF͋ްÿZؤ4NLR¼øþ(•Üa àæ=45|Á[œD·ãdJ¬"¹˜„o7²R,dÕLþÎÇW‰öy\p4¾!É9ÂO0¾º†èƒ™y‰ë(DÃΕ‰—™â§†_! lÏdëDî¾·\¶Œèõ1b"ܤVå{ …2z×›á"I‘+Uëx Øûî?$uIu÷€%—ß§€¼¾žÅëó.¢xæ“Y¬Å?U&(aÿ4^zC‹fZ^°à²rÝï—sÇ=uB­j(¶¶UR}p4:Çþ²›Ú—oƒÏ‘ÿËtöI€F‹ä.°Ú`yÙ9—PÐ÷ H¿†ÞÓï;§‡ &o[™ïö«©ý—Š¢ËÔt}|Á5ܰÂ^õ$bDQÃs4$VEáF@1 :"@ „-Ò¦Òdzìø[„1‰×¾ÃH€Çr>9úXǽfÈöº@¼lŠÔÌa`àptEÅ™W3¸»À¸¯¼Šr±+ì€úþqF%—¬Þ#Æns[9ñ9J.cªŒ‡©Ü;—YoyêYÚÞp¸­ïÞ §‡Såw}3 '||þØÒ}¢$ß qeâB“#¸q¶–—ÜõEvDÑ…l>5:úm$í¼7$°lkçÎî±ÑñaPt4DwùïSJ7–?é2è4‹¥xÊïJ¯Ô Ö ež­¿Ðó˜ÍgžiíEK¤<ý©Sýo0o•å Vvæ¡jžùûS–½°ÇÜêqQUYhì±"]vÙVp· 0ãôi½zîg‚˜tmÃE>`Gk³õ‘]›¾:ä' ÷lž.ø6@ yðY*‡ª9ž•.2Õò…ðÀü[1üÁ_k"Z”ÝngZž«yÂkzœ³EÛ.õÅY¦¯Ç2õ§8\Úµºá$h‘ŸA—*ì–À·‰¢¯ç ˜`œ·]Ë̶AÒÄŸ ×±!ÅŠ±+íK¾B9ÐX>ÝÿDÓ„—÷ñ•b=•¸ÇO[¨ô!Wïe$«F$Æ¿ï{(Ÿw¥ÏU> sSOù'ƒ£ì á¤X¹X,ÜkÙ£Z ¼ è`¢œé3U?\X¾:ƒ²ÑW$!£†ZÈ9‡’¿ÿ=0³-¾µùЏUß¾é}EèË\‘œÏþ%7[WÆÙq'¶%À*™#äšXábãP3–,ï‰ †úÌ\fà°ìä7@ &E5¢ß^_Fæ"¯<£íb‹Ò!¨¡{Džð ­â36 ¼éDqß…QrϪ$³2Vå›Ùèøb!%«áz'P—$‚ò€Ó»’”Ó¾—”Íz)ƒ Tø™ž¦BB¶êv•)v–Hìo¾RpZMXŒËΟ¥©Vö¹Lt¨âÐ9³ä° ¨Wô<â7ª_–µ0^©t¿éX$hð¢þÆE}ŸëüX‚±ëJl´6;ÄOý}u> rôDBYn¹}N:y»ÏmAüí`S»·[#öjË´¨hæ4å XYÙóÖ8 Ï0éFÇø–„÷4D¢óÛ¢Š§éòÕŠ«³+ÄáiÈy,”sS!%™­9çä®Ltã2Å¥G€‡s¡w™_]oe­(Åúƒ–ÄÍ‹UÆkæõBYF‹9 n乿#QÒpkÞ˹¬Yrö¸¹çk•Ž…ïHft ôy¶IãenBò¥ô©3T¿l‚1ž,…ë²ÓLs/„.ŒŸN'®b#$^nÿ±msîª(¸Åi.eänÿä«{¹ÄmÔyàClé€î™Š³á÷—}S+)|ÚªÿÎiW(=)¾ Õ§<'?\ð-!ÚªM¾¡jÌa;I'ƒ{D÷ 5ÚqOKø£f”œ=U;M!&náŠómø4ŽÁÝÉŠ¿ 1dó' x|2+5û“%Ñ®(×Ó<%¨nÐ$‚*üÔÙ]kF"T̽l_â• –¦öVšâùT‹Rö]ùóµZ,˜ÙÕ[Nµ‡Å²Òã Egˆ!K¢˜úŠVØñ³{Töà6[h7Áö 2õ€í›Ü)³rÛÜ 6”Ϫ jB¿ÈñÑeÅ¿ëå>3S¥ÕäÏÃ’¬œ9€<À(ûB’æî’ ÛñU¬¾‰/µ%}yïr<£§îãÇŒ&Ì zW óˆØ€uÖjô³m„òÝïÙ2Tñƒ7›5‡uI múQ¿àÓRlÐ)Ëü«ÝÔÄ þï!J,­•­÷~7Idë½/ŽÖ¢5€4Až& #Ï, \#&ù£Xæäÿ- ­ Ó´ŒSøñœÝCIö.NÆ2ÍLŒê Çv €™·î¬B)Ù-(±.YýÍ€0è¾Ù7ÛkØ]¿`¼ò€‹Ì"ZmÒ_‰öNWV«•йÑý„ÔxK?j`¸s×ûñÂÛ[B§ ‡Ô¢Î"¢ êþHDP7|™^qêÑÆ;Æjñ¾’Ùe+Ã" ÅIp`%-ƒ.mœc=ë!®—»É§øž6 z—cá0°Q¦)Ó¬vãÀxAÇR{¼äÄØDÂnçØšQKæ4 &a»=ô?Zö™W{x'Áæ¯Ò†&NĵWE ú†â/u¼ – þ}.ÖÒ6ƒÞ)W‰ü ÞmTº€}YF>iû]'‘Çïé½°ì=U[„û1’¦çêWcGP?¡ü‰óÆ=9KryºÃzÆ!ßNhå{GÚ[Ù‚6±], endstream endobj 224 0 obj 4736 endobj 223 0 obj << /Length 225 0 R /Filter /FlateDecode >> stream ó[:•ÇT›-1±u40£‡ä'–öþ¦swÂC÷eÊè)ˆ«VN˜²O5åÊŠä¾ endstream endobj 225 0 obj 48 endobj 226 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAC+LiberationSansNarrow-Italic /Encoding /Identity-H /ToUnicode 227 0 R /DescendantFonts [228 0 R] >> endobj 228 0 obj << /Type /Font /BaseFont /EAAAAC+LiberationSansNarrow-Italic /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (?«jÜ…zDqAÏ÷ªYŸ2QMae/Vüv¨ß;‹DkE) /Ordering (bæ Iÿ—Û:#!\)£ÑOä}ž&[äb\\ÄÒÖ) /Supplement 0 >> /FontDescriptor 221 0 R /DW 0 /W [ 0 [228 228 272 456 456 456 228 272 456 456 456 182 456 456 410 ] ] >> endobj 227 0 obj << /Length 229 0 R /Filter /FlateDecode >> stream gû²Ø&ñH£‡mWŸž;ä{¹Ò³£­ÇØwÚ.AÛ¼È&I”©8t¢ƒSQ“쎼Kßž À–+Œ½`hñ‘©‡ôÓóM(?Ô$/¢vë…Ù€OÜ?MòC­¬P® &‚œ‘›Ò¨óxï'ˆ!NÂøÙ¥_\¬Ç'Cýø±Óêá5#ÀØÇÞ þùéÝTñ—KãŽÛFÎq•Y†º•[BT 'é1q…ÈŠs½ž&îÁ Éo;§è¬~û=9ñè6|mVÛÆCxF”«¥§Ð@ˆÀ”Â^JRáЙÕ ,à1PĈY76²˜ý—mh _OKúI}thòÔdÞ¼lõò›ª ”ò–'¤’×K°½=SšGMa‹#o8K¬N`…ßÉBüa$Z L#w øg˜Ø˜ 8'ã9¬ endstream endobj 229 0 obj 320 endobj 230 0 obj << /Type /FontDescriptor /FontName /EAAAAD+LiberationSansNarrow-Bold /FontBBox [-151 -303 1000 1033] /Flags 33 /CapHeight 687 /Ascent 1033 /Descent -303 /ItalicAngle 0 /StemV 0 /MissingWidth 500 /FontFile2 231 0 R /CIDSet 232 0 R >> endobj 231 0 obj << /Length1 15180 /Length 233 0 R /Filter /FlateDecode >> stream -ÇA‡·(ʤt™èÁÇÅô|ϼ……§ÉQæ–xþ˜¤³dw‰dAM7 îõþ ­Û¼}»lÃi¹š=ò6éM4ÀÁ%tÊUpÒ`êÇQ屬à.æ+°‹Â¨ç:´¥Ÿ„ÑtQó$ v€8„è ~$f--â‡êÝ••£åÀùfB=L’-¾ýaÞz¨èW$`míH.¯3¹ã55W©ÜJs}…Ñû1ø,D2Z=¥rí™Ü=a“\u !÷>ê,U;œÒyíŠ&Ï3µ«wt~FEA¤Olþ²•½!š6Z£‘·š†œ6<„ :ßC3Ó£*¬\Šó±ÕÈ1¹ ˜aÖ`7ìI×v#îæMóaÃÙÔ*óÖ~þæ±1ó~žËìGÁǰ‡¸pÍÃÈs Ïȹ¡ÜêFHT:§iµ'q%ïΉEdâØüáx¥N³6žHSu·É€|7Ø5X±QPˆ»b·PrÔ—”­[dalÍi‡Ë%Él/U@˜M‹=²ö“ĬÃç:p¿ ÿŽeH¡ýSï©K½¹L0>‚ܾFÂ~Aã*é¶•¹6”#èÆî*Q€Ûó  @9 xÂAáœ6[Ý»0>D‘÷Àã@´ÆÖž¦G.7ªÛÊCJvVÓ8ùÙbp .W$NÝ>¢ýã¨çCi,ìåÏþÔŽ2ßɶ kTµã Æì9_[ã‡Ê 4D”"ÄYs¾\Dƒäc‹†öOe7m ó‹ûïËSýMÕŒf‰ÕC`[Ÿ`퇸[¼Õƒªb¿£âs† m$µ3ã ’D ] f¾CI(òGçp–-‰¢•/-–nÇB°è»26ÃÅÚŠNéXÿ×#®x ¦ Çi!ÖÎÇäyL¿ÉÂ#Šèþ0åêwÉUD16,8퉼‰E³Èè«i§3ä5•s -ôÌÝpL?™(ø¥¾òý¥Þ€] [%NNÍ,Øþaå[KÂa@b+öÁP£Ð\œ_Dî|”A ¡øg:b>åÙßÙ§€!#×ï?ÆþÃMOHYgÛMÙäÏŸýê¡[;[9D[.~¸ÆèߺÏ>MáqíÛ L{æ˜O³Ìâ.‹®Z¡½gMʆ2y/«…„hVÞ›gU ¡3kaÝ왉ø‚{‚ÔLÅ’$ÔÉ™”¤ViÙKƒKPOA'7°—‹«d ÂÀ\gè†äXÍ¢Wsh“Ϥ¸¡nVGnèÔ.h 7Ò£sÜ6¢ ÒnUœ\}¿-ÍÎɽï×ñzus¢)ºUÅRTVƒ´-GW.|£… ”M¯ŽïÇÇfnöµÈµî™?øe{ÿ¦¼WAÒ>Þ] eº|h6ƒú¨ÒBβÛúRË7³§+úèÓô½ìMn»,4òɹ©àáÂó,lç“Ú‰¿í¶;¶Sª'¦\Ô¿c:¯RÃ.çWFK:Œ˜¿RJ\AÃúSdi*sùkBg£àvX ˆº†RϘ¢š“úàõ ¬;´Ã«øï²#?»'eñnRò>þP1³à0î6»Ô[Ë·j¿ B’çž:ðø>QjpÅQ?éŠBcç`0 õ¢…¾ÆÃ^»EÊ=€aƒçS`½C´éZ«Yýw$Ò°AQ¹)´ˆW}·UæaÈý W:œzEÈØE@#lÍ©ëß –û£ë@hG¢âUÌìÇ ò<ÔÏÍFÚ÷U6Z,ÜuåȦ9öŸAbn =rÙ5[‡0ÊÐÿõ¼,îßt0)…› ?Žzê¿.‘ ÛMGc9OhÞ™,Èg¹€_’À©ÁÝ͆q„ epIšIæ8 ¨ÂÙWôÿtèDnâ§.GÛùj¯oÁñrY¦>l±Bp¿Ç¯Ÿ‹ÅÁ‡…ú7¡q˜¿Î)¨ €óÀ¯8’g)Ö¬3ô|~®$r¬·ÓqÜEàî´%Xjɳ+¶hwœóÞ›ü×ê˦±WÂ^,êAy–±·ªë~á¡ùQš»9ÍùCÄFGgþSÁ]÷O‚!Ð^‘1b¦Ì«i5Œ²å'7OÉ-á[Øò¹fW]ŽŽš_¬ÇO“ÓÈhö»²"Þh¼ÍëÖN­c=õCikî«Ã}"_^ëÊ^VÅ$&2üÜêŒ+™ø}ï8y°Ê¸›ÖM& Š:´EwÐ3”ÊYµJ~>ò3Í6![]Ù D'“‹ŒÜWP)¤3á® k[ 1½æ °~tbÕ+¦Ÿ­KÞ2•qP¿MAcî½]­M—æòŸÏm5rÛ&5_3ãzñ¤¨¦Æ ¨vãËÞÍ“E–A‰cÅȉÊÖ ¦6))"ÄAzC5§“w|[áÑÌ48¾e©Ù}h"k#ÙñHƒƒs,8}Æ‚»äF¿‚½ƒ˜¢•2!G«Õú7MÚŸ²¥¡Ä*ªµ÷QÞÖNíõ§ÚÁœb›åÈŒÜãG/-ÞC ý¹º0ÎÈÝ‘Øûøq5l¤jåRùY·{« âøÚX"C¿ ŸÞŒf'£Ò<™lœQ‚ö𬀀á‚Öm; aûŠ^-Ý]^¯ææsKxãHéUt¨ÏXy­€F/ÀEz§«¬„’KW•F5N3V«J!zrç=𛑠[Ù_1 >ˆF…0PI„PAŒÞ>®s–2t*Çq‡…ûÙ¡£[Åù¨¢#šÆ3@«$&+0<‚…Èj˵/ºÚŸ#/Él3îWpôNÐg} Ù´_°œ±#kgô­e–é$”ïëQéqÙDà­ü“Q‚$¡×‹Ï0EWμzgd¤ú‹™GnÙަkU s¸þ‘:BUçeMö‰i›1{K%–…CU[20¬…Ü4¾{iÞ¬ ä°ñس(*hü&{LÅxX{í§°gÏÝî˜Mêa‚œÕõzkÖ«Zä"‹6Ÿ˜Œ˜œ–¥t€QÞÌJkõ3²BopYUSY)°ê Í—,Ѱ6ü~èf_2HLáXØÛõp¡·î0bÜ$ ù3D‹VA„°f˜Å$‘®ÚäGÁ3•=HáOô'ÿºCJ·ö•F"ÈŠ2ÛpÑǬœßËÌñ2œƒ³#fò ønÜä»jàE»‘=²U ™”P¼]7¸Ç)žI¥µŽŸ{£îä`Mz¥,7äËŸäæZiöXœ¬°í ãÙy)zäœêg'Ÿ÷[HœUŒº„Ã… ˆ4)¥}>ózI\Öè]ô.¼ù!¡d^!A˜ÞÞª¤,b•y?±M0ÀóMXÂ?ã'áz¡Qd/¬—…?½oMñ^¿I–ãŒA Ø{«.c”TÈK‚˜«RÕèó"¬g³ímS®Ë‚‘•KY*þ°ûŽÿ­Å%<4ÝïH2ÜÔY<ªy¾Q>–2´ärÖ…`fÍt•Ù›üƒ@Ù=ãEê"‡ã–BûpWp"žÜåëgL }uSZGòÞèû³l^g1õ/@AšT:Ey†¹k"vÓÅ‘rÒн(fQ%l¨ÓFÂß¹ o£mM”Ü¿^áЏ’y†ß"‡I£_«±Ý¤°Òª€U6]ùÙ0Ë[”¤k Ça7xБæŸeO`â’fY™Ä¾9ÉÄÉRK­u Ê_;heïáÝ^ö£RdO %z€ØhüÃ,–;H(·‚À§jáQv(ÇbP¼²~\lÄqÝJ<ù*‚Øe.È‘£Ê•:¦Ú;p¼º#]Ì5RÔáàß,ÙÔvú±ç:Bý­Kç:½ôkÇ÷š0SsøýRsvn†"שœÄ㢤à5´GµQŽœòªe5êêJ”n.^&J´ ÌhA&®îšl1!B)båÅL~Ÿ½tƒÜª9+.9Þ\•Y Ži~HJÐ%rè¶|þ­ï@½ß ¾ª\w¹8ZÀ³Zò~ÊŠ1…M¥èÜëçu›Bà>hpŠ­5fªÁ³‰ŽÚ²‚è@nS†]4#± ~MqÝàŸ 28@– Zm2§¹HkÝ‹€6bW´jør~›FF\ÿ#!„¯[Zµd[¨O‹‚“ –UKÃàåÔ–_àìíYvè"Òÿ[¸§2-2ûeˆÁíÝô‰›Yôg!Uñäüå%– YC6ÒÇh]ˆ„¦¾SíÚEo˜dåN[Xeàæ´¹dä[¸h3&ïlxÎ6Oõšj÷mM‘r¥Ñ{Õ+†.f`PN—çFæ£a®µ¢Ò?tiN4{1鉓´(P1ÿ«±„EÙD ÃZ §çÎ7V5÷âÜwf@ Ûçûà«Pc?>öK˜öd•~«÷uÀ:à)[À¤qH_‹9íºèŠ’¹­žnÈÕ1ä„nóÍ mÂð*¦ï'þ´ŠÌ—›ýþ6Š=@ÙRØC}YBšGk¬ < â‡nV\*Nâɬe?lƒÛÔ‚ëªPj(Öhëíþ¨ÃÿTøFN¸nx½Mr|³ñÅú›šJ!‡!„Þ‘"¾î·ÕÇÓÝHÚ SBÚd—5&pßš¹q¦ÿÎ^J‰ƒ&)çÎû|¸>ž$[|¶fµópÃÇÃá5˜ÌVAP{á,7õtÿc:â´¨zúìÚTOFT`¼\Òl£R'ÝÎo“‰ìöÓñqä|id»^+’˜^c /ð[¸ìFËVÉ/õioepŸ ß3 {Xz&Ø” ÁV6š0¼¢‰>…F•¿!×¼šË؇ÐËÁƒdOÇ?‘dÕZ¼³•hZxÀåó› mM$Ý%Ò£c×ÉõÊ,Ø÷ÿ ¤ÔkuÆÉX"^ï¨yóò¶wB±Ø&ºÀ ež‘¯H«lÀ_=êúͺõ@­jRéÙ½ˆ¼Ø!ŒP¸.êJ‘kNÇ®K wk7P½ 9bXy•<'þ°é›uÉÑÔ¢çÆV;ÇÔ„“%ÌôÓe(|É%|’€âý¨¼6¸Óý:¥@ŽÌ‰wü/‡ûˆ ™jò„+u_›­aR<[¢á°.÷5û5„2,#š3Bk•fòˆì½JtŽ—}ƒëHW&W|/E<œÃ$n]Wñ&}?_–Òº)Æ®£ÏÖ®žÃN¸Rà©ìs¢}BÚ›áºw,ÈD' I+QÅþ| Ÿ$²+l€qlØÊ*^!]º¶7ÄÑÛ£ žÖŽ{ ‰òÛúÉQm3UÕ4Ç—ºÊ+i†x¤ûÜÒ‘½Å…Qÿ}=¿ F$åz­©ãŸ‚D(Ó8à1˜ë%îP|rh@“W-"Ýãi“#Þu+"žª‘Ë<Ö ¡ä…¼›VÅ[7æE/ùÔù꣛[փжµD„Ð Vlnµã÷®‹45´)¼®Žïú¬Òý#OËÏ«v@k ˜I;¦ä™ˆ>Ù•˜MÎIœvé-Øà·A[÷g7|·“ »õQÃÿ§»‡*å¨oÛ$EîàÌX„_å~¦ë…ËJ‹.zíö¹¤-ôS CÐY§¥ §PÁê¹zA¡€àå Cò$ÖQÚƘFå¿”S\KyÌ×¹rs†¡k$â.ßrš³êæ ωÎx1l 'lm‹ô'Qé,ê9(>À‡~<Š^9EZ8$€ZÁ§¦­rJ&Ì… bnh×­ÊbE¸/OÃ=¾^£öiÔ‡ÚFõ;ót©uä«ÂÒ¡–VG+d“w Ï 1.Ÿ²‘õ'ôa?µ[ÕIÒCàÇô=uš“*çkëØˆÄpý r' •ÁeM6߇}ÒÉ#T“†"ª–ŠuŸ±Kˆ{*o)Ãñ⬶Nérûd‘‰Hæï£ÊŽK˜Í@âAï­S§dŒqw2=†-M“âŽö …9¦»aM°L’1 [o(Þ(`ƒn(;4MàÓ¤•Ó3ôySGô«£©ÿ8;t—¬{ OøîªJQ;3˜*LÚh(XrÎR©= S+§k,T-e<ÎeJ6ÃF‰U+Lz.hsŽVž5ƒÉä=ã[—¸R׫^µb@ìV„‘; H'_ ×ÊŽ®jª§JKž‘G«®ä¸Ûð\žä>»ˆ,”09³¦§•Âé™Å4øóØ’R³‚î¦.Ùåý/ór «y&¸š.ã`ªŽq :‹åUïS,™ÁxÞ·ˆ`CJ°L–uVü^¥™êˆ†¦{ù,[0¸¿Û?º²QvµÄÝŸ'¼òàÓ=dµí9üO¥|½PÙÿNaöAþòŽë¿×åT¹à!Utˆ¿—‘s"Bꦜ8‹¹hwk3ìš×·ïL‘h‹J³È0?ò-®Pˆ.¼šW ®‹±ö œœ3ò·ÅßKÂÿl}±ŠÈƒoõN¯áÒØ™y³Ù©Ÿ€IIçµW¦Vž –^öœY ¬âõùg¿X{—ü¬vîƒÒ+¢óˆp"¸Œf°wšŸ$¹==,ˆ¨RsO±êÀöÔõ0ùcƒ7=©)îÇï–ÓÁtf€©¯áˆDª©g瀌ŒOÖ¸c(— Eò™]NDþØz) ”8Ñ*o*` Ôˆ©x½ÁÊÌJ[v`ÂFæ Inàa•H6·ºyç`NZ%¤9‹Ã9äIªš¿÷ {wçg2dœóÅüÂü› Éþ¡zˆ¦—ù'žÆêÌÜ»Wü2ýJlÔÞK…O)¶AÐK÷r_lT ð1s o9ÿ³.DáåýêýuµžÙäæ6áI72•›¶ÓhûæõABÄ È”NbCGj)ÎÇ4—š©hƒ“ÔÚÁ’¼ˆ0È'd‰«9˜ne€×ŠRwK²ãI¹±Âbq püûçîh%¤ihmO®2œ]AE¸¡ËŠ«º,4ɉZ]3P1NÅ“ôz¸Þ°pD£7?¨a¬§ÌÛ©•2Ôû߶ަýB©ÙU š_UÈK±€ÿÿäQÞ~”/}ô£S§FSGÅ£K/±Þ-ž/ž£/[˜{¤frð?Ë@ f;s‹›~÷j˜Wa”›b? ÌHªÆú>)=ßÁ¸‰[ìÍ”Å$Ã-¯v_çm!ˆùò`}Ô\)Û— 4ëÇAJ&|ÀR‰­oùÐÊ€SØ%Oér°/•´Ï£,ÓýzbìipœGûÅÈûÜ­ðY›ÝbÙ´‘JSñ?s#ÛkiˆÚÕ±^¿Ãg…·È>ƒ×1Iï¤!Uipޏ|ÑÊ7 ³Ø¬e§4KoÁ¬üÌ Heçó§wM‡w¹ÃéíLJÏåDqèÑ,³÷ÿÜž.±÷|Ò;Úë3kƾï’Ö¿šª;ìSÉRêènu^_Ù^ó£×9ÕÁÚ0ÞøS¥ˆ6((¥l^¥s4²±‹sáÇEþrþ¾ó ÓVš!õhTYB-ÀËÞºðºk&,»žëÏÈåMÀîÁ(efßú«RC§û„Ü„¦tx)ÕdßënÿîÔ­òåõ†z”ÊÂ"òe'™Ðº2§ÔÀG;äo¡V:×ó}€€Q›¥¼7yålV⣖èc¤bŒHša‰Dóßv”ÄÄø¿°Õ}™. õ¶Æ×97ª·MìxSyþáGÚ¶æñ:<šÎí[‰ÔáXs5fLÔbhŠhõÙ= r¨4bB„îk8€dXè·/lfðEÖI’‹î4>_° örœ×£"]*È›Or/Éœ™ï\m%XC^|þ9 E®ÿ ø6éÙ…eŽß\§Ä¿„A´¹vÁn&ú:Âý(Yeb} õ²™þ€Œ'çHÔü#z‘iƒ€õê牞M¦ŠÓ¿+3–¸ƒ°ÉŽ ”’y1ˆ*ìØbßüåZ;Hk[j1ã§ bøj¹@-pQÛ;Ï%3ÖpÞ§`‹@×GmȈkõ ä'5ãÍÕCÍÙ:>W 3n ·3‰»ÆcUbU¼r¤›¯}Í»x CÆþß ŒªÂ–Ý>v^¤$ Vo¡þÅàh:¶v€fnHº øº;œˆRÄžû§Âëðïǰ5ß`ZØÍ”ñÈÄI cq»çZÑOî ÜFãÏ$fß-—YUn Žc ÃÑ’.íÝûŠ‘v OVª3³Yߘ¨g+?ޝñ›é?8ø‡½²-ý•S«( Á®8k(vÕ¼ÈÃóo9iZ‚µê6¬f½Ít‡ÃÃÒß§îšÓ )én›ÐCRp@ÅMñ ׇð?)§Wàz¼ eŒjw:mVx1ß±EŽ­h»ÆIØÌoÒöù›‘óÚƒ22¬üzxrj¼ò+ù:ǧ÷ ¸=@^¢g»xeý$õ»?5\ŠE+ï{ßzð ùPš`5ê¨L³ù½÷´cm}ß@ÜÆ/÷…£ä¤Uñ@í·¤­ÅáLéCcTë…ö×t© 1©Â%6./õµžŽ½Ÿäô°!›¡¹’[}kï|Áö½û¸SýÿTÃûÈFÐ2[öÚUïÕbt§ä6ÿ"hÌ*  ym B«í±A¦C^µ])YçCiÎKµFM~fø»B® 3Ò¾O·˜Bv1÷Yä—(«øk¨PC &fávëÿS¯à›/²Y—óÙKç£2åüPeyk"€ñÂm*J«V»H¼4f‹|'8¸ä’„Ë^0^l­‰ÖŽe8:ŸÓN Fƒ™"i!«E‰IÌ¡á*µË6´Nüu(B+úš ÏÆÂYo!g¨OV¤ Ü—Ë>¼ÁAšˆÆQí©Bg½(IñyÜßè!&è¼KE)DÙzÇ$7jv¢S°‹<©Ýß/80ÌQ YÀ!«uh!•Ιٙ¡?k LÅ‚5~QFv]9jì˜Fö×&¶£ÝºZÏrW“0Ò¬Ç>a^(@ƒ0 8¯Â•™P¿C±Ë®ÚªäSôåéÏ>ïB`a¾6An¸P2‘fzŒõ¬õÀë}\] .ø~·i‘`ÈP7·U*p A¦ä––Öf ÍiFO5zò%µ¥z©͉¼¦„Ò…pË—Øޱb·[~“êY; b*¬zè±9 ¹--ê’p$L£Bæö‘@÷ûFZùêà“Cép°Ç¥¸Ñ³q¶I ²¶ËÔo½Ýsµ• _!{ª¬*þ§U­äÀdmëiù(òÒM­Ü>ûº ÉŠ^鱯I'ãèÜ-a겦襕ö}…ÝG³àè†{¨”WN¯j] †œ=ZT‡íÊ݆´Ÿ¾´²g0'g’Tlòn£…€¯o i¶qv<]Jr xÚµL´ÅD4‰ÑÍÖàe˜ï O¯$Jh­«Ì²áwdÄǪò>®•/8,&Ñ¥*¼QC¥lä$ÓÎÿUwÿø®1[>([µÍ•‹ùdkÜÔY؉+)Òßlû‚¹xˆä¾4þ?Æþž¬T­g|Q7%)TûŒ 3ÞBh²üß ÊÇfx-¡ÓÜÉ1¿|lÀG¶ݪê,Xj$}g^¸´³lË#Å„‹ 9¬Oû¦œ‡i âòñL6rLÐnû½ó±Š.|œZí"%©Uóœ †6ŒáÏòU?6Ì•™îËm€^°ƒA ’Ñ~Þ¾ôñ3$b¥lC…>´MRQaFüK…T'ºËÏõ@“°e¤òÕ4ÿ™rÖÑŠž¾¶’N’S*¡ÝCe˜}n gñ,¤¨ÜdRޤÙ>©MR9cžRì¬Ò–ÔèJ?ÐOÿ¬€B#¤Uú )9ÿÄ?yý޵ìJÁ5Yí︵èÛðØ4EVG”­U5/ѹ,†t¯©ÁR$´¯ôþ¢ºèo !7âšêö½†·xQФâ¿h#Xãss XfªͲƜñ¿rhÅR(·K×\sà?‰h¼ZÕMöñ²Ÿ¸भ&„»±=K $&]é¿K¬+é?ZiJ([•?‚öÂY]ãB“T)ÃnÙ/°fñ]”WOÆü %ýnPN±ÀÄpqijã.æðP¿BÄKÒ ‚=ÖŽ‹°Œ‡\2Wš¬ .:ÑÝ3ËW©º­-AÌn¾Ä 2‘ëWH£eÆ™bÄ Çè ²gªó•$,é Œež`ÍÍèÜ’SJmð2‰p(b(ª…lÉ¥Ü)t aiF¥.ÿ²ÖÄ`g÷ií&vadzp\çT;Qž[+À­¯ñÙÓ;+L¨Ð“¬í¶ôº¯ÉIVKOONŸTÓy„…Š€7ú¡•œåǹo¿‚üZ›àw®ì“Ëe‰q¯*Ù™#E_Ñí=¤t‘âòYRYh<»kž7?¥M¶ÉláÐ^ÏtF¿ˆ%.¡Çqž‘ä~D²åÀx½Î6•òö1øùŸÅ‹N¥V5WG)Mý] ”`q`s ÂÚýˆO/¾*öÍDa8ìî¾UB |4—`hidb•Ä0WºCi€Žƒ˜ó$ÙZìË1¨@Hnöí9´æŒŠ©K”†+¯Š¬âãÒ[{¹ŸÎéOVV¶rp‰o„B ô£s†Z#Q{³BJ–L e ex‹ŒvÁpEÅ·v=LÀÄÆöÚüR‡ k5ƒöœ—܇ Ê2:šf sξ؂ïÔwܽ­þ`ì`ŸGÄ¡ù:ïàa-":O¢ÁSÌê›n>¬º[Õkß•Re—QU„ç íÖ§ö.ÄU-Æ!ô‰òx¼ça ‘Áä-$-¨»ñÄ·T aá“€Ò,q1çvLÿ6 û¤RŽ_ˆV€­wM9K8¬&!}”¦)Ð~èñ Ü,0÷Yò OÂr`QBu1ôí14w Aš6ÃÝ€L!×q¨ÛØé×1®Ÿä¶ ÜD›PZ*áéMTu›vÕyÜež#ŽëJcäx gFþ TÿáŠ?BöT+Xáb`ÍúMpeÜ™ðÞ@Áüõ•/ÅéÊ<¨STW5›Ï…€ƒ’èC£T÷Âõ®÷““¸ÑRu=´G¤S¥§Æc¶¾@/2ž7O…eyd;²UBµŠ\¢~(÷Ä [ûºUÆHÐo suü¼x¿ý´`§ÔvÃ;¬'Î „²¾òB΃'Y®*ŽJˆÜßKNOËmk¹F}Û„¡ Æã’vJŽjÃlcˆÓôôoëî u'É2í˜5󉛘õ#©²žlÎÁïüAl/À?x–ª_1Ë—‡ÒBŠz0â+s¥×.°vúòäüfæ&&UÈ\1ìäRãÿ9ÿt¾&(K³|Jà`å+‡…°££—)I• Tº'®‹cçRÇ][ßçߎ.ˆéEh_÷ôs ; endstream endobj 233 0 obj 10032 endobj 232 0 obj << /Length 234 0 R /Filter /FlateDecode >> stream ´ÌþÐ0ÛK ‰-RŒUkú<ýÔíø´»±÷v´æx‡¿¿fLù?ÙÃ…¯ÖÙú! â endstream endobj 234 0 obj 48 endobj 235 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAD+LiberationSansNarrow-Bold /Encoding /Identity-H /ToUnicode 236 0 R /DescendantFonts [237 0 R] >> endobj 237 0 obj << /Type /Font /BaseFont /EAAAAD+LiberationSansNarrow-Bold /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (¶1=\b.‰dŒD*g‘¨Ô®â1wÍ´}*R>x2¬×K) /Ordering (7á,\f£™NVæ²÷ÚÒn»W¾œ¶Ì¡m=üæðtèæ) /Supplement 0 >> /FontDescriptor 230 0 R /DW 0 /W [ 0 [228 228 272 456 546 456 456 456 500 272 228 456 546 729 456 318 456 683 456 500 500 500 228 500 456 500 500 456 456 591 456 773 456 456 591 591 500 500 272 272 456 591 410 591 500 638 456 546 500 228 591 456 456 ] ] >> endobj 236 0 obj << /Length 238 0 R /Filter /FlateDecode >> stream HÂ,†{î?#²«ü~êˆÀÊ þO)'ŒÄcæ£Èåó]“&v)׎’9N•æ:“¶ Äœè±W®½ðÄr;t% Ï­ #‡ ®–Š}á­¶,A6Æqª~à8)Ì¥××A"†ÆØF8iJátcß–ÆÕùO¿-gËÌØì‘/#è½#}ô4IÿÑßò¬ãý—Kä~ “âÝÕkÁFÀöÙf® 8€A÷ó'‹Ðl”0fýpcö@í"`ò¶Ð›Ü< ¥P±Jh,ïBÛTýðïmV¥ÅÒ¨]å¯\ÉŽ‘FÞµž¬¼ÌœŒäùÊ9õF–ÄêXˆß­ýAæy¨VõûH‚îS´T ÊaÊ9–½ŽSñÈŒàîÖºwëDŽeSºÛFÖ÷ï@T;,D<Ù€gíÃç2{ŽßNÓÀË–° £4™ú'¼ÄÏñëd»–`ºŽUgf$ mUè"Hª´Ž 'Šú’yZW®êÛ ¬­ÿ‡¡á-X3o7èíÑÚQßЃA+QÇѦvlsŒËåÏäú%y¡ž˜ÿÔx=»—&´B7ÀJPhùõóUtv¥'?×£©Ä#'“?eâÑ5C0õÓ§í endstream endobj 238 0 obj 480 endobj 239 0 obj << /Type /FontDescriptor /FontName /EAAAAE+LiberationMono /FontBBox [-481 -300 742 980] /Flags 35 /CapHeight 658 /Ascent 980 /Descent -300 /ItalicAngle 0 /StemV 0 /MissingWidth 500 /FontFile2 240 0 R /CIDSet 241 0 R >> endobj 240 0 obj << /Length1 18020 /Length 242 0 R /Filter /FlateDecode >> stream y¥fky 5 V`;n¯aØÇŒžƒÓê§Ýe)f‘ì\.¶Äiw«`@˜ .Ñkz}±O‡¤t7!±`Dä™Åð†¿ —¸*D³ÀÊÚ¶§X´Ü’|…7"Å„Cï^:0ÓàÏd5BÙˆ¡Çu©-?&-ëµÆÚjãGÔ<WUplj¦ÂwýR …•ª£6^qOæàÁH…-|¿Ù'оå%תãz °/þ ¢ÉŠº:%#›jX€dötâFívô`Å"ÔU熀I˜²QFš²á½Ö§)öøÇAÊœ¤Nî¥ÁpÉjÓ„3J•C5É¢€Ä+L5¢üéþ0Uú‹YB»rÍØ<ƒ)¿ÁµšÿÄfæÓhô©À+þšÀ&*¢÷È9xÓ¿Ísè|±„GȱD†Å<øšôkÁÏVtÖ\Ò|é‰B¨òï›ÐÐE–Ïh¶ûv²K3jOÆؼz[{Éóó@‚”¢‘ÙU¾OBÕ #€žÁ†­€—2\U–è¾¢y%WÍœu F‚càKÖ-'v›¨Õ8uk*h`«Ëßôƒ·LÙºt)ØŒñ{u…[ùùçFî‰jù°ªzû¼ó4-,ºî ü§•;ÿ^žKþTŽïH¬¾É\ž«¯¾äƒ|"”¦à;d8©Î+P'PªØ†|‘Mèÿㆾ©û#Ëž{¥¤Ä÷ªþSÅ3`?VÂ2PÂÖ—ÛQœÞ´z]žD»§•¾€Ý-â®,|¹ïb0²#{V*îzy!$jÂÉrJá‡27ÞçxéŒ<>…¿Dc`( \1=ð|¨=•> Úöˆ’?2”ò&Ó‚^Oæ¹¥#;gy˜5 uW'ÂþnË0øi„å§ Ì@(vy‰ªŸ¡> Uà$Z641®P*#§Û£þóž`º‰ìÑM^Wm ›y¥›p«à2-($°&ä™á|w­¬u€¸Æ¦ÕŸô{gt £Ô ½š`Žœ‰°s~dg“Ù0(1&y¯CÇU´-ú^ü°†F5DRÌ®wI hïߥ÷&Hf®þ@“²ËƒÎ¢©žA KH(šù‡ŸÓŒ¹×)>à RYC5ì¿çÇ·˜±^j?²aú!Òû2ygÓÿÙ:ž(ñ|"jÍŠÖÍÆžFjkñ¨r…ñnÑÿ—0þ¸ž2.>ÁØl¦”á|c b¡¥}ô}ô¬Gs…ë$Q‘ÉÂÍÔ›r,–S¦§Œ\í«†íß`i«wŠšA5lIUˆã«éÊ ‘]¼Øô9ìk~ܧ^Zo°Ø|âÍ<`Út ²ox•˜á,V•ñ£sÞ!evÈKèˆ.e¹òcÇœ¡ké 4¨~ Ò² ÜÊŒÖYÚ'šÎ3)ëו›‹¢ÖzL½¥Ç¼éRzÑ 2“V¨ò‡%m’¨ã¥L)5UÛ|¶@4žÛè’Ìܘ¥ÇÍiTÃäzH¶ó¹Åµ±Ž(RkRXµŽ .ç-ÜÍ|¤¥E~Lµq‘þNhZ3 ê@Ø¥+$»Ò®s‹Z\3ÙÈq¯ä%ÿ†_+¸ÿD±°óI£è´Ö¡fˆÈ°ŒO¼Õ^ø|X ïQ[q]ƒô´9ît¹\½PyÞ?›’:L¬%õ¥ª[Ÿ7~]ØBá•xkù¯iÞ£†Ï@ãº$’iZõ©Î6éiÜAß â@f,‚¦%|°´“­jQ‡üìØâ .J¶FR˜Þ£ð†™¼n-¡o¼g+„]q a‚\™>‹«jV(­i‡æW¶Dm "ãÜè¸Êã„|i§á¾öPf“˜J†·±Lƒl\!êùF°©‚hCiY*DêäkUUcùE%þßIì|âaK×1“õSŒÈL<ŠÊœÂ´îÀhº¸q,¨(GÔßʵ}+»Â¡ÑºFÉ€{(ð‡ý“½¸µp¾·Ës:`€PÄOîG ‡TéƒkÕ‘Áئ ÞÑ–f©LÌ ­cpµóÓGÛ}$ F÷ôuc¾ç í¨…+'3ÝÙ—Dw#Gûnæ”´/²mW¬ËMÂ×è%§eèûô©Ê”nlù)ÖÃòð/ÚI±Å+æsi5àÕÝ”rÅùM”'ËOns’ÞŸ ¢xpÔÜràcdS³‡ë£g]âÝ$—Êʸ>vÁêº-@áÞâ€t¤&/ph77í>éÒ£zàG Z"dçËÕgU†ú'ù$#Ÿt‚æCé§_[3!¶ i @+üzVÁõïàˆgS\ì~>¿1С‰IÀÄ¿yFjéÕË ÔE˜ÌIG’¸Š€c”¨jõ‰.-6Ï÷nÓy©t’Qg-/e9þó¡í~™Ÿ×k„V ³*™ÂJ¡;A¡Š7XMà[¡ä¯1T—w@«œKÚ #ëÝ>‰e}tÂ7ãd>\¬]² ˜.¹òŸ›ûò- ôÉ‹ý0¡Ï®uÌÕVæmµ!ö¡fRÆÆbåÄ<> èŸÈáiðn We—§F |û]â{íºR–—üÀÉøÄ8Ü£Œï`%j‘Ãtéç¾×"çigCØY)I8KÆC?—z¶ÍmµyÄ0Ïæ |¼PfÉ$!©-NQþÑ ³<»okÆœÝÞ˜”×=àÊAg„Mš‡¯ñÂ3¨—ËõWÊŸ_r8aÏj/ÆjÉr¸íR´gºØ ú–ùeéà+ÇjÛÖ6\^L•¥)k8… óÄ8²&-Ä¡Rqkû Lpð2¹q‘>ÖÚ°Lݵ±®}²Gcc)m;=X0’dì«^®ÀÃW iiIJHÚ¥Ýô¿Áþ6 Á[Ðl‡àÔ—_${kãNN¼¡êÕæ 5bz²UÏdì+Wì~—Þ¼Ôù>díˆÅ»‚%3‰#1bSQúø{¦’oªbÑF(ÎÂ…€ô×Ãù3:L9p'(ÅaȺ‘-n§ÑM]qœÝJsþX/¼à+kÍTç%WŽOMßèÝËPÆõ9.´ÉÁ$HÂ~:‘â5ÁC5GœÂöð>¯¦±ùe‡îÓª‰9np²ßé!0Ñs¤:Ïí¥aNbºà«+Ü J^cÓªqµ‡Všàµ;üøV´6|Úƒ ¬öœaÑ#7=³®`i_·"¿ ŠC¯dú’þ²yiƒ:ZžGÂ?Dÿ¥ý¢ ­WrN5mY2«Âž»Û1Ñ•(„¿òL ÌÖ6õ§}m5“ T·‚$Ç$÷.@WÓW\X¢UàªåGµú­‰®ªŸU®°Ú"¬LôÃãXäw>°‚H @Ø¿D^˜õAä:TE^ñ7uì‘:FÀ]ÂïH˜÷Fµ÷Ëw¶»£Ä§¬‡8Ùqìå§r ¢ƒéA-¬…~*&õžvëPî1šÒdȘ.¤É\´ã’Jæ‚XVÔ"”ôü«Ù®+Ò¿Õl3pôÇ2vÅSnFu¼$ºÈj˜4 <÷Ôê°yñVš H†p)_¤{¥§4ƒB©Õö†‹^Û4Uß®?e.r¿“(êjhý̲Ì*•<][ Žz5®Ê ”e¿Îû›%,&wb žÄ>Þ}¦´¨)6¤(JÊjÈí¶Wã„‘e ÝBklD&W$T¸`÷¿’.Óí…J)œƒ½Ž÷¼¡ª–†“sKÁ¦üÔ)÷˳ýD‰…ªb,‹‘ôbDB/Ì–˜Ê ŒOÞ]Ä“W;–«Aea£å¼êJúl2kÿï´þ|vÃ܈>¾| Úgh/;gn³¡½˜ùŒ Ï¿“ˆÎrrh‡|4 Óx¼ºc?ë `s`ðM}iÃM–"ñÌuDZävÅ18L¶òЊ½ìçù@ʯŒ9rïÀ¾´6 ÃR2פÂß_Ù¹<:E(Ô8šÄR  ˜§)›HVY¨dÇðd?jÆüî)?lz »fBÍ+D8;¼ó{öÃ…ät°=qÇêˆ&NRìÅ —]dwUò|‡Íµ6qñœYFIOó $ZÚäpþD"ÿ,Î(:nßÚ6jó$ÏŠ¯-W)…‡¾D´…çv’R ­:~Ì,ñÍfG>·ÛD±È¦ݙճÇõuåmè²j',~röç1&‚ü­·7ÉÓIêM¹—¾x=3)M|c÷íãS‡Àõr`òÁüìsâ)…0px—Ñ•FòQF` ©Æ[ÏØ¹ÑtO}Ö'u.ÄÀA¼àïÚ3âš³/šÉZe„¥bÚ—!òe¾/˜ô¸‹d#^9 ˪ •Y¦˜"ˆÞF­F蚌9ÇR¢ú| jUéë´ð°¢ ÊkÓqZ¾Qi 4íWfe„öb¯×5ßl Ö¨úž„®(‰ÄM‘Ê}J·ÔÞ/ —â’s°›U^'¾§0înË€íp)¯ ¶gÞíëõ9ƒ—qž #¸¨ÞtüÌSÂBø‰/øêújSR˜h—…)YKŠŒ¤É«˜‹"Su„gýK[Ív›¸„$,À›\(çgj3Ê2âðKd lÒX9JÃÉO~}1 g±7‡”«þã8ÎO¡}Ä‚›BQ_Ðpå{}ìw¿| Ü›$É—a%ÄŸEa2&Õ€¥^{Ù”L~Ey·Õ8¹Æ@8ä+0²yæŠ`‹ë´£äOñ†÷úøà‹Å’Û›Ù4;´8·}x*Ip@›~øjG¥ØGHkÿ¼Ox0íñ¸Þ³²³¤nJm‘b¤,(’Jvºõt¯ì‰ì!²‹G2Ý¡ç8æ¿¢ÿÒR¯i;MBGdüˆ…2yó-Ý’­˜Ý꡾=£âüå¹Â_VéB[“rk#O“¾ÁN…Ó äôiqΧ2«-%²ïQ ï<¦Wöæ­bç*_Ó­V¥xtö¢³þÇÏZSzuüIT£eRŽ. /lŠ)”ª#‰í™fQŽïIÚŠ¤*°BÈ3ï7£­®àßq5N%ÆAÃ(‹6å§áìî¯rdA4¼gý)Ð-âŠõþÛ¿ t§OFº‹Ê>+ôòqE—æ·ÑZijßÕš+ß²#eRóøqÐá[U1 b0²‚Ùðâ40ñ?dwŠfÂE«F¬¼ ¹›ÆõO‡AÓÝ“ js}愵x»Ýºá»?ÏF‘æH;Š–¢9ý(IjiDxl‰yùÉ@œ Ä9yZtVgkyøÃÌ̃¦Øn#ÊŒK eýò–ƒÃ—ÇÛ>Žú\éÞ<Öz¥”Éž#0ãÉ’u·ÊÌÓ?!¤•=õ"<7{LïsPVà°’‰kt Þhõ ¢Xf ˜¤³½ÀÏX‘x¼êÙ3OA Ò²"H—ÖDØþ^ yK©¯Ê¬0ká; bü©è #uÀ˜ {AH/¨Ð[^*6ê&âþ)[6)ðìÁ¢gk]ÉÍÿ÷Æ.FßeàT¢,RP(Åw(Àýº™_u¯ÀÔê¨APÅÕt }´€ÛßL€\爰å+µ™õ[Ò‘û¹ †˜MJ—´‡ù¶, 4Õ„Râ\fy®ÏÂÏ9 ò·!I¨QeÉ(FyÊfýÂ<_·cÜ:q„wë_¤Ò°_è?ÒP¬Óµj[\ŠåP,ªí½®>¡T˜k.#…?Å©Aœµ•Â{ÝI 岬1£ «¡~õQT;ZÀãêdî[ã?!ÀÌÏè½JÎ6‘ ½Ìcb «MžàpŒ8‹D`zPÜ­›uÛaï0Ç6­vvŸ µº1Ý–U;)ëS¢.·nN¼f¡›÷L5¢ îü‚Oª™›iÏ6Óžz×GF>¢0Wéž®6Äúƒà÷_认ü]›q4¸ØnlD% oE¿QÔEÊ”‰¶ð%œ²Ü"ó!«ÝàáÀ¦T²Ø~üˆnlÄ à€Ï ¤ˆøSóÚ‚¶uÖå3D€™Æ«`´$O¿ð/?öÁežÈ1"TVƒîÃòCU(̘v5Èöàæ´Iû13š«§€èB·Ú=«2ì — ;h-àˆñ~ÚU¨v~¤œ£l Ô³r·º¨ ¶¢g¡/úPt²°Û1ÍÏ[{ÇÙD;+p)S ör@Ÿ˜z×õ7ÄçïBwÄM¥u÷V×OX–gGI’ À/6¹\Ú@N‚1lÍ|€RZ8¼1¦ÓehI=j<­¹饰<)€ ®-˜ zûƒKXP*Y^éÏî– òß ä¥똜@ÍŽ ü ´ µ»s󞑦Ëk±?…s©²gaξê¢F½Es2^”Fä(¨GÚQô¶Ð5»þ¯/ˆ¯Ï"ïØªbóy.4)‡bHiƒÌމ21Üæ‡AE(¾Ñ[J™#qOêM¢ï¢b›.R®%)¡A>‚ŽcÿKN±ÚwnKŽFó%töAé“öbDÅS—|qêU%θõÚ¶« ë+-üPÐÖ ÕóPñ¼1)ƒ>EØEF’ô'ìüGH7Q®vÀY »­ü fÈTý6‰÷'l²¤ìAnõI]•Òqcï&0â£t?f¨bçté”÷x÷Q îÈbß³§!¨š,Eü0Ì.ÿ¨”|MDçu¯nõi€>ؾ.“áɰZd…„1J£Ã*¨.™~r¶>›+'féÅ!SÒo>UÛŸ}~JCr€A`dÜm⑯늠¬ÂpKÐ ®žŽÍܬÐÛhû'3Nþýó,9&ð,¡!d ×s‚Il¹å¡>Ý){¾‡át"µƒ‹I™-žj ÇÆÑ†v¼zkÄKŠåÈ!Ò ëÕŠÇ ¯ú iú¡üL±&—V\†º‡¢Š#äénF½Å|`©7Ÿêä‚òM~ÕPÙ‹¨{:f¨\®Gi~Euǽ(Rçw˜ÃÊÞ¬@/—,yÕöìk­±-A ƒèð®"T{²Y%’†÷B€ü< 9«"'=]0pDɱž_I+ÂA'M¤ÃŠ9tùÑ®úBö#&Y7bì`#ulc™ûr lº,ïtB7DPCr‡ ¯ïEfn.:ØÎ…T“Ö‘hYr%ht¨6ÌsÏãŸZ:#48q›QÔ­, `XjtH_2¼Ì74éµPG2†¯è‚[h|þbú®sÎGŽP¤ªÄÒÏäÔsóYè]t Â'Ы²µÀÃÖkU”m§ú¾~É9™%7=®qê/¯çÇÐxzž90NÞ‡¼NJT÷ÌXT¨ïÍÚy¿Ñ„‡&†ÎŽG dÓJ;U¡û$` ‰5FX5ö¹<ÅÒ±­å§ÚVçzI·sÙAþÝ ú{sãEže¨dýQBÊj­´"l_ d™SŒsΗãCBÕTæß¨ð“”>øÑ¹ZvÆÙ_üD×~túïzsF4£â“}{#/9šOo©‡¸òF ŒÎsvWT„å>âTírª³8ŽÃ²ÆWpÖÃjòLÁªË‹ žVÅ&’!ÿDÄ8%æ0 …X¤I™¼Ëû+ÕGlà•{ÒŠ dOñ•?‚“K€Ý6—™ÑVVõÕ(S$‚è¬ûŸµÇqQ]É—'k¿G]}é¢ÛÆ^M¥öÓ¦)±qî-¸*Ac—4ÊÉ-¤2Á Ñl„!³ßy¿$ÿ ,.„Y+ n€ë @š%›S,lIž|9·÷æ&É£±ú Ý&NI~€F…ör?±7¤¿Äúan¿k£T2ûó=l¹m—¦ö´ï.Sý¨úŠa«ó¿ÙìW¬¯®ªjÇkÐBC·Êî®ÞÅøÌÀp}¦1Ë(ž¬K¶¹ Ðqd¥R€¾[iÎOÒU"3ô|RÝЉCçL-rØ÷±êÔ1õÖ™! ièɲ”VÉ&ï¿ìÝÂè)ˆ¡¨Ð\Š‹ ©¶›&d#KÇ}÷&{³2íÇúmÍômÝâbv£ñ•aZ½ÿçC^›0Çë"8<ÒÈèˆpc5ÜÜÅ6‹£g@õ:V%¢kÅðg›ƒi¯©+Cæ-qgüZ“ÞˆpI$RÜ0}ÁŒuc¨Y£½™¶ zŒ=r^¹µ?Ä`¯0 þé#øI]UIjaÊ)š¡^?»õ/ ´p£…Ï¥ý‰3Z¢0¾T7:°]ZõvËtεÒ™µ#ÌbèÆVFÕ•,«´R ”„[7§/ÿ|6pïaPÓJGZK$î=ë@›!~˜Âçíà15e°©J—¤LS¤=i"«¯<—í–%>‹Î'.VÕ𜠷ⓘ¥¦ É(µé¥h’ÃÐ%B Å‹¹pá½<쀟hë®Á^ÞR¼éËYÚѸ׊ÀV¹((YôÈ‹|@–Ûê¾Ñ(õS^;€Ëþšo…,Üwaÿ×Rú‘ÞÃûMGˆÄ%:VÏfÃA6É å }ˆ~0æ »al»Å‹WÖ EãR8•SQå ø9}ï'ÏÊáÅ(Ãs´ >©Ð7…mSs¦Š×%Áœä¤ÉFŽªÑuŸW2¯÷°¬-„Y¤g¾üešŸkWvÚ‡g¹zh¿_m‚lÇ÷³ é-ÿ¯!Z³}jeiGõ¾¿*ˆÇ„ÓküÏsL-VŸæýpg’†F=¥¦¥ÔüŸNÀásÚÚ'ízkw "Զו$Õp7Ì•Y,H¨ÚÎTXï‚‘ÿ¤±UÄ.Ö˜ø¦Ÿ¶éy³ÞÂ[*z½CÓŤÞ(—ÏÎÎZ¢ R²‹Cp¯YsÙ\gH•1ÿ™…-MÕ°þ§„JÆJ,¬ÿ1§Ìµ7ù7B¨iß•¡6pŽxiLÛÛºÕ\AMO¶µ˜ò`\=ÙÍ+qT΃™GQy: ™g:GgA|?¡«UÈÙ©çjÈ ‰ÛÎÖŽÄçK¦/1粋Àð1#´ §›~ݾ‰-–¶þ¬Jì²á—æ°-²×7Ì.ݱ+…Ü“â êlÀ€æjƒs ±CÙl˜ü1oýþ´T;šþõüíu9ÐWPtõÙ—Ú甈åºxÎ.7{x÷Ñ2©­Ge]¯áw‘ÃüæÎŸÆB¢§ªˆ^ñe?ÁÒÿø³æ{÷L¥¢ø±g¢Ë‚Uœ¾…ð7n0)“ñÖÝ ¶ž-Dö(pÁOMr•"¡™eGW¦” 0­œÇ>¾C•c~ƒ0“€bÁ¶Ž9œs”9 e2)iÎ×+c®:¿#s 6¿mFÁd4CðåN£fjè;0»Rìâ+ „xvÀÑ'j€`º%¬/X×êaðsýC 4€¤zpU]Ǩé=ÑGØÙ—6)Œï‰Ì×YáÍûš‚´%Âü .w´Y6}—Ê¡ ¥bã‡çtà*/W¡:Ûe³LÁ×ÄU`ˆÓÒOÍ•g¥ÊVN½ä”fíŠgø¯ÎaæÑ0.h'´q/ýN?Ò×ñ>㪅Q©ê&´’ÕN‰ŸËPFW‡JºïŠ€ÆL¿õÉöŽÌ75pi86pä SïÄað¶.µ§†³Gµp‚7l†¢ÝaÕÊZÉ›7L¡ÓÜŸ!vD±€‡bp3²›2Tž_ÇØiD'víÇ”XZšœcU>ư•§Üœ†«‡™S‡yãŠ8šÛ¯7=ícñÑl·F±Ia=”ëÏâ¨Òݦ‹-C3¾Ç) w½W£M1~_$Cý´)œU0K—nû« ¬ÆŸt£¬xѳܥtHpú]î|ìž·¼¼ÃÒÒ`Ù–êä^hj|Y_•×±v§›×Dï߄ʕ£&ΩÕw»#|›Ø}ä[l×Q3Ë%Ú‡'o†]*¥êÍg8»cxµ|âê¼BÆW¬‡Hx¨WønŒæ@Îfåáö ª—¹FÍ  ‹žÔãN_šÄEâl‚H]–ŽWˆ*acw†Gk÷õ*­&JʵäêhmVYÁùŒ¼7fÙWòÀá‹[­_]›‚Ý̦œÞž ß< ÐDS‹#Z&ßcÎ6¸ ŸBòUä–Â8H"¯žœoÐzöBMKléMû[±™ ïSj_ƒÑç„«n (>ã‹‹ìVØúèí>e×á‡ú1„[,$Ý\þõ¤ü¿‚ùñQáÝKŸL¤¿]9:bŠÅ2&"yPÄ®wm‚Â줩•bÛh…ÄÌ©Ý…ÐæïqóøU©—NARíý¬ùýϧH¥\ÇÄɰ$m‡U'9zBý~´¢uÎkEµE%]û*\Žî<OLCÁ‡d¹pTj|GöÜ2iöb¼,ä\°¾~Έds÷Š—x,Åë·àiõE8æ@i…Þm”WWÑÑÓ䩉œ›­’Z¢>+5q²‡#7І´Ågaq À“œSoÔ–[èéb¨”j0HÝzJÙŸ“`SµÊ~d†¹!p´¡XÚï¬Ôß{`®ßS‡¹pfGçÚJû©C5rþ yO]tšÏ Y8Ó¥žK¼á©Â_qåîÆÚûÀKÚ\N("z·£/âÔ˽°"BÝÄ)©X  ðRàÊþEHåR¸1Éýì:CYdÒdoØså‘)Ñÿ#åHP‹×ÚA܉d1œb^ õ”! {qR(²ÕÙ6ÚéS6{f3\…2& œUÉyñ=`—¥SUØzÍFÈG¤3 NTŸã‰ö²Cö„˼¿ôÃ;¬¨Ùl Ž•< ~þæµ2ÊJç nk?—»9“Þ#³e½{^ù²~j+¯Ü¾¯ˆðW€„j…Dò…É„P`6T[¤Ìì4d$À)ü9&?&JŒži3 ïÂí+–†º~²5¯™á•¼ËÎmKªð̺ÀR^õ£9:WD\!¡äNjœ#™K=QÄ$sÚfÞªl‘‘·‡—_ªbç~FÇÊ9;ißHxñíÜ Rœ¤ Ó—“EL ëÂÖ<ÆžƒŠ¼‰¼¶ùÆXù¦$Sò"ߣžQhácgij„Íã£1¦`[ ‚ Þû~ÁˆâOzáãdS4˜iå8&–Z™qâÍšX‘§€Ž£"ãŒ{ŒBA(q­‘9ÚxöªYåXçtü É£€PÂ9uvRfµä‡|–S™@(",E¹:¶õJÕaœSXÆs;Ü tbßRüûÀ<‹x,¶YN$M­…š'Š’$V1u’ÉÄ"X?µ`x¯$ÇÚ&>²¯Ú™T§Û£öæÛ»Œb¹Mò'We®ÙN ƒñn ˜ìG‘,eËèûArlEq®µ¿/ãŸI‚³:íI—û·2øý] góæƒÐú—UèÆÐ ã&dåàõê4Ïz5gzƒ@ö”æ¼Þ¨[·4~¡Ú¦â#«-3÷ äýÿÃ…üWç4Þ¢ ¿‘ü«UiÉ7â+hð/!û*øI¿ëÑN|T¬©€h4£RLê©UÊz8œ|^ðîËùHY;I0¤FŒ¤6Ð 'Wq`ìÇZ|b`WŸÉ¯“ª1&ú->‚v¦ àfê7ßBŒõDF¸øO¶{6úñÀ!KQ ¡£çyä²E/B[É}D‘ÍýjÕkl4Ü®[òLùMèv|H›õ§ÄÁ'u݈ªÖp…hiÞ6BY¼q±.r„l焲ð²aE˜XÆí×c:n»×Nãë‹[àŽª€453_?L‰TÆæþnKIyô§_è_n°!Ax½¡]©…AôwPÍ<ïÅe.7)“)é)Ô¢¹3³ÐÌjÂgÖ°=dþå™aRÿSYnþ "s(š,9×Éóú Cÿ!7J—F¯öù} £÷¥.“ÅÕ&O§Õ.¡Ïó%î$ü+pÒoTÖèhº wä'tò€\êr^¶‰b~ X¤·çý"„Êv•¨ Ê(vuBáN;ãæùaðXÿÉÙ–D¦FÇ7K?éåõ M«F‹‚"¯nÆXrª¼èn¡\-NS€^Åaôòä pi7˜Á›tî'î2o_/ ¿ KL‚é¸1É›9¼Ç¦CŸ&S bGf'Б SDX,D½È*'ñ–ÐïÀmR†À•‘Wà•?“|)(eÛñÒVÙeÒX9C(¡2Ýø\Æ$¡§;Ì›Èpgþ,¿òwd‘° g׬"È—w"°sª9Ÿ;5!I}ÒhfmÍoíýÒõw5zÓ¬nyiäU5b3qY©„CrM‹bPV[#Qab»(ŽÜ55Õ“¦Ù†dšÛÙºuû´evé\põ¢~ˆÓˆ×߉$®x0ðȸÿ<  HájÍàNJm¦w©Z¿1Á ^z²’7;#gù©Þ>ÆüGÏÀ®ÀïŠP¹ÿWÚô+F÷{Ó0M±{θì|=PKŸ€º bÇî¬Ã{YŒðxѯc€:ëòhêz3Ð%ÛUß…Y½k}ÈMXçJš‘þÙ™"z8y endstream endobj 242 0 obj 12064 endobj 241 0 obj << /Length 243 0 R /Filter /FlateDecode >> stream I××ârÇͧÏâzáO‡.TR’·ì fl v‡±(OÚH6F9Ð7\ƒi8¨ä» endstream endobj 243 0 obj 48 endobj 244 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAE+LiberationMono /Encoding /Identity-H /ToUnicode 245 0 R /DescendantFonts [246 0 R] >> endobj 246 0 obj << /Type /Font /BaseFont /EAAAAE+LiberationMono /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (ôž•,C`ICÀ€Y3`xœÈe&úžoáb\fWQ) /Ordering (^ðP‰²ß" ^½›_'\b”sÁ ù]ÌzòiÙj4>) /Supplement 0 >> /FontDescriptor 239 0 R /DW 0 /W [ 0 [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 ] ] >> endobj 245 0 obj << /Length 247 0 R /Filter /FlateDecode >> stream j94ž²ô •³É"¡ÜºØï&¬ž¡7À ”4Qe®lØJi¿•-ìb ½\¯OË»0ƒ†ja})Žñ™ï¡E&{ë§Ù±‡. Žï®uPÖ»qs¹äõ÷ûw¾öB«ß5P=İò×~/哽‚ˆ/ßOOTýùÿð<:%»á¸ðo«mð,VöÒ *;˜”¼?áuuddÍå?˜¬Íεêé9êâ<ó”çóá°æš9¾@Sâe‘RÊ€•MÊq€£VQ½Á‡Šh2¤§’þ›Ÿ{w3ý§øxk vÎ:JHØ8Wã«çç_|ÙÐT±ø ¥SúÃßY$Ä$o³ËÂb!üI#Ñ=éÜŸŠ ®8æ}%^¡s$mæž—“x”cŸÉCQ„c·ï@ CïfªðeÔuÉ<¿éÎ5g”æR ü›¼~×4 Zyó‡Î àΰðÇͨB”7ÝРŽ¤2 Eá©BóÈ ¬î¸P khª$é·ÉàŒ'6Ž¿§ £,aG “ÔWbX¼0¬S!Nã«X C¾4W?Ë›7/k—Õ¤üÓ'ÆCÉ ªŸM_tWE× ¬íÂIšÙ6ÚêɺEć3"£šÜ=¸Aµ¡ŸÔîšIµ>ýÏRǬPʃ"—ÁÐw0F2z¾£êá‰>lfèŽÝ¤ìFÞñɹ€,±+3©‹:D_Í]èöó¡_(”ÅÅȹÐ- ã͉T‰Ô endstream endobj 247 0 obj 560 endobj 17 0 obj << /Type /Pages /Count 19 /Kids [15 0 R 20 0 R 202 0 R 27 0 R 71 0 R 35 0 R 90 0 R 38 0 R 48 0 R 51 0 R 57 0 R 67 0 R 70 0 R 93 0 R 101 0 R 104 0 R 107 0 R 110 0 R 113 0 R ] >> endobj 248 0 obj << /Type /Catalog /Pages 17 0 R /Lang (v—3³²s\b¶ÛÑ42R1»{¸[H>¦\(É­ôßû‘óN¬·ï˜oʹñwuø) /Version /1.7 /Metadata 5 0 R /PageLabels 249 0 R >> endobj 16 0 obj << /Font << /F52 208 0 R /F43 217 0 R /F45 226 0 R /F44 235 0 R /F47 244 0 R >> /ProcSet [/PDF /ImageB /ImageC /Text] /XObject << /Im1 7 0 R /Im2 9 0 R /Im3 11 0 R /Im4 39 0 R /Im5 41 0 R /Im6 58 0 R /Im7 60 0 R >> /ColorSpace << /DefaultRGB 4 0 R >> >> endobj 250 0 obj << /Filter /Standard /V 5 /R 5 /Length 256 /P -1036 /O <42A2E7C17C7065A2DBF624B9BF68FDDA7F6F8EB8FBD492FAEE2D07CD874AF447626DB2D828CB85B64DC72E447D1E3FB8> /U /OE <618B3B96514FAF511E87A25BDC98978656C6C3AAD3D596AFEA4A73C0E077EBDE> /UE <594B6FF177E25FDE05255A998665EB96DF8BF6A66902ED0FAED57B4D42381ED7> /Perms /EncryptMetadata true /CF <>>> /StmF /StdCF /StrF /StdCF >> endobj 249 0 obj << /Nums [0 << /S /D >> 2 << /S /D /St 3 >> 3 << /S /D /St 4 >> 4 << /S /D /St 5 >> 5 << /S /D /St 6 >> 6 << /S /D /St 7 >> 7 << /S /D /St 8 >> 13 << /S /D /St 14 >>] >> endobj xref 0 251 0000000000 65535 f 0000000015 00000 n 0000000273 00000 n 0000002854 00000 n 0000002874 00000 n 0000002907 00000 n 0000003876 00000 n 0000003895 00000 n 0000195951 00000 n 0000195973 00000 n 0000242609 00000 n 0000242631 00000 n 0000286689 00000 n 0000286711 00000 n 0000287507 00000 n 0000287527 00000 n 0000452862 00000 n 0000452496 00000 n 0000287753 00000 n 0000290693 00000 n 0000290714 00000 n 0000290940 00000 n 0000291106 00000 n 0000291245 00000 n 0000291385 00000 n 0000294181 00000 n 0000294202 00000 n 0000294236 00000 n 0000294479 00000 n 0000361111 00000 n 0000294619 00000 n 0000361193 00000 n 0000294758 00000 n 0000296562 00000 n 0000296583 00000 n 0000296617 00000 n 0000296860 00000 n 0000299304 00000 n 0000299325 00000 n 0000299551 00000 n 0000300792 00000 n 0000300813 00000 n 0000302540 00000 n 0000302561 00000 n 0000302759 00000 n 0000302898 00000 n 0000304398 00000 n 0000304419 00000 n 0000304446 00000 n 0000304689 00000 n 0000308573 00000 n 0000308594 00000 n 0000308820 00000 n 0000309050 00000 n 0000309190 00000 n 0000313858 00000 n 0000313879 00000 n 0000313906 00000 n 0000314149 00000 n 0000334695 00000 n 0000334717 00000 n 0000354047 00000 n 0000354069 00000 n 0000354235 00000 n 0000354373 00000 n 0000357233 00000 n 0000357254 00000 n 0000357281 00000 n 0000357524 00000 n 0000360864 00000 n 0000360885 00000 n 0000366140 00000 n 0000361275 00000 n 0000361357 00000 n 0000361497 00000 n 0000361579 00000 n 0000361718 00000 n 0000361800 00000 n 0000361938 00000 n 0000362075 00000 n 0000362212 00000 n 0000362349 00000 n 0000366057 00000 n 0000366078 00000 n 0000366383 00000 n 0000366520 00000 n 0000366657 00000 n 0000366794 00000 n 0000369430 00000 n 0000369451 00000 n 0000369492 00000 n 0000369735 00000 n 0000371027 00000 n 0000371048 00000 n 0000371274 00000 n 0000371440 00000 n 0000371580 00000 n 0000371746 00000 n 0000371886 00000 n 0000374858 00000 n 0000374879 00000 n 0000374914 00000 n 0000375159 00000 n 0000376917 00000 n 0000376939 00000 n 0000377167 00000 n 0000378413 00000 n 0000378435 00000 n 0000378663 00000 n 0000380853 00000 n 0000380875 00000 n 0000381103 00000 n 0000382557 00000 n 0000382579 00000 n 0000382807 00000 n 0000382890 00000 n 0000383030 00000 n 0000383172 00000 n 0000383314 00000 n 0000383397 00000 n 0000383537 00000 n 0000383679 00000 n 0000383821 00000 n 0000383904 00000 n 0000384042 00000 n 0000384182 00000 n 0000384322 00000 n 0000384405 00000 n 0000384545 00000 n 0000384687 00000 n 0000384829 00000 n 0000384912 00000 n 0000385051 00000 n 0000385192 00000 n 0000385333 00000 n 0000385415 00000 n 0000385555 00000 n 0000385697 00000 n 0000385839 00000 n 0000385922 00000 n 0000386062 00000 n 0000386204 00000 n 0000386346 00000 n 0000386429 00000 n 0000386569 00000 n 0000386710 00000 n 0000386852 00000 n 0000386935 00000 n 0000387075 00000 n 0000387217 00000 n 0000387359 00000 n 0000387442 00000 n 0000387582 00000 n 0000387724 00000 n 0000387866 00000 n 0000387949 00000 n 0000388089 00000 n 0000388231 00000 n 0000388373 00000 n 0000388456 00000 n 0000388596 00000 n 0000388738 00000 n 0000388880 00000 n 0000388963 00000 n 0000389103 00000 n 0000389245 00000 n 0000389387 00000 n 0000389470 00000 n 0000389610 00000 n 0000389752 00000 n 0000389893 00000 n 0000390032 00000 n 0000390173 00000 n 0000390313 00000 n 0000390452 00000 n 0000390593 00000 n 0000390733 00000 n 0000390871 00000 n 0000391011 00000 n 0000391150 00000 n 0000391234 00000 n 0000391374 00000 n 0000391516 00000 n 0000391657 00000 n 0000391741 00000 n 0000391880 00000 n 0000392020 00000 n 0000392160 00000 n 0000392244 00000 n 0000392384 00000 n 0000392526 00000 n 0000392667 00000 n 0000392751 00000 n 0000392891 00000 n 0000393033 00000 n 0000393174 00000 n 0000393258 00000 n 0000393399 00000 n 0000393541 00000 n 0000393682 00000 n 0000395232 00000 n 0000395254 00000 n 0000395803 00000 n 0000396049 00000 n 0000396306 00000 n 0000404155 00000 n 0000404133 00000 n 0000404297 00000 n 0000404317 00000 n 0000404986 00000 n 0000404475 00000 n 0000405576 00000 n 0000405597 00000 n 0000405864 00000 n 0000418850 00000 n 0000418827 00000 n 0000418976 00000 n 0000418996 00000 n 0000419759 00000 n 0000419166 00000 n 0000420413 00000 n 0000420434 00000 n 0000420712 00000 n 0000425568 00000 n 0000425546 00000 n 0000425694 00000 n 0000425714 00000 n 0000426249 00000 n 0000425891 00000 n 0000426647 00000 n 0000426668 00000 n 0000426942 00000 n 0000437096 00000 n 0000437073 00000 n 0000437222 00000 n 0000437242 00000 n 0000437925 00000 n 0000437417 00000 n 0000438483 00000 n 0000438504 00000 n 0000438764 00000 n 0000450950 00000 n 0000450927 00000 n 0000451076 00000 n 0000451096 00000 n 0000451837 00000 n 0000451260 00000 n 0000452475 00000 n 0000452689 00000 n 0000453734 00000 n 0000453164 00000 n trailer << /Root 248 0 R /Info 1 0 R /ID [<6A93FBB8258FD1A2AD5D5F2C55BBC02E> <6A93FBB8258FD1A2AD5D5F2C55BBC02E>] /Encrypt 250 0 R /Size 251 >> startxref 453921 %%EOF sudo-rs-0.2.5/docs/man/su.1.md000064400000000000000000000020071046102023000140360ustar 00000000000000--- title: SU(1) sudo-rs 0.2.5 | sudo-rs --- # NAME `su` - run a shell or command as another user # SYNOPSIS `su` [options] [-] [<*user*> [<*argument*>...]] # OPTIONS `-c` *command*, `--command`=*command* : Pass a single command to the shell with `-c`. `-g` *group*, `--group`=*group* : Specify the primary group `-G` *group*, `--supp-group`=*group* : Specify a supplemental group `-h`, `--help` : Show a help message. `-`, `-l`, `--login` : Make the shell a login shell `-m`, `-p`, `--preserve-environment` : Do not reset environment variables `-P`, `--pty` : Create a new pseudo-terminal when running the shell. `-w` *list*, `--whitelist-environment`=*list* : Do not reset the environment variables specified by the *list*. Multiple variables can be separated by commas. `-s` *shell*, `--shell`=*shell* : Run *shell* if `/etc/shells` allows running as that shell instead of the default shell for the user. `-V`, `--version` : Show the program version. # SEE ALSO [sudo(8)](sudo.8.md) sudo-rs-0.2.5/docs/man/sudo.8.md000064400000000000000000000120001046102023000143620ustar 00000000000000--- title: SUDO(8) sudo-rs 0.2.5 | sudo-rs --- # NAME `sudo` - execute a command as another user # SYNOPSIS `sudo` [`-u` *user*] [`-g` *group*] [`-D` *directory*] [`-BknS`] [`-i` | `-s`] [`VAR=value`] [<*command*>] \ `sudo` `-h` | `-K` | `-k` | `-V` # DESCRIPTION `sudo` allows a user that is permitted to do so to execute a *command* as another user (for example *root*). Permissions are specified by a security policy specified in `/etc/sudoers` (see sudoers(5)). Sudo-rs is a safety oriented and memory safe re-implementation of the original sudo implementation by Todd Miller. When a command is run, a session record is stored for that specific session allowing users to run additional commands without having to re-authenticate. The timeout for session records can be specified in the policy. Some care is taken to pass signals received by sudo-rs to the child process, even if that process runs in its own pseudo terminal. # OPTIONS `-B`, `--bell` : Ring the bell as part of the password prompt when a terminal is present. `-D` *directory*, `--chdir`=*directory* : Run the *command* in the specified *directory* instead of the current working directory. The security policy may return an error if the user does not have the permission to specify the working directory. `-g` *group*, `--group`=*group* : Use this *group* as the primary group instead of using the primary group specified in the password database for the target user. `-h`, `--help` : Show a help message. `-i`, `--login` : Run the shell specified by the target user's password database entry as a login shell. This means that login-specific resource files such as *.profile*, *.bash_profile* or *.login* will be read by the shell. If a *command* is specified, it is passed to the shell using the `-c` option. `-K`, `--remove-timestamp` : Removes every cached session record for the user, regardless of where the command is executed. The next time sudo-rs is run, authentication will take place if the policy requires it. No password is required to run this command. `-k`, `--reset-timestamp` : When used without a command, invalidates the user's session record for the current session. The next time sudo-rs is run, authentication will take place if the policy requires it. When used in conjunction with a *command* or an option that may require a password, this option will cause sudo-rs to ignore the user's session record. As a result, authentication will take place if the policy requires it. When used in conjunction with a *command* no invalidation of existing session records will take place. `-n`, `--non-interactive` : Avoid prompting the user for input of any kind. If any input is required for the *command* to run, sudo-rs will display an error message and exit. `p`, `--prompt`=*prompt* : Use a custom authentication prompt with optional escape sequences. The following percent (‘%’) escape sequences are supported: %H expanded to the local host name %h expanded to the local host name without the domain name %p expanded to the name of the user whose password is being requested (this respects the rootpw, targetpw flags) %U expanded to the login name of the user the command will be run as (defaults to root unless the -u option is also specified) %u expanded to the invoking user's login name %% two consecutive ‘%’ characters are collapsed into a single ‘%’ character The custom prompt will override the default prompt or the one specified by the SUDO_PROMPT enviroment variable. No *prompt* will suppress the the prompt provided by PAM, unless the requested *prompt* is empty (`""`) `-S`, `--stdin` : Read from standard input instead of using the terminal device. `-s`, `--shell` : Run the shell specified by the `SHELL` environment variable. If no shell was specified, the shell from the user's password database entry will be used instead. If a *command* is specified, it is passed to the shell using the `-c` option. `-u` *user*, `--user`=*user* : Run the *command* as another user than the default (**root**). `-V`, `--version` : Display the current version of sudo-rs. `-v`, `--validate` : Update the session record for the current session, authenticating the user if necessary. `--` : Indicates the end of the sudo-rs options and start of the *command*. Environment variables to be set for the command may be passed on the command line in the form of VAR=value. Variables passed on the command line are subject to restrictions imposed by the security policy. Variables passed on the command line are subject to the same restrictions as normal environment variables with one important exception: If the command to be run has the SETENV tag set or the command matched is ALL, the user may set variables that would otherwise be forbidden. See [sudoers(5)](sudoers.5.md) for more information. # SEE ALSO [su(1)](su.1.md), [sudoers(5)](sudoers.5.md), [visudo(8)](visudo.8.md) sudo-rs-0.2.5/docs/man/sudoers.5.md000064400000000000000000001031341046102023000151020ustar 00000000000000--- title: SUDOERS(5) sudo-rs 0.2.4 | sudo-rs --- # NAME `sudoers` - sudo-compatible security configuration # DESCRIPTION The `sudo-rs` policy determines a user's sudo privileges. The policy is driven by the */etc/sudoers file*. The policy format is described in detail in the **SUDOERS FILE FORMAT** section. The format used by sudo-rs is a subset of the one used by the sudo-project as maintained by Todd Miller, but syntax-compatible. ## User Authentication The sudoers security policy requires that most users authenticate themselves before they can use sudo. A password is not required if the invoking user is root, if the target user is the same as the invoking user, or if the policy has disabled authentication for the user or command. Unlike `su`, when `sudo-rs` requires authentication, it validates the invoking user's credentials, not the target user's (or root's) credentials. This can be changed via the *rootpw* flag, described later. `sudo-rs` uses per-user timestamp files for credential caching. Once a user has been authenticated, a record is written containing the user-ID that was used to authenticate, the terminal session ID, the start time of the session leader (or parent process) and a timestamp (using a monotonic clock if one is available). The user may then use sudo without a password for a short period of time (15 minutes unless overridden by the timestamp_timeout option). By default, `sudo-rs` uses a separate record for each terminal, which means that a user's login sessions are authenticated separately. The timestamp_type option can be used to select the type of timestamp record sudoers will use. ## Logging By default, `sudo-rs` logs both successful and unsuccessful attempts (as well as errors). Messages are logged to syslog(3). ## Command environment Since environment variables can influence program behavior, `sudo-rs` restricts which variables from the user's environment are inherited by the command to be run. In `sudo-rs`, the *env_reset* flag cannot be disabled. This causes commands to be executed with a new, minimal environment. The `HOME`, `MAIL`, `SHELL`, `LOGNAME` and `USER` environment variables are initialized based on the target user and the `SUDO_*` variables are set based on the invoking user. Additional variables, such as `DISPLAY`, `PATH` and `TERM`, are preserved from the invoking user's environment if permitted by the *env_check* or *env_keep* options. A few environment variables are treated specially. If the `PATH` and `TERM` variables are not preserved from the user's environment, they will be set to default values. The `LOGNAME` and `USER` are handled as a single entity. If one of them is preserved (or removed) from the user's environment, the other will be as well. If `LOGNAME` and `USER` are to be preserved but only one of them is present in the user's environment, the other will be set to the same value. This avoids an inconsistent environment where one of the variables describing the user name is set to the invoking user and one is set to the target user. Environment variables with a value beginning with `()` are removed, as they may be interpreted as functions by the bash shell. Environment variables specified by *env_check* or *env_keep* may include one or more ‘*’ characters which will match zero or more characters. No other wildcard characters are supported. Other sudoers options may influence the command environment, such as *secure_path*. Variables in the PAM environment may be merged in to the environment. If a variable in the PAM environment is already present in the user's environment, the value will only be overridden if the variable was not preserved by `sudo-rs`. Variables preserved from the invoking user's environment by the *env_keep* list take precedence over those in the PAM environment. Note that the dynamic linker on most operating systems will remove variables that can control dynamic linking from the environment of set-user-ID executables, including sudo. Depending on the operating system this may include `_RLD*`, `DYLD_*`, `LD_*`, `LDR_*`, `LIBPATH`, `SHLIB_PATH`, and others. These type of variables are removed from the environment before sudo even begins execution and, as such, it is not possible for sudo to preserve them. ## Resource limits sudo uses the operating system's native method of setting resource limits for the target user. On Linux systems, resource limits are usually set by the *pam_limits.so* PAM module. On some BSD systems, the */etc/login.conf* file specifies resource limits for the user. If there is no system mechanism to set per-user resource limits, the command will run with the same limits as the invoking user. # SUDOERS FILE FORMAT The sudoers file is composed of two types of entries: aliases (basically variables) and user specifications (which specify who may run what). When multiple entries match for a user, they are applied in order. Where there are multiple matches, the last match is used (which is not necessarily the most specific match). The sudoers file grammar will be described below in Extended Backus-Naur Form (EBNF) borrowed from Todd Miller's sudoers documentation. ## Quick guide to EBNF EBNF is a concise and exact way of describing the grammar of a language. Each EBNF definition is made up of production rules. E.g., symbol ::= definition | alternate1 | alternate2 ... Each production rule references others and thus makes up a grammar for the language. EBNF also contains the following operators, which many readers will recognize from regular expressions. Do not, however, confuse them with “wildcard†characters, which have different meanings. ? Means that the preceding symbol (or group of symbols) is optional. That is, it may appear once or not at all. * Means that the preceding symbol (or group of symbols) may appear zero or more times. + Means that the preceding symbol (or group of symbols) may appear one or more times. Parentheses may be used to group symbols together. For clarity, we will use single quotes ('') to designate what is a verbatim character string (as opposed to a symbol name). ## Aliases There are four kinds of aliases: User_Alias, Runas_Alias, Host_Alias and Cmnd_Alias. Alias ::= 'User_Alias' User_Alias_Spec (':' User_Alias_Spec)* | 'Runas_Alias' Runas_Alias_Spec (':' Runas_Alias_Spec)* | 'Host_Alias' Host_Alias_Spec (':' Host_Alias_Spec)* | 'Cmnd_Alias' Cmnd_Alias_Spec (':' Cmnd_Alias_Spec)* | 'Cmd_Alias' Cmnd_Alias_Spec (':' Cmnd_Alias_Spec)* User_Alias ::= NAME User_Alias_Spec ::= User_Alias '=' User_List Runas_Alias ::= NAME Runas_Alias_Spec ::= Runas_Alias '=' Runas_List Host_Alias ::= NAME Host_Alias_Spec ::= Host_Alias '=' Host_List Cmnd_Alias ::= NAME Cmnd_Alias_Spec ::= Cmnd_Alias '=' Cmnd_List NAME ::= [A-Z]([A-Z][0-9]_)* Each alias definition is of the form Alias_Type NAME = item1, item2, ... where *Alias_Type* is one of User_Alias, Runas_Alias, Host_Alias, or Cmnd_Alias. A NAME is a string of uppercase letters, numbers, and underscore characters (‘_’). A NAME must start with an uppercase letter. It is possible to put several alias definitions of the same type on a single line, joined by a colon (‘:’). E.g., Alias_Type NAME = item1, item2, item3 : NAME = item4, item5 The definitions of what constitutes a valid alias member follow. User_List ::= User | User ',' User_List User ::= '!'* user name | '!'* #user-ID | '!'* %group | '!'* %#group-ID | '!'* User_Alias A User_List is made up of one or more user names, user-IDs (prefixed with ‘#’), system group names and IDs (prefixed with ‘%’ and ‘%#’ respectively) and User_Aliases. Each list item may be prefixed with zero or more ‘!’ operators. An odd number of ‘!’ operators negate the value of the item; an even number just cancel each other out. Runas_List ::= Runas_Member | Runas_Member ',' Runas_List Runas_Member ::= '!'* user name | '!'* #user-ID | '!'* %group | '!'* %#group-ID | '!'* Runas_Alias A Runas_List is similar to a User_List except that instead of User_Aliases it can contain Runas_Aliases. Note that user names and groups are matched as strings. In other words, two users (groups) with the same user (group) ID are considered to be distinct. If you wish to match all user names with the same user-ID (e.g., root and toor), you can use a user-ID instead of a name (`#0` in the example given). Host_List ::= Host | Host ',' Host_List Host ::= '!'* host name | '!'* Host_Alias A Host_List is made up of one or more host names. Again, the value of an item may be negated with the ‘!’ operator. Cmnd_List ::= Cmnd | Cmnd ',' Cmnd_List command name ::= file name | file name args | file name '""' Cmnd ::= '!'* command name | '!'* directory | '!'* Cmnd_Alias A Cmnd_List is a list of one or more command names, directories, and other aliases. A command name is a fully qualified file name which may include shell-style wildcards (see the Wildcards section below). A simple file name allows the user to run the command with any arguments they wish. However, you may also specify command line arguments (which in sudo-rs may *not* include wildcards). Alternately, you can specify "" to indicate that the command may only be run without command line arguments. A directory is a fully qualified path name ending in a ‘/’. When you specify a directory in a Cmnd_List, the user will be able to run any file within that directory (but not in any sub-directories therein). If a Cmnd has associated command line arguments, then the arguments in the Cmnd must match exactly those given by the user on the command line. Note that the following characters must be escaped with a ‘\’ if they are used in command arguments: ‘,’, ‘:’, ‘=’, ‘\’. ## Defaults Certain configuration options may be changed from their default values at run-time via one or more Default_Entry lines. These may affect all users on any host, all users on a specific host, a specific user, a specific command, or commands being run as a specific user. Note that per-command entries may not include command line arguments. If you need to specify arguments, define a Cmnd_Alias and reference that instead. Default_Type ::= 'Defaults' | 'Defaults' '@' Host_List | 'Defaults' ':' User_List | 'Defaults' '!' Cmnd_List | 'Defaults' '>' Runas_List Default_Entry ::= Default_Type Parameter_List Parameter_List ::= Parameter | Parameter ',' Parameter_List Parameter ::= Parameter '=' Value | Parameter '+=' Value | Parameter '-=' Value | '!'* Parameter Parameters may be flags, integer values, strings, or lists. Flags are implicitly boolean and can be turned off via the ‘!’ operator. Some integer, string and list parameters may also be used in a boolean context to disable them. Values may be enclosed in double quotes ("") when they contain multiple words. Special characters may be escaped with a backslash (‘\’). To include a literal backslash character in a command line argument you must escape the backslash twice. For example, to match ‘\n’ as part of a command line argument, you must use ‘\\\\n’ in the sudoers file. This is due to there being two levels of escaping, one in the sudoers parser itself and another when command line arguments are matched by the fnmatch(3) function. Lists have two additional assignment operators, *+=* and *-=*. These operators are used to add to and delete from a list respectively. It is not an error to use the -= operator to remove an element that does not exist in a list. Defaults entries are parsed in the following order: generic, host, user, and runas Defaults are processed in the order they appear, with per-command defaults being processed in a second pass after that. See **SUDOERS OPTIONS** for a list of supported Defaults parameters. ## User specification User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \ (':' Host_List '=' Cmnd_Spec_List)* Cmnd_Spec_List ::= Cmnd_Spec | Cmnd_Spec ',' Cmnd_Spec_List Cmnd_Spec ::= Runas_Spec? Chdir_Spec? Tag_Spec* Cmnd Runas_Spec ::= '(' Runas_List? (':' Runas_List)? ')' Chdir_Spec ::= 'CWD=directory' Tag_Spec ::= ('PASSWD:' | 'NOPASSWD:' | 'SETENV:' | 'NOSETENV:') A user specification determines which commands a user may run (and as what user) on specified hosts. By default, commands are run as root, but this can be changed on a per-command basis. The basic structure of a user specification is “who where = (as_whom) whatâ€. Let's break that down into its constituent parts: ## Runas_Spec A Runas_Spec determines the user and/or the group that a command may be run as. A fully-specified Runas_Spec consists of two Runas_Lists (as defined above) separated by a colon (‘:’) and enclosed in a set of parentheses. The first Runas_List indicates which users the command may be run as via the -u option. The second defines a list of groups that may be specified via the -g option (in addition to any of the target user's groups). If both Runas_Lists are specified, the command may be run with any combination of users and groups listed in their respective Runas_Lists. If only the first is specified, the command may be run as any user in the list and, optionally, with any group the target user belongs to. If the first Runas_List is empty but the second is specified, the command may be run as the invoking user with the group set to any listed in the Runas_List. If both Runas_Lists are empty, the command may only be run as the invoking user and the group, if specified, must be one that the invoking user is a member of. If no Runas_Spec is specified, the command may only be run as root and the group, if specified, must be one that root is a member of. A Runas_Spec sets the default for the commands that follow it. What this means is that for the entry: dgb boulder = (operator) /bin/ls, /bin/kill, /usr/bin/lprm The user dgb may run /bin/ls, /bin/kill, and /usr/bin/lprm on the host boulder—but only as operator. E.g., $ sudo -u operator /bin/ls It is also possible to override a Runas_Spec later on in an entry. If we modify the entry like so: dgb boulder = (operator) /bin/ls, (root) /bin/kill, /usr/bin/lprm Then user dgb is now allowed to run /bin/ls as operator, but /bin/kill and /usr/bin/lprm as root. We can extend this to allow dgb to run /bin/ls with either the user or group set to operator: dgb boulder = (operator : operator) /bin/ls, (root) /bin/kill,\ /usr/bin/lprm Note that while the group portion of the Runas_Spec permits the user to run as command with that group, it does not force the user to do so. If no group is specified on the command line, the command will run with the group listed in the target user's password database entry. The following would all be permitted by the sudoers entry above: $ sudo -u operator /bin/ls $ sudo -u operator -g operator /bin/ls $ sudo -g operator /bin/ls In the following example, user tcm may run commands that access a modem device file with the dialer group. tcm boulder = (:dialer) /usr/bin/tip, /usr/bin/cu,\ /usr/local/bin/minicom Note that in this example only the group will be set, the command still runs as user tcm. E.g. $ sudo -g dialer /usr/bin/cu Multiple users and groups may be present in a Runas_Spec, in which case the user may select any combination of users and groups via the -u and -g options. In this example: alan ALL = (root, bin : operator, system) ALL user alan may run any command as either user root or bin, optionally setting the group to operator or system. ## Chdir_Spec The working directory that the command will be run in can be specified using the CWD setting. The directory must be a fully-qualified path name beginning with a ‘/’ or ‘~’ character, or the special value “*â€. A value of “*†indicates that the user may specify the working directory by running sudo with the -D option. By default, commands are run from the invoking user's current working directory, unless the -i option is given. Path names of the form ~user/path/name are interpreted as being relative to the named user's home directory. If the user name is omitted, the path will be relative to the runas user's home directory. ## Tag_Spec A command may have zero or more tags associated with it. The following tag values are supported: PASSWD, NOPASSWD, SETENV, and NOSETENV. Once a tag is set on a Cmnd, subsequent Cmnds in the Cmnd_Spec_List, inherit the tag unless it is overridden by the opposite tag (in other words, PASSWD overrides NOPASSWD and NOSETENV overrides SETENV). ### PASSWD and NOPASSWD By default, sudo requires that a user authenticate before running a command. This behavior can be modified via the NOPASSWD tag. Like a Runas_Spec, the NOPASSWD tag sets a default for the commands that follow it in the Cmnd_Spec_List. Conversely, the PASSWD tag can be used to reverse things. For example: queen rushmore = NOPASSWD: /bin/kill, /bin/ls, /usr/bin/lprm would allow the user queen to run /bin/kill, /bin/ls, and /usr/bin/lprm as root on the machine “rushmore†without authenticating himself. If we only want queen to be able to run /bin/kill without a password the entry would be: queen rushmore = NOPASSWD: /bin/kill, PASSWD: /bin/ls, /usr/bin/lprm Note, however, that the PASSWD tag has no effect on users who are in the group specified by the exempt_group setting. By default, if the NOPASSWD tag is applied to any of a user's entries for the current host, the user will be able to run “sudo -l†without a password. Additionally, a user may only run “sudo -v†without a password if all of the user's entries for the current host have the NOPASSWD tag. This behavior may be overridden via the verifypw and listpw options. ### SETENV and NOSETENV These tags override the value of the setenv flag on a per-command basis. Note that if SETENV has been set for a command, the user may disable the env_reset flag from the command line via the -E option. Additionally, environment variables set on the command line are not subject to the restrictions imposed by env_check, env_delete, or env_keep. As such, only trusted users should be allowed to set variables in this manner. If the command matched is ALL, the SETENV tag is implied for that command; this default may be overridden by use of the NOSETENV tag. ## Wildcards sudo allows shell-style wildcards (aka meta or glob characters) to be used in host names, path names, and command line arguments in the sudoers file. Wildcard matching is done via the glob(3) and fnmatch(3) functions as specified by IEEE Std 1003.1 (“POSIX.1â€). * Matches any set of zero or more characters (including white space). ? Matches any single character (including white space). [...] Matches any character in the specified range. [!...] Matches any character not in the specified range. \x For any character ‘x’, evaluates to ‘x’. This is used to escape special characters such as: ‘*’, ‘?’, ‘[’, and ‘]’. Note that these are not regular expressions. Unlike a regular expression there is no way to match one or more characters within a range. Wildcards in command line arguments are not supported---using these in original versions of sudo was usually a sign of mis-configuration and consequently sudo-rs simply forbids using them. ## Including other files from within sudoers It is possible to include other sudoers files from within the sudoers file currently being parsed using the *@include* and *@includedir* directives. For compatibility with Todd Miller's sudo versions prior to 1.9.1, *#include* and *#includedir* are also accepted. An include file can be used, for example, to keep a site-wide sudoers file in addition to a local, per-machine file. For the sake of this example the site-wide sudoers file will be /etc/sudoers and the per-machine one will be /etc/sudoers.local. To include /etc/sudoers.local from within /etc/sudoers one would use the following line in /etc/sudoers: @include /etc/sudoers.local When sudo reaches this line it will suspend processing of the current file (/etc/sudoers) and switch to /etc/sudoers.local. Upon reaching the end of /etc/sudoers.local, the rest of /etc/sudoers will be processed. Files that are included may themselves include other files. A hard limit of 128 nested include files is enforced to prevent include file loops. The path to the include file may contain white space if it is escaped with a backslash (‘\’). Alternately, the entire path may be enclosed in double quotes (""), in which case no escaping is necessary. To include a literal backslash in the path, ‘\\’ should be used. If the path to the include file is not fully-qualified (does not begin with a ‘/’), it must be located in the same directory as the sudoers file it was included from. For example, if /etc/sudoers contains the line: @include sudoers.local The @includedir directive can be used to create a sudoers.d directory that the system package manager can drop sudoers file rules into as part of package installation. For example, given: @includedir /etc/sudoers.d sudo will suspend processing of the current file and read each file in /etc/sudoers.d, skipping file names that end in ‘~’ or contain a ‘.’ character to avoid causing problems with package manager or editor temporary/backup files. Files are parsed in sorted lexical order. That is, /etc/sudoers.d/01_first will be parsed before /etc/sudoers.d/10_second. Be aware that because the sorting is lexical, not numeric, /etc/sudoers.d/1_whoops would be loaded after /etc/sudoers.d/10_second. Using a consistent number of leading zeroes in the file names can be used to avoid such problems. After parsing the files in the directory, control returns to the file that contained the @includedir directive. Note that unlike files included via @include, visudo will not edit the files in a @includedir directory unless one of them contains a syntax error. It is still possible to run visudo with the -f flag to edit the files directly, but this will not catch the redefinition of an alias that is also present in a different file. ## Other special characters and reserved words The pound sign (‘#’) is used to indicate a comment (unless it is part of a #include directive or unless it occurs in the context of a user name and is followed by one or more digits, in which case it is treated as a user-ID). Both the comment character and any text after it, up to the end of the line, are ignored. The reserved word *ALL* is a built-in alias that always causes a match to succeed. It can be used wherever one might otherwise use a Cmnd_Alias, User_Alias, Runas_Alias, or Host_Alias. Attempting to define an alias named ALL will result in a syntax error. Please note that using ALL can be dangerous since in a command context, it allows the user to run any command on the system. An exclamation point (‘!’) can be used as a logical not operator in a list or alias as well as in front of a Cmnd. This allows one to exclude certain values. For the ‘!’ operator to be effective, there must be something for it to exclude. For example, to match all users except for root one would use: ALL,!root If the ALL, is omitted, as in: !root it would explicitly deny root but not match any other users. This is different from a true “negation†operator. Note, however, that using a ‘!’ in conjunction with the built-in ALL alias to allow a user to run “all but a few†commands rarely works as intended (see SECURITY NOTES below). White space between elements in a list as well as special syntactic characters in a User Specification (‘=’, ‘:’, ‘(’, ‘)’) is optional. The following characters must be escaped with a backslash (‘\’) when used as part of a word (e.g., a user name or host name): ‘!’, ‘=’, ‘:’, ‘,’, ‘(’, ‘)’, ‘\’. ## SUDOERS OPTIONS sudo's behavior can be modified by Default_Entry lines, as explained earlier. A list of all supported Defaults parameters, grouped by type, are listed below. ### Boolean Flags: * env_editor If set, visudo will use the value of the SUDO_EDITOR, VISUAL or EDITOR environment variables before falling back on the default editor list. Note that visudo is typically run as root so this flag may allow a user with visudo privileges to run arbitrary commands as root without logging. An alternative is to place a colon-separated list of “safe†editors int the editor setting. visudo will then only use SUDO_EDITOR, VISUAL or EDITOR if they match a value specified in editor. If the env_reset flag is enabled, the SUDO_EDITOR, VISUAL and/or EDITOR environment variables must be present in the env_keep list for the env_editor flag to function when visudo is invoked via sudo. This flag is on by default. * pwfeedback By default, sudo reads the password like most other Unix programs, by turning off echo until the user hits the return (or enter) key. Some users become confused by this as it appears to them that sudo has hung at this point. When pwfeedback is set, sudo will provide visual feedback when the user presses a key. Note that this does have a security impact as an onlooker may be able to determine the length of the password being entered. This flag is off by default. * rootpw If set, sudo will prompt for the root password instead of the password of the invoking user when running a command or editing a file. This flag is off by default. * use_pty If set, and sudo is running in a terminal, the command will be run in a pseudo-terminal (even if no I/O logging is being done). If the sudo process is not attached to a terminal, use_pty has no effect. A malicious program run under sudo may be capable of injecting commands into the user's terminal or running a background process that retains access to the user's terminal device even after the main program has finished executing. By running the command in a separate pseudo-terminal, this attack is no longer possible. This flag is on by default. ## Integers: * passwd_tries The number of tries a user gets to enter his/her password before sudo logs the failure and exits. The default is 3. ## Integers that can be used in a boolean context: * timestamp_timeout Number of minutes that can elapse before sudo will ask for a passwd again. The timeout may include a fractional component if minute granularity is insufficient, for example 2.5. The default is 15. Set this to 0 to always prompt for a password. ## Strings that can be used in a boolean context: * secure_path If set, sudo will use this value in place of the user's PATH environment variable. This option can be used to reset the PATH to a known good value that contains directories for system administrator commands such as /usr/sbin. This option is not set by default. ## Lists that can be used in a boolean context: * env_check Environment variables to be removed from the user's environment unless they are considered “safeâ€. For all variables except TZ, “safe†means that the variable's value does not contain any ‘%’ or ‘/’ char†acters. This can be used to guard against printf-style format vulnerabilities in poorly-written programs. The TZ variable is considered unsafe if any of the following are true: • It consists of a fully-qualified path name, optionally prefixed with a colon (‘:’), that does not match the location of the zoneinfo directory. • It contains a .. path element. • It contains white space or non-printable characters. • It is longer than the value of PATH_MAX. The argument may be a double-quoted, space-separated list or a single value without double-quotes. The list can be replaced, added to, deleted from, or disabled by using the =, +=, -=, and ! operators respectively. Regardless of whether the env_reset option is enabled or disabled, variables specified by env_check will be preserved in the environment if they pass the aforementioned check. The global list of environment variables to check is displayed when sudo is run by root with the -V option. * env_keep Environment variables to be preserved in the user's environment when the env_reset option is in effect. This allows fine-grained control over the environment sudo-spawned processes will receive. The argument may be a double-quoted, space-separated list or a single value without double-quotes. The list can be replaced, added to, deleted from, or disabled by using the =, +=, -=, and ! operators respectively. The global list of variables to keep is displayed when sudo is run by root with the -V option. Preserving the HOME environment variable has security implications since many programs use it when searching for configuration or data files. Adding HOME to env_keep may enable a user to run unrestricted commands via sudo and is strongly discouraged. ## LOG FORMAT sudo-rs logs events via syslog(3). ## FILES /etc/sudoers-rs List of who can run what (for co-existence of sudo-rs and Todd Miller's sudo) /etc/sudoers List of who can run what (sudo-compatible) /run/sudo/ts Directory containing timestamps for the sudoers security policy ## SECURITY NOTES ### Limitations of the ‘!’ operator It is generally not effective to “subtract†commands from ALL using the ‘!’ operator. A user can trivially circumvent this by copying the desired command to a different name and then executing that. For example: bill ALL = ALL, !SU, !SHELLS Doesn't really prevent bill from running the commands listed in SU or SHELLS since he can simply copy those commands to a different name, or use a shell escape from an editor or other program. Therefore, these kind of restrictions should be considered advisory at best (and reinforced by policy). In general, if a user has sudo ALL there is nothing to prevent them from creating their own program that gives them a root shell (or making their own copy of a shell) regardless of any ‘!’ elements in the user specification. ### Security implications of `fast_glob` sudo-rs uses `fast_glob, which further means it is not possible to reliably negate commands where the path name includes globbing (aka wildcard) characters. This is because the Rust library's fnmatch function cannot resolve relative paths. While this is typically only an inconvenience for rules that grant privileges, it can result in a security issue for rules that subtract or revoke privileges. For example, given the following sudoers file entry: john ALL = /usr/bin/passwd [a-zA-Z0-9]*, /usr/bin/chsh [a-zA-Z0-9]*,\ /usr/bin/chfn [a-zA-Z0-9]*, !/usr/bin/* root User john can still run /usr/bin/passwd root if fast_glob is enabled by changing to /usr/bin and running ./passwd root instead. ### Preventing shell escapes Once sudo executes a program, that program is free to do whatever it pleases, including run other programs. This can be a security issue since it is not uncommon for a program to allow shell escapes, which lets a user bypass sudo's access control and logging. Common programs that permit shell escapes include shells (obviously), editors, paginators (such as *less*), mail, and terminal programs. sudo-rs currently doesn't offer Todd Miller's sudo's protection mechanisms; i.e. be very careful that when a user is not supposed to receive shell access, that the commands that they have access to does not allow escaping to the shell. ### Timestamp file checks sudo-rs will check the ownership of its timestamp directory (/run/sudo/ts by default) and ignore the directory's contents if it is not owned by root or if it is writable by a user other than root. While the timestamp directory should be cleared at reboot time, to avoid potential problems, sudo-rs will ignore timestamp files that date from before the machine booted on systems where the boot time is available. Some systems with graphical desktop environments allow unprivileged users to change the system clock. Since sudo-rs relies on the system clock for timestamp validation, it may be possible on such systems for a user to run sudo for longer than *timestamp_timeout* by setting the clock back. To combat this, `sudo-rs` uses a monotonic clock (which never moves backwards) for its timestamps if the system supports it. sudo-rs will not honor timestamps set far in the future. ## SEE ALSO su(1), fnmatch(3), glob(3), sudo(8), visudo(8) ## CAVEATS The sudoers file should always be edited by the visudo utility which locks the file and checks for syntax errors. If sudoers contains syntax errors, you may lock yourself out of being able to use sudo. ## BUGS If you feel you have found a bug in sudo-rs, please submit a bug report at https://github.com/trifectatechfoundation/sudo-rs/issues/ # AUTHORS This man page is a modified version of the sudoers(5) documentation written by Todd Miller; see https://www.sudo.ws/ for the original. ## DISCLAIMER sudo-rs 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. sudo-rs-0.2.5/docs/man/visudo.8.md000064400000000000000000000014061046102023000147310ustar 00000000000000--- title: VISUDO(8) sudo-rs 0.2.5 | sudo-rs --- # NAME `visudo` - safely edit the sudoers file # SYNOPSIS `visudo` [`-chqsV`] [[`-f`] *sudoers*] # DESCRIPTION `visudo` edits the *sudoers* file in a safe manner, similar to vipw(8). # OPTIONS `-c`, `--check` : Only check if there are errors in the existing sudoers file. `-f` *sudoers*, `--file`=*sudoers* : Instead of editing the default `/etc/sudoers`, edit the file specified as *sudoers* instead. `-h`, `--help` : Show a help message. `-I`, `--no-includes` : Do not edit included files. `-q`, `--quiet` : Less verbose syntax error messages. `-s`, `--strict` : Strict syntax checking. `-V`, `--version` : Display version information and exit. # SEE ALSO [sudo(8)](sudo.8.md), sudoers(5) sudo-rs-0.2.5/docs/sudo-cve.md000064400000000000000000000173061046102023000142320ustar 00000000000000# Past sudo CVEs This listing contains security issues originally found in sudo but which could also be relevant for sudo-rs. ## Possibly relevant CVEs / advisories These CVEs/advisories are possibly relevant to sudo-rs: | CVE | Tests | Sudo Advisory / Attack notes | | ---------------------- | ----- | --------------------------------------------------------------------------- | | CVE-1999-0958 [^1] | | Relative path attack (.. attack) | | CVE-1999-1496 [^2] | | Information leakage on which commands exist | | - [^3] | | https://www.sudo.ws/security/advisories/heap_corruption/ | | CVE-2004-1051 [^4] | | https://www.sudo.ws/security/advisories/bash_functions/ | | CVE-2005-1119 [^5] | | Corrupt arbitrary files via a symlink attack | | CVE-2005-1993 [^6] | | https://www.sudo.ws/security/advisories/path_race/ | | CVE-2005-4890 [^7] | | TTY hijacking when a privileged user uses sudo to run unprivileged commands | | - [^9] | | https://www.sudo.ws/security/advisories/cmnd_alias_negation/ | | CVE-2010-1646 [^10] | | https://www.sudo.ws/security/advisories/secure_path/ | | CVE-2010-2956 [^11] | | https://www.sudo.ws/security/advisories/runas_group/ | | CVE-2011-0010 [^12] | | https://www.sudo.ws/security/advisories/runas_group_pw/ | | CVE-2012-0809 [^13] | | https://www.sudo.ws/security/advisories/sudo_debug/ | | CVE-2013-1775 [^14] | | https://www.sudo.ws/security/advisories/epoch_ticket/ | | CVE-2013-1776 [^15] | | https://www.sudo.ws/security/advisories/tty_tickets/ | | CVE-2013-2776 [^15] | | https://www.sudo.ws/security/advisories/tty_tickets/ | | CVE-2013-2777 [^15] | | https://www.sudo.ws/security/advisories/tty_tickets/ | | CVE-2014-9680 [^16] | | https://www.sudo.ws/security/advisories/tz/ | | CVE-2017-1000367 [^17] | | https://www.sudo.ws/security/advisories/linux_tty/ | | CVE-2017-1000368 [^17] | | https://www.sudo.ws/security/advisories/linux_tty/ | | CVE-2023-28486 [^19] | | Syslog messages do not escape control characters | [^1]: All our path checks should only ever be done with absolute paths [^2]: We try to take care to only expose relevant information to the user [^3]: Our usage of Rust should mostly prevent heap corruption bugs from occurring [^4]: env_reset is always enabled in sudo-rs, additionally we apply filtering to several variables to prevent any additional attack paths [^5]: - [^6]: Sudo-rs uses the suggested realpath function, as it is considered available enough for our target systems [^7]: To prevent attacks, a PTY must be used when running commands within a TTY, which is enabled by default in sudo-rs [^9]: - [^10]: - [^11]: - [^12]: - [^13]: - [^14]: - [^15]: - [^16]: - [^17]: - [^19]: - ## Non-applicable CVEs These CVEs are almost entirely not applicable in the current sudo-rs codebase, mainly because the feature they relate to is not implemented. Sometimes this is done purposefully, because the feature has security implications. Sometimes the feature will be implemented at a later time, these CVEs might become relevant at that time. | CVE | Reason | | -------------- | ----------------------------------------------------------------------------------------------------------- | | CVE-2002-0043 | mail functionality is not implemented, https://www.sudo.ws/security/advisories/postfix/ | | CVE-2002-0184 | setting a custom prompt via `-p` is not implemented, https://www.sudo.ws/security/advisories/prompt/ | | CVE-2004-1689 | `sudoedit`/`sudo -e` is not implemented, https://www.sudo.ws/security/advisories/sudoedit/ | | CVE-2005-2959 | env_reset is always enabled / blacklist is not supported, https://www.sudo.ws/security/advisories/bash_env/ | | CVE-2005-4158 | env_reset is always enabled / blacklist is not supported, https://www.sudo.ws/security/advisories/perl_env/ | | CVE-2006-0151 | env_reset is always enabled / blacklist is not supported | | CVE-2007-3149 | Kerberos functionality is not implemented, https://www.sudo.ws/security/advisories/kerberos5/ | | CVE-2009-0034 | The group matching logic does not have this bug, https://www.sudo.ws/security/advisories/group_vector/ | | CVE-2010-0426 | `sudoedit`/`sudo -e` is not implemented, https://www.sudo.ws/security/advisories/sudoedit_escalate/ | | CVE-2010-0427 | runas_default is not implemented | | CVE-2010-1163 | `sudoedit`/`sudo -e` is not implemented, https://www.sudo.ws/security/advisories/sudoedit_escalate2/ | | CVE-2012-2337 | No host-based rule matching is currently implemented, https://www.sudo.ws/security/advisories/netmask/ | | CVE-2012-3440 | Related to Red Hat specific script and not sudo directly | | CVE-2014-0106 | Disabling env_reset is not supported, https://www.sudo.ws/security/advisories/env_add/ | | CVE-2015-5602 | `sudoedit`/`sudo -e` is not implemented | | CVE-2015-8239 | The sha2 digest feature is not implemented | | CVE-2016-7032 | The noexec functionality is not implemented, https://www.sudo.ws/security/advisories/noexec_bypass/ | | CVE-2016-7076 | The noexec functionality is not implemented, https://www.sudo.ws/security/advisories/noexec_wordexp/ | | CVE-2019-14287 | This bug is not present, https://www.sudo.ws/security/advisories/minus_1_uid/ | | CVE-2019-18634 | The pwfeedback functionality is not implemented, https://www.sudo.ws/security/advisories/pwfeedback/ | | CVE-2021-3156 | `sudoedit`/`sudo -e` is not implemented, https://www.sudo.ws/security/advisories/unescape_overflow/ | | CVE-2021-23239 | `sudoedit`/`sudo -e` is not implemented | | CVE-2021-23240 | `sudoedit`/`sudo -e` is not implemented, https://www.sudo.ws/security/advisories/sudoedit_selinux/ | | CVE-2022-43995 | crypt/password backend is not implemented, only PAM | | CVE-2023-22809 | `sudoedit`/`sudo -e` is not implemented, https://www.sudo.ws/security/advisories/sudoedit_any/ | | CVE-2023-27320 | The chroot functionality is not implemented, https://www.sudo.ws/security/advisories/double_free/ | | CVE-2023-28487 | Sudoreplay is not implemented | ## Disputed CVEs While these CVEs are related to sudo, they are disputed as security issues. Either the behavior described in the CVE is intended behavior, or the issue cannot be replicated. | CVE | Notes | | -------------- | ----- | | CVE-2005-1831 | | | CVE-2019-18684 | | | CVE-2019-19234 | | | CVE-2019-19232 | | sudo-rs-0.2.5/get-pam-variant.bash000075500000000000000000000003301046102023000150560ustar 00000000000000#!/usr/bin/env bash # FIXME read headers to find the actually used variant case $(uname) in Linux) echo linuxpam ;; FreeBSD) echo openpam ;; *) echo "Unsupported platform" exit 1 ;; esac sudo-rs-0.2.5/make-lcov-info.bash000075500000000000000000000013011046102023000146700ustar 00000000000000#!/bin/bash set -euo pipefail rustup component add llvm-tools llvm_profdata=$(find "$(rustc --print sysroot)" -name llvm-profdata) profdata="$SUDO_TEST_PROFRAW_DIR"/sudo-rs.profdata $llvm_profdata merge \ -sparse \ "$SUDO_TEST_PROFRAW_DIR"/**/*.profraw \ -o "$profdata" binary="$SUDO_TEST_PROFRAW_DIR"/sudo-rs dockerid=$(docker create sudo-test-rs) docker cp "$dockerid":/usr/bin/sudo "$binary" docker rm "$dockerid" llvm_cov="$(dirname "$llvm_profdata")"/llvm-cov $llvm_cov export \ -format=lcov \ --ignore-filename-regex='/usr/local/cargo/registry' \ --ignore-filename-regex='/rustc' \ --instr-profile="$profdata" \ --object "$binary" \ -path-equivalence=/usr/src/sudo,"$(pwd)" >lcov.info sudo-rs-0.2.5/proofs/sudoers.mlw000064400000000000000000000050201046102023000147360ustar 00000000000000(* Why3 specification file for selected code in the sudoers crate. All proof goals generated by Why3 1.5.1 can be discharged using CVC4 1.8 *) module Sudoers use array.Array use option.Option use ref.Ref use int.Int (* loose models for the types in sudoers::Ast *) type metavar 'a = All | Only 'a type qualified 'a = Yes 'a | No 'a type spec 'tag 'a = { inner: qualified (metavar 'a); info: 'tag } predicate contains (pred: 'a -> bool) (a: array 'a) = exists i. 0 <= i < length a /\ pred a[i] function who (item: spec 'tag 'a): metavar 'a = match item.inner with | Yes x -> x | No x -> x end function condition (item: spec 'tag 'a): option 'tag = match item with | { inner = Yes _; info = tag } -> Some tag | _ -> None end function matched_by (pred: 'a -> bool) (item: spec 'tag 'a): bool = match who item with | All -> true | Only x -> pred x end let function bool_then (b: bool) (x: 'a): option 'a = if b then Some x else None predicate final_match (pred: 'a -> bool) (a: array 'a) (f: 'a -> 'b) (x: 'b) = exists i. 0 <= i < length a /\ pred a[i] /\ f a[i] = x /\ forall k. i < k < length a -> not pred a[k] (* Why3 model of the sudoers::find_item function *) let find_item (items: array (spec 'tag 'a)) (pred: 'a -> bool): option 'tag returns { | Some tag -> final_match (matched_by pred) items condition (Some tag) | None -> not contains (matched_by pred) items \/ final_match (matched_by pred) items condition None } = let result = ref None in for i = 0 to items.length - 1 do invariant { forall tag. !result = Some tag <-> exists j. 0 <= j < i /\ matched_by pred items[j] /\ Some tag = condition items[j] /\ forall k. j < k < i -> not matched_by pred items[k] } let (judgement, who) = match items[i].inner with | No x -> (false, x) | Yes x -> (true, x) end in let info = items[i].info in match who with | All -> result := judgement.bool_then(info) | Only id -> if pred id then result := judgement.bool_then(info); end; done; (* perform a "virtual loop" to strength the case !result = None; this could also be solved by adding a ghost variable above *) ghost if is_none !result && contains (matched_by pred) items then for i = items.length - 1 downto 0 do invariant { forall k. i < k < items.length -> not matched_by pred items[k] } invariant { exists k. 0 <= k <= i /\ matched_by pred items[k] } if matched_by pred items[i] then break done; !result; end sudo-rs-0.2.5/src/common/bin_serde.rs000064400000000000000000000075241046102023000156130ustar 00000000000000//! Binary serialization, and an implementation over Unix pipes. use sealed::DeSerializeBytes; use std::{ io::{self, Read, Write}, marker::PhantomData, os::{ fd::{AsFd, BorrowedFd}, unix::net::UnixStream, }, }; mod sealed { pub trait DeSerializeBytes { fn zero_init() -> Self; fn as_mut_ref(&mut self) -> &mut [u8]; } impl DeSerializeBytes for [u8; N] { fn zero_init() -> [u8; N] { [0; N] } fn as_mut_ref(&mut self) -> &mut [u8] { self.as_mut_slice() } } } /// Serialization/deserialization trait using a byte array as storage. pub trait DeSerialize { /// Usually `[u8; std::mem::size_of::()]`. type Bytes: sealed::DeSerializeBytes; fn serialize(&self) -> Self::Bytes; fn deserialize(bytes: Self::Bytes) -> Self; } /// A binary pipe that can send and recieve typed messages. /// /// By default, if only one generic is included, /// the types of the [BinPipe::write()] and [BinPipe::read()] messages /// are the same. pub struct BinPipe { sock: UnixStream, _read_marker: PhantomData, _write_marker: PhantomData, } impl BinPipe { /// A pipe abstracting over a [UnixStream] with easier /// binary serialization, to help with the buffer sizes and ser/de steps. /// Uses [UnixStream::pair()]. pub fn pair() -> io::Result<(BinPipe, BinPipe)> { let (first, second) = UnixStream::pair()?; Ok(( BinPipe { sock: first, _read_marker: PhantomData::, _write_marker: PhantomData::, }, // R and W are inverted here since the type of what's written in one // pipe is read in the other, and vice versa. BinPipe { sock: second, _read_marker: PhantomData::, _write_marker: PhantomData::, }, )) } /// Read a `R` from the pipe. pub fn read(&mut self) -> io::Result { let mut bytes = R::Bytes::zero_init(); self.sock.read_exact(bytes.as_mut_ref())?; Ok(R::deserialize(bytes)) } /// Write a `W` to the pipe. pub fn write(&mut self, bytes: &W) -> io::Result<()> { self.sock.write_all(bytes.serialize().as_mut_ref())?; Ok(()) } /// Calls [std::net::TcpStream::set_nonblocking] on the underlying socket. #[cfg(debug_assertions)] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.sock.set_nonblocking(nonblocking) } } impl AsFd for BinPipe { fn as_fd(&self) -> BorrowedFd { self.sock.as_fd() } } impl DeSerialize for i32 { type Bytes = [u8; std::mem::size_of::()]; fn serialize(&self) -> Self::Bytes { self.to_ne_bytes() } fn deserialize(bytes: Self::Bytes) -> Self { Self::from_ne_bytes(bytes) } } #[cfg(test)] mod tests { use super::*; #[test] pub fn single_type() { let (mut tx, mut rx) = BinPipe::pair().unwrap(); tx.write(&42i32).unwrap(); assert_eq!(rx.read().unwrap(), 42); rx.write(&23i32).unwrap(); assert_eq!(tx.read().unwrap(), 23); } impl DeSerialize for u8 { type Bytes = [u8; std::mem::size_of::()]; fn serialize(&self) -> [u8; 1] { self.to_ne_bytes() } fn deserialize(bytes: [u8; 1]) -> Self { Self::from_ne_bytes(bytes) } } #[test] pub fn different_types() { let (mut tx, mut rx) = BinPipe::pair().unwrap(); tx.write(&42i32).unwrap(); assert_eq!(rx.read().unwrap(), 42); rx.write(&23u8).unwrap(); assert_eq!(tx.read().unwrap(), 23); } } sudo-rs-0.2.5/src/common/command.rs000064400000000000000000000135171046102023000152760ustar 00000000000000use std::{ fmt::Display, path::{Path, PathBuf}, }; use crate::system::escape_os_str_lossy; use super::resolve::{canonicalize, resolve_path}; #[derive(Debug, Default)] #[cfg_attr(test, derive(PartialEq))] pub struct CommandAndArguments { pub(crate) command: PathBuf, pub(crate) arguments: Vec, pub(crate) resolved: bool, pub(crate) arg0: Option, } impl Display for CommandAndArguments { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let cmd = escape_os_str_lossy(self.command.as_os_str()); let args = self .arguments .iter() .map(|a| a.escape_default().collect::()) .collect::>() .join(" "); write!(f, "{} {}", cmd, args) } } // when -i and -s are used, the arguments given to sudo are escaped "except for alphanumerics, underscores, hyphens, and dollar signs." fn escaped(arguments: Vec) -> String { arguments .into_iter() .map(|arg| { arg.chars() .map(|c| match c { '_' | '-' | '$' => c.to_string(), c if c.is_alphanumeric() => c.to_string(), _ => ['\\', c].iter().collect(), }) .collect() }) .collect::>() .join(" ") } //checks whether the Path is actually describing a qualified path (i.e. contains "/") //or just specifying the name of a file (in which case we are going to resolve it via PATH) fn is_qualified(path: impl AsRef) -> bool { path.as_ref().parent() != Some(Path::new("")) } impl CommandAndArguments { pub fn build_from_args(shell: Option, mut arguments: Vec, path: &str) -> Self { let mut resolved = true; let mut command; let mut arg0 = None; if let Some(chosen_shell) = shell { command = chosen_shell; if !arguments.is_empty() { arguments = vec!["-c".to_string(), escaped(arguments)] } } else { command = arguments.first().map(|s| s.into()).unwrap_or_default(); arguments.remove(0); // remember the original binary name before resolving symlinks; this is not // to be used except for setting the `arg0` arg0 = Some(command.clone()); // resolve the command, remembering errors (but not propagating them) if !is_qualified(&command) { match resolve_path(&command, path) { Some(qualified_path) => command = qualified_path, None => resolved = false, } } } // resolve symlinks, even if the command was obtained through a PATH or SHELL // once again, failure to canonicalize should not stop the pipeline match canonicalize(&command) { Ok(canon_path) => command = canon_path, Err(_) => resolved = false, } CommandAndArguments { command, arguments, resolved, arg0, } } } #[cfg(test)] mod test { use super::{escaped, CommandAndArguments}; #[test] fn test_escaped() { let test = |src: &[&str], target: &str| { assert_eq!( &escaped(src.iter().map(|s| s.to_string()).collect()), target ); }; test(&["a", "b", "c"], "a b c"); test(&["a", "b c"], "a b\\ c"); test(&["a", "b-c"], "a b-c"); test(&["a", "b#c"], "a b\\#c"); test(&["1 2 3"], "1\\ 2\\ 3"); test(&["! @ $"], "\\!\\ \\@\\ $"); } #[test] fn test_build_command_and_args() { assert_eq!( CommandAndArguments::build_from_args( None, vec!["/usr/bin/fmt".into(), "hello".into()], "/bin" ), CommandAndArguments { command: "/usr/bin/fmt".into(), arguments: vec!["hello".into()], resolved: true, arg0: Some("/usr/bin/fmt".into()), } ); assert_eq!( CommandAndArguments::build_from_args( None, vec!["fmt".into(), "hello".into()], "/tmp:/usr/bin:/bin" ), CommandAndArguments { command: "/usr/bin/fmt".into(), arguments: vec!["hello".into()], resolved: true, arg0: Some("fmt".into()), } ); assert_eq!( CommandAndArguments::build_from_args( None, vec!["thisdoesnotexist".into(), "hello".into()], "" ), CommandAndArguments { command: "thisdoesnotexist".into(), arguments: vec!["hello".into()], resolved: false, arg0: Some("thisdoesnotexist".into()), } ); assert_eq!( CommandAndArguments::build_from_args( Some("shell".into()), vec!["ls".into(), "hello".into()], "/bin" ), CommandAndArguments { command: "shell".into(), arguments: vec!["-c".into(), "ls hello".into()], resolved: false, arg0: None, } ); } #[test] fn qualified_paths() { use super::is_qualified; assert!(is_qualified("foo/bar")); assert!(is_qualified("a/b/bar")); assert!(is_qualified("a/b//bar")); assert!(is_qualified("/bar")); assert!(is_qualified("/bar/")); assert!(is_qualified("/bar/foo/")); assert!(is_qualified("/")); assert!(is_qualified("")); // don't try to resolve "" assert!(!is_qualified("bar")); } } sudo-rs-0.2.5/src/common/context.rs000064400000000000000000000153061046102023000153420ustar 00000000000000use std::io; use crate::common::{HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2}; use crate::exec::RunOptions; use crate::sudo::{SudoListOptions, SudoRunOptions, SudoValidateOptions}; use crate::sudoers::Sudoers; use crate::system::{Group, Hostname, Process, User}; use super::{ command::CommandAndArguments, resolve::{resolve_shell, resolve_target_user_and_group, CurrentUser}, Error, SudoPath, }; #[derive(Debug)] pub struct Context { // cli options pub launch: LaunchType, pub chdir: Option, pub command: CommandAndArguments, pub target_user: User, pub target_group: Group, pub stdin: bool, pub bell: bool, pub prompt: Option, pub non_interactive: bool, pub use_session_records: bool, // system pub hostname: Hostname, pub current_user: CurrentUser, pub process: Process, // policy pub use_pty: bool, pub password_feedback: bool, } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[repr(u32)] pub enum LaunchType { #[default] Direct = HARDENED_ENUM_VALUE_0, Shell = HARDENED_ENUM_VALUE_1, Login = HARDENED_ENUM_VALUE_2, } impl Context { pub fn from_run_opts( sudo_options: SudoRunOptions, policy: &mut Sudoers, ) -> Result { let hostname = Hostname::resolve(); let current_user = CurrentUser::resolve()?; let (target_user, target_group) = resolve_target_user_and_group(&sudo_options.user, &sudo_options.group, ¤t_user)?; let launch = if sudo_options.login { LaunchType::Login } else if sudo_options.shell { LaunchType::Shell } else { LaunchType::Direct }; let shell = resolve_shell(launch, ¤t_user, &target_user); let override_path = policy.search_path(&hostname, ¤t_user, &target_user); let command = { let system_path; let path = if let Some(path) = override_path { path } else { system_path = std::env::var("PATH").unwrap_or_default(); system_path.as_ref() }; CommandAndArguments::build_from_args(shell, sudo_options.positional_args, path) }; Ok(Context { hostname, command, current_user, target_user, target_group, use_session_records: !sudo_options.reset_timestamp, launch, chdir: sudo_options.chdir, stdin: sudo_options.stdin, bell: sudo_options.bell, prompt: sudo_options.prompt, non_interactive: sudo_options.non_interactive, process: Process::new(), use_pty: true, password_feedback: false, }) } pub fn from_validate_opts(sudo_options: SudoValidateOptions) -> Result { let hostname = Hostname::resolve(); let current_user = CurrentUser::resolve()?; let (target_user, target_group) = resolve_target_user_and_group(&sudo_options.user, &sudo_options.group, ¤t_user)?; Ok(Context { hostname, command: Default::default(), current_user, target_user, target_group, use_session_records: !sudo_options.reset_timestamp, launch: Default::default(), chdir: None, stdin: sudo_options.stdin, bell: sudo_options.bell, prompt: sudo_options.prompt, non_interactive: sudo_options.non_interactive, process: Process::new(), use_pty: true, password_feedback: false, }) } pub fn from_list_opts( sudo_options: SudoListOptions, policy: &mut Sudoers, ) -> Result { let hostname = Hostname::resolve(); let current_user = CurrentUser::resolve()?; let (target_user, target_group) = resolve_target_user_and_group(&sudo_options.user, &sudo_options.group, ¤t_user)?; let override_path = policy.search_path(&hostname, ¤t_user, &target_user); let command = if sudo_options.positional_args.is_empty() { Default::default() } else { let system_path; let path = if let Some(path) = override_path { path } else { system_path = std::env::var("PATH").unwrap_or_default(); system_path.as_ref() }; CommandAndArguments::build_from_args(None, sudo_options.positional_args, path) }; Ok(Context { hostname, command, current_user, target_user, target_group, use_session_records: !sudo_options.reset_timestamp, launch: Default::default(), chdir: None, stdin: sudo_options.stdin, bell: sudo_options.bell, prompt: sudo_options.prompt, non_interactive: sudo_options.non_interactive, process: Process::new(), use_pty: true, password_feedback: false, }) } pub(crate) fn try_as_run_options(&self) -> io::Result> { Ok(RunOptions { command: if self.command.resolved { &self.command.command } else { return Err(io::ErrorKind::NotFound.into()); }, arguments: &self.command.arguments, arg0: self.command.arg0.as_deref(), chdir: self.chdir.as_deref(), is_login: self.launch == LaunchType::Login, user: &self.target_user, group: &self.target_group, use_pty: self.use_pty, }) } } #[cfg(test)] mod tests { use crate::{ sudo::SudoAction, system::{interface::UserId, Hostname}, }; use super::Context; #[test] fn test_build_run_context() { let options = SudoAction::try_parse_from(["sudo", "echo", "hello"]) .unwrap() .try_into_run() .ok() .unwrap(); let context = Context::from_run_opts(options, &mut Default::default()).unwrap(); if cfg!(target_os = "linux") { // this assumes /bin is a symlink on /usr/bin, like it is on modern Debian/Ubuntu assert_eq!(context.command.command.to_str().unwrap(), "/usr/bin/echo"); } else { assert_eq!(context.command.command.to_str().unwrap(), "/bin/echo"); } assert_eq!(context.command.arguments, ["hello"]); assert_eq!(context.hostname, Hostname::resolve()); assert_eq!(context.target_user.uid, UserId::ROOT); } } sudo-rs-0.2.5/src/common/error.rs000064400000000000000000000102261046102023000150030ustar 00000000000000use crate::{pam::PamError, system::Hostname}; use std::{borrow::Cow, fmt, path::PathBuf}; use super::{SudoPath, SudoString}; #[derive(Debug)] pub enum Error { Silent, NotAllowed { username: SudoString, command: Cow<'static, str>, hostname: Hostname, other_user: Option, }, SelfCheck, KernelCheck, CommandNotFound(PathBuf), InvalidCommand(PathBuf), ChDirNotAllowed { chdir: SudoPath, command: PathBuf, }, UserNotFound(String), GroupNotFound(String), Authorization(String), InteractionRequired, EnvironmentVar(Vec), Configuration(String), Options(String), Pam(PamError), Io(Option, std::io::Error), MaxAuthAttempts(usize), PathValidation(PathBuf), StringValidation(String), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::Silent => Ok(()), Error::NotAllowed { username, command, hostname, other_user, } => { if let Some(other_user) = other_user { write!( f, "Sorry, user {username} is not allowed to execute '{command}' as {other_user} on {hostname}.", ) } else { write!( f, "Sorry, user {username} may not run {command} on {hostname}.", ) } } Error::SelfCheck => { f.write_str("sudo must be owned by uid 0 and have the setuid bit set") } Error::KernelCheck => f.write_str("sudo-rs needs a Linux kernel newer than v5.9"), Error::CommandNotFound(p) => write!(f, "'{}': command not found", p.display()), Error::InvalidCommand(p) => write!(f, "'{}': invalid command", p.display()), Error::UserNotFound(u) => write!(f, "user '{u}' not found"), Error::GroupNotFound(g) => write!(f, "group '{g}' not found"), Error::Authorization(u) => write!(f, "I'm sorry {u}. I'm afraid I can't do that"), Error::InteractionRequired => write!(f, "interactive authentication is required"), Error::EnvironmentVar(vs) => { write!( f, "you are not allowed to set the following environment variables:" )?; let mut sep = ""; for v in vs { write!(f, "{sep} {v}")?; sep = ","; } Ok(()) } Error::Configuration(e) => write!(f, "invalid configuration: {e}"), Error::Options(e) => write!(f, "{e}"), Error::Pam(e) => write!(f, "{e}"), Error::Io(location, e) => { if let Some(path) = location { write!(f, "cannot execute '{}': {e}", path.display()) } else { write!(f, "IO error: {e}") } } Error::MaxAuthAttempts(num) => { write!(f, "Maximum {num} incorrect authentication attempts") } Error::ChDirNotAllowed { chdir, command } => write!( f, "you are not allowed to use '--chdir {}' with '{}'", chdir.display(), command.display() ), Error::StringValidation(string) => { write!(f, "invalid string: {string:?}") } Error::PathValidation(path) => { write!(f, "invalid path: {path:?}") } } } } impl From for Error { fn from(err: PamError) -> Self { Error::Pam(err) } } impl From for Error { fn from(err: std::io::Error) -> Self { Error::Io(None, err) } } impl Error { /// Returns `true` if the error is [`Silent`]. /// /// [`Silent`]: Error::Silent #[must_use] pub fn is_silent(&self) -> bool { matches!(self, Self::Silent) } } sudo-rs-0.2.5/src/common/mod.rs000064400000000000000000000017711046102023000144360ustar 00000000000000#![forbid(unsafe_code)] pub use command::CommandAndArguments; pub use context::Context; pub use error::Error; pub use path::SudoPath; pub use string::SudoString; pub mod bin_serde; pub mod command; pub mod context; pub mod error; mod path; pub mod resolve; mod string; // Hardened enum values used for critical enums to mitigate attacks like Rowhammer. // See for example https://arxiv.org/pdf/2309.02545.pdf // The values are copied from https://github.com/sudo-project/sudo/commit/7873f8334c8d31031f8cfa83bd97ac6029309e4f#diff-b8ac7ab4c3c4a75aed0bb5f7c5fd38b9ea6c81b7557f775e46c6f8aa115e02cd pub const HARDENED_ENUM_VALUE_0: u32 = 0x52a2925; // 0101001010100010100100100101 pub const HARDENED_ENUM_VALUE_1: u32 = 0xad5d6da; // 1010110101011101011011011010 pub const HARDENED_ENUM_VALUE_2: u32 = 0x69d61fc8; // 1101001110101100001111111001000 pub const HARDENED_ENUM_VALUE_3: u32 = 0x1629e037; // 0010110001010011110000000110111 pub const HARDENED_ENUM_VALUE_4: u32 = 0x1fc8d3ac; // 11111110010001101001110101100 sudo-rs-0.2.5/src/common/path.rs000064400000000000000000000052711046102023000146120ustar 00000000000000use std::{ ffi::OsString, ops, os::unix::prelude::OsStrExt, path::{Path, PathBuf}, str, }; use super::{Error, SudoString}; /// A `PathBuf` guaranteed to not contain null bytes and be UTF-8 encoded #[derive(Clone, Debug, PartialEq)] #[cfg_attr(test, derive(Eq))] pub struct SudoPath { inner: String, } impl SudoPath { pub fn new(path: PathBuf) -> Result { let bytes = path.as_os_str().as_bytes(); if bytes.contains(&0) { return Err(Error::PathValidation(path)); } // check this through a reference so we can return `path` in the error case if str::from_utf8(bytes).is_err() { return Err(Error::PathValidation(path)); } Ok(Self { // NOTE(unwrap): UTF-8 encoding is checked above inner: path.into_os_string().into_string().unwrap(), }) } pub fn from_cli_string(cli_string: impl Into) -> Self { Self::new(cli_string.into().into()) .expect("strings that come in from CLI should not have interior null bytes") } /// Resolve the use of a '~' that occurs in this `SudoPathBuf`; based on the sudoers context pub fn expand_tilde_in_path(&self, default_username: &SudoString) -> Result { if let Some(prefix) = self.inner.strip_prefix('~') { let (username, relpath) = prefix.split_once('/').unwrap_or((prefix, "")); let username = if username.is_empty() { default_username.clone() } else { SudoString::new(username.to_string()).unwrap() }; let home_dir = crate::system::User::from_name(username.as_cstr()) .ok() .flatten() .ok_or(Error::UserNotFound(username.to_string()))? .home; let path = home_dir.join(relpath); Self::new(path) } else { Ok(self.clone()) } } } impl From for PathBuf { fn from(value: SudoPath) -> Self { value.inner.into() } } impl AsRef for SudoPath { fn as_ref(&self) -> &Path { self.inner.as_ref() } } impl ops::Deref for SudoPath { type Target = Path; fn deref(&self) -> &Self::Target { self.as_ref() } } impl TryFrom for SudoPath { type Error = Error; fn try_from(value: String) -> Result { Self::new(value.into()) } } impl From for OsString { fn from(value: SudoPath) -> Self { value.inner.into() } } #[cfg(test)] impl From<&'_ str> for SudoPath { fn from(value: &'_ str) -> Self { Self::new(value.into()).unwrap() } } sudo-rs-0.2.5/src/common/resolve.rs000064400000000000000000000276301046102023000153400ustar 00000000000000use crate::system::interface::UserId; use crate::system::{Group, User}; use core::fmt; use std::{ env, ffi::CStr, fs, io, ops, os::unix::prelude::MetadataExt, path::{Path, PathBuf}, str::FromStr, }; use super::SudoString; use super::{context::LaunchType, Error}; #[derive(PartialEq, Debug)] enum NameOrId<'a, T: FromStr> { Name(&'a SudoString), Id(T), } impl<'a, T: FromStr> NameOrId<'a, T> { pub fn parse(input: &'a SudoString) -> Option { if input.is_empty() { None } else if let Some(stripped) = input.strip_prefix('#') { stripped.parse::().ok().map(|id| Self::Id(id)) } else { Some(Self::Name(input)) } } } #[derive(Clone)] pub struct CurrentUser { inner: User, } impl From for User { fn from(value: CurrentUser) -> Self { value.inner } } impl fmt::Debug for CurrentUser { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("CurrentUser").field(&self.inner).finish() } } impl ops::Deref for CurrentUser { type Target = User; fn deref(&self) -> &Self::Target { &self.inner } } impl CurrentUser { #[cfg(test)] pub fn fake(user: User) -> Self { Self { inner: user } } pub fn resolve() -> Result { Ok(Self { inner: User::real()?.ok_or(Error::UserNotFound("current user".to_string()))?, }) } } #[derive(Clone, Debug)] pub struct AuthUser(User); impl AuthUser { pub fn from_current_user(user: CurrentUser) -> Self { Self(user.inner) } pub fn resolve_root_for_rootpw() -> Result { Ok(Self( User::from_uid(UserId::ROOT)?.ok_or(Error::UserNotFound("root".to_string()))?, )) } pub fn from_user_for_targetpw(user: User) -> Self { Self(user) } } impl ops::Deref for AuthUser { type Target = User; fn deref(&self) -> &Self::Target { &self.0 } } type Shell = Option; pub(super) fn resolve_shell( launch_type: LaunchType, current_user: &User, target_user: &User, ) -> Shell { match launch_type { LaunchType::Login => Some(target_user.shell.clone()), LaunchType::Shell => Some( env::var("SHELL") .map(|s| s.into()) .unwrap_or_else(|_| current_user.shell.clone()), ), LaunchType::Direct => None, } } pub(crate) fn resolve_target_user_and_group( target_user_name_or_id: &Option, target_group_name_or_id: &Option, current_user: &CurrentUser, ) -> Result<(User, Group), Error> { // resolve user name or # to a user let mut target_user = resolve_from_name_or_id(target_user_name_or_id, User::from_name, User::from_uid)?; // resolve group name or # to a group let mut target_group = resolve_from_name_or_id(target_group_name_or_id, Group::from_name, Group::from_gid)?; match (&target_user_name_or_id, &target_group_name_or_id) { // when -g is specified, but -u is not specified default -u to the current user (None, Some(_)) => { target_user = Some(current_user.clone().into()); } // when -u is specified but -g is not specified, default -g to the primary group of the specified user (Some(_), None) => { if let Some(user) = &target_user { target_group = Some(user.primary_group()?); } } // when no -u or -g is specified, default to root:root (None, None) => { target_user = User::from_name(cstr!("root"))?; target_group = Group::from_name(if cfg!(target_os = "linux") { cstr!("root") } else { cstr!("wheel") })?; } _ => {} } match (target_user, target_group) { (Some(user), Some(group)) => { // resolve success Ok((user, group)) } // group name or id not found (Some(_), None) => Err(Error::GroupNotFound( target_group_name_or_id .as_deref() .unwrap_or_default() .to_string(), )), // user (and maybe group) name or id not found _ => Err(Error::UserNotFound( target_user_name_or_id .as_deref() .unwrap_or_default() .to_string(), )), } } fn resolve_from_name_or_id( input: &Option, from_name: impl FnOnce(&CStr) -> Result, E>, from_id: impl FnOnce(I) -> Result, E>, ) -> Result, E> where I: FromStr, { match input.as_ref().and_then(NameOrId::parse) { Some(NameOrId::Name(name)) => from_name(name.as_cstr()), Some(NameOrId::Id(id)) => from_id(id), None => Ok(None), } } /// Check whether a path points to a regular file and any executable flag is set pub(crate) fn is_valid_executable(path: &PathBuf) -> bool { if path.is_file() { match fs::metadata(path) { Ok(meta) => meta.mode() & 0o111 != 0, _ => false, } } else { false } } /// Resolve a executable name based in the PATH environment variable /// When resolving a path, this code checks whether the target file is /// a regular file and has any executable bits set. It does not specifically /// check for user, group, or others' executable bit. pub(crate) fn resolve_path(command: &Path, path: &str) -> Option { // To prevent command spoofing, sudo checks "." and "" (both denoting current directory) // last when searching for a command in the user's PATH (if one or both are in the PATH). // Depending on the security policy, the user's PATH environment variable may be modified, // replaced, or passed unchanged to the program that sudo executes. let mut resolve_current_path = false; path.split(':') // register whether to look in the current directory, but first check the other PATH segments .filter(|&path| { if path.is_empty() || path == "." { resolve_current_path = true; false } else { true } }) // construct a possible executable absolute path candidate .map(|path| PathBuf::from(path).join(command)) // check whether the candidate is a regular file and any executable flag is set .find(is_valid_executable) // if no no executable could be resolved try the current directory // if it was present in the PATH .or_else(|| { if resolve_current_path { env::current_dir() .ok() .map(|dir| dir.join(command)) .and_then(|path| { if is_valid_executable(&path) { Some(path) } else { None } }) } else { None } }) } #[cfg(test)] mod tests { use std::path::PathBuf; use crate::common::resolve::CurrentUser; use crate::system::ROOT_GROUP_NAME; use super::{is_valid_executable, resolve_path, resolve_target_user_and_group, NameOrId}; #[test] fn test_resolve_path() { // Assume any linux distro has utilities in this PATH let path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; assert!(is_valid_executable( &resolve_path(&PathBuf::from("yes"), path).unwrap() )); assert!(is_valid_executable( &resolve_path(&PathBuf::from("whoami"), path).unwrap() )); assert!(is_valid_executable( &resolve_path(&PathBuf::from("env"), path).unwrap() )); assert_eq!( resolve_path(&PathBuf::from("thisisnotonyourfs"), path), None ); assert_eq!(resolve_path(&PathBuf::from("thisisnotonyourfs"), "."), None); } #[test] fn test_name_or_id() { assert_eq!(NameOrId::::parse(&"".into()), None); assert_eq!( NameOrId::::parse(&"mies".into()), Some(NameOrId::Name(&"mies".into())) ); assert_eq!( NameOrId::::parse(&"1337".into()), Some(NameOrId::Name(&"1337".into())) ); assert_eq!( NameOrId::::parse(&"#1337".into()), Some(NameOrId::Id(1337)) ); assert_eq!(NameOrId::::parse(&"#-1".into()), None); } #[test] fn test_resolve_target_user_and_group() { let current_user = CurrentUser::resolve().unwrap(); // fallback to root let (user, group) = resolve_target_user_and_group(&None, &None, ¤t_user).unwrap(); assert_eq!(user.name, "root"); assert_eq!(group.name.unwrap(), ROOT_GROUP_NAME); // unknown user let result = resolve_target_user_and_group(&Some("non_existing_ghost".into()), &None, ¤t_user); assert!(result.is_err()); // unknown user let result = resolve_target_user_and_group(&None, &Some("non_existing_ghost".into()), ¤t_user); assert!(result.is_err()); // fallback to current user when different group specified let (user, group) = resolve_target_user_and_group(&None, &Some(ROOT_GROUP_NAME.into()), ¤t_user) .unwrap(); assert_eq!(user.name, current_user.name); assert_eq!(group.name.unwrap(), ROOT_GROUP_NAME); // fallback to current users group when no group specified let (user, group) = resolve_target_user_and_group(&Some(current_user.name.clone()), &None, ¤t_user) .unwrap(); assert_eq!(user.name, current_user.name); assert_eq!(group.gid, current_user.gid); } } /// Resolve symlinks in all the directories leading up to a file, but /// not the file itself; this allows sudo to specify a precise policy with /// tools like busybox or pgrep (which is a symlink to pgrep on systems) pub fn canonicalize>(path: P) -> io::Result { let path = path.as_ref(); let Some(parent) = path.parent() else { // path is "/" or a prefix return Ok(path.to_path_buf()); }; let canon_path = fs::canonicalize(parent)?; let reconstructed_path = if let Some(file_name) = path.file_name() { canon_path.join(file_name) } else { canon_path }; // access the object to generate the regular error if it does not exist let _ = fs::metadata(&reconstructed_path)?; Ok(reconstructed_path) } #[cfg(test)] mod test { use super::canonicalize; use std::path::Path; #[test] fn canonicalization() { assert_eq!(canonicalize("/").unwrap(), Path::new("/")); assert_eq!(canonicalize("").unwrap(), Path::new("")); if cfg!(any(target_os = "linux", target_os = "macos")) { // this test REQUIRES /usr/bin/unxz to be a symlink for /usr/bin/xz assert_eq!( canonicalize("/usr/bin/unxz").unwrap(), Path::new("/usr/bin/unxz") ); // this assumes /bin is a symlink on /usr/bin, like it is on modern Debian/Ubuntu assert_eq!( canonicalize("/bin/unxz").unwrap(), Path::new("/usr/bin/unxz") ); } else if cfg!(target_os = "freebsd") { // this test REQUIRES /usr/bin/pkill to be a symlink for /usr/bin/pgrep assert_eq!( canonicalize("/usr/bin/pkill").unwrap(), Path::new("/usr/bin/pkill") ); assert_eq!(canonicalize("/bin/pkill").unwrap(), Path::new("/bin/pkill")); } else { panic!( "canonicalization test not yet adapted for {}", std::env::consts::OS ); } } } sudo-rs-0.2.5/src/common/string.rs000064400000000000000000000063761046102023000151730ustar 00000000000000use core::fmt; use std::{ ffi::{CStr, OsString}, ops, }; use crate::common::Error; const NULL_BYTE: char = '\0'; const NULL_BYTE_UTF8_LEN: usize = NULL_BYTE.len_utf8(); /// A UTF-8 encoded string with no interior null bytes /// /// This type can be converted into a C (null-terminated) string at no cost #[derive(Clone, PartialEq, Eq)] pub struct SudoString { inner: String, } impl SudoString { pub fn new(mut string: String) -> Result { if string.as_bytes().contains(&0) { return Err(Error::StringValidation(string)); } string.push(NULL_BYTE); Ok(Self { inner: string }) } pub fn from_cli_string(cli_string: impl Into) -> Self { Self::new(cli_string.into()) .expect("strings that come in from CLI should not have interior null bytes") } pub fn as_cstr(&self) -> &CStr { CStr::from_bytes_with_nul(self.inner.as_bytes()).unwrap() } pub fn as_str(&self) -> &str { self } } impl Default for SudoString { fn default() -> Self { Self { inner: NULL_BYTE.into(), } } } #[cfg(test)] impl From<&'_ str> for SudoString { fn from(value: &'_ str) -> Self { SudoString::try_from(value.to_string()).unwrap() } } impl TryFrom for SudoString { type Error = Error; fn try_from(value: String) -> Result { Self::new(value) } } impl From for String { fn from(value: SudoString) -> Self { let mut s = value.inner; s.pop(); s } } impl From for OsString { fn from(value: SudoString) -> Self { let mut s = value.inner; s.pop(); OsString::from(s) } } impl ops::Deref for SudoString { type Target = str; fn deref(&self) -> &Self::Target { let num_bytes = self.inner.len(); &self.inner[..num_bytes - NULL_BYTE_UTF8_LEN] } } impl fmt::Debug for SudoString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s: &str = self; fmt::Debug::fmt(s, f) } } impl fmt::Display for SudoString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self) } } impl PartialEq for SudoString { fn eq(&self, other: &str) -> bool { let s: &str = self; s == other } } impl PartialEq<&'_ str> for SudoString { fn eq(&self, other: &&str) -> bool { let s: &str = self; s == *other } } #[cfg(test)] mod tests { use std::ffi::CString; use super::*; #[test] fn null_byte_is_utf8_encoded_as_a_single_byte() { assert_eq!(1, NULL_BYTE_UTF8_LEN) } #[test] fn sanity_check() { let expected = "hello"; let s = SudoString::new("hello".to_string()).unwrap(); assert_eq!(expected, &*s); } #[test] fn cstr_conversion() { let expected = "hello"; let cstr = CString::from_vec_with_nul((expected.to_string() + "\0").into_bytes()).unwrap(); let s = SudoString::new(expected.to_string()).unwrap(); assert_eq!(&*cstr, s.as_cstr()); } #[test] fn rejects_string_that_contains_interior_null() { assert!(SudoString::new("he\0llo".to_string()).is_err()); } } sudo-rs-0.2.5/src/cutils/mod.rs000064400000000000000000000111071046102023000144430ustar 00000000000000use std::{ ffi::{CStr, OsStr, OsString}, os::{ fd::{AsRawFd, BorrowedFd}, unix::prelude::OsStrExt, }, }; pub fn cerr>(res: Int) -> std::io::Result { match res.try_into() { Ok(-1) => Err(std::io::Error::last_os_error()), _ => Ok(res), } } extern "C" { #[cfg_attr( any(target_os = "macos", target_os = "ios", target_os = "freebsd"), link_name = "__error" )] #[cfg_attr( any(target_os = "openbsd", target_os = "netbsd", target_os = "android"), link_name = "__errno" )] #[cfg_attr(target_os = "linux", link_name = "__errno_location")] fn errno_location() -> *mut libc::c_int; } pub fn set_errno(no: libc::c_int) { // SAFETY: errno_location is a thread-local pointer to an integer, so we are the only writers unsafe { *errno_location() = no }; } pub fn sysconf(name: libc::c_int) -> Option { set_errno(0); // SAFETY: sysconf will always respond with 0 or -1 for every input cerr(unsafe { libc::sysconf(name) }).ok() } /// Create a Rust string copy from a C string pointer /// WARNING: This uses `to_string_lossy` so should not be used for data where /// information loss is unacceptable (use `os_string_from_ptr` instead) /// /// # Safety /// This function assumes that the pointer is either a null pointer or that /// it points to a valid NUL-terminated C string. pub unsafe fn string_from_ptr(ptr: *const libc::c_char) -> String { if ptr.is_null() { String::new() } else { // SAFETY: the function contract says that CStr::from_ptr is safe let cstr = unsafe { CStr::from_ptr(ptr) }; cstr.to_string_lossy().to_string() } } /// Create an `OsString` copy from a C string pointer. /// /// # Safety /// This function assumes that the pointer is either a null pointer or that /// it points to a valid NUL-terminated C string. pub unsafe fn os_string_from_ptr(ptr: *const libc::c_char) -> OsString { if ptr.is_null() { OsString::new() } else { // SAFETY: the function contract says that CStr::from_ptr is safe let cstr = unsafe { CStr::from_ptr(ptr) }; OsStr::from_bytes(cstr.to_bytes()).to_owned() } } /// Rust's standard library IsTerminal just directly calls isatty, which /// we don't want since this performs IOCTL calls on them and file descriptors are under /// the control of the user; so this checks if they are a character device first. pub fn safe_isatty(fildes: BorrowedFd) -> bool { // The Rust standard library doesn't have FileTypeExt on Std{in,out,err}, so we // can't just use FileTypeExt::is_char_device and have to resort to libc::fstat. let mut maybe_stat = std::mem::MaybeUninit::::uninit(); // SAFETY: we are passing fstat a pointer to valid memory if unsafe { libc::fstat(fildes.as_raw_fd(), maybe_stat.as_mut_ptr()) } == 0 { // SAFETY: if `fstat` returned 0, maybe_stat will be initialized let mode = unsafe { maybe_stat.assume_init() }.st_mode; // To complicate matters further, the S_ISCHR macro isn't in libc as well. let is_char_device = (mode & libc::S_IFMT) == libc::S_IFCHR; if is_char_device { // SAFETY: isatty will return 0 or 1 unsafe { libc::isatty(fildes.as_raw_fd()) != 0 } } else { false } } else { false } } #[allow(clippy::undocumented_unsafe_blocks)] #[cfg(test)] mod test { use super::{os_string_from_ptr, string_from_ptr}; #[test] fn miri_test_str_to_ptr() { let strp = |ptr| unsafe { string_from_ptr(ptr) }; assert_eq!(strp(std::ptr::null()), ""); assert_eq!(strp("\0".as_ptr() as *const libc::c_char), ""); assert_eq!(strp("hello\0".as_ptr() as *const libc::c_char), "hello"); } #[test] fn miri_test_os_str_to_ptr() { let strp = |ptr| unsafe { os_string_from_ptr(ptr) }; assert_eq!(strp(std::ptr::null()), ""); assert_eq!(strp("\0".as_ptr() as *const libc::c_char), ""); assert_eq!(strp("hello\0".as_ptr() as *const libc::c_char), "hello"); } #[test] fn test_tty() { use crate::system::term::Pty; use std::fs::File; use std::os::fd::{AsFd, BorrowedFd}; assert!(!super::safe_isatty(File::open("/bin/sh").unwrap().as_fd())); assert!(!super::safe_isatty(unsafe { BorrowedFd::borrow_raw(-837492) })); let pty = Pty::open().unwrap(); assert!(super::safe_isatty(pty.leader.as_fd())); assert!(super::safe_isatty(pty.follower.as_fd())); } } sudo-rs-0.2.5/src/defaults/mod.rs000064400000000000000000000146121046102023000147530ustar 00000000000000#![forbid(unsafe_code)] pub type SettingsModifier = Box; pub enum ListMode { Set, Add, Del, } #[repr(u32)] pub enum SettingKind { Flag(SettingsModifier) = crate::common::HARDENED_ENUM_VALUE_0, Integer(fn(&str) -> Option) = crate::common::HARDENED_ENUM_VALUE_1, Text(fn(&str) -> Option) = crate::common::HARDENED_ENUM_VALUE_2, List(fn(ListMode, Vec) -> SettingsModifier) = crate::common::HARDENED_ENUM_VALUE_3, } mod settings_dsl; use settings_dsl::{ defaults, emit, has_standard_negator, ifdef, initializer_of, modifier_of, referent_of, result_of, storage_of, }; defaults! { always_query_group_plugin = false #ignored always_set_home = false #ignored env_reset = true #ignored fqdn = false #ignored lecture = false #ignored mailerpath = None (!= None) #ignored mail_badpass = true #ignored match_group_by_gid = false #ignored use_pty = true visiblepw = false #ignored pwfeedback = false env_editor = true rootpw = false targetpw = false passwd_tries = 3 [0..=1000] secure_path = None (!= None) verifypw = all (!= never) [all, always, any, never] #ignored timestamp_timeout = (15*60) (!= 0) {fractional_minutes} env_keep = ["COLORS", "DISPLAY", "HOSTNAME", "KRB5CCNAME", "LS_COLORS", "PATH", "PS1", "PS2", "XAUTHORITY", "XAUTHORIZATION", "XDG_CURRENT_DESKTOP"] env_check = ["COLORTERM", "LANG", "LANGUAGE", "LC_*", "LINGUAS", "TERM", "TZ"] env_delete = ["IFS", "CDPATH", "LOCALDOMAIN", "RES_OPTIONS", "HOSTALIASES", "NLSPATH", "PATH_LOCALE", "LD_*", "_RLD*", "TERMINFO", "TERMINFO_DIRS", "TERMPATH", "TERMCAP", "ENV", "BASH_ENV", "PS4", "GLOBIGNORE", "BASHOPTS", "SHELLOPTS", "JAVA_TOOL_OPTIONS", "PERLIO_DEBUG", "PERLLIB", "PERL5LIB", "PERL5OPT", "PERL5DB", "FPATH", "NULLCMD", "READNULLCMD", "ZDOTDIR", "TMPPREFIX", "PYTHONHOME", "PYTHONPATH", "PYTHONINSPECT", "PYTHONUSERBASE", "RUBYLIB", "RUBYOPT", "*=()*"] #ignored } /// A custom parser to parse seconds as fractional "minutes", the format used by /// passwd_timeout and timestamp_timeout. fn fractional_minutes(input: &str) -> Option { if let Some((integral, fractional)) = input.split_once('.') { // - 'input' is maximally 18 characters, making fractional.len() at most 17; // 1e17 < 2**63, so the definition of 'shift' will not overflow. // - for the same reason, if both parses in the definition of 'seconds' succeed, // we will have constructed an integer < 1e17. //- 1e17 * 60 = 6e18 < 9e18 < 2**63, so the final line also will not overflow let shift = 10i64.pow(fractional.len().try_into().ok()?); let seconds = integral.parse::().ok()? * shift + fractional.parse::().ok()?; Some(seconds * 60 / shift) } else { input.parse::().ok()?.checked_mul(60) } } #[cfg(test)] mod test { use super::*; #[allow(clippy::bool_assert_comparison)] #[test] fn check() { let mut def = Settings::default(); assert_eq! { def.always_query_group_plugin, false }; assert_eq! { def.always_set_home, false }; assert_eq! { def.env_reset, true }; assert_eq! { def.mail_badpass, true }; assert_eq! { def.match_group_by_gid, false }; assert_eq! { def.use_pty, true }; assert_eq! { def.visiblepw, false }; assert_eq! { def.env_editor, true }; assert_eq! { def.passwd_tries, 3 }; assert_eq! { def.secure_path, None }; assert_eq! { def.env_check, ["COLORTERM", "LANG", "LANGUAGE", "LC_*", "LINGUAS", "TERM", "TZ"].iter().map(|s| s.to_string()).collect() }; assert_eq! { def.verifypw, enums::verifypw::all }; negate("env_check").unwrap()(&mut def); negate("env_reset").unwrap()(&mut def); negate("secure_path").unwrap()(&mut def); negate("verifypw").unwrap()(&mut def); assert_eq! { def.always_query_group_plugin, false }; assert_eq! { def.always_set_home, false }; assert_eq! { def.env_reset, false }; assert_eq! { def.mail_badpass, true }; assert_eq! { def.match_group_by_gid, false }; assert_eq! { def.use_pty, true }; assert_eq! { def.visiblepw, false }; assert_eq! { def.env_editor, true }; assert_eq! { def.passwd_tries, 3 }; assert_eq! { def.secure_path, None }; assert! { def.env_check.is_empty() }; assert_eq! { def.verifypw, enums::verifypw::never }; let SettingKind::Flag(f) = set("env_reset").unwrap() else { panic!() }; f(&mut def); let SettingKind::Text(f) = set("secure_path").unwrap() else { panic!() }; f("/bin").unwrap()(&mut def); let SettingKind::Integer(f) = set("passwd_tries").unwrap() else { panic!() }; f("5").unwrap()(&mut def); let SettingKind::Text(f) = set("verifypw").unwrap() else { panic!() }; f("any").unwrap()(&mut def); let SettingKind::Integer(f) = set("timestamp_timeout").unwrap() else { panic!() }; f("25.25").unwrap()(&mut def); assert_eq! { def.always_query_group_plugin, false }; assert_eq! { def.always_set_home, false }; assert_eq! { def.env_reset, true }; assert_eq! { def.mail_badpass, true }; assert_eq! { def.match_group_by_gid, false }; assert_eq! { def.use_pty, true }; assert_eq! { def.visiblepw, false }; assert_eq! { def.env_editor, true }; assert_eq! { def.passwd_tries, 5 }; assert_eq! { def.timestamp_timeout, 25*60 + 60/4 }; assert_eq! { def.secure_path, Some("/bin".into()) }; assert! { def.env_check.is_empty() }; assert_eq! { def.verifypw, enums::verifypw::any }; assert!(set("notanoption").is_none()); assert!(f("notanoption").is_none()); } } sudo-rs-0.2.5/src/defaults/settings_dsl.rs000064400000000000000000000163531046102023000167020ustar 00000000000000macro_rules! storage_of { ($id:ident, true) => { bool }; ($id:ident, false) => { bool }; ($id:ident, [ $($value: expr),* ]) => { std::collections::HashSet }; ($id:ident, $(=int $check: expr;)+ $_: expr) => { i64 }; ($id:ident, $(=enum $k: ident;)+ $_: ident) => { $crate::defaults::enums::$id }; ($id:ident, $_: expr) => { Option> }; } macro_rules! referent_of { ($id:ident, true) => { bool }; ($id:ident, false) => { bool }; ($id:ident, [ $($value: expr),* ]) => { &std::collections::HashSet }; ($id:ident, $(=int $check: expr;)+ $_: expr) => { i64 }; ($id:ident, $(=enum $k: ident;)+ $_: ident) => { $crate::defaults::enums::$id }; ($id:ident, $_: expr) => { Option<&str> }; } macro_rules! initializer_of { ($id:ident, true) => { true }; ($id:ident, false) => { false }; ($id:ident, [ $($value: expr),* ]) => { [$($value),*].into_iter().map(|s: &str| s.to_string()).collect::>() }; ($id:ident, $(=int $check: expr;)+ $value: expr) => { $value }; ($id:ident, $(=enum $k: ident;)+ $value: ident) => { $crate::defaults::enums::$id::$value }; ($id:ident, None) => { None }; ($id:ident, $value: expr) => { Some($value.into()) }; ($id:ident, $($_: tt)*) => { return None }; } macro_rules! result_of { ($id:expr, true) => { $id }; ($id:expr, false) => { $id }; ($id:expr, [ $($value: expr),* ]) => { &$id }; ($id:expr, $(=value $k: expr;)+ $_: expr) => { $id }; ($id:expr, $_: expr) => { $id.as_deref() }; } macro_rules! modifier_of { ($id:ident, true) => { $crate::defaults::SettingKind::Flag(Box::new(move |obj: &mut Settings| obj.$id = true)) }; ($id:ident, false) => { $crate::defaults::SettingKind::Flag(Box::new(move |obj: &mut Settings| obj.$id = true)) }; ($id:ident, [ $($value: expr),* ]) => { $crate::defaults::SettingKind::List(|mode, list| { Box::new(move |obj: &mut Settings| match mode { ListMode::Set => obj.$id = list.into_iter().collect(), ListMode::Add => obj.$id.extend(list), ListMode::Del => { for key in list { obj.$id.remove(&key); } } }) }) }; ($id:ident, =int $first:literal ..= $last: literal $(@ $radix: literal)?; $value: expr) => { #[allow(clippy::from_str_radix_10)] $crate::defaults::SettingKind::Integer(|text| { i64::from_str_radix(text, 10$(*0 + $radix)?) .ok() .filter(|val| ($first ..= $last).contains(val)) .map(|i| { Box::new(move |obj: &mut Settings| obj.$id = i) as SettingsModifier }) }) }; ($id:ident, =int $fn: expr; $value: expr) => { $crate::defaults::SettingKind::Integer(|text| { $fn(&text).map(|i| { Box::new(move |obj: &mut Settings| obj.$id = i) as SettingsModifier }) }) }; ($id:ident, $(=int $check: expr;)+ $value: expr) => { compile_error!("bla") }; ($id:ident, $(=enum $key: ident;)+ $value: ident) => { $crate::defaults::SettingKind::Text(|key| match key { $( stringify!($key) => { Some(Box::new(move |obj: &mut Settings| obj.$id = $crate::defaults::enums::$id::$key)) }, )* _ => None, }) }; ($id:ident, None) => { $crate::defaults::SettingKind::Text(|text| { let text = text.into(); Some(Box::new(move |obj: &mut Settings| obj.$id = Some(text))) }) }; ($id:ident, $value: expr) => { $crate::defaults::SettingKind::Text(|text| { let text = text.into(); Some(Box::new(move |obj: &mut Settings| obj.$id = Some(text))) }) }; } macro_rules! has_standard_negator { (true) => { true }; (false) => { true }; ([$($_: expr),*]) => { true }; ($($_: tt)*) => { false }; } // this macro allows us to help the compiler generate more efficient code in 'fn negate' // and enables the way 'fn set' is made macro_rules! ifdef { (; $then: expr; $else: expr) => { $else }; ($($_: expr)+; $then: expr; $else: expr) => { $then }; } macro_rules! emit { (ignored; $($def: tt)*) => { }; ( ; $($def: tt)*) => { $($def)* }; } macro_rules! defaults { ($($name:ident = $value:tt $((!= $negate:tt))? $([$($key:ident),*])? $([$first:literal ..= $last:literal$(; radix: $radix: expr)?])? $({$fn: expr})? $(#$attribute:ident)?)*) => { #[allow(non_camel_case_types)] mod enums { $($( #[derive(Clone,Copy,Debug,Default)] #[cfg_attr(test, derive(PartialEq, Eq))] pub enum $name { #[default] $($key),* } )?)* } #[derive(Clone)] pub struct Settings { $($name: storage_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value)),* } // we add setters to make sure the settings-object is read only, and to generate 'unused variable' warnings impl Settings { $( emit! { $($attribute)?; pub fn $name(&self) -> referent_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value) { result_of!(self.$name, $(=value $fn;)?$(=value $first;)?$($(=value $key;)*)? $value) } } )* } impl Default for Settings { #[allow(unused_parens)] fn default() -> Self { Self { $($name: initializer_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value)),* } } } #[allow(clippy::diverging_sub_expression)] #[allow(unreachable_code)] pub fn negate(name: &str) -> Option { match name { $( stringify!($name) if ifdef!($($negate)?; true; has_standard_negator!($value)) => { let value = ifdef!($($negate)?; // this setting has an explicit negation; use that initializer_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $($negate)?); // for bool and sets, false/empty works (for other types this is dead code) Default::default() ); Some(Box::new(move |obj: &mut Settings| obj.$name = value)) }, )* _ => None } } pub fn set(name: &str) -> Option { match name { $( stringify!($name) => Some(modifier_of!($name, $(=int $fn;)?$(=int $first ..= $last $(@ $radix)?;)?$($(=enum $key;)*)? $value)), )* _ => None, } } }; } pub(super) use defaults; pub(super) use emit; pub(super) use has_standard_negator; pub(super) use ifdef; pub(super) use initializer_of; pub(super) use modifier_of; pub(super) use referent_of; pub(super) use result_of; pub(super) use storage_of; sudo-rs-0.2.5/src/exec/event.rs000064400000000000000000000176331046102023000144400ustar 00000000000000use std::{ fmt::Debug, io, os::fd::{AsFd, AsRawFd, RawFd}, }; use libc::{c_short, pollfd, POLLIN, POLLOUT}; use crate::common::{HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1}; use crate::{cutils::cerr, log::dev_debug}; pub(super) trait Process: Sized { /// IO Events that this process should handle. type Event: Copy + Debug; /// Reason why the event loop should break. /// /// See [`EventRegistry::set_break`] for more information. type Break; /// Reason why the event loop should exit. /// /// See [`EventRegistry::set_exit`] for more information. type Exit; /// Handle the corresponding event. fn on_event(&mut self, event: Self::Event, registry: &mut EventRegistry); } #[repr(u32)] enum Status { Continue = HARDENED_ENUM_VALUE_0, Stop(StopReason) = HARDENED_ENUM_VALUE_1, } impl Status { fn is_break(&self) -> bool { matches!(self, Self::Stop(StopReason::Break(_))) } fn take_stop(&mut self) -> Option> { // If the status ends up to be `Continue`, we are replacing it by another `Continue`. let status = std::mem::replace(self, Self::Continue); match status { Status::Continue => None, Status::Stop(reason) => Some(reason), } } fn take_exit(&mut self) -> Option { match self.take_stop()? { reason @ StopReason::Break(_) => { // Replace back the status because it was not an `Exit`. *self = Self::Stop(reason); None } StopReason::Exit(exit_reason) => Some(exit_reason), } } } pub(super) enum StopReason { Break(T::Break), Exit(T::Exit), } #[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)] struct EventId(usize); pub(super) struct EventHandle { id: EventId, should_poll: bool, } impl EventHandle { /// Ignore the event associated with this handle, meaning that the file descriptor for this /// event will not be polled anymore for that specific event. pub(super) fn ignore(&mut self, registry: &mut EventRegistry) { if self.should_poll { if let Some(poll_fd) = registry.poll_fds.get_mut(self.id.0) { poll_fd.should_poll = false; self.should_poll = false; } } } /// Stop ignoring the event associated with this handle, meaning that the file descriptor for /// this event will be polled for that specific event. pub(super) fn resume(&mut self, registry: &mut EventRegistry) { if !self.should_poll { if let Some(poll_fd) = registry.poll_fds.get_mut(self.id.0) { poll_fd.should_poll = true; self.should_poll = true; } } } } /// The kind of event that will be monitored for a file descriptor. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum PollEvent { /// Data may be read without blocking. Readable, /// Data may be written without blocking. Writable, } struct PollFd { // FIXME ensure that the fd is not closed while the event registry is still open raw_fd: RawFd, event_flags: c_short, should_poll: bool, event: T::Event, } /// A type able to register file descriptors to be polled. pub(super) struct EventRegistry { poll_fds: Vec>, status: Status, } impl EventRegistry { /// Create a new and empty registry.. pub(super) const fn new() -> Self { Self { poll_fds: Vec::new(), status: Status::Continue, } } /// Set the `fd` descriptor to be polled for `poll_event` events and produce `event` when `fd` is /// ready. pub(super) fn register_event( &mut self, fd: &F, poll_event: PollEvent, event_fn: impl Fn(PollEvent) -> T::Event, ) -> EventHandle { let id = EventId(self.poll_fds.len()); self.poll_fds.push(PollFd { raw_fd: fd.as_fd().as_raw_fd(), event_flags: match poll_event { PollEvent::Readable => POLLIN, PollEvent::Writable => POLLOUT, }, should_poll: true, event: event_fn(poll_event), }); EventHandle { id, should_poll: true, } } /// Poll the file descriptors of that are not being ignored and return the ID of the /// descriptors that are ready to be read or written. /// /// Calling this function will block until one of the file descriptors in the set is ready. fn poll(&mut self) -> io::Result> { let (mut ids, mut fds): (Vec, Vec) = self .poll_fds .iter() .enumerate() .filter_map(|(index, poll_fd)| { poll_fd.should_poll.then_some({ ( EventId(index), pollfd { fd: poll_fd.raw_fd, events: poll_fd.event_flags, revents: 0, }, ) }) }) .unzip(); // Don't call poll if there are no file descriptors to be polled. if ids.is_empty() { return Ok(ids); } // SAFETY: `poll` expects a pointer to an array of file descriptors (first argument), // the length of which is indicated by the second argument; the third argument being -1 // denotes an infinite timeout. // FIXME: we should set either a timeout or use ppoll when available. cerr(unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as _, -1) })?; // Remove the ids that correspond to file descriptors that were not ready. for (i, fd) in fds.iter().enumerate().rev() { let events = fd.events & fd.revents; if !((events & POLLIN != 0) || (events & POLLOUT != 0)) { ids.remove(i); } } Ok(ids) } /// Stop the event loop when the current event has been handled and set a reason for it. /// /// This means that the event loop will stop even if other events are ready. pub(super) fn set_break(&mut self, reason: T::Break) { self.status = Status::Stop(StopReason::Break(reason)); } /// Stop the event loop when the events that are ready by now have been handled and set a /// reason for it. pub(super) fn set_exit(&mut self, reason: T::Exit) { self.status = Status::Stop(StopReason::Exit(reason)); } /// Return whether a break reason has been set already. pub(super) fn got_break(&self) -> bool { self.status.is_break() } /// Run the event loop over this registry using `process` to handle the produced events. /// /// The event loop will continue indefinitely unless you call [`EventRegistry::set_break`] or /// [`EventRegistry::set_exit`]. #[track_caller] pub(super) fn event_loop(mut self, process: &mut T) -> StopReason { let mut event_queue = Vec::with_capacity(self.poll_fds.len()); loop { // FIXME: maybe we shout return the IO error instead. if let Ok(ids) = self.poll() { for EventId(index) in ids { let event = self.poll_fds[index].event; dev_debug!("event {event:?} is ready"); event_queue.push(event); } for event in event_queue.drain(..) { process.on_event(event, &mut self); if let Some(reason) = self.status.take_exit() { return StopReason::Exit(reason); } } } if let Some(reason) = self.status.take_stop() { return reason; } } } } sudo-rs-0.2.5/src/exec/io_util.rs000064400000000000000000000011321046102023000147460ustar 00000000000000use std::io; /// Return `true` if the IO error is an interruption. pub(super) fn was_interrupted(err: &io::Error) -> bool { // ogsudo checks against `EINTR` and `EAGAIN`. matches!( err.kind(), io::ErrorKind::Interrupted | io::ErrorKind::WouldBlock ) } /// Call `f` repeatedly until it succeds or it encounters a non-interruption error. pub(super) fn retry_while_interrupted(mut f: impl FnMut() -> io::Result) -> io::Result { loop { match f() { Err(err) if was_interrupted(&err) => {} result => return result, } } } sudo-rs-0.2.5/src/exec/mod.rs000064400000000000000000000147171046102023000140760ustar 00000000000000mod event; mod io_util; mod no_pty; mod use_pty; use std::{ borrow::Cow, env, ffi::{c_int, OsStr}, io, os::unix::ffi::OsStrExt, os::unix::process::CommandExt, path::Path, process::Command, time::Duration, }; use crate::{ exec::no_pty::exec_no_pty, log::{dev_info, dev_warn, user_error}, system::{ interface::ProcessId, killpg, signal::{consts::*, signal_name}, wait::{Wait, WaitError, WaitOptions}, }, system::{kill, set_target_user, signal::SignalNumber, term::UserTerm, Group, User}, }; use self::{ event::{EventRegistry, Process}, io_util::was_interrupted, use_pty::{exec_pty, SIGCONT_BG, SIGCONT_FG}, }; pub struct RunOptions<'a> { pub command: &'a Path, pub arguments: &'a [String], pub arg0: Option<&'a Path>, pub chdir: Option<&'a Path>, pub is_login: bool, pub user: &'a User, pub group: &'a Group, pub use_pty: bool, } /// Based on `ogsudo`s `exec_pty` function. /// /// Returns the [`ExitReason`] of the command and a function that restores the default handler for /// signals once its called. pub fn run_command( options: RunOptions<'_>, env: impl IntoIterator, impl AsRef)>, ) -> io::Result { // FIXME: should we pipe the stdio streams? let qualified_path = options.command; let mut command = Command::new(qualified_path); // reset env and set filtered environment command.args(options.arguments).env_clear().envs(env); // set the arg0 to the requested string // TODO: this mechanism could perhaps also be used to set the arg0 for login shells, as below if let Some(arg0) = options.arg0 { command.arg0(arg0); } if options.is_login { // signal to the operating system that the command is a login shell by prefixing "-" let mut process_name = qualified_path .file_name() .map(|osstr| osstr.as_bytes().to_vec()) .unwrap_or_default(); process_name.insert(0, b'-'); command.arg0(OsStr::from_bytes(&process_name)); } // Decide if the pwd should be changed. `--chdir` takes precedence over `-i`. let path = options .chdir .map(|chdir| chdir.to_owned()) .or_else(|| options.is_login.then(|| options.user.home.clone().into())) .clone(); // set target user and groups set_target_user(&mut command, options.user.clone(), options.group.clone()); // change current directory if necessary. if let Some(path) = path { let is_chdir = options.chdir.is_some(); // SAFETY: Chdir as used internally by set_current_dir is async-signal-safe. The logger we // use is also async-signal-safe. unsafe { command.pre_exec(move || { if let Err(err) = env::set_current_dir(&path) { user_error!("unable to change directory to {}: {}", path.display(), err); if is_chdir { return Err(err); } } Ok(()) }); } } let sudo_pid = ProcessId::new(std::process::id() as i32); if options.use_pty { match UserTerm::open() { Ok(user_tty) => exec_pty(sudo_pid, command, user_tty), Err(err) => { dev_info!("Could not open user's terminal, not allocating a pty: {err}"); exec_no_pty(sudo_pid, command) } } } else { exec_no_pty(sudo_pid, command) } } /// Exit reason for the command executed by sudo. #[derive(Debug)] pub enum ExitReason { Code(i32), Signal(i32), } // Kill the process with increasing urgency. // // Based on `terminate_command`. fn terminate_process(pid: ProcessId, use_killpg: bool) { let kill_fn = if use_killpg { killpg } else { kill }; kill_fn(pid, SIGHUP).ok(); kill_fn(pid, SIGTERM).ok(); std::thread::sleep(Duration::from_secs(2)); kill_fn(pid, SIGKILL).ok(); } trait HandleSigchld: Process { const OPTIONS: WaitOptions; fn on_exit(&mut self, exit_code: c_int, registry: &mut EventRegistry); fn on_term(&mut self, signal: SignalNumber, registry: &mut EventRegistry); fn on_stop(&mut self, signal: SignalNumber, registry: &mut EventRegistry); } fn handle_sigchld( handler: &mut T, registry: &mut EventRegistry, child_name: &'static str, child_pid: ProcessId, ) { let status = loop { match child_pid.wait(T::OPTIONS) { Err(WaitError::Io(err)) if was_interrupted(&err) => {} // This only happens if we receive `SIGCHLD` but there's no status update from the // monitor. Err(WaitError::Io(err)) => { return dev_info!("cannot wait for {child_pid} ({child_name}): {err}"); } // This only happens if the monitor exited and any process already waited for the // monitor. Err(WaitError::NotReady) => { return dev_info!("{child_pid} ({child_name}) has no status report"); } Ok((_pid, status)) => break status, } }; if let Some(exit_code) = status.exit_status() { dev_info!("{child_pid} ({child_name}) exited with status code {exit_code}"); handler.on_exit(exit_code, registry) } else if let Some(signal) = status.stop_signal() { dev_info!( "{child_pid} ({child_name}) was stopped by {}", signal_fmt(signal), ); handler.on_stop(signal, registry) } else if let Some(signal) = status.term_signal() { dev_info!( "{child_pid} ({child_name}) was terminated by {}", signal_fmt(signal), ); handler.on_term(signal, registry) } else if status.did_continue() { dev_info!("{child_pid} ({child_name}) continued execution"); } else { dev_warn!("unexpected wait status for {child_pid} ({child_name})") } } fn signal_fmt(signal: SignalNumber) -> Cow<'static, str> { match signal_name(signal) { name @ Cow::Owned(_) => match signal { SIGCONT_BG => "SIGCONT_BG".into(), SIGCONT_FG => "SIGCONT_FG".into(), _ => name, }, name => name, } } const fn cond_fmt<'a>(cond: bool, true_s: &'a str, false_s: &'a str) -> &'a str { if cond { true_s } else { false_s } } const fn opt_fmt(cond: bool, s: &str) -> &str { cond_fmt(cond, s, "") } sudo-rs-0.2.5/src/exec/no_pty.rs000064400000000000000000000255061046102023000146250ustar 00000000000000use std::{ffi::c_int, io, os::unix::process::CommandExt, process::Command}; use super::{ event::PollEvent, event::{EventRegistry, Process, StopReason}, io_util::was_interrupted, terminate_process, ExitReason, HandleSigchld, }; use crate::{ common::bin_serde::BinPipe, system::signal::{ consts::*, register_handlers, SignalHandler, SignalHandlerBehavior, SignalNumber, SignalSet, SignalStream, }, }; use crate::{ exec::{handle_sigchld, signal_fmt}, log::{dev_error, dev_info, dev_warn}, system::{ _exit, fork, getpgid, getpgrp, interface::ProcessId, kill, killpg, term::{Terminal, UserTerm}, wait::WaitOptions, FileCloser, ForkResult, }, }; pub(super) fn exec_no_pty(sudo_pid: ProcessId, mut command: Command) -> io::Result { // FIXME (ogsudo): Initialize the policy plugin's session here. // Block all the signals until we are done setting up the signal handlers so we don't miss // SIGCHLD. let original_set = match SignalSet::full().and_then(|set| set.block()) { Ok(original_set) => Some(original_set), Err(err) => { dev_warn!("cannot block signals: {err}"); None } }; let mut file_closer = FileCloser::new(); // FIXME (ogsudo): Some extra config happens here if selinux is available. // Use a pipe to get the IO error if `exec` fails. let (mut errpipe_tx, errpipe_rx) = BinPipe::pair()?; // Don't close the error pipe as we need it to retrieve the error code if the command execution // fails. file_closer.except(&errpipe_tx); // SAFETY: There should be no other threads at this point. let ForkResult::Parent(command_pid) = unsafe { fork() }.map_err(|err| { dev_warn!("unable to fork command process: {err}"); err })? else { // Restore the signal mask now that the handlers have been setup. if let Some(set) = original_set { if let Err(err) = set.set_mask() { dev_warn!("cannot restore signal mask: {err}"); } } // SAFETY: We immediately exec after this call and if the exec fails we only access stderr // and errpipe before exiting without running atexit handlers using _exit if let Err(err) = unsafe { file_closer.close_the_universe() } { dev_warn!("failed to close the universe: {err}"); // Send the error to the monitor using the pipe. if let Some(error_code) = err.raw_os_error() { errpipe_tx.write(&error_code).ok(); } // We call `_exit` instead of `exit` to avoid flushing the parent's IO streams by accident. _exit(1); } let err = command.exec(); dev_warn!("failed to execute command: {err}"); // If `exec` returns, it means that executing the command failed. Send the error to the // monitor using the pipe. if let Some(error_code) = err.raw_os_error() { errpipe_tx.write(&error_code).ok(); } // We call `_exit` instead of `exit` to avoid flushing the parent's IO streams by accident. _exit(1); }; dev_info!("executed command with pid {command_pid}"); let mut registry = EventRegistry::new(); let mut closure = ExecClosure::new(command_pid, sudo_pid, errpipe_rx, &mut registry)?; // Restore the signal mask now that the handlers have been setup. if let Some(set) = original_set { if let Err(err) = set.set_mask() { dev_warn!("cannot restore signal mask: {err}"); } } let command_exit_reason = match registry.event_loop(&mut closure) { StopReason::Break(err) => return Err(err), StopReason::Exit(reason) => reason, }; // Restore signal handlers drop(closure.signal_handlers); Ok(command_exit_reason) } struct ExecClosure { command_pid: Option, sudo_pid: ProcessId, parent_pgrp: ProcessId, errpipe_rx: BinPipe, signal_stream: &'static SignalStream, signal_handlers: [SignalHandler; ExecClosure::SIGNALS.len()], } impl ExecClosure { const SIGNALS: [SignalNumber; 12] = [ SIGINT, SIGQUIT, SIGTSTP, SIGTERM, SIGHUP, SIGALRM, SIGPIPE, SIGUSR1, SIGUSR2, SIGCHLD, SIGCONT, SIGWINCH, ]; fn new( command_pid: ProcessId, sudo_pid: ProcessId, errpipe_rx: BinPipe, registry: &mut EventRegistry, ) -> io::Result { registry.register_event(&errpipe_rx, PollEvent::Readable, |_| ExecEvent::ErrPipe); let signal_stream = SignalStream::init()?; registry.register_event(signal_stream, PollEvent::Readable, |_| ExecEvent::Signal); let signal_handlers = register_handlers(Self::SIGNALS)?; Ok(Self { command_pid: Some(command_pid), errpipe_rx, sudo_pid, parent_pgrp: getpgrp(), signal_stream, signal_handlers, }) } /// Decides if the signal sent by the process with `signaler_pid` PID is self-terminating. /// /// A signal is self-terminating if `signaler_pid`: /// - is the same PID of the command, or /// - is in the process group of the command and either sudo or the command is the leader. fn is_self_terminating(&self, signaler_pid: ProcessId) -> bool { if signaler_pid.is_valid() { if Some(signaler_pid) == self.command_pid { return true; } if let Ok(signaler_pgrp) = getpgid(signaler_pid) { if Some(signaler_pgrp) == self.command_pid || signaler_pgrp == self.sudo_pid { return true; } } } false } /// Suspend the main process. fn suspend_parent(&self, signal: SignalNumber) { let mut opt_tty = UserTerm::open().ok(); let mut opt_pgrp = None; if let Some(tty) = opt_tty.as_ref() { if let Ok(saved_pgrp) = tty.tcgetpgrp() { // Save the terminal's foreground process group so we can restore it after resuming // if needed. opt_pgrp = Some(saved_pgrp); } else { opt_tty.take(); } } if let Some(saved_pgrp) = opt_pgrp { // This means that the command was stopped trying to access the terminal. If the // terminal has a different foreground process group and we own the terminal, we give // it to the command and let it continue. if let SIGTTOU | SIGTTIN = signal { if saved_pgrp == self.parent_pgrp { if let Some(command_pgrp) = self.command_pid.and_then(|pid| getpgid(pid).ok()) { if command_pgrp != self.parent_pgrp && opt_tty .as_ref() .is_some_and(|tty| tty.tcsetpgrp_nobg(command_pgrp).is_ok()) { if let Err(err) = killpg(command_pgrp, SIGCONT) { dev_warn!("cannot send SIGCONT to command ({command_pgrp}): {err}"); } return; } } } } } let sigtstp_handler = if signal == SIGTSTP { SignalHandler::register(signal, SignalHandlerBehavior::Default) .map_err(|err| dev_warn!("cannot set handler for {}: {err}", signal_fmt(signal))) .ok() } else { None }; if let Err(err) = kill(self.sudo_pid, signal) { dev_warn!( "cannot send {} to {} (sudo): {err}", signal_fmt(signal), self.sudo_pid ); } drop(sigtstp_handler); if let Some(saved_pgrp) = opt_pgrp { // Restore the foreground process group after resuming. if saved_pgrp != self.parent_pgrp { if let Some(tty) = opt_tty { tty.tcsetpgrp_nobg(saved_pgrp).ok(); } } } } fn on_signal(&mut self, registry: &mut EventRegistry) { let info = match self.signal_stream.recv() { Ok(info) => info, Err(err) => { dev_error!("sudo could not receive signal: {err}"); return; } }; dev_info!("received{}", info); let Some(command_pid) = self.command_pid else { dev_info!("command was terminated, ignoring signal"); return; }; match info.signal() { SIGCHLD => handle_sigchld(self, registry, "command", command_pid), signal => { // FIXME: we should handle SIGWINCH here if we want to support I/O plugins that // react on window change events. if let Some(pid) = info.signaler_pid() { if self.is_self_terminating(pid) { // Skip the signal if it was sent by the user and it is self-terminating. return; } } if signal == SIGALRM { terminate_process(command_pid, false); } else { kill(command_pid, signal).ok(); } } } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ExecEvent { Signal, ErrPipe, } impl Process for ExecClosure { type Event = ExecEvent; type Break = io::Error; type Exit = ExitReason; fn on_event(&mut self, event: Self::Event, registry: &mut EventRegistry) { match event { ExecEvent::Signal => self.on_signal(registry), ExecEvent::ErrPipe => { match self.errpipe_rx.read() { Err(err) if was_interrupted(&err) => { /* Retry later */ } Err(err) => registry.set_break(err), Ok(error_code) => { // Received error code from the command, forward it to the parent. registry.set_break(io::Error::from_raw_os_error(error_code)); } } } } } } impl HandleSigchld for ExecClosure { const OPTIONS: WaitOptions = WaitOptions::new().all().untraced().no_hang(); fn on_exit(&mut self, exit_code: c_int, registry: &mut EventRegistry) { registry.set_exit(ExitReason::Code(exit_code)); self.command_pid = None; } fn on_term(&mut self, signal: SignalNumber, registry: &mut EventRegistry) { registry.set_exit(ExitReason::Signal(signal)); self.command_pid = None; } fn on_stop(&mut self, signal: SignalNumber, _registry: &mut EventRegistry) { self.suspend_parent(signal); } } sudo-rs-0.2.5/src/exec/use_pty/backchannel.rs000064400000000000000000000221741046102023000172340ustar 00000000000000use std::{ ffi::c_int, io, mem::size_of, os::fd::{AsFd, BorrowedFd}, }; use crate::{ common::bin_serde::{BinPipe, DeSerialize}, exec::signal_fmt, system::interface::ProcessId, }; use super::CommandStatus; type Prefix = u8; type ParentData = c_int; type MonitorData = c_int; const PREFIX_LEN: usize = size_of::(); const PARENT_DATA_LEN: usize = size_of::(); const MONITOR_DATA_LEN: usize = size_of::(); pub(super) struct BackchannelPair { pub(super) parent: ParentBackchannel, pub(super) monitor: MonitorBackchannel, } impl BackchannelPair { pub(super) fn new() -> io::Result { let (sock1, sock2) = BinPipe::pair()?; #[cfg(debug_assertions)] { sock1.set_nonblocking(true)?; sock2.set_nonblocking(true)?; } Ok(Self { parent: ParentBackchannel { socket: sock1, #[cfg(debug_assertions)] nonblocking_asserts: false, }, monitor: MonitorBackchannel { socket: sock2, #[cfg(debug_assertions)] nonblocking_asserts: false, }, }) } } pub(super) enum ParentMessage { IoError(c_int), CommandStatus(CommandStatus), CommandPid(ProcessId), ShortRead, } impl ParentMessage { const LEN: usize = PREFIX_LEN + PARENT_DATA_LEN; const IO_ERROR: Prefix = 0; const CMD_STAT_EXIT: Prefix = 1; const CMD_STAT_TERM: Prefix = 2; const CMD_STAT_STOP: Prefix = 3; const CMD_PID: Prefix = 4; const SHORT_READ: Prefix = 5; fn from_parts(prefix: Prefix, data: ParentData) -> Self { match prefix { Self::IO_ERROR => Self::IoError(data), Self::CMD_STAT_EXIT => Self::CommandStatus(CommandStatus::Exit(data)), Self::CMD_STAT_TERM => Self::CommandStatus(CommandStatus::Term(data)), Self::CMD_STAT_STOP => Self::CommandStatus(CommandStatus::Stop(data)), Self::CMD_PID => Self::CommandPid(ProcessId::new(data)), Self::SHORT_READ => Self::ShortRead, _ => unreachable!(), } } fn to_parts(&self) -> (Prefix, ParentData) { let prefix = match self { ParentMessage::IoError(_) => Self::IO_ERROR, ParentMessage::CommandStatus(CommandStatus::Exit(_)) => Self::CMD_STAT_EXIT, ParentMessage::CommandStatus(CommandStatus::Term(_)) => Self::CMD_STAT_TERM, ParentMessage::CommandStatus(CommandStatus::Stop(_)) => Self::CMD_STAT_STOP, ParentMessage::CommandPid(_) => Self::CMD_PID, ParentMessage::ShortRead => Self::SHORT_READ, }; let data = match self { ParentMessage::IoError(data) => *data, ParentMessage::CommandPid(data) => data.inner(), ParentMessage::CommandStatus(status) => match status { CommandStatus::Exit(data) | CommandStatus::Term(data) | CommandStatus::Stop(data) => *data, }, ParentMessage::ShortRead => 0, }; (prefix, data) } } impl TryFrom for ParentMessage { type Error = io::Error; fn try_from(err: io::Error) -> Result { err.raw_os_error() .map(Self::IoError) .or_else(|| (err.kind() == io::ErrorKind::UnexpectedEof).then_some(Self::ShortRead)) .ok_or(err) } } impl From for ParentMessage { fn from(status: CommandStatus) -> Self { Self::CommandStatus(status) } } impl DeSerialize for ParentMessage { type Bytes = [u8; ParentMessage::LEN]; fn serialize(&self) -> Self::Bytes { let mut buf = [0; ParentMessage::LEN]; let (prefix_buf, data_buf) = buf.split_at_mut(PREFIX_LEN); let (prefix, data) = self.to_parts(); prefix_buf.copy_from_slice(&prefix.to_ne_bytes()); data_buf.copy_from_slice(&data.to_ne_bytes()); buf } fn deserialize(buf: Self::Bytes) -> Self { let (prefix_buf, data_buf) = buf.split_at(PREFIX_LEN); let prefix = Prefix::from_ne_bytes(prefix_buf.try_into().unwrap()); let data = MonitorData::from_ne_bytes(data_buf.try_into().unwrap()); ParentMessage::from_parts(prefix, data) } } /// A socket use for commmunication between the monitor and the parent process. pub(super) struct ParentBackchannel { socket: BinPipe, #[cfg(debug_assertions)] nonblocking_asserts: bool, } impl ParentBackchannel { /// Send a [`MonitorMessage`]. /// /// Calling this method will block until the socket is ready for writing. #[track_caller] pub(super) fn send(&mut self, event: &MonitorMessage) -> io::Result<()> { self.socket.write(event).map_err(|err| { #[cfg(debug_assertions)] if self.nonblocking_asserts { assert_ne!(err.kind(), io::ErrorKind::WouldBlock); } err }) } /// Receive a [`ParentMessage`]. /// /// Calling this method will block until the socket is ready for reading. #[track_caller] pub(super) fn recv(&mut self) -> io::Result { let msg = self.socket.read().map_err(|err| { #[cfg(debug_assertions)] if self.nonblocking_asserts { assert_ne!(err.kind(), io::ErrorKind::WouldBlock); } err })?; Ok(msg) } pub(super) fn set_nonblocking_asserts(&mut self, _doit: bool) { #[cfg(debug_assertions)] { self.nonblocking_asserts = _doit; } } } impl AsFd for ParentBackchannel { fn as_fd(&self) -> BorrowedFd { self.socket.as_fd() } } /// Different messages exchanged between the monitor and the parent process using a [`ParentBackchannel`]. #[derive(PartialEq, Eq)] pub(super) enum MonitorMessage { ExecCommand, Signal(c_int), } impl MonitorMessage { const LEN: usize = PREFIX_LEN + MONITOR_DATA_LEN; const EXEC_CMD: Prefix = 0; const SIGNAL: Prefix = 1; fn from_parts(prefix: Prefix, data: MonitorData) -> Self { match prefix { Self::EXEC_CMD => Self::ExecCommand, Self::SIGNAL => Self::Signal(data), _ => unreachable!(), } } fn to_parts(&self) -> (Prefix, MonitorData) { let prefix = match self { MonitorMessage::ExecCommand => Self::EXEC_CMD, MonitorMessage::Signal(_) => Self::SIGNAL, }; let data = match self { MonitorMessage::ExecCommand => 0, MonitorMessage::Signal(data) => *data, }; (prefix, data) } } impl std::fmt::Debug for MonitorMessage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::ExecCommand => "ExecCommand".fmt(f), &Self::Signal(signal) => write!(f, "Signal({})", signal_fmt(signal)), } } } impl DeSerialize for MonitorMessage { type Bytes = [u8; MonitorMessage::LEN]; fn serialize(&self) -> Self::Bytes { let mut buf = [0; MonitorMessage::LEN]; let (prefix_buf, data_buf) = buf.split_at_mut(PREFIX_LEN); let (prefix, data) = self.to_parts(); prefix_buf.copy_from_slice(&prefix.to_ne_bytes()); data_buf.copy_from_slice(&data.to_ne_bytes()); buf } fn deserialize(bytes: Self::Bytes) -> Self { let (prefix_buf, data_buf) = bytes.split_at(PREFIX_LEN); let prefix = Prefix::from_ne_bytes(prefix_buf.try_into().unwrap()); let data = MonitorData::from_ne_bytes(data_buf.try_into().unwrap()); MonitorMessage::from_parts(prefix, data) } } /// A socket use for commmunication between the monitor and the parent process. pub(super) struct MonitorBackchannel { socket: BinPipe, #[cfg(debug_assertions)] nonblocking_asserts: bool, } impl MonitorBackchannel { /// Send a [`ParentMessage`]. /// /// Calling this method will block until the socket is ready for writing. #[track_caller] pub(super) fn send(&mut self, event: &ParentMessage) -> io::Result<()> { self.socket.write(event).map_err(|err| { #[cfg(debug_assertions)] if self.nonblocking_asserts { assert_ne!(err.kind(), io::ErrorKind::WouldBlock); } err }) } /// Receive a [`MonitorMessage`]. /// /// Calling this method will block until the socket is ready for reading. #[track_caller] pub(super) fn recv(&mut self) -> io::Result { let msg = self.socket.read().map_err(|err| { #[cfg(debug_assertions)] if self.nonblocking_asserts { assert_ne!(err.kind(), io::ErrorKind::WouldBlock); } err })?; Ok(msg) } pub(super) fn set_nonblocking_assertions(&mut self, _doit: bool) { #[cfg(debug_assertions)] { self.nonblocking_asserts = _doit; } } } impl AsFd for MonitorBackchannel { fn as_fd(&self) -> BorrowedFd { self.socket.as_fd() } } sudo-rs-0.2.5/src/exec/use_pty/mod.rs000064400000000000000000000006361046102023000155610ustar 00000000000000mod backchannel; mod monitor; mod parent; mod pipe; use std::ffi::c_int; pub(super) use parent::exec_pty; use crate::system::signal::SignalNumber; /// Continue running in the foreground pub(super) const SIGCONT_FG: SignalNumber = -2; /// Continue running in the background pub(super) const SIGCONT_BG: SignalNumber = -3; enum CommandStatus { Exit(c_int), Term(SignalNumber), Stop(SignalNumber), } sudo-rs-0.2.5/src/exec/use_pty/monitor.rs000064400000000000000000000411331046102023000164660ustar 00000000000000use std::{convert::Infallible, ffi::c_int, io, os::unix::process::CommandExt, process::Command}; use crate::exec::{opt_fmt, signal_fmt}; use crate::system::signal::{ consts::*, register_handlers, SignalHandler, SignalHandlerBehavior, SignalNumber, SignalSet, SignalStream, }; use crate::{ common::bin_serde::BinPipe, exec::{ event::{EventRegistry, Process}, io_util::{retry_while_interrupted, was_interrupted}, use_pty::backchannel::{MonitorBackchannel, MonitorMessage, ParentMessage}, }, }; use crate::{ exec::{ event::{PollEvent, StopReason}, use_pty::{SIGCONT_BG, SIGCONT_FG}, }, log::{dev_error, dev_info, dev_warn}, system::FileCloser, }; use crate::{ exec::{handle_sigchld, terminate_process, HandleSigchld}, system::{ _exit, fork, getpgid, getpgrp, interface::ProcessId, kill, setpgid, setsid, term::{PtyFollower, Terminal}, wait::{Wait, WaitError, WaitOptions}, ForkResult, }, }; use super::CommandStatus; pub(super) fn exec_monitor( pty_follower: PtyFollower, command: Command, foreground: bool, backchannel: &mut MonitorBackchannel, mut file_closer: FileCloser, original_set: Option, ) -> io::Result { // SIGTTIN and SIGTTOU are ignored here but the docs state that it shouldn't // be possible to receive them in the first place. Investigate match SignalHandler::register(SIGTTIN, SignalHandlerBehavior::Ignore) { Ok(handler) => handler.forget(), Err(err) => dev_warn!("cannot set handler for SIGTTIN: {err}"), } match SignalHandler::register(SIGTTOU, SignalHandlerBehavior::Ignore) { Ok(handler) => handler.forget(), Err(err) => dev_warn!("cannot set handler for SIGTTOU: {err}"), } // Start a new terminal session with the monitor as the leader. setsid().map_err(|err| { dev_warn!("cannot start a new session: {err}"); err })?; // Set the follower side of the pty as the controlling terminal for the session. pty_follower.make_controlling_terminal().map_err(|err| { dev_warn!("cannot set the controlling terminal: {err}"); err })?; // Use a pipe to get the IO error if `exec_command` fails. let (errpipe_tx, errpipe_rx) = BinPipe::pair()?; // Don't close the error pipe as we need it to retrieve the error code if the command execution // fails. file_closer.except(&errpipe_tx); // Wait for the parent to give us green light before spawning the command. This avoids race // conditions when the command exits quickly. let event = retry_while_interrupted(|| backchannel.recv()).map_err(|err| { dev_warn!("cannot receive green light from parent: {err}"); err })?; // Given that `UnixStream` delivers messages in order it shouldn't be possible to // receive an event different to `ExecCommand` at the beginning. debug_assert_eq!(event, MonitorMessage::ExecCommand); // FIXME (ogsudo): Some extra config happens here if selinux is available. // SAFETY: There should be no other threads at this point. let ForkResult::Parent(command_pid) = unsafe { fork() }.map_err(|err| { dev_warn!("unable to fork command process: {err}"); err })? else { drop(errpipe_rx); match exec_command( command, foreground, pty_follower, file_closer, errpipe_tx, original_set, ) {} }; // Send the command's PID to the parent. if let Err(err) = backchannel.send(&ParentMessage::CommandPid(command_pid)) { dev_warn!("cannot send command PID to parent: {err}"); } let mut registry = EventRegistry::new(); let mut closure = MonitorClosure::new( command_pid, pty_follower, errpipe_rx, backchannel, &mut registry, )?; // Restore the signal mask now that the handlers have been setup. if let Some(set) = original_set { if let Err(err) = set.set_mask() { dev_warn!("cannot restore signal mask: {err}"); } } // Set the foreground group for the pty follower. if foreground { if let Err(err) = closure.pty_follower.tcsetpgrp(closure.command_pgrp) { dev_error!( "cannot set foreground progess group to {} (command): {err}", closure.command_pgrp ); } } // FIXME (ogsudo): Here's where the signal mask is removed because the handlers for the signals // have been setup after initializing the closure. // Start the event loop. let reason = registry.event_loop(&mut closure); // Terminate the command if it's not terminated. if let Some(command_pid) = closure.command_pid { terminate_process(command_pid, true); loop { match command_pid.wait(WaitOptions::new()) { Err(WaitError::Io(err)) if was_interrupted(&err) => {} _ => break, } } } // Take the controlling tty so the command's children don't receive SIGHUP when we exit. if let Err(err) = closure.pty_follower.tcsetpgrp(closure.monitor_pgrp) { dev_error!( "cannot set foreground process group to {} (monitor): {err}", closure.monitor_pgrp ); } // Disable nonblocking assetions as we will not poll the backchannel anymore. closure.backchannel.set_nonblocking_assertions(false); match reason { StopReason::Break(err) => match err.try_into() { Ok(msg) => { if let Err(err) = closure.backchannel.send(&msg) { dev_warn!("cannot send message over backchannel: {err}") } } Err(err) => { dev_warn!("socket error `{err:?}` cannot be converted to a message") } }, StopReason::Exit(command_status) => { if let Err(err) = closure.backchannel.send(&command_status.into()) { dev_warn!("cannot send message over backchannel: {err}") } } } // FIXME (ogsudo): The tty is restored here if selinux is available. // We call `_exit` instead of `exit` to avoid flushing the parent's IO streams by accident. _exit(1); } fn exec_command( mut command: Command, foreground: bool, pty_follower: PtyFollower, file_closer: FileCloser, mut errpipe_tx: BinPipe, original_set: Option, ) -> ! { // FIXME (ogsudo): Do any additional configuration that needs to be run after `fork` but before `exec` let command_pid = ProcessId::new(std::process::id() as i32); setpgid(ProcessId::new(0), command_pid).ok(); // Wait for the monitor to set us as the foreground group for the pty if we are in the // foreground. if foreground { while !pty_follower.tcgetpgrp().is_ok_and(|pid| pid == command_pid) { std::thread::yield_now(); } } // Done with the pty follower. drop(pty_follower); // Restore the signal mask now that the handlers have been setup. if let Some(set) = original_set { if let Err(err) = set.set_mask() { dev_warn!("cannot restore signal mask: {err}"); } } // SAFETY: We immediately exec after this call and if the exec fails we only access stderr // and errpipe before exiting without running atexit handlers using _exit if let Err(err) = unsafe { file_closer.close_the_universe() } { dev_warn!("failed to close the universe: {err}"); // Send the error to the monitor using the pipe. if let Some(error_code) = err.raw_os_error() { errpipe_tx.write(&error_code).ok(); } // We call `_exit` instead of `exit` to avoid flushing the parent's IO streams by accident. _exit(1); } let err = command.exec(); dev_warn!("failed to execute command: {err}"); // If `exec_command` returns, it means that executing the command failed. Send the error to // the monitor using the pipe. if let Some(error_code) = err.raw_os_error() { errpipe_tx.write(&error_code).ok(); } // We call `_exit` instead of `exit` to avoid flushing the parent's IO streams by accident. _exit(1); } struct MonitorClosure<'a> { /// The command PID. /// /// This is `Some` iff the process is still running. command_pid: Option, command_pgrp: ProcessId, monitor_pgrp: ProcessId, pty_follower: PtyFollower, errpipe_rx: BinPipe, backchannel: &'a mut MonitorBackchannel, signal_stream: &'static SignalStream, _signal_handlers: [SignalHandler; MonitorClosure::SIGNALS.len()], } impl<'a> MonitorClosure<'a> { const SIGNALS: [SignalNumber; 8] = [ SIGINT, SIGQUIT, SIGTSTP, SIGTERM, SIGHUP, SIGUSR1, SIGUSR2, SIGCHLD, ]; fn new( command_pid: ProcessId, pty_follower: PtyFollower, errpipe_rx: BinPipe, backchannel: &'a mut MonitorBackchannel, registry: &mut EventRegistry, ) -> io::Result { // Store the pgid of the monitor. let monitor_pgrp = getpgrp(); // Register the callback to receive the IO error if the command fails to execute. registry.register_event(&errpipe_rx, PollEvent::Readable, |_| { MonitorEvent::ReadableErrPipe }); // Enable nonblocking assertions as we will poll this inside the event loop. backchannel.set_nonblocking_assertions(true); // Register the callback to receive events from the backchannel registry.register_event(backchannel, PollEvent::Readable, |_| { MonitorEvent::ReadableBackchannel }); let signal_stream = SignalStream::init()?; registry.register_event(signal_stream, PollEvent::Readable, |_| MonitorEvent::Signal); let signal_handlers = register_handlers(Self::SIGNALS)?; // Put the command in its own process group. let command_pgrp = command_pid; if let Err(err) = setpgid(command_pid, command_pgrp) { dev_warn!("cannot set process group ID for process: {err}"); }; Ok(Self { command_pid: Some(command_pid), command_pgrp, monitor_pgrp, pty_follower, errpipe_rx, backchannel, signal_stream, _signal_handlers: signal_handlers, }) } /// Based on `mon_backchannel_cb` fn read_backchannel(&mut self, registry: &mut EventRegistry) { match self.backchannel.recv() { Err(err) => { // We can try later if receive is interrupted. if err.kind() != io::ErrorKind::Interrupted { // There's something wrong with the backchannel, break the event loop. dev_warn!("cannot read from backchannel: {err}"); registry.set_break(err); } } Ok(event) => { match event { // We shouldn't receive this event more than once. MonitorMessage::ExecCommand => unreachable!(), // Forward signal to the command. MonitorMessage::Signal(signal) => { if let Some(command_pid) = self.command_pid { self.send_signal(signal, command_pid, true) } } } } } } fn read_errpipe(&mut self, registry: &mut EventRegistry) { match self.errpipe_rx.read() { Err(err) if was_interrupted(&err) => { /* Retry later */ } Err(err) => registry.set_break(err), Ok(error_code) => { // Received error code from the command, forward it to the parent. self.backchannel .send(&ParentMessage::IoError(error_code)) .ok(); } } } /// Send a signal to the command. fn send_signal(&self, signal: c_int, command_pid: ProcessId, from_parent: bool) { dev_info!( "sending {}{} to command", signal_fmt(signal), opt_fmt(from_parent, " from parent"), ); // FIXME: We should call `killpg` instead of `kill`. match signal { SIGALRM => { terminate_process(command_pid, false); } SIGCONT_FG => { // Continue with the command as the foreground process group if let Err(err) = self.pty_follower.tcsetpgrp(self.command_pgrp) { dev_error!( "cannot set the foreground process group to {} (command): {err}", self.command_pgrp ); } kill(command_pid, SIGCONT).ok(); } SIGCONT_BG => { // Continue with the monitor as the foreground process group if let Err(err) = self.pty_follower.tcsetpgrp(self.monitor_pgrp) { dev_error!( "cannot set the foreground process group to {} (monitor): {err}", self.monitor_pgrp ); } kill(command_pid, SIGCONT).ok(); } signal => { // Send the signal to the command. kill(command_pid, signal).ok(); } } } fn on_signal(&mut self, registry: &mut EventRegistry) { let info = match self.signal_stream.recv() { Ok(info) => info, Err(err) => { dev_error!("could not receive signal: {err}"); return; } }; dev_info!("monitor received{}", info); // Don't do anything if the command has terminated already let Some(command_pid) = self.command_pid else { dev_info!("command was terminated, ignoring signal"); return; }; match info.signal() { SIGCHLD => handle_sigchld(self, registry, "command", command_pid), signal => { if let Some(pid) = info.signaler_pid() { if is_self_terminating(pid, command_pid, self.command_pgrp) { // Skip the signal if it was sent by the user and it is self-terminating. return; } } self.send_signal(signal, command_pid, false) } } } } /// Decides if the signal sent by the process with `signaler_pid` PID is self-terminating. /// /// A signal is self-terminating if `signaler_pid`: /// - is the same PID of the command, or /// - is in the process group of the command and the command is the leader. fn is_self_terminating( signaler_pid: ProcessId, command_pid: ProcessId, command_pgrp: ProcessId, ) -> bool { if signaler_pid.is_valid() { if signaler_pid == command_pid { return true; } if let Ok(grp_leader) = getpgid(signaler_pid) { if grp_leader == command_pgrp { return true; } } } false } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum MonitorEvent { Signal, ReadableErrPipe, ReadableBackchannel, } impl Process for MonitorClosure<'_> { type Event = MonitorEvent; type Break = io::Error; type Exit = CommandStatus; fn on_event(&mut self, event: Self::Event, registry: &mut EventRegistry) { match event { MonitorEvent::Signal => self.on_signal(registry), MonitorEvent::ReadableErrPipe => self.read_errpipe(registry), MonitorEvent::ReadableBackchannel => self.read_backchannel(registry), } } } impl HandleSigchld for MonitorClosure<'_> { const OPTIONS: WaitOptions = WaitOptions::new().untraced().no_hang(); fn on_exit(&mut self, exit_code: c_int, registry: &mut EventRegistry) { registry.set_exit(CommandStatus::Exit(exit_code)); self.command_pid = None; } fn on_term(&mut self, signal: c_int, registry: &mut EventRegistry) { registry.set_exit(CommandStatus::Term(signal)); self.command_pid = None; } fn on_stop(&mut self, signal: c_int, _registry: &mut EventRegistry) { // Save the foreground process group ID so we can restore it later. if let Ok(pgrp) = self.pty_follower.tcgetpgrp() { if pgrp != self.monitor_pgrp { self.command_pgrp = pgrp; } } self.backchannel .send(&CommandStatus::Stop(signal).into()) .ok(); } } sudo-rs-0.2.5/src/exec/use_pty/parent.rs000064400000000000000000000643331046102023000162770ustar 00000000000000use std::collections::VecDeque; use std::ffi::c_int; use std::io; use std::process::{Command, Stdio}; use crate::exec::event::{EventHandle, EventRegistry, PollEvent, Process, StopReason}; use crate::exec::use_pty::monitor::exec_monitor; use crate::exec::use_pty::SIGCONT_FG; use crate::exec::{cond_fmt, handle_sigchld, signal_fmt, terminate_process, HandleSigchld}; use crate::exec::{ io_util::retry_while_interrupted, use_pty::backchannel::{BackchannelPair, MonitorMessage, ParentBackchannel, ParentMessage}, ExitReason, }; use crate::log::{dev_error, dev_info, dev_warn}; use crate::system::signal::{ consts::*, register_handlers, SignalHandler, SignalHandlerBehavior, SignalNumber, SignalSet, SignalStream, }; use crate::system::term::{Pty, PtyFollower, PtyLeader, TermSize, Terminal, UserTerm}; use crate::system::wait::WaitOptions; use crate::system::{ chown, fork, getpgrp, kill, killpg, FileCloser, ForkResult, Group, User, _exit, }; use crate::system::{getpgid, interface::ProcessId}; use super::pipe::Pipe; use super::{CommandStatus, SIGCONT_BG}; pub(in crate::exec) fn exec_pty( sudo_pid: ProcessId, mut command: Command, user_tty: UserTerm, ) -> io::Result { // Allocate a pseudoterminal. let pty = get_pty()?; // Create backchannels to communicate with the monitor. let mut backchannels = BackchannelPair::new().map_err(|err| { dev_error!("cannot create backchannel: {err}"); err })?; // We don't want to receive SIGTTIN/SIGTTOU match SignalHandler::register(SIGTTIN, SignalHandlerBehavior::Ignore) { Ok(handler) => handler.forget(), Err(err) => dev_warn!("cannot set handler for SIGTTIN: {err}"), } match SignalHandler::register(SIGTTOU, SignalHandlerBehavior::Ignore) { Ok(handler) => handler.forget(), Err(err) => dev_warn!("cannot set handler for SIGTTOU: {err}"), } // FIXME (ogsudo): Initialize the policy plugin's session here by calling // `policy_init_session`. // FIXME (ogsudo): initializes ttyblock sigset here by calling `init_ttyblock` // Fetch the parent process group so we can signals to it. let parent_pgrp = getpgrp(); let mut file_closer = FileCloser::new(); // Set all the IO streams for the command to the follower side of the pty. let mut clone_follower = || -> io::Result { let follower = pty.follower.try_clone().map_err(|err| { dev_error!("cannot clone pty follower: {err}"); err })?; // Don't close these as we will need them so they are dupped inside `Command::exec`. file_closer.except(&follower); Ok(follower) }; command.stdin(clone_follower()?); command.stdout(clone_follower()?); command.stderr(clone_follower()?); let mut registry = EventRegistry::::new(); // Pipe data between both terminals let mut tty_pipe = Pipe::new( user_tty, pty.leader, &mut registry, ParentEvent::Tty, ParentEvent::Pty, ); let user_tty = tty_pipe.left_mut(); // Check if we are the foreground process let mut foreground = user_tty .tcgetpgrp() .is_ok_and(|tty_pgrp| tty_pgrp == parent_pgrp); dev_info!( "sudo is runnning in the {}", cond_fmt(foreground, "foreground", "background") ); // FIXME: maybe all these boolean flags should be on a dedicated type. // Whether we're running on a pipeline let mut pipeline = false; // Whether the command should be executed in the background (this is not the `-b` flag) let mut exec_bg = false; // Whether the user's terminal is in raw mode or not. let mut term_raw = false; // Check if we are part of a pipeline. // FIXME: Here's where we should intercept the IO streams if we want to implement IO logging. // FIXME: ogsudo creates pipes for the IO streams and uses events to read from the strams to // the pipes. Investigate why. if !io::stdin().is_terminal() { dev_info!("stdin is not a terminal, command will inherit it"); pipeline = true; command.stdin(Stdio::inherit()); if foreground && parent_pgrp != sudo_pid { // If sudo is not the process group leader and stdin is not a terminal we might be // running as a background job via a shell script. Starting in the foreground would // change the terminal mode. exec_bg = true; } } if !io::stdout().is_terminal() { dev_info!("stdout is not a terminal, command will inherit it"); pipeline = true; command.stdout(Stdio::inherit()); } if !io::stderr().is_terminal() { dev_info!("stderr is not a terminal, command will inherit it"); command.stderr(Stdio::inherit()); } // Copy terminal settings from `/dev/tty` to the pty. if let Err(err) = user_tty.copy_to(&pty.follower) { dev_error!("cannot copy terminal settings to pty: {err}"); foreground = false; } // Start in raw mode unless we're part of a pipeline or backgrounded. if foreground && !pipeline && !exec_bg { // Clearer this way that set_raw_mode only conditionally runs #[allow(clippy::collapsible_if)] if user_tty.set_raw_mode(false).is_ok() { term_raw = true; } } let tty_size = tty_pipe.left().get_size().map_err(|err| { dev_error!("cannot get terminal size: {err}"); err })?; // Block all the signals until we are done setting up the signal handlers so we don't miss // SIGCHLD. let original_set = match SignalSet::full().and_then(|set| set.block()) { Ok(original_set) => Some(original_set), Err(err) => { dev_warn!("cannot block signals: {err}"); None } }; // SAFETY: There should be no other threads at this point. let ForkResult::Parent(monitor_pid) = (unsafe { fork() }).map_err(|err| { dev_error!("cannot fork monitor process: {err}"); err })? else { // Close the file descriptors that we don't access drop(tty_pipe); drop(backchannels.parent); // If `exec_monitor` returns, it means we failed to execute the command somehow. match exec_monitor( pty.follower, command, foreground && !pipeline && !exec_bg, &mut backchannels.monitor, file_closer, original_set, ) { Ok(exec_output) => match exec_output {}, Err(err) => { // Disable nonblocking assertions as we will not poll the backchannel anymore. backchannels.monitor.set_nonblocking_assertions(true); match err.try_into() { Ok(msg) => { if let Err(err) = backchannels.monitor.send(&msg) { dev_error!("cannot send status to parent: {err}"); } } Err(err) => { dev_warn!("execution error {err:?} cannot be send over backchannel") } } } } // We call `_exit` instead of `exit` to avoid flushing the parent's IO streams by accident. _exit(1); }; // Close the file descriptors that we don't access drop(pty.follower); drop(backchannels.monitor); // Send green light to the monitor after closing the follower. retry_while_interrupted(|| backchannels.parent.send(&MonitorMessage::ExecCommand)).map_err( |err| { dev_error!("cannot send green light to monitor: {err}"); err }, )?; let mut closure = ParentClosure::new( monitor_pid, sudo_pid, parent_pgrp, backchannels.parent, tty_pipe, tty_size, foreground, term_raw, &mut registry, )?; // Restore the signal mask now that the handlers have been setup. if let Some(set) = original_set { if let Err(err) = set.set_mask() { dev_warn!("cannot restore signal mask: {err}"); } } let exit_reason = closure.run(registry); // FIXME (ogsudo): Retry if `/dev/tty` is revoked. // Flush the terminal closure.tty_pipe.flush_left().ok(); // Restore the terminal settings if closure.term_raw { // Only restore the terminal if sudo is the foreground process. if let Ok(pgrp) = closure.tty_pipe.left().tcgetpgrp() { if pgrp == closure.parent_pgrp { match closure.tty_pipe.left_mut().restore(false) { Ok(()) => closure.term_raw = false, Err(err) => dev_warn!("cannot restore terminal settings: {err}"), } } } } // Restore signal handlers drop(closure.signal_handlers); exit_reason } fn get_pty() -> io::Result { let tty_gid = Group::from_name(cstr!("tty")) .unwrap_or(None) .map(|group| group.gid); let pty = Pty::open().map_err(|err| { dev_error!("cannot allocate pty: {err}"); io::Error::new(io::ErrorKind::NotFound, "unable to open pty") })?; let euid = User::effective_uid(); let gid = tty_gid.unwrap_or(User::effective_gid()); chown(&pty.path, euid, gid).map_err(|err| { dev_error!("cannot change owner for pty: {err}"); err })?; Ok(pty) } struct ParentClosure { // The monitor PID. // /// This is `Some` iff the process is still running. monitor_pid: Option, sudo_pid: ProcessId, parent_pgrp: ProcessId, command_pid: Option, tty_pipe: Pipe, tty_size: TermSize, foreground: bool, term_raw: bool, backchannel: ParentBackchannel, message_queue: VecDeque, backchannel_write_handle: EventHandle, signal_stream: &'static SignalStream, signal_handlers: [SignalHandler; ParentClosure::SIGNALS.len()], } impl ParentClosure { const SIGNALS: [SignalNumber; 11] = [ SIGINT, SIGQUIT, SIGTSTP, SIGTERM, SIGHUP, SIGALRM, SIGUSR1, SIGUSR2, SIGCHLD, SIGCONT, SIGWINCH, ]; #[allow(clippy::too_many_arguments)] fn new( monitor_pid: ProcessId, sudo_pid: ProcessId, parent_pgrp: ProcessId, mut backchannel: ParentBackchannel, tty_pipe: Pipe, tty_size: TermSize, foreground: bool, term_raw: bool, registry: &mut EventRegistry, ) -> io::Result { // Enable nonblocking assertions as we will poll this inside the event loop. backchannel.set_nonblocking_asserts(true); registry.register_event(&backchannel, PollEvent::Readable, ParentEvent::Backchannel); let mut backchannel_write_handle = registry.register_event(&backchannel, PollEvent::Writable, ParentEvent::Backchannel); // Ignore write events on the backchannel as we don't want to poll it for writing if there // are no messages in the queue. backchannel_write_handle.ignore(registry); let signal_stream = SignalStream::init()?; registry.register_event(signal_stream, PollEvent::Readable, |_| ParentEvent::Signal); let signal_handlers = register_handlers(Self::SIGNALS)?; Ok(Self { monitor_pid: Some(monitor_pid), sudo_pid, parent_pgrp, command_pid: None, tty_pipe, tty_size, foreground, term_raw, backchannel, message_queue: VecDeque::new(), backchannel_write_handle, signal_stream, signal_handlers, }) } fn run(&mut self, registry: EventRegistry) -> io::Result { match registry.event_loop(self) { StopReason::Break(err) | StopReason::Exit(ParentExit::Backchannel(err)) => Err(err), StopReason::Exit(ParentExit::Command(exit_reason)) => Ok(exit_reason), } } /// Read an event from the backchannel and return the event if it should break the event loop. fn on_message_received(&mut self, registry: &mut EventRegistry) { match self.backchannel.recv() { Err(err) => { match err.kind() { // If we get EOF the monitor exited or was killed io::ErrorKind::UnexpectedEof => { dev_info!("received EOF from backchannel"); registry.set_exit(err.into()); } // We can try later if receive is interrupted. io::ErrorKind::Interrupted => {} // Failed to read command status. This means that something is wrong with the socket // and we should stop. _ => { dev_error!("cannot receive message from backchannel: {err}"); if !registry.got_break() { registry.set_break(err); } } } } Ok(event) => { match event { // Received the PID of the command. This means that the command is already // executing. ParentMessage::CommandPid(pid) => { dev_info!("received command PID ({pid}) from monitor"); self.command_pid = pid.into(); } ParentMessage::CommandStatus(status) => { // The command terminated or the monitor was not able to spawn it. We should stop // either way. match status { CommandStatus::Exit(exit_code) => { dev_info!("command exited with status code {exit_code}"); registry.set_exit(ExitReason::Code(exit_code).into()); } CommandStatus::Term(signal) => { dev_info!("command was terminated by {}", signal_fmt(signal)); registry.set_exit(ExitReason::Signal(signal).into()); } CommandStatus::Stop(signal) => { dev_info!( "command was stopped by {}, suspending parent", signal_fmt(signal) ); // Suspend parent and tell monitor how to resume on return if let Some(signal) = self.suspend_pty(signal, registry) { self.schedule_signal(signal, registry); } self.tty_pipe.resume_events(registry); } } } ParentMessage::IoError(code) => { let err = io::Error::from_raw_os_error(code); dev_info!("received error ({code}) for monitor: {err}"); registry.set_break(err); } ParentMessage::ShortRead => { dev_info!("received short read error for monitor"); registry.set_break(io::ErrorKind::UnexpectedEof.into()); } } } } } /// Decides if the signal sent by the process with `signaler_pid` PID is self-terminating. /// /// A signal is self-terminating if `signaler_pid`: /// - is the same PID of the command, or /// - is in the process group of the command and either sudo or the command is the leader. fn is_self_terminating(&self, signaler_pid: ProcessId) -> bool { if signaler_pid.is_valid() { if Some(signaler_pid) == self.command_pid { return true; } if let Ok(signaler_pgrp) = getpgid(signaler_pid) { if Some(signaler_pgrp) == self.command_pid || signaler_pgrp == self.sudo_pid { return true; } } } false } /// Schedule sending a signal event to the monitor using the backchannel. /// /// The signal message will be sent once the backchannel is ready to be written. fn schedule_signal(&mut self, signal: c_int, registry: &mut EventRegistry) { dev_info!("scheduling message with {} for monitor", signal_fmt(signal)); self.message_queue.push_back(MonitorMessage::Signal(signal)); // Start polling the backchannel for writing if not already. self.backchannel_write_handle.resume(registry); } /// Send the first message in the event queue using the backchannel, if any. /// /// Calling this function will block until the backchannel can be written. fn check_message_queue(&mut self, registry: &mut EventRegistry) { if let Some(msg) = self.message_queue.front() { dev_info!("sending message {msg:?} to monitor over backchannel"); match self.backchannel.send(msg) { // The event was sent, remove it from the queue Ok(()) => { self.message_queue.pop_front().unwrap(); // Stop polling the backchannel for writing if the queue is empty. if self.message_queue.is_empty() { self.backchannel_write_handle.ignore(registry); } } Err(err) => { // We can try later if send is interrupted. if err.kind() != io::ErrorKind::Interrupted { // There's something wrong with the backchannel, break the event loop. dev_error!("cannot send via backchannel {err}"); registry.set_break(err); } } } } } /// Suspend sudo if the command is suspended. /// /// Return `SIGCONT_FG` or `SIGCONT_BG` to state whether the command should be resumend in the /// foreground or not. fn suspend_pty( &mut self, signal: SignalNumber, registry: &mut EventRegistry, ) -> Option { // Ignore `SIGCONT` while suspending to avoid resuming the terminal twice. let sigcont_handler = SignalHandler::register(SIGCONT, SignalHandlerBehavior::Ignore) .map_err(|err| dev_warn!("cannot set handler for SIGCONT: {err}")) .ok(); if let SIGTTOU | SIGTTIN = signal { // If sudo is already the foreground process we can resume the command in the // foreground. Otherwise, we have to suspend and resume later. if !self.foreground && self.check_foreground().is_err() { // User's tty was revoked. return None; } if self.foreground { dev_info!( "command received {}, parent running in the foreground", signal_fmt(signal) ); if !self.term_raw { if self.tty_pipe.left_mut().set_raw_mode(false).is_ok() { self.term_raw = true; } // Resume command in the foreground return Some(SIGCONT_FG); } } } // Stop polling the terminals. self.tty_pipe.ignore_events(registry); if self.term_raw { match self.tty_pipe.left_mut().restore(false) { Ok(()) => self.term_raw = false, Err(err) => dev_warn!("cannot restore terminal settings: {err}"), } } let signal_handler = if signal != SIGSTOP { SignalHandler::register(signal, SignalHandlerBehavior::Default) .map_err(|err| dev_warn!("cannot set handler for {}: {err}", signal_fmt(signal))) .ok() } else { None }; if self.parent_pgrp != self.sudo_pid && kill(self.parent_pgrp, 0).is_err() || killpg(self.parent_pgrp, signal).is_err() { dev_error!("no parent to suspend, terminating command"); if let Some(command_pid) = self.command_pid.take() { terminate_process(command_pid, true); } } drop(signal_handler); if self.command_pid.is_none() || self.resume_terminal().is_err() { return None; } let ret_signal = if self.term_raw { SIGCONT_FG } else { SIGCONT_BG }; // Restore the handler for SIGCONT. drop(sigcont_handler); Some(ret_signal) } /// Check whether we are part of the foreground process group and update the foreground flag. fn check_foreground(&mut self) -> io::Result<()> { let pgrp = self.tty_pipe.left().tcgetpgrp()?; self.foreground = pgrp == self.parent_pgrp; Ok(()) } /// Restore the terminal when sudo resumes after receving `SIGCONT`. fn resume_terminal(&mut self) -> io::Result<()> { self.check_foreground()?; // Update the pty settings based on the user's tty. self.tty_pipe .left() .copy_to(self.tty_pipe.right()) .map_err(|err| { dev_error!("cannot copy terminal settings to pty: {err}"); err })?; // FIXME: sync the terminal size here. dev_info!( "parent is in {} ({} -> {})", cond_fmt(self.foreground, "foreground", "background"), cond_fmt(self.term_raw, "raw", "cooked"), cond_fmt(self.foreground, "raw", "cooked"), ); if self.foreground { // We're in the foreground, set tty to raw mode. if self.tty_pipe.left_mut().set_raw_mode(false).is_ok() { self.term_raw = true; } } else { // We're in the background, cannot access tty. self.term_raw = false; } Ok(()) } fn on_signal(&mut self, registry: &mut EventRegistry) { let info = match self.signal_stream.recv() { Ok(info) => info, Err(err) => { dev_error!("parent could not receive signal: {err}"); return; } }; dev_info!("parent received{}", info); let Some(monitor_pid) = self.monitor_pid else { dev_info!("monitor was terminated, ignoring signal"); return; }; match info.signal() { SIGCHLD => handle_sigchld(self, registry, "monitor", monitor_pid), SIGCONT => { self.resume_terminal().ok(); } SIGWINCH => { if let Err(err) = self.handle_sigwinch() { dev_warn!("cannot resize terminal: {}", err); } } signal => { if let Some(pid) = info.signaler_pid() { if self.is_self_terminating(pid) { // Skip the signal if it was sent by the user and it is self-terminating. return; } } // FIXME: check `send_command_status` self.schedule_signal(signal, registry) } } } fn handle_sigwinch(&mut self) -> io::Result<()> { let new_size = self.tty_pipe.left().get_size()?; if new_size != self.tty_size { dev_info!("updating pty size from {} to {new_size}", self.tty_size); // Set the pty size. self.tty_pipe.right().set_size(&new_size)?; // Send SIGWINCH to the command. if let Some(command_pid) = self.command_pid { killpg(command_pid, SIGWINCH).ok(); } // Update the terminal size. self.tty_size = new_size; } Ok(()) } } enum ParentExit { /// Error while reading from the backchannel. Backchannel(io::Error), /// The command exited. Command(ExitReason), } impl From for ParentExit { fn from(err: io::Error) -> Self { Self::Backchannel(err) } } impl From for ParentExit { fn from(reason: ExitReason) -> Self { Self::Command(reason) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ParentEvent { Signal, Tty(PollEvent), Pty(PollEvent), Backchannel(PollEvent), } impl Process for ParentClosure { type Event = ParentEvent; type Break = io::Error; type Exit = ParentExit; fn on_event(&mut self, event: Self::Event, registry: &mut EventRegistry) { match event { ParentEvent::Signal => self.on_signal(registry), ParentEvent::Tty(poll_event) => { // Check if tty which existed is now gone. if self.tty_pipe.left().tcgetsid().is_err() { dev_warn!("tty gone (closed/detached), ignoring future events"); self.tty_pipe.ignore_events(registry); } else { self.tty_pipe.on_left_event(poll_event, registry).ok(); } } ParentEvent::Pty(poll_event) => { self.tty_pipe.on_right_event(poll_event, registry).ok(); } ParentEvent::Backchannel(poll_event) => match poll_event { PollEvent::Readable => self.on_message_received(registry), PollEvent::Writable => self.check_message_queue(registry), }, } } } impl HandleSigchld for ParentClosure { const OPTIONS: WaitOptions = WaitOptions::new().all().untraced().no_hang(); fn on_exit(&mut self, _exit_code: c_int, _registry: &mut EventRegistry) { self.monitor_pid = None; } fn on_term(&mut self, _signal: SignalNumber, _registry: &mut EventRegistry) { self.monitor_pid = None; } fn on_stop(&mut self, signal: SignalNumber, registry: &mut EventRegistry) { if let Some(signal) = self.suspend_pty(signal, registry) { self.schedule_signal(signal, registry); } self.tty_pipe.resume_events(registry); } } sudo-rs-0.2.5/src/exec/use_pty/pipe/mod.rs000064400000000000000000000136561046102023000165240ustar 00000000000000mod ring_buffer; use std::{ io::{self, Read, Write}, marker::PhantomData, os::fd::AsFd, }; use crate::exec::event::{EventHandle, EventRegistry, PollEvent, Process}; use self::ring_buffer::RingBuffer; // A pipe able to stream data bidirectionally between two read-write types. pub(super) struct Pipe { left: L, right: R, buffer_lr: Buffer, buffer_rl: Buffer, } impl Pipe { /// Create a new pipe between two read-write types and register them to be polled. pub fn new( left: L, right: R, registry: &mut EventRegistry, f_left: fn(PollEvent) -> T::Event, f_right: fn(PollEvent) -> T::Event, ) -> Self { Self { buffer_lr: Buffer::new( registry.register_event(&left, PollEvent::Readable, f_left), registry.register_event(&right, PollEvent::Writable, f_right), registry, ), buffer_rl: Buffer::new( registry.register_event(&right, PollEvent::Readable, f_right), registry.register_event(&left, PollEvent::Writable, f_left), registry, ), left, right, } } /// Get a reference to the left side of the pipe. pub(super) fn left(&self) -> &L { &self.left } /// Get a mutable reference to the left side of the pipe. pub(super) fn left_mut(&mut self) -> &mut L { &mut self.left } /// Get a reference to the right side of the pipe. pub(super) fn right(&self) -> &R { &self.right } /// Stop the poll events of this pipe. pub(super) fn ignore_events(&mut self, registry: &mut EventRegistry) { self.buffer_lr.read_handle.ignore(registry); self.buffer_lr.write_handle.ignore(registry); self.buffer_rl.read_handle.ignore(registry); self.buffer_rl.write_handle.ignore(registry); } /// Resume the poll events of this pipe pub(super) fn resume_events(&mut self, registry: &mut EventRegistry) { self.buffer_lr.read_handle.resume(registry); self.buffer_lr.write_handle.resume(registry); self.buffer_rl.read_handle.resume(registry); self.buffer_rl.write_handle.resume(registry); } /// Handle a poll event for the left side of the pipe. pub(super) fn on_left_event( &mut self, poll_event: PollEvent, registry: &mut EventRegistry, ) -> io::Result<()> { match poll_event { PollEvent::Readable => self.buffer_lr.read(&mut self.left, registry), PollEvent::Writable => self.buffer_rl.write(&mut self.left, registry), } } /// Handle a poll event for the right side of the pipe. pub(super) fn on_right_event( &mut self, poll_event: PollEvent, registry: &mut EventRegistry, ) -> io::Result<()> { match poll_event { PollEvent::Readable => self.buffer_rl.read(&mut self.right, registry), PollEvent::Writable => self.buffer_lr.write(&mut self.right, registry), } } /// Ensure that all the contents of the pipe's internal buffer are written to the left side. pub(super) fn flush_left(&mut self) -> io::Result<()> { self.buffer_rl.flush(&mut self.left) } } /// A buffer that stores the bytes read from `R` before they are written to `W`. struct Buffer { internal: RingBuffer, /// The handle for the event of the reader. read_handle: EventHandle, /// The handle for the event of the writer. write_handle: EventHandle, marker: PhantomData<(R, W)>, } impl Buffer { /// Create a new, empty buffer fn new( read_handle: EventHandle, mut write_handle: EventHandle, registry: &mut EventRegistry, ) -> Self { // The buffer is empty, don't write write_handle.ignore(registry); Self { internal: RingBuffer::new(), read_handle, write_handle, marker: PhantomData, } } /// Read bytes into the buffer. /// /// Calling this function will block until `read` is ready to be read. fn read( &mut self, read: &mut R, registry: &mut EventRegistry, ) -> io::Result<()> { // If the buffer is full, there is nothing to be read. if self.internal.is_full() { self.read_handle.ignore(registry); return Ok(()); } // Read bytes and insert them into the buffer. let inserted_len = self.internal.insert(read)?; // If we inserted something, the buffer is not empty anymore and we can resume writing. if inserted_len > 0 { self.write_handle.resume(registry); } Ok(()) } /// Write bytes from the buffer. /// /// Calling this function will block until `write` is ready to be written. fn write( &mut self, write: &mut W, registry: &mut EventRegistry, ) -> io::Result<()> { // If the buffer is empty, there is nothing to be written. if self.internal.is_empty() { self.write_handle.ignore(registry); return Ok(()); } // Remove bytes from the buffer and write them. let removed_len = self.internal.remove(write)?; // If we removed something, the buffer is not full anymore and we can resume reading. if removed_len > 0 { self.read_handle.resume(registry); } Ok(()) } /// Flush this buffer, ensuring that all the contents of its internal buffer are written. fn flush(&mut self, write: &mut W) -> io::Result<()> { // Remove bytes from the buffer and write them. self.internal.remove(write)?; write.flush() } } sudo-rs-0.2.5/src/exec/use_pty/pipe/ring_buffer.rs000064400000000000000000000251021046102023000202220ustar 00000000000000use std::io::{self, IoSlice, IoSliceMut, Read, Write}; pub(super) struct RingBuffer { storage: Box<[u8; Self::LEN]>, // The start index of the non-empty section of the buffer. start: usize, // The length of the non-empty section of the buffer. len: usize, } impl RingBuffer { /// The size of the internal storage of the ring buffer. const LEN: usize = 8 * 1024; /// Create a new, empty buffer. pub(super) fn new() -> Self { Self { storage: Box::new([0; Self::LEN]), start: 0, len: 0, } } pub(super) fn is_full(&self) -> bool { self.len == self.storage.len() } pub(super) fn insert(&mut self, read: &mut R) -> io::Result { let inserted_len = if self.is_empty() { // Case 1.1. The buffer is empty, meaning that there are two unfilled slices in // `storage`:`start..` and `..start`. let (second_slice, first_slice) = self.storage.split_at_mut(self.start); read.read_vectored(&mut [first_slice, second_slice].map(IoSliceMut::new))? } else { let &mut Self { start, len, .. } = self; let end = start + len; if end >= self.storage.len() { // Case 1.2. The buffer is not empty and the filled section wraps around `storage`. // Meaning that there is only one unfilled slice in `storage`: `end..start`. let end = end % self.storage.len(); read.read(&mut self.storage[end..start])? } else { // Case 1.3. The buffer is not empty and the filled section is a contiguous slice // of `storage`. Meaning that there are two unfilled slices in `storage`: `..start` // and `end..`. let (mid, first_slice) = self.storage.split_at_mut(end); let second_slice = &mut mid[..start]; read.read_vectored(&mut [first_slice, second_slice].map(IoSliceMut::new))? } }; self.len += inserted_len; debug_assert!(self.start < Self::LEN); debug_assert!(self.len <= Self::LEN); Ok(inserted_len) } pub(super) fn is_empty(&self) -> bool { self.len == 0 } pub(super) fn remove(&mut self, write: &mut W) -> io::Result { let removed_len = if self.is_full() { // Case 2.1. The buffer is full, meaning that there are two filled slices in `storage`: // `start..` and `..start`. let (second_slice, first_slice) = self.storage.split_at(self.start); write.write_vectored(&[first_slice, second_slice].map(IoSlice::new))? } else { let end = self.start + self.len; if end >= self.storage.len() { // Case 2.2. The buffer is not full and the filled section wraps around `storage`. // Meaning that there are two non-empty slices in `storage`: `start..` and `..end`. let end = end % self.storage.len(); let first_slice = &self.storage[self.start..]; let second_slice = &self.storage[..end]; write.write_vectored(&[first_slice, second_slice].map(IoSlice::new))? } else { // Case 2.3. The buffer is not full and the filled section is a contiguous slice // of `storage.` Meaning that there is only one filled slice in `storage`: // `start..end`. write.write(&self.storage[self.start..end])? } }; self.start += removed_len; self.start %= Self::LEN; self.len -= removed_len; debug_assert!(self.start < Self::LEN); debug_assert!(self.len <= Self::LEN); Ok(removed_len) } } #[cfg(test)] mod tests { use super::RingBuffer; #[test] fn empty_buffer_is_empty() { let buf = RingBuffer::new(); assert!(buf.is_empty()); } #[test] fn full_buffer_is_full() { let mut buf = RingBuffer::new(); let inserted_len = buf.insert(&mut [0x45; RingBuffer::LEN].as_slice()).unwrap(); assert_eq!(inserted_len, RingBuffer::LEN); assert!(buf.is_full()); } #[test] fn buffer_is_fifo() { let mut buf = RingBuffer::new(); let expected = (0..=u8::MAX).collect::>(); let inserted_len = buf.insert(&mut expected.as_slice()).unwrap(); assert_eq!(inserted_len, expected.len()); let mut found = vec![]; let removed_len = buf.remove(&mut found).unwrap(); assert_eq!(removed_len, expected.len()); assert_eq!(expected, found); } #[test] fn insert_into_empty_buffer_with_offset() { const HALF_LEN: usize = RingBuffer::LEN / 2; let mut buf = RingBuffer::new(); // This should leave the buffer empty but with the start field pointing to the middle of // the buffer. // ┌───────────────────┠// │ │ // └───────────────────┘ // â–² // │ // start buf.insert(&mut [0u8; HALF_LEN].as_slice()).unwrap(); buf.remove(&mut vec![]).unwrap(); assert_eq!(buf.start, HALF_LEN); assert_eq!(buf.len, 0); // Then we fill the first half of the buffer with ones and the second one with twos in a // single insertion. This tests case 1.1. // ┌─────────┬─────────┠// │ 2 │ 1 │ // └─────────┴─────────┘ // â–² // │ // start let mut expected = vec![1; HALF_LEN]; expected.extend_from_slice(&[2; HALF_LEN]); buf.insert(&mut expected.as_slice()).unwrap(); assert_eq!(buf.start, HALF_LEN); assert_eq!(buf.len, RingBuffer::LEN); // When we remove all the elements of the buffer we should find them in the same order we // inserted them. This tests case 2.1. let mut found = vec![]; let removed_len = buf.remove(&mut found).unwrap(); assert_eq!(removed_len, expected.len()); assert_eq!(expected, found); assert_eq!(buf.start, HALF_LEN); assert_eq!(buf.len, 0); } #[test] fn insert_into_non_empty_wrapping_buffer() { const QUARTER_LEN: usize = RingBuffer::LEN / 4; let mut buf = RingBuffer::new(); // This should leave the buffer empty but with the start field pointing to the middle of // the buffer. // ┌───────────────────────┠// │ │ // └───────────────────────┘ // â–² // │ // start buf.insert(&mut [0; 2 * QUARTER_LEN].as_slice()).unwrap(); buf.remove(&mut vec![]).unwrap(); assert_eq!(buf.start, 2 * QUARTER_LEN); assert_eq!(buf.len, 0); // Then we fill one quarter of the buffer with ones. This gives us a non-empty buffer whose // empty section is not contiguous. // ┌───────────┬─────┬─────┠// │ │ 1 │ │ // └───────────┴─────┴─────┘ // â–² // │ // start let mut expected = vec![1; QUARTER_LEN]; buf.insert(&mut expected.as_slice()).unwrap(); assert_eq!(buf.start, 2 * QUARTER_LEN); assert_eq!(buf.len, QUARTER_LEN); // Then we fill one quarter of the buffer with twos and another quarter of the buffer with // threes in a single insertion. This tests case 1.2. // ┌─────┬─────┬─────┬─────┠// │ 3 │ │ 1 │ 2 │ // └─────┴─────┴─────┴─────┘ // â–² // │ // start let mut second_half = vec![2; QUARTER_LEN]; second_half.extend_from_slice(&[3; QUARTER_LEN]); buf.insert(&mut second_half.as_slice()).unwrap(); expected.extend(second_half); assert_eq!(buf.start, 2 * QUARTER_LEN); assert_eq!(buf.len, 3 * QUARTER_LEN); // When we remove all the elements of the buffer we should find them in the same order we // inserted them. This tests case 2.2. let mut found = vec![]; let removed_len = buf.remove(&mut found).unwrap(); assert_eq!(removed_len, expected.len()); assert_eq!(expected, found); assert_eq!(buf.start, QUARTER_LEN); assert_eq!(buf.len, 0); } #[test] fn insert_into_non_empty_non_wrapping_buffer() { const QUARTER_LEN: usize = RingBuffer::LEN / 4; let mut buf = RingBuffer::new(); // We fill one quarter of the buffer with ones. This gives us a non-empty buffer whose // empty section is contiguous. // ┌─────┬────────────────┠// │ 1 │ │ // └─────┴────────────────┘ // â–² // │ // â”” start let mut expected = vec![1; QUARTER_LEN]; buf.insert(&mut expected.as_slice()).unwrap(); assert_eq!(buf.start, 0); assert_eq!(buf.len, QUARTER_LEN); // Then we fill one quarter of the buffer with twos. This tests case 1.3. // ┌─────┬─────┬──────────┠// │ 1 │ 2 │ │ // └─────┴─────┴──────────┘ // â–² // │ // â”” start let second_half = vec![2; QUARTER_LEN]; buf.insert(&mut second_half.as_slice()).unwrap(); expected.extend(second_half); assert_eq!(buf.start, 0); assert_eq!(buf.len, 2 * QUARTER_LEN); // When we remove all the elements of the buffer we should find them in the same order we // inserted them. This tests case 2.3. let mut found = vec![]; let removed_len = buf.remove(&mut found).unwrap(); assert_eq!(removed_len, expected.len()); assert_eq!(expected, found); assert_eq!(buf.start, 2 * QUARTER_LEN); assert_eq!(buf.len, 0); } } sudo-rs-0.2.5/src/lib.rs000064400000000000000000000007331046102023000131320ustar 00000000000000#[macro_use] mod macros; pub(crate) mod common; pub(crate) mod cutils; pub(crate) mod defaults; pub(crate) mod exec; pub(crate) mod log; pub(crate) mod pam; pub(crate) mod sudoers; pub(crate) mod system; mod su; mod sudo; mod visudo; pub use su::main as su_main; pub use sudo::main as sudo_main; pub use visudo::main as visudo_main; #[cfg(feature = "do-not-use-all-features")] compile_error!("Refusing to compile using 'cargo --all-features' --- please read the README"); sudo-rs-0.2.5/src/log/mod.rs000064400000000000000000000112351046102023000137230ustar 00000000000000#![allow(unused_macros)] use self::simple_logger::SimpleLogger; use self::syslog::Syslog; pub use log::Level; use std::ops::Deref; mod simple_logger; mod syslog; // TODO: logger_macro has an allow_unused that should be removed macro_rules! logger_macro { ($name:ident is $rule_level:ident to $target:expr, $d:tt) => { macro_rules! $name { ($d($d arg:tt)+) => (::log::log!(target: $target, $crate::log::Level::$rule_level, $d($d arg)+)); } #[allow(unused)] pub(crate) use $name; }; ($name:ident is $rule_level:ident to $target:expr) => { logger_macro!($name is $rule_level to $target, $); }; } logger_macro!(auth_error is Error to "sudo::auth"); logger_macro!(auth_warn is Warn to "sudo::auth"); logger_macro!(auth_info is Info to "sudo::auth"); logger_macro!(auth_debug is Debug to "sudo::auth"); logger_macro!(auth_trace is Trace to "sudo::auth"); logger_macro!(user_error is Error to "sudo::user"); logger_macro!(user_warn is Warn to "sudo::user"); logger_macro!(user_info is Info to "sudo::user"); logger_macro!(user_debug is Debug to "sudo::user"); logger_macro!(user_trace is Trace to "sudo::user"); // TODO: dev_logger_macro has an allow_unused that should be removed macro_rules! dev_logger_macro { ($name:ident is $rule_level:ident to $target:expr, $d:tt) => { macro_rules! $name { ($d($d arg:tt)+) => { if std::cfg!(feature = "dev") { (::log::log!( target: $target, $crate::log::Level::$rule_level, "{}: {}", std::panic::Location::caller(), format_args!($d($d arg)+) )); } }; } #[allow(unused)] pub(crate) use $name; }; ($name:ident is $rule_level:ident to $target:expr) => { dev_logger_macro!($name is $rule_level to $target, $); }; } dev_logger_macro!(dev_error is Error to "sudo::dev"); dev_logger_macro!(dev_warn is Warn to "sudo::dev"); dev_logger_macro!(dev_info is Info to "sudo::dev"); dev_logger_macro!(dev_debug is Debug to "sudo::dev"); dev_logger_macro!(dev_trace is Trace to "sudo::dev"); #[derive(Default)] pub struct SudoLogger(Vec<(String, Box)>); impl SudoLogger { pub fn new(prefix: &'static str) -> Self { let mut logger: Self = Default::default(); logger.add_logger("sudo::auth", Syslog); logger.add_logger("sudo::user", SimpleLogger::to_stderr(prefix)); #[cfg(feature = "dev")] { let path = option_env!("SUDO_DEV_LOGS") .map(|s| s.into()) .unwrap_or_else(|| { std::env::temp_dir().join(format!("sudo-dev-{}.log", std::process::id())) }); logger.add_logger("sudo::dev", SimpleLogger::to_file(path, "").unwrap()); } logger } pub fn into_global_logger(self) { log::set_boxed_logger(Box::new(self)) .map(|()| log::set_max_level(log::LevelFilter::Trace)) .expect("Could not set previously set logger"); } /// Add a logger for a specific prefix to the stack fn add_logger( &mut self, prefix: impl ToString + Deref, logger: impl log::Log + 'static, ) { self.add_boxed_logger(prefix, Box::new(logger)) } /// Add a boxed logger for a specific prefix to the stack fn add_boxed_logger( &mut self, prefix: impl ToString + Deref, logger: Box, ) { let prefix = if prefix.ends_with("::") { prefix.to_string() } else { // given a prefix `my::prefix`, we want to match `my::prefix::somewhere` // but not `my::prefix_to_somewhere` format!("{}::", prefix.to_string()) }; self.0.push((prefix, logger)) } } impl log::Log for SudoLogger { fn enabled(&self, metadata: &log::Metadata) -> bool { self.0.iter().any(|(_, l)| l.enabled(metadata)) } fn log(&self, record: &log::Record) { for (prefix, l) in self.0.iter() { if record.target() == &prefix[..prefix.len() - 2] || record.target().starts_with(prefix) { l.log(record); } } } fn flush(&self) { for (_, l) in self.0.iter() { l.flush(); } } } #[cfg(test)] mod tests { use super::SudoLogger; #[test] fn can_construct_logger() { let logger = SudoLogger::new("sudo: "); let len = if cfg!(feature = "dev") { 3 } else { 2 }; assert_eq!(logger.0.len(), len); } } sudo-rs-0.2.5/src/log/simple_logger.rs000064400000000000000000000055741046102023000160050ustar 00000000000000use std::io::Write; #[cfg(feature = "dev")] use std::{fs::File, path::Path}; use log::Log; pub struct SimpleLogger where for<'a> &'a W: Write, { target: W, prefix: &'static str, } impl Log for SimpleLogger where for<'a> &'a W: Write, { fn enabled(&self, metadata: &log::Metadata) -> bool { metadata.level() <= log::max_level() && metadata.level() <= log::STATIC_MAX_LEVEL } fn log(&self, record: &log::Record) { let _ = writeln!(&self.target, "{}{}", self.prefix, record.args()); } fn flush(&self) { let _ = (&self.target).flush(); } } impl SimpleLogger { pub fn to_stderr(prefix: &'static str) -> SimpleLogger { SimpleLogger { target: std::io::stderr(), prefix, } } } #[cfg(feature = "dev")] impl SimpleLogger { pub fn to_file>(name: P, prefix: &'static str) -> Result { let target = std::fs::OpenOptions::new() .append(true) .create(true) .open(name)?; Ok(Self { target, prefix }) } } #[cfg(test)] mod tests { use std::{ io, sync::{Arc, RwLock}, }; use super::SimpleLogger; use log::{LevelFilter, Log}; #[derive(Clone, Default)] struct MyString { inner: Arc>, } impl MyString { fn read(&self) -> String { self.inner.read().unwrap().clone() } } impl io::Write for &'_ MyString { fn write(&mut self, buf: &[u8]) -> io::Result { self.inner .write() .unwrap() .push_str(std::str::from_utf8(buf).unwrap()); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { self.write(b"flushed").map(drop) } } #[test] fn test_default_level() { let logger = SimpleLogger::to_stderr("test"); let metadata = log::Metadata::builder().level(log::Level::Trace).build(); log::set_max_level(LevelFilter::Trace); assert!(logger.enabled(&metadata)); log::set_max_level(LevelFilter::Info); assert!(!logger.enabled(&metadata)); } #[test] fn test_write_and_flush() { let target = MyString::default(); let logger = SimpleLogger { target: target.clone(), prefix: "[test] ", }; let record = log::Record::builder() .args(format_args!("Hello World!")) .level(log::Level::Info) .build(); logger.log(&record); let value = target.read(); assert_eq!(value, "[test] Hello World!\n"); drop(value); logger.flush(); let value = target.read(); assert_eq!(value, "[test] Hello World!\nflushed"); drop(value); } } sudo-rs-0.2.5/src/log/syslog.rs000064400000000000000000000217721046102023000144730ustar 00000000000000use core::fmt::{self, Write}; use log::{Level, Log, Metadata}; pub struct Syslog; mod internal { use crate::system::syslog; use std::ffi::CStr; const DOTDOTDOT_START: &[u8] = b"[...] "; const DOTDOTDOT_END: &[u8] = b" [...]"; const MAX_MSG_LEN: usize = 960; const NULL_BYTE_LEN: usize = 1; // for C string compatibility const BUFSZ: usize = MAX_MSG_LEN + DOTDOTDOT_END.len() + NULL_BYTE_LEN; pub struct SysLogMessageWriter { buffer: [u8; BUFSZ], cursor: usize, facility: libc::c_int, priority: libc::c_int, } // - whenever a SysLogMessageWriter has been constructed, a syslog message WILL be created // for one specific event; this struct functions as a low-level interface for that message // - the caller of the pub functions will have to take care never to `append` more bytes than // are `available`, or a panic will occur. // - the impl guarantees that after `line_break()`, there will be enough room available for at // least a single UTF8 character sequence (which is true since MAX_MSG_LEN >= 10) impl SysLogMessageWriter { pub fn new(priority: libc::c_int, facility: libc::c_int) -> Self { Self { buffer: [0; BUFSZ], cursor: 0, priority, facility, } } pub fn append(&mut self, bytes: &[u8]) { let num_bytes = bytes.len(); self.buffer[self.cursor..self.cursor + num_bytes].copy_from_slice(bytes); self.cursor += num_bytes; } pub fn line_break(&mut self) { self.append(DOTDOTDOT_END); self.commit_to_syslog(); self.append(DOTDOTDOT_START); } fn commit_to_syslog(&mut self) { self.append(&[0]); let message = CStr::from_bytes_with_nul(&self.buffer[..self.cursor]).unwrap(); syslog(self.priority, self.facility, message); self.cursor = 0; } pub fn available(&self) -> usize { MAX_MSG_LEN - self.cursor } } impl Drop for SysLogMessageWriter { fn drop(&mut self) { self.commit_to_syslog(); } } } use internal::SysLogMessageWriter; /// `floor_char_boundary` is currently unstable in Rust fn floor_char_boundary(data: &str, mut index: usize) -> usize { if index >= data.len() { return data.len(); } while !data.is_char_boundary(index) { index -= 1; } index } /// This function REQUIRES that `message` is larger than `max_size` (or a panic will occur). /// This function WILL return a non-zero result if `max_size` is large enough to fit /// at least the first character of `message`. fn suggested_break(message: &str, max_size: usize) -> usize { // method A: try to split the message in two non-empty parts on an ASCII white space character // method B: split on the utf8 character boundary that consumes the most data if let Some(pos) = message.as_bytes()[1..max_size] .iter() .rposition(|c| c.is_ascii_whitespace()) { // since pos+1 contains ASCII whitespace, it acts as a valid utf8 boundary as well pos + 1 } else { floor_char_boundary(message, max_size) } } impl Write for SysLogMessageWriter { fn write_str(&mut self, mut message: &str) -> fmt::Result { while message.len() > self.available() { let truncate_boundary = suggested_break(message, self.available()); let left = &message[..truncate_boundary]; let right = &message[truncate_boundary..]; self.append(left.as_bytes()); self.line_break(); // This loop while terminate, since either of the following is true: // 1. truncate_boundary is strictly positive: // message.len() has strictly decreased, and self.available() has not decreased // 2. truncate_boundary is zero: // message.len() has remained unchanged, but self.available() has strictly increased; // this latter is true since, for truncate_boundary to be 0, self.available() must // have been not large enough to fit a single UTF8 character message = right; } self.append(message.as_bytes()); Ok(()) } } const FACILITY: libc::c_int = libc::LOG_AUTH; impl Log for Syslog { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= log::max_level() && metadata.level() <= log::STATIC_MAX_LEVEL } fn log(&self, record: &log::Record) { let priority = match record.level() { Level::Error => libc::LOG_ERR, Level::Warn => libc::LOG_WARNING, Level::Info => libc::LOG_INFO, Level::Debug => libc::LOG_DEBUG, Level::Trace => libc::LOG_DEBUG, }; let mut writer = SysLogMessageWriter::new(priority, FACILITY); let _ = write!(writer, "{}", record.args()); } fn flush(&self) { // pass } } #[cfg(test)] mod tests { use log::Log; use std::fmt::Write; use super::{SysLogMessageWriter, Syslog, FACILITY}; #[test] fn can_write_to_syslog() { let logger = Syslog; let record = log::Record::builder() .args(format_args!("Hello World!")) .level(log::Level::Info) .build(); logger.log(&record); } #[test] fn can_handle_multiple_writes() { let mut writer = SysLogMessageWriter::new(libc::LOG_DEBUG, FACILITY); for i in 1..20 { let _ = write!(writer, "{}", "Test 123 ".repeat(i)); } } #[test] fn can_truncate_syslog() { let logger = Syslog; let record = log::Record::builder() .args(format_args!("This is supposed to be a very long syslog message but idk what to write, so I am just going to tell you about the time I tried to make coffee with a teapot. So I woke up one morning and decided to make myself a pot of coffee, however after all the wild coffee parties and mishaps the coffee pot had evetually given it's last cup on a tragic morning I call wednsday. So it came to, that the only object capable of giving me hope for the day was my teapot. As I stood in the kitchen and reached for my teapot it, as if sensing the impending horrors that awaited the innocent little teapot, emmited a horse sheak of desperation. \"three hundred and seven\", it said. \"What?\" I asked with a voice of someone who clearly did not want to be bothered until he had his daily almost medically necessary dose of caffine. \"I am a teapot\" it responded with a voice of increasing forcefulness. \"I am a teapot, not a coffee pot\". It was then, in my moments of confusion that my brain finally understood, this was a teapot.")) .level(log::Level::Info) .build(); logger.log(&record); } #[test] fn can_truncate_syslog_with_no_spaces() { let logger = Syslog; let record = log::Record::builder() .args(format_args!("iwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercasesiwillhandlecornercases")) .level(log::Level::Info) .build(); logger.log(&record); } #[test] fn will_not_break_utf8() { let mut writer = SysLogMessageWriter::new(libc::LOG_DEBUG, FACILITY); let _ = write!(writer, "{}¢", "x".repeat(959)); } } sudo-rs-0.2.5/src/macros.rs000064400000000000000000000031151046102023000136450ustar 00000000000000// the `std::print` macros panic on any IO error. these are non-panicking alternatives macro_rules! println_ignore_io_error { ($($tt:tt)*) => {{ use std::io::Write; let _ = writeln!(std::io::stdout(), $($tt)*); }} } macro_rules! eprintln_ignore_io_error { ($($tt:tt)*) => {{ use std::io::Write; let _ = writeln!(std::io::stderr(), $($tt)*); }} } // catch unintentional uses of `print*` macros with the test suite #[allow(unused_macros)] #[cfg(debug_assertions)] macro_rules! eprintln { ($($tt:tt)*) => { compile_error!("do not use `eprintln!`; use the `write!` macro instead") }; } #[allow(unused_macros)] #[cfg(debug_assertions)] macro_rules! eprint { ($($tt:tt)*) => { compile_error!("do not use `eprint!`; use the `write!` macro instead") }; } #[allow(unused_macros)] #[cfg(debug_assertions)] macro_rules! println { ($($tt:tt)*) => { compile_error!("do not use `println!`; use the `write!` macro instead") }; } #[allow(unused_macros)] #[cfg(debug_assertions)] macro_rules! print { ($($tt:tt)*) => { compile_error!("do not use `print!`; use the `write!` macro instead") }; } macro_rules! cstr { ($lit:literal) => {{ // this `const` item produces compile time errors = it performs the checks at compile time const CS: &'static std::ffi::CStr = match std::ffi::CStr::from_bytes_until_nul(concat!($lit, "\0").as_bytes()) { Ok(x) => x, Err(_) => panic!("string literal did not pass CStr checks"), }; CS }}; } sudo-rs-0.2.5/src/pam/converse.rs000064400000000000000000000343751046102023000147760ustar 00000000000000use crate::cutils::string_from_ptr; use super::sys::*; use super::{error::PamResult, rpassword, securemem::PamBuffer, PamError, PamErrorType}; /// Each message in a PAM conversation will have a message style. Each of these /// styles must be handled separately. #[derive(Clone, Copy)] pub enum PamMessageStyle { /// Prompt for input using a message. The input should considered secret /// and should be hidden from view. PromptEchoOff = PAM_PROMPT_ECHO_OFF as isize, /// Prompt for input using a message. The input does not have to be /// considered a secret and may be displayed to the user. PromptEchoOn = PAM_PROMPT_ECHO_ON as isize, /// Display an error message. The user should not be prompted for any input. ErrorMessage = PAM_ERROR_MSG as isize, /// Display some informational text. The user should not be prompted for any /// input. TextInfo = PAM_TEXT_INFO as isize, } impl PamMessageStyle { pub fn from_int(val: libc::c_int) -> Option { use PamMessageStyle::*; match val as _ { PAM_PROMPT_ECHO_OFF => Some(PromptEchoOff), PAM_PROMPT_ECHO_ON => Some(PromptEchoOn), PAM_ERROR_MSG => Some(ErrorMessage), PAM_TEXT_INFO => Some(TextInfo), _ => None, } } } pub trait Converser { /// Handle a normal prompt, i.e. present some message and ask for a value. /// The value is not considered a secret. fn handle_normal_prompt(&self, msg: &str) -> PamResult; /// Handle a hidden prompt, i.e. present some message and ask for a value. /// The value is considered secret and should not be visible. fn handle_hidden_prompt(&self, msg: &str) -> PamResult; /// Display an error message to the user, the user does not need to input a /// value. fn handle_error(&self, msg: &str) -> PamResult<()>; /// Display an informational message to the user, the user does not need to /// input a value. fn handle_info(&self, msg: &str) -> PamResult<()>; } /// Handle a single message in a conversation. fn handle_message( app_data: &ConverserData, style: PamMessageStyle, msg: &str, ) -> PamResult> { use PamMessageStyle::*; match style { PromptEchoOn => { if app_data.no_interact { return Err(PamError::InteractionRequired); } app_data.converser.handle_normal_prompt(msg).map(Some) } PromptEchoOff => { if app_data.no_interact { return Err(PamError::InteractionRequired); } let final_prompt = match app_data.auth_prompt.as_deref() { None => { // Suppress password prompt entirely when -p '' is passed. String::new() } Some(prompt) => { format!("[{}: {prompt}] {msg}", app_data.converser_name) } }; app_data .converser .handle_hidden_prompt(&final_prompt) .map(Some) } ErrorMessage => app_data.converser.handle_error(msg).map(|()| None), TextInfo => app_data.converser.handle_info(msg).map(|()| None), } } /// A converser that uses stdin/stdout/stderr to display messages and to request /// input from the user. pub struct CLIConverser { pub(super) name: String, pub(super) use_stdin: bool, pub(super) bell: bool, pub(super) password_feedback: bool, } use rpassword::Terminal; impl CLIConverser { fn open(&self) -> std::io::Result { if self.use_stdin { Terminal::open_stdie() } else { Terminal::open_tty() } } } impl Converser for CLIConverser { fn handle_normal_prompt(&self, msg: &str) -> PamResult { let mut tty = self.open()?; tty.prompt(&format!("[{}: input needed] {msg} ", self.name))?; Ok(tty.read_cleartext()?) } fn handle_hidden_prompt(&self, msg: &str) -> PamResult { let mut tty = self.open()?; if self.bell && !self.use_stdin { tty.bell()?; } tty.prompt(msg)?; if self.password_feedback { Ok(tty.read_password_with_feedback()?) } else { Ok(tty.read_password()?) } } fn handle_error(&self, msg: &str) -> PamResult<()> { let mut tty = self.open()?; Ok(tty.prompt(&format!("[{} error] {msg}\n", self.name))?) } fn handle_info(&self, msg: &str) -> PamResult<()> { let mut tty = self.open()?; Ok(tty.prompt(&format!("[{}] {msg}\n", self.name))?) } } /// Helper struct that contains the converser as well as panic boolean pub(super) struct ConverserData { pub(super) converser: C, pub(super) converser_name: String, pub(super) no_interact: bool, pub(super) auth_prompt: Option, pub(super) panicked: bool, } /// This function implements the conversation function of `pam_conv`. /// /// This function should always be called with an appdata_ptr that implements /// the `Converser` trait. It then collects the messages provided into a vector /// that is passed to the converser. The converser can then respond to those /// messages and add their replies (where applicable). Finally the replies are /// converted back to the C interface and returned to PAM. This function tries /// to catch any unwinding panics and sets state to indicate that a panic /// occured. /// /// # Safety /// * If called with an appdata_ptr that does not correspond with the Converser /// this function will exhibit undefined behavior. /// * The messages from PAM are assumed to be formatted correctly. pub(super) unsafe extern "C" fn converse( num_msg: libc::c_int, msg: *mut *const pam_message, response: *mut *mut pam_response, appdata_ptr: *mut libc::c_void, ) -> libc::c_int { let result = std::panic::catch_unwind(|| { let mut resp_bufs = Vec::with_capacity(num_msg as usize); for i in 0..num_msg as usize { // convert the input messages to Rust types // SAFETY: the PAM contract ensures that `num_msg` does not exceed the amount // of messages presented to this function in `msg`, and that it is not being // written to at the same time as we are reading it. Note that the reference // we create does not escape this loopy body. let message: &pam_message = unsafe { &**msg.add(i) }; // SAFETY: PAM ensures that the messages passed are properly null-terminated let msg = unsafe { string_from_ptr(message.msg) }; let style = if let Some(style) = PamMessageStyle::from_int(message.msg_style) { style } else { // early return if there is a failure to convert, pam would have given us nonsense return PamErrorType::ConversationError; }; // send the conversation off to the Rust part // SAFETY: appdata_ptr contains the `*mut ConverserData` that is untouched by PAM let app_data = unsafe { &mut *(appdata_ptr as *mut ConverserData) }; let Ok(resp_buf) = handle_message(app_data, style, &msg) else { return PamErrorType::ConversationError; }; resp_bufs.push(resp_buf); } // Allocate enough memory for the responses, which are initialized with zero. // SAFETY: this will either allocate the required amount of (initialized) bytes, // or return a null pointer. let temp_resp = unsafe { libc::calloc( num_msg as libc::size_t, std::mem::size_of::() as libc::size_t, ) } as *mut pam_response; if temp_resp.is_null() { return PamErrorType::BufferError; } // Store the responses for (i, resp_buf) in resp_bufs.into_iter().enumerate() { // SAFETY: `i` will not exceed `num_msg` by the way `conversation_messages` // is constructed, so `temp_resp` will have allocated-and-initialized data at // the required offset that only we have a writable pointer to. let response: &mut pam_response = unsafe { &mut *(temp_resp.add(i)) }; if let Some(secbuf) = resp_buf { response.resp = secbuf.leak().as_ptr().cast(); } } // Set the responses // SAFETY: PAM contract says that we are passed a valid, non-null, writeable pointer here. unsafe { *response = temp_resp }; PamErrorType::Success }); // handle any unwinding panics that occured here let res = match result { Ok(r) => r, Err(_) => { // notify caller that a panic has occured // SAFETY: appdata_ptr contains the `*mut ConverserData` that is untouched by PAM let app_data = unsafe { &mut *(appdata_ptr as *mut ConverserData) }; app_data.panicked = true; PamErrorType::ConversationError } }; res.as_int() } #[allow(clippy::undocumented_unsafe_blocks)] #[cfg(test)] mod test { use super::*; use std::pin::Pin; use PamMessageStyle::*; struct PamMessage { msg: String, style: PamMessageStyle, } impl Converser for String { fn handle_normal_prompt(&self, msg: &str) -> PamResult { Ok(PamBuffer::new(format!("{self} says {msg}").into_bytes())) } fn handle_hidden_prompt(&self, msg: &str) -> PamResult { Ok(PamBuffer::new(msg.as_bytes().to_vec())) } fn handle_error(&self, msg: &str) -> PamResult<()> { panic!("{msg}") } fn handle_info(&self, _msg: &str) -> PamResult<()> { Ok(()) } } // essentially do the inverse of the "conversation function" fn dummy_pam(msgs: &[PamMessage], talkie: &pam_conv) -> Vec> { let pam_msgs = msgs .iter() .map(|PamMessage { msg, style, .. }| pam_message { msg: std::ffi::CString::new(&msg[..]).unwrap().into_raw(), msg_style: *style as i32, }) .rev() .collect::>(); let mut ptrs = pam_msgs .iter() .map(|x| x as *const pam_message) .rev() .collect::>(); let mut raw_response = std::ptr::null_mut::(); let conv_err = unsafe { talkie.conv.expect("non-null fn ptr")( ptrs.len() as i32, ptrs.as_mut_ptr(), &mut raw_response, talkie.appdata_ptr, ) }; // deallocate the leaky strings for rec in ptrs { unsafe { drop(std::ffi::CString::from_raw((*rec).msg as *mut _)); } } if conv_err != 0 { return vec![]; } let result = msgs .iter() .enumerate() .map(|(i, _)| unsafe { let ptr = raw_response.add(i); if (*ptr).resp.is_null() { None } else { // "The resp_retcode member of this struct is unused and should be set to zero." assert_eq!((*ptr).resp_retcode, 0); let response = string_from_ptr((*ptr).resp); libc::free((*ptr).resp as *mut _); Some(response) } }) .collect(); unsafe { libc::free(raw_response as *mut _) }; result } fn msg(style: PamMessageStyle, msg: &str) -> PamMessage { let msg = msg.to_string(); PamMessage { style, msg } } // sanity check on the test cases; lib.rs is expected to manage the lifetime of the pointer // inside the pam_conv object explicitly. use std::marker::PhantomData; struct PamConvBorrow<'a> { pam_conv: pam_conv, _marker: std::marker::PhantomData<&'a ()>, } impl<'a> PamConvBorrow<'a> { fn new(data: Pin<&'a mut ConverserData>) -> PamConvBorrow<'a> { let appdata_ptr = unsafe { data.get_unchecked_mut() as *mut ConverserData as *mut libc::c_void }; PamConvBorrow { pam_conv: pam_conv { conv: Some(converse::), appdata_ptr, }, _marker: PhantomData, } } fn borrow(&self) -> &pam_conv { &self.pam_conv } } #[test] fn miri_pam_gpt() { let mut hello = Box::pin(ConverserData { converser: "tux".to_string(), converser_name: "tux".to_string(), no_interact: false, auth_prompt: Some("authenticate".to_owned()), panicked: false, }); let cookie = PamConvBorrow::new(hello.as_mut()); let pam_conv = cookie.borrow(); assert_eq!(dummy_pam(&[], pam_conv), vec![]); assert_eq!( dummy_pam(&[msg(PromptEchoOn, "hello")], pam_conv), vec![Some("tux says hello".to_string())] ); assert_eq!( dummy_pam(&[msg(PromptEchoOff, "fish")], pam_conv), vec![Some("[tux: authenticate] fish".to_string())] ); assert_eq!(dummy_pam(&[msg(TextInfo, "mars")], pam_conv), vec![None]); assert_eq!( dummy_pam( &[ msg(PromptEchoOff, "banging the rocks together"), msg(TextInfo, ""), msg(PromptEchoOn, ""), ], pam_conv ), vec![ Some("[tux: authenticate] banging the rocks together".to_string()), None, Some("tux says ".to_string()), ] ); //assert!(!hello.panicked); // not allowed by borrow checker let real_hello = unsafe { &mut *(pam_conv.appdata_ptr as *mut ConverserData) }; assert!(!real_hello.panicked); assert_eq!(dummy_pam(&[msg(ErrorMessage, "oops")], pam_conv), vec![]); assert!(hello.panicked); // allowed now } } sudo-rs-0.2.5/src/pam/error.rs000064400000000000000000000226321046102023000142740ustar 00000000000000use std::{ffi::NulError, fmt, str::Utf8Error}; use crate::cutils::string_from_ptr; use super::sys::*; pub type PamResult = Result; // TODO: add missing doc-comments #[derive(PartialEq, Eq, Debug)] pub enum PamErrorType { /// There was no error running the PAM command Success, OpenError, SymbolError, ServiceError, SystemError, BufferError, ConversationError, PermissionDenied, /// The maximum number of authentication attempts was reached and no more /// attempts should be made. MaxTries, /// The user failed to authenticate correctly. AuthError, NewAuthTokenRequired, /// The application does not have enough credentials to authenticate the /// user. This can for example happen if we wanted to update the user /// password from a non-root process, which we cannot do. CredentialsInsufficient, /// PAM modules were unable to access the authentication information (for /// example due to a network error). AuthInfoUnavailable, /// The specified user is unknown to an authentication service. UserUnknown, /// Failed to retrieve the credentials (i.e. password) for a user. CredentialsUnavailable, /// The credentials (i.e. password) for this user were expired. CredentialsExpired, /// There was an error setting the user credentials. CredentialsError, /// The user account is expired and can no longer be used. AccountExpired, AuthTokenExpired, SessionError, AuthTokenError, AuthTokenRecoveryError, AuthTokenLockBusy, AuthTokenDisableAging, NoModuleData, Ignore, /// The application should exit immediately. Abort, TryAgain, ModuleUnknown, /// The application tried to set/delete an undefined or inaccessible item. BadItem, // Extension in OpenPAM and LinuxPAM // DomainUnknown, // OpenPAM only // BadHandle // OpenPAM only // BadFeature // OpenPAM only // BadConstant // OpenPAM only // ConverseAgain // LinuxPAM only // Incomplete // LinuxPAM only UnknownErrorType(i32), } impl PamErrorType { pub(super) fn from_int(errno: libc::c_int) -> PamErrorType { use PamErrorType::*; match errno as _ { PAM_SUCCESS => Success, PAM_OPEN_ERR => OpenError, PAM_SYMBOL_ERR => SymbolError, PAM_SERVICE_ERR => ServiceError, PAM_SYSTEM_ERR => SystemError, PAM_BUF_ERR => BufferError, PAM_CONV_ERR => ConversationError, PAM_PERM_DENIED => PermissionDenied, PAM_MAXTRIES => MaxTries, PAM_AUTH_ERR => AuthError, PAM_NEW_AUTHTOK_REQD => NewAuthTokenRequired, PAM_CRED_INSUFFICIENT => CredentialsInsufficient, PAM_AUTHINFO_UNAVAIL => AuthInfoUnavailable, PAM_USER_UNKNOWN => UserUnknown, PAM_CRED_UNAVAIL => CredentialsUnavailable, PAM_CRED_EXPIRED => CredentialsExpired, PAM_CRED_ERR => CredentialsError, PAM_ACCT_EXPIRED => AccountExpired, PAM_AUTHTOK_EXPIRED => AuthTokenExpired, PAM_SESSION_ERR => SessionError, PAM_AUTHTOK_ERR => AuthTokenError, PAM_AUTHTOK_RECOVERY_ERR => AuthTokenRecoveryError, PAM_AUTHTOK_LOCK_BUSY => AuthTokenLockBusy, PAM_AUTHTOK_DISABLE_AGING => AuthTokenDisableAging, PAM_NO_MODULE_DATA => NoModuleData, PAM_IGNORE => Ignore, PAM_ABORT => Abort, PAM_TRY_AGAIN => TryAgain, PAM_MODULE_UNKNOWN => ModuleUnknown, PAM_BAD_ITEM => BadItem, // PAM_DOMAIN_UNKNOWN => DomainUnknown, // PAM_BAD_HANDLE => BadHandle, // PAM_BAD_FEATURE => BadFeature, // PAM_BAD_CONSTANT => BadConstant, // PAM_CONV_AGAIN => ConverseAgain, // PAM_INCOMPLETE => Incomplete, _ => UnknownErrorType(errno), } } pub fn as_int(&self) -> libc::c_int { use PamErrorType::*; match self { Success => PAM_SUCCESS as libc::c_int, OpenError => PAM_OPEN_ERR as libc::c_int, SymbolError => PAM_SYMBOL_ERR as libc::c_int, ServiceError => PAM_SERVICE_ERR as libc::c_int, SystemError => PAM_SYSTEM_ERR as libc::c_int, BufferError => PAM_BUF_ERR as libc::c_int, ConversationError => PAM_CONV_ERR as libc::c_int, PermissionDenied => PAM_PERM_DENIED as libc::c_int, MaxTries => PAM_MAXTRIES as libc::c_int, AuthError => PAM_AUTH_ERR as libc::c_int, NewAuthTokenRequired => PAM_NEW_AUTHTOK_REQD as libc::c_int, CredentialsInsufficient => PAM_CRED_INSUFFICIENT as libc::c_int, AuthInfoUnavailable => PAM_AUTHINFO_UNAVAIL as libc::c_int, UserUnknown => PAM_USER_UNKNOWN as libc::c_int, CredentialsUnavailable => PAM_CRED_UNAVAIL as libc::c_int, CredentialsExpired => PAM_CRED_EXPIRED as libc::c_int, CredentialsError => PAM_CRED_ERR as libc::c_int, AccountExpired => PAM_ACCT_EXPIRED as libc::c_int, AuthTokenExpired => PAM_AUTHTOK_EXPIRED as libc::c_int, SessionError => PAM_SESSION_ERR as libc::c_int, AuthTokenError => PAM_AUTHTOK_ERR as libc::c_int, AuthTokenRecoveryError => PAM_AUTHTOK_RECOVERY_ERR as libc::c_int, AuthTokenLockBusy => PAM_AUTHTOK_LOCK_BUSY as libc::c_int, AuthTokenDisableAging => PAM_AUTHTOK_DISABLE_AGING as libc::c_int, NoModuleData => PAM_NO_MODULE_DATA as libc::c_int, Ignore => PAM_IGNORE as libc::c_int, Abort => PAM_ABORT as libc::c_int, TryAgain => PAM_TRY_AGAIN as libc::c_int, ModuleUnknown => PAM_MODULE_UNKNOWN as libc::c_int, BadItem => PAM_BAD_ITEM as libc::c_int, // DomainUnknown => PAM_DOMAIN_UNKNOWN as libc::c_int, // BadHandle => PAM_BAD_HANDLE as libc::c_int, // BadFeature => PAM_BAD_FEATURE as libc::c_int, // BadConstant => PAM_BAD_CONSTANT as libc::c_int, // ConverseAgain => PAM_CONV_AGAIN as libc::c_int, // Incomplete => PAM_INCOMPLETE as libc::c_int, UnknownErrorType(e) => *e, } } fn get_err_msg(&self) -> String { // SAFETY: pam_strerror technically takes a pam handle as the first argument, // but we do not know of any implementation that actually uses the pamh // argument. See also the netbsd man page for `pam_strerror`. let data = unsafe { pam_strerror(std::ptr::null_mut(), self.as_int()) }; if data.is_null() { String::from("Error unresolved by PAM") } else { // SAFETY: pam_strerror returns a pointer to a null-terminated string unsafe { string_from_ptr(data) } } } } #[derive(Debug)] pub enum PamError { UnexpectedNulByte(NulError), Utf8Error(Utf8Error), Pam(PamErrorType), IoError(std::io::Error), EnvListFailure, InteractionRequired, } impl From for PamError { fn from(err: std::io::Error) -> Self { PamError::IoError(err) } } impl From for PamError { fn from(err: NulError) -> Self { PamError::UnexpectedNulByte(err) } } impl From for PamError { fn from(err: Utf8Error) -> Self { PamError::Utf8Error(err) } } impl fmt::Display for PamError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PamError::UnexpectedNulByte(_) => write!(f, "Unexpected nul byte in input"), PamError::Utf8Error(_) => write!(f, "Could not read input data as UTF-8 string"), PamError::Pam(PamErrorType::AuthError) => { write!(f, "Account validation failure, is your account locked?") } PamError::Pam(PamErrorType::NewAuthTokenRequired) => { write!( f, "Account or password is expired, reset your password and try again" ) } PamError::Pam(PamErrorType::AuthTokenExpired) => { write!(f, "Password expired, contact your system administrator") } PamError::Pam(tp) => write!(f, "PAM error: {}", tp.get_err_msg()), PamError::IoError(e) => write!(f, "IO error: {e}"), PamError::EnvListFailure => { write!( f, "It was not possible to get a list of environment variables" ) } PamError::InteractionRequired => write!(f, "Interaction is required"), } } } impl PamError { /// Create a new PamError based on the error number from pam. pub(super) fn from_pam(errno: libc::c_int) -> PamError { let tp = PamErrorType::from_int(errno); PamError::Pam(tp) } } /// Returns `Ok(())` if the error code is `PAM_SUCCESS` or a `PamError` in other cases pub(super) fn pam_err(err: libc::c_int) -> Result<(), PamError> { if err == PAM_SUCCESS as libc::c_int { Ok(()) } else { Err(PamError::from_pam(err)) } } #[cfg(test)] mod test { use super::PamErrorType; #[test] fn isomorphy() { for i in -100..100 { let pam = PamErrorType::from_int(i); assert_eq!(pam.as_int(), i); assert_eq!(PamErrorType::from_int(pam.as_int()), pam); } } } sudo-rs-0.2.5/src/pam/mod.rs000064400000000000000000000334271046102023000137260ustar 00000000000000use std::{ ffi::{CStr, CString, OsStr, OsString}, io, os::raw::c_char, os::unix::prelude::OsStrExt, ptr::NonNull, }; use converse::ConverserData; use error::pam_err; pub use error::{PamError, PamErrorType, PamResult}; use sys::*; mod converse; mod error; mod rpassword; mod securemem; #[cfg_attr(target_os = "linux", path = "sys_linuxpam.rs")] #[cfg_attr(target_os = "freebsd", path = "sys_openpam.rs")] #[allow(nonstandard_style)] pub mod sys; #[cfg(target_os = "freebsd")] const PAM_DATA_SILENT: std::ffi::c_int = 0; pub use converse::CLIConverser; pub struct PamContext { data_ptr: *mut ConverserData, pamh: *mut pam_handle_t, silent: bool, allow_null_auth_token: bool, last_pam_status: Option, session_started: bool, } impl PamContext { /// Build the PamContext with the CLI conversation function. /// /// The target user is optional and may also be set after the context was /// constructed or not set at all in which case PAM will ask for a /// username. /// /// This function will error when initialization of the PAM session somehow failed. pub fn new_cli( converser_name: &str, service_name: &str, use_stdin: bool, bell: bool, no_interact: bool, password_feedback: bool, target_user: Option<&str>, ) -> PamResult { let converser = CLIConverser { bell, name: converser_name.to_owned(), use_stdin, password_feedback, }; let c_service_name = CString::new(service_name)?; let c_user = target_user.map(CString::new).transpose()?; let c_user_ptr = match c_user { Some(ref c) => c.as_ptr(), None => std::ptr::null(), }; // this will be de-allocated explicitly in this type's drop method let data_ptr = Box::into_raw(Box::new(ConverserData { converser, converser_name: converser_name.to_owned(), no_interact, auth_prompt: std::env::var("SUDO_PROMPT") .ok() .filter(|s| !s.is_empty()) .or(Some("authenticate".to_owned())), panicked: false, })); let mut pamh = std::ptr::null_mut(); // SAFETY: we are passing the required fields to `pam_start`; in particular, the value // of `pamh` set above is not used, but will be overwritten by `pam_start`. let res = unsafe { pam_start( c_service_name.as_ptr(), c_user_ptr, &pam_conv { conv: Some(converse::converse::), appdata_ptr: data_ptr as *mut libc::c_void, }, &mut pamh, ) }; pam_err(res)?; assert!(!pamh.is_null()); Ok(PamContext { data_ptr, pamh, silent: false, allow_null_auth_token: true, last_pam_status: None, session_started: false, }) } pub fn set_auth_prompt(&mut self, prompt: Option) { // SAFETY: self.data_ptr was created by Box::into_raw unsafe { (*self.data_ptr).auth_prompt = prompt; } } /// Set whether output of pam calls should be silent or not, by default /// PAM calls are not silent. pub fn mark_silent(&mut self, silent: bool) { self.silent = silent; } /// Set whether or not to allow empty authentication tokens, by default such /// tokens are allowed. pub fn mark_allow_null_auth_token(&mut self, allow: bool) { self.allow_null_auth_token = allow; } /// Get the PAM flag value for the silent flag fn silent_flag(&self) -> i32 { if self.silent { PAM_SILENT as _ } else { 0 } } /// Get the PAM flag value for the disallow_null_authtok flag fn disallow_null_auth_token_flag(&self) -> i32 { if self.allow_null_auth_token { 0 } else { PAM_DISALLOW_NULL_AUTHTOK as _ } } /// Run authentication for the account pub fn authenticate(&mut self) -> PamResult<()> { let mut flags = 0; flags |= self.silent_flag(); flags |= self.disallow_null_auth_token_flag(); // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`) pam_err(unsafe { pam_authenticate(self.pamh, flags) })?; if self.has_panicked() { panic!("Panic during pam authentication"); } Ok(()) } /// Check that the account is valid pub fn validate_account(&mut self) -> PamResult<()> { let mut flags = 0; flags |= self.silent_flag(); flags |= self.disallow_null_auth_token_flag(); // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`) pam_err(unsafe { pam_acct_mgmt(self.pamh, flags) }) } /// Attempt to validate the account, if that fails because the authentication /// token is outdated, then an update of the authentication token is requested. pub fn validate_account_or_change_auth_token(&mut self) -> PamResult<()> { let check_val = self.validate_account(); match check_val { Ok(()) => Ok(()), Err(PamError::Pam(PamErrorType::NewAuthTokenRequired)) => { self.change_auth_token(true)?; Ok(()) } Err(e) => Err(e), } } /// Set the user that will be authenticated. pub fn set_user(&mut self, user: &str) -> PamResult<()> { let c_user = CString::new(user)?; // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`); furthermore, // `c_user.as_ptr()` will point to a correct null-terminated string. pam_err(unsafe { pam_set_item( self.pamh, PAM_USER as _, c_user.as_ptr() as *const libc::c_void, ) }) } /// Get the user that is currently active in the PAM handle pub fn get_user(&mut self) -> PamResult { let mut data = std::ptr::null(); // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`) pam_err(unsafe { pam_get_item(self.pamh, PAM_USER as _, &mut data) })?; // safety check to make sure that we were not passed a null pointer by PAM, // or that in fact PAM did not write to `data` at all. if data.is_null() { return Err(PamError::IoError(io::Error::new( io::ErrorKind::InvalidData, "PAM didn't return username", ))); } // SAFETY: the contract for `pam_get_item` ensures that if `data` was touched by // `pam_get_item`, it will point to a valid null-terminated string. let cstr = unsafe { CStr::from_ptr(data as *const c_char) }; Ok(cstr.to_str()?.to_owned()) } /// Set the TTY path for the current TTY that this PAM session started from. pub fn set_tty>(&mut self, tty_path: P) -> PamResult<()> { let data = CString::new(tty_path.as_ref().as_bytes())?; // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`); furthermore, // `data.as_ptr()` will point to a correct null-terminated string. pam_err(unsafe { pam_set_item( self.pamh, PAM_TTY as _, data.as_ptr() as *const libc::c_void, ) }) } // Set the user that requested the actions in this PAM instance. pub fn set_requesting_user(&mut self, user: &str) -> PamResult<()> { let data = CString::new(user.as_bytes())?; // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`); furthermore, // `data.as_ptr()` will point to a correct null-terminated string. pam_err(unsafe { pam_set_item( self.pamh, PAM_RUSER as _, data.as_ptr() as *const libc::c_void, ) }) } /// Re-initialize the credentials stored in PAM pub fn credentials_reinitialize(&mut self) -> PamResult<()> { self.credentials(PAM_REINITIALIZE_CRED as libc::c_int) } /// Updates to the credentials stored in PAM fn credentials(&mut self, action: libc::c_int) -> PamResult<()> { let mut flags = action; flags |= self.silent_flag(); // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`). pam_err(unsafe { pam_setcred(self.pamh, flags) }) } /// Ask the user to change the authentication token (password). /// /// If `expired_only` is set to true, only expired authentication tokens /// will be asked to be replaced, otherwise a replacement will always be /// requested. pub fn change_auth_token(&mut self, expired_only: bool) -> PamResult<()> { let mut flags = 0; flags |= self.silent_flag(); if expired_only { flags |= PAM_CHANGE_EXPIRED_AUTHTOK as libc::c_int; } // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`). pam_err(unsafe { pam_chauthtok(self.pamh, flags) }) } /// Start a user session for the authenticated user. pub fn open_session(&mut self) -> PamResult<()> { assert!(!self.session_started); // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`). pam_err(unsafe { pam_open_session(self.pamh, self.silent_flag()) })?; self.session_started = true; Ok(()) } /// End the user session. pub fn close_session(&mut self) { // closing the pam session is best effort, if any error occurs we cannot // do anything with it if self.session_started { // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`). let _ = pam_err(unsafe { pam_close_session(self.pamh, self.silent_flag()) }); self.session_started = false; } } /// Get a full listing of the current PAM environment pub fn env(&mut self) -> PamResult> { let mut res = Vec::new(); // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`). // The man page for pam_getenvlist states that: // The format of the memory is a malloc()'d array of char pointers, the last element // of which is set to NULL. Each of the non-NULL entries in this array point to a // NUL terminated and malloc()'d char string of the form: "name=value". // // The pam_getenvlist function returns NULL on failure. let envs = unsafe { pam_getenvlist(self.pamh) }; if envs.is_null() { return Err(PamError::EnvListFailure); } let mut curr_env = envs; // SAFETY: the loop invariant is as follows: // - `curr_env` itself is always a valid pointer to an array of valid (possibly NULL) pointers // - if `curr_env` points to a pointer that is not-null, that data is a c-string allocated by malloc() // - `curr_env` points to NULL if and only if it is the final element in the array while let Some(curr_str) = NonNull::new(unsafe { curr_env.read() }) { let data = { // SAFETY: `curr_str` points to a valid null-terminated string per the above let cstr = unsafe { CStr::from_ptr(curr_str.as_ptr()) }; let bytes = cstr.to_bytes(); if let Some(pos) = bytes.iter().position(|b| *b == b'=') { let key = OsStr::from_bytes(&bytes[..pos]).to_owned(); let value = OsStr::from_bytes(&bytes[pos + 1..]).to_owned(); Some((key, value)) } else { None } }; if let Some((k, v)) = data { res.push((k, v)); } // SAFETY: curr_str was obtained via libc::malloc() so we are responsible for freeing it. // At this point, curr_str is also the only remaining pointer/reference to that allocated data // (the data was copied above), so it can be deallocated without risk of use-after-free errors. unsafe { libc::free(curr_str.as_ptr().cast()) }; // SAFETY: curr_env was not NULL, so it was not the last element in the list and so PAM // ensures that the next offset also is a valid pointer, and points to valid data. curr_env = unsafe { curr_env.offset(1) }; } // SAFETY: `envs` itself was obtained by malloc(), so we are reponsible for freeing it. unsafe { libc::free(envs.cast()) }; Ok(res) } /// Check if anything panicked since the last call. pub fn has_panicked(&self) -> bool { // SAFETY: self.data_ptr was created by Box::into_raw unsafe { (*self.data_ptr).panicked } } } impl Drop for PamContext { fn drop(&mut self) { // data_ptr's pointee is de-allocated in this scope // SAFETY: self.data_ptr was created by Box::into_raw let _data = unsafe { Box::from_raw(self.data_ptr) }; self.close_session(); // It looks like PAM_DATA_SILENT is important to set for our sudo context, but // it is unclear what it really does and does not do, other than the vague // documentation description to 'not take the call to seriously' // Also see https://github.com/systemd/systemd/issues/22318 // SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`) unsafe { pam_end( self.pamh, self.last_pam_status.unwrap_or(PAM_SUCCESS as libc::c_int) | PAM_DATA_SILENT as libc::c_int, ) }; } } sudo-rs-0.2.5/src/pam/rpassword.rs000064400000000000000000000215501046102023000151650ustar 00000000000000/// Parts of the code below are Copyright (c) 2023, Conrad Kleinespel et al /// /// This module contains code that was originally written by Conrad Kleinespel for the rpassword /// crate. No copyright notices were found in the original code. /// /// See: https://docs.rs/rpassword/latest/rpassword/ /// /// Most code was replaced and so is no longer a derived work; work that we kept: /// /// - the "HiddenInput" struct and implementation, with changes: /// * replaced occurences of explicit 'i32' and 'c_int' with RawFd /// * open the TTY ourselves to mitigate Linux CVE-2023-2002 /// - the general idea of a "SafeString" type that clears its memory /// (although much more robust than in the original code) /// use std::io::{self, Error, ErrorKind, Read}; use std::os::fd::{AsFd, AsRawFd}; use std::{fs, mem}; use libc::{tcsetattr, termios, ECHO, ECHONL, ICANON, TCSANOW, VEOF, VERASE, VKILL}; use crate::cutils::cerr; use super::securemem::PamBuffer; struct HiddenInput { tty: fs::File, term_orig: termios, } impl HiddenInput { fn new(feedback: bool) -> io::Result> { // control ourselves that we are really talking to a TTY // mitigates: https://marc.info/?l=oss-security&m=168164424404224 let Ok(tty) = fs::File::open("/dev/tty") else { // if we have nothing to show, we have nothing to hide return Ok(None); }; // Make two copies of the terminal settings. The first one will be modified // and the second one will act as a backup for when we want to set the // terminal back to its original state. let mut term = safe_tcgetattr(&tty)?; let term_orig = safe_tcgetattr(&tty)?; // Hide the password. This is what makes this function useful. term.c_lflag &= !ECHO; // But don't hide the NL character when the user hits ENTER. term.c_lflag |= ECHONL; if feedback { // Disable canonical mode to read character by character when pwfeedback is enabled. term.c_lflag &= !ICANON; } // Save the settings for now. // SAFETY: we are passing tcsetattr a valid file descriptor and pointer-to-struct cerr(unsafe { tcsetattr(tty.as_raw_fd(), TCSANOW, &term) })?; Ok(Some(HiddenInput { tty, term_orig })) } } impl Drop for HiddenInput { fn drop(&mut self) { // Set the the mode back to normal // SAFETY: we are passing tcsetattr a valid file descriptor and pointer-to-struct unsafe { tcsetattr(self.tty.as_raw_fd(), TCSANOW, &self.term_orig); } } } fn safe_tcgetattr(tty: impl AsFd) -> io::Result { let mut term = mem::MaybeUninit::::uninit(); // SAFETY: we are passing tcgetattr a pointer to valid memory cerr(unsafe { ::libc::tcgetattr(tty.as_fd().as_raw_fd(), term.as_mut_ptr()) })?; // SAFETY: if the previous call was a success, `tcgetattr` has initialized `term` Ok(unsafe { term.assume_init() }) } /// Reads a password from the given file descriptor fn read_unbuffered(source: &mut dyn io::Read) -> io::Result { let mut password = PamBuffer::default(); let mut pwd_iter = password.iter_mut(); const EOL: u8 = 0x0A; let input = source.bytes().take_while(|x| x.as_ref().ok() != Some(&EOL)); for read_byte in input { if let Some(dest) = pwd_iter.next() { *dest = read_byte? } else { return Err(Error::new( ErrorKind::OutOfMemory, "incorrect password attempt", )); } } Ok(password) } fn erase_feedback(sink: &mut dyn io::Write, i: usize) { const BACKSPACE: u8 = 0x08; for _ in 0..i { if sink.write(&[BACKSPACE, b' ', BACKSPACE]).is_err() { return; } } } /// Reads a password from the given file descriptor while showing feedback to the user. fn read_unbuffered_with_feedback( source: &mut dyn io::Read, sink: &mut dyn io::Write, hide_input: &HiddenInput, ) -> io::Result { let mut password = PamBuffer::default(); let mut pw_len = 0; // invariant: the amount of nonzero-bytes in the buffer correspond // with the amount of asterisks on the terminal (both tracked in `pw_len`) for read_byte in source.bytes() { let read_byte = read_byte?; if read_byte == b'\n' || read_byte == b'\r' { erase_feedback(sink, pw_len); let _ = sink.write(b"\n"); break; } if read_byte == hide_input.term_orig.c_cc[VEOF] { erase_feedback(sink, pw_len); password.fill(0); break; } if read_byte == hide_input.term_orig.c_cc[VERASE] { if pw_len > 0 { erase_feedback(sink, 1); password[pw_len - 1] = 0; pw_len -= 1; } } else if read_byte == hide_input.term_orig.c_cc[VKILL] { erase_feedback(sink, pw_len); password.fill(0); pw_len = 0; } else { #[allow(clippy::collapsible_else_if)] if let Some(dest) = password.get_mut(pw_len) { *dest = read_byte; pw_len += 1; let _ = sink.write(b"*"); } else { erase_feedback(sink, pw_len); return Err(Error::new( ErrorKind::OutOfMemory, "incorrect password attempt", )); } } } Ok(password) } /// Write something and immediately flush fn write_unbuffered(sink: &mut dyn io::Write, text: &[u8]) -> io::Result<()> { sink.write_all(text)?; sink.flush() } /// A data structure representing either /dev/tty or /dev/stdin+stderr pub enum Terminal<'a> { Tty(fs::File), StdIE(io::StdinLock<'a>, io::StderrLock<'a>), } impl Terminal<'_> { /// Open the current TTY for user communication pub fn open_tty() -> io::Result { Ok(Terminal::Tty( fs::OpenOptions::new() .read(true) .write(true) .open("/dev/tty")?, )) } /// Open standard input and standard error for user communication pub fn open_stdie() -> io::Result { Ok(Terminal::StdIE(io::stdin().lock(), io::stderr().lock())) } /// Reads input with TTY echo disabled pub fn read_password(&mut self) -> io::Result { let input = self.source(); let _hide_input = HiddenInput::new(false)?; read_unbuffered(input) } /// Reads input with TTY echo disabled, but do provide visual feedback while typing. pub fn read_password_with_feedback(&mut self) -> io::Result { if let Some(hide_input) = HiddenInput::new(true)? { match self { Terminal::StdIE(x, y) => read_unbuffered_with_feedback(x, y, &hide_input), Terminal::Tty(x) => read_unbuffered_with_feedback(&mut &*x, &mut &*x, &hide_input), } } else { read_unbuffered(self.source()) } } /// Reads input with TTY echo enabled pub fn read_cleartext(&mut self) -> io::Result { read_unbuffered(self.source()) } /// Display information pub fn prompt(&mut self, text: &str) -> io::Result<()> { write_unbuffered(self.sink(), text.as_bytes()) } /// Ring the bell pub fn bell(&mut self) -> io::Result<()> { const BELL: &[u8; 1] = b"\x07"; write_unbuffered(self.sink(), BELL) } // boilerplate reduction functions fn source(&mut self) -> &mut dyn io::Read { match self { Terminal::StdIE(x, _) => x, Terminal::Tty(x) => x, } } fn sink(&mut self) -> &mut dyn io::Write { match self { Terminal::StdIE(_, x) => x, Terminal::Tty(x) => x, } } } #[cfg(test)] mod test { use super::{read_unbuffered, write_unbuffered}; #[test] fn miri_test_read() { let mut data = "password123\nhello world".as_bytes(); let buf = read_unbuffered(&mut data).unwrap(); // check that the \n is not part of input assert_eq!( buf.iter() .map(|&b| b as char) .take_while(|&x| x != '\0') .collect::(), "password123" ); // check that the \n is also consumed but the rest of the input is still there assert_eq!(std::str::from_utf8(data).unwrap(), "hello world"); } #[test] fn miri_test_longpwd() { assert!(read_unbuffered(&mut "a".repeat(511).as_bytes()).is_ok()); assert!(read_unbuffered(&mut "a".repeat(512).as_bytes()).is_err()); } #[test] fn miri_test_write() { let mut data = Vec::new(); write_unbuffered(&mut data, b"prompt").unwrap(); assert_eq!(std::str::from_utf8(&data).unwrap(), "prompt"); } } sudo-rs-0.2.5/src/pam/securemem.rs000064400000000000000000000076551046102023000151400ustar 00000000000000//! Routines for "secure" memory operations; i.e. data that we need to send to Linux-PAM and don't //! want any copies to leak (that we would then need to zeroize). use std::{ alloc::{self, Layout}, ptr::NonNull, slice, }; const SIZE: usize = super::sys::PAM_MAX_RESP_SIZE as usize; pub struct PamBuffer(NonNull<[u8; SIZE]>); const LAYOUT: Layout = match Layout::from_size_align(SIZE, 1) { Ok(layout) => layout, Err(_) => unreachable!(), }; impl PamBuffer { // consume this buffer and return its internal pointer // (ending the type-level security, but guaranteeing you need unsafe code to access the data) pub fn leak(self) -> NonNull { let result = self.0; std::mem::forget(self); result.cast() } // initialize the buffer with already existing data (otherwise populating it is a bit hairy) // this is inferior than placing the data into the securebuffer directly #[cfg(test)] pub fn new(mut src: impl AsMut<[u8]>) -> Self { let mut buffer = PamBuffer::default(); let src = src.as_mut(); buffer[..src.len()].copy_from_slice(src); wipe_memory(src); buffer } } impl Default for PamBuffer { fn default() -> Self { // SAFETY: `calloc` returns either a cleared, allocated chunk of `SIZE` bytes // or NULL to indicate that the allocation request failed let res = unsafe { libc::calloc(1, SIZE) }; if let Some(nn) = NonNull::new(res) { PamBuffer(nn.cast()) } else { alloc::handle_alloc_error(LAYOUT) } } } impl std::ops::Deref for PamBuffer { type Target = [u8]; fn deref(&self) -> &[u8] { // SAFETY: `self.0.as_ptr()` is non-null, aligned, and initialized, and points to `SIZE` bytes. // The lifetime of the slice does not exceed that of `self`. // // We make the slice one less in size to guarantee the existence of a terminating NUL. unsafe { slice::from_raw_parts(self.0.as_ptr().cast(), SIZE - 1) } } } impl std::ops::DerefMut for PamBuffer { fn deref_mut(&mut self) -> &mut [u8] { // SAFETY: see above unsafe { slice::from_raw_parts_mut(self.0.as_ptr().cast(), SIZE - 1) } } } impl Drop for PamBuffer { fn drop(&mut self) { // SAFETY: same as for `deref()` and `deref_mut()` wipe_memory(unsafe { self.0.as_mut() }); // SAFETY: `self.0.as_ptr()` was obtained via `calloc`, so calling `free` is proper. unsafe { libc::free(self.0.as_ptr().cast()) } } } /// Used to zero out memory and protect sensitive data from leaking; inspired by Conrad Kleinespel's /// Rustatic rtoolbox::SafeString, fn wipe_memory(memory: &mut [u8]) { use std::sync::atomic; let nonsense: u8 = 0x55; for c in memory { // SAFETY: `c` is safe for writes (it comes from a &mut reference) unsafe { std::ptr::write_volatile(c, nonsense) }; } atomic::fence(atomic::Ordering::SeqCst); atomic::compiler_fence(atomic::Ordering::SeqCst); } #[allow(clippy::undocumented_unsafe_blocks)] #[cfg(test)] mod test { use super::PamBuffer; #[test] fn miri_test_leaky_cstring() { let test = |text: &str| unsafe { let buf = PamBuffer::new(text.to_string().as_bytes_mut()); assert_eq!(&buf[..text.len()], text.as_bytes()); let nn = buf.leak(); let result = crate::cutils::string_from_ptr(nn.as_ptr().cast()); libc::free(nn.as_ptr().cast()); result }; assert_eq!(test(""), ""); assert_eq!(test("hello"), "hello"); } #[test] fn miri_test_wipe() { let mut memory: [u8; 3] = [1, 2, 3]; let fix = PamBuffer::new(&mut memory); assert_eq!(memory, [0x55, 0x55, 0x55]); assert_eq!(fix[0..=2], [1, 2, 3]); assert!(fix[3..].iter().all(|&x| x == 0)); std::mem::drop(fix); } } sudo-rs-0.2.5/src/pam/sys_linuxpam.rs000064400000000000000000000073471046102023000157040ustar 00000000000000/* automatically generated by rust-bindgen 0.70.1, minified by cargo-minify */ pub type pam_handle_t = u8; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct pam_message { pub msg_style: libc::c_int, pub msg: *const libc::c_char, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct pam_response { pub resp: *mut libc::c_char, pub resp_retcode: libc::c_int, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct pam_conv { pub conv: ::std::option::Option< unsafe extern "C" fn( num_msg: libc::c_int, msg: *mut *const pam_message, resp: *mut *mut pam_response, appdata_ptr: *mut libc::c_void, ) -> libc::c_int, >, pub appdata_ptr: *mut libc::c_void, } pub const PAM_SUCCESS: u32 = 0; pub const PAM_OPEN_ERR: u32 = 1; pub const PAM_SYMBOL_ERR: u32 = 2; pub const PAM_SERVICE_ERR: u32 = 3; pub const PAM_SYSTEM_ERR: u32 = 4; pub const PAM_BUF_ERR: u32 = 5; pub const PAM_PERM_DENIED: u32 = 6; pub const PAM_AUTH_ERR: u32 = 7; pub const PAM_CRED_INSUFFICIENT: u32 = 8; pub const PAM_AUTHINFO_UNAVAIL: u32 = 9; pub const PAM_USER_UNKNOWN: u32 = 10; pub const PAM_MAXTRIES: u32 = 11; pub const PAM_NEW_AUTHTOK_REQD: u32 = 12; pub const PAM_ACCT_EXPIRED: u32 = 13; pub const PAM_SESSION_ERR: u32 = 14; pub const PAM_CRED_UNAVAIL: u32 = 15; pub const PAM_CRED_EXPIRED: u32 = 16; pub const PAM_CRED_ERR: u32 = 17; pub const PAM_NO_MODULE_DATA: u32 = 18; pub const PAM_CONV_ERR: u32 = 19; pub const PAM_AUTHTOK_ERR: u32 = 20; pub const PAM_AUTHTOK_RECOVERY_ERR: u32 = 21; pub const PAM_AUTHTOK_LOCK_BUSY: u32 = 22; pub const PAM_AUTHTOK_DISABLE_AGING: u32 = 23; pub const PAM_TRY_AGAIN: u32 = 24; pub const PAM_IGNORE: u32 = 25; pub const PAM_ABORT: u32 = 26; pub const PAM_AUTHTOK_EXPIRED: u32 = 27; pub const PAM_MODULE_UNKNOWN: u32 = 28; pub const PAM_BAD_ITEM: u32 = 29; pub const PAM_SILENT: u32 = 32768; pub const PAM_DISALLOW_NULL_AUTHTOK: u32 = 1; pub const PAM_REINITIALIZE_CRED: u32 = 8; pub const PAM_CHANGE_EXPIRED_AUTHTOK: u32 = 32; pub const PAM_USER: u32 = 2; pub const PAM_TTY: u32 = 3; pub const PAM_RUSER: u32 = 8; pub const PAM_DATA_SILENT: u32 = 1073741824; pub const PAM_PROMPT_ECHO_OFF: u32 = 1; pub const PAM_PROMPT_ECHO_ON: u32 = 2; pub const PAM_ERROR_MSG: u32 = 3; pub const PAM_TEXT_INFO: u32 = 4; pub const PAM_MAX_RESP_SIZE: u32 = 512; extern "C" { pub fn pam_set_item( pamh: *mut pam_handle_t, item_type: libc::c_int, item: *const libc::c_void, ) -> libc::c_int; } extern "C" { pub fn pam_get_item( pamh: *const pam_handle_t, item_type: libc::c_int, item: *mut *const libc::c_void, ) -> libc::c_int; } extern "C" { pub fn pam_strerror(pamh: *mut pam_handle_t, errnum: libc::c_int) -> *const libc::c_char; } extern "C" { pub fn pam_getenvlist(pamh: *mut pam_handle_t) -> *mut *mut libc::c_char; } extern "C" { pub fn pam_start( service_name: *const libc::c_char, user: *const libc::c_char, pam_conversation: *const pam_conv, pamh: *mut *mut pam_handle_t, ) -> libc::c_int; } extern "C" { pub fn pam_end(pamh: *mut pam_handle_t, pam_status: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_authenticate(pamh: *mut pam_handle_t, flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_setcred(pamh: *mut pam_handle_t, flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_acct_mgmt(pamh: *mut pam_handle_t, flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_open_session(pamh: *mut pam_handle_t, flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_close_session(pamh: *mut pam_handle_t, flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_chauthtok(pamh: *mut pam_handle_t, flags: libc::c_int) -> libc::c_int; } sudo-rs-0.2.5/src/pam/sys_openpam.rs000064400000000000000000000104341046102023000154750ustar 00000000000000/* automatically generated by rust-bindgen 0.70.1, minified by cargo-minify */ pub type pam_handle_t = u8; pub type _bindgen_ty_1 = libc::c_uint; pub type _bindgen_ty_2 = libc::c_uint; pub type _bindgen_ty_3 = libc::c_int; pub type _bindgen_ty_4 = libc::c_uint; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct pam_message { pub msg_style: libc::c_int, pub msg: *mut libc::c_char, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct pam_response { pub resp: *mut libc::c_char, pub resp_retcode: libc::c_int, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct pam_conv { pub conv: ::std::option::Option< unsafe extern "C" fn( arg1: libc::c_int, arg2: *mut *const pam_message, arg3: *mut *mut pam_response, arg4: *mut libc::c_void, ) -> libc::c_int, >, pub appdata_ptr: *mut libc::c_void, } pub const PAM_SUCCESS: _bindgen_ty_1 = 0; pub const PAM_OPEN_ERR: _bindgen_ty_1 = 1; pub const PAM_SYMBOL_ERR: _bindgen_ty_1 = 2; pub const PAM_SERVICE_ERR: _bindgen_ty_1 = 3; pub const PAM_SYSTEM_ERR: _bindgen_ty_1 = 4; pub const PAM_BUF_ERR: _bindgen_ty_1 = 5; pub const PAM_CONV_ERR: _bindgen_ty_1 = 6; pub const PAM_PERM_DENIED: _bindgen_ty_1 = 7; pub const PAM_MAXTRIES: _bindgen_ty_1 = 8; pub const PAM_AUTH_ERR: _bindgen_ty_1 = 9; pub const PAM_NEW_AUTHTOK_REQD: _bindgen_ty_1 = 10; pub const PAM_CRED_INSUFFICIENT: _bindgen_ty_1 = 11; pub const PAM_AUTHINFO_UNAVAIL: _bindgen_ty_1 = 12; pub const PAM_USER_UNKNOWN: _bindgen_ty_1 = 13; pub const PAM_CRED_UNAVAIL: _bindgen_ty_1 = 14; pub const PAM_CRED_EXPIRED: _bindgen_ty_1 = 15; pub const PAM_CRED_ERR: _bindgen_ty_1 = 16; pub const PAM_ACCT_EXPIRED: _bindgen_ty_1 = 17; pub const PAM_AUTHTOK_EXPIRED: _bindgen_ty_1 = 18; pub const PAM_SESSION_ERR: _bindgen_ty_1 = 19; pub const PAM_AUTHTOK_ERR: _bindgen_ty_1 = 20; pub const PAM_AUTHTOK_RECOVERY_ERR: _bindgen_ty_1 = 21; pub const PAM_AUTHTOK_LOCK_BUSY: _bindgen_ty_1 = 22; pub const PAM_AUTHTOK_DISABLE_AGING: _bindgen_ty_1 = 23; pub const PAM_NO_MODULE_DATA: _bindgen_ty_1 = 24; pub const PAM_IGNORE: _bindgen_ty_1 = 25; pub const PAM_ABORT: _bindgen_ty_1 = 26; pub const PAM_TRY_AGAIN: _bindgen_ty_1 = 27; pub const PAM_MODULE_UNKNOWN: _bindgen_ty_1 = 28; pub const PAM_BAD_ITEM: _bindgen_ty_1 = 31; pub const PAM_PROMPT_ECHO_OFF: _bindgen_ty_2 = 1; pub const PAM_PROMPT_ECHO_ON: _bindgen_ty_2 = 2; pub const PAM_ERROR_MSG: _bindgen_ty_2 = 3; pub const PAM_TEXT_INFO: _bindgen_ty_2 = 4; pub const PAM_MAX_RESP_SIZE: _bindgen_ty_2 = 512; pub const PAM_SILENT: _bindgen_ty_3 = -2147483648; pub const PAM_DISALLOW_NULL_AUTHTOK: _bindgen_ty_3 = 1; pub const PAM_REINITIALIZE_CRED: _bindgen_ty_3 = 4; pub const PAM_CHANGE_EXPIRED_AUTHTOK: _bindgen_ty_3 = 4; pub const PAM_USER: _bindgen_ty_4 = 2; pub const PAM_TTY: _bindgen_ty_4 = 3; pub const PAM_RUSER: _bindgen_ty_4 = 8; extern "C" { pub fn pam_acct_mgmt(_pamh: *mut pam_handle_t, _flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_authenticate(_pamh: *mut pam_handle_t, _flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_chauthtok(_pamh: *mut pam_handle_t, _flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_close_session(_pamh: *mut pam_handle_t, _flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_end(_pamh: *mut pam_handle_t, _status: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_get_item( _pamh: *const pam_handle_t, _item_type: libc::c_int, _item: *mut *const libc::c_void, ) -> libc::c_int; } extern "C" { pub fn pam_getenvlist(_pamh: *mut pam_handle_t) -> *mut *mut libc::c_char; } extern "C" { pub fn pam_open_session(_pamh: *mut pam_handle_t, _flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_set_item( _pamh: *mut pam_handle_t, _item_type: libc::c_int, _item: *const libc::c_void, ) -> libc::c_int; } extern "C" { pub fn pam_setcred(_pamh: *mut pam_handle_t, _flags: libc::c_int) -> libc::c_int; } extern "C" { pub fn pam_start( _service: *const libc::c_char, _user: *const libc::c_char, _pam_conv: *const pam_conv, _pamh: *mut *mut pam_handle_t, ) -> libc::c_int; } extern "C" { pub fn pam_strerror( _pamh: *const pam_handle_t, _error_number: libc::c_int, ) -> *const libc::c_char; } sudo-rs-0.2.5/src/pam/wrapper.h000064400000000000000000000000371046102023000144210ustar 00000000000000#include sudo-rs-0.2.5/src/su/cli.rs000064400000000000000000000547351046102023000135750ustar 00000000000000use std::{mem, path::PathBuf}; use crate::common::SudoString; use super::DEFAULT_USER; #[cfg_attr(test, derive(Debug, PartialEq))] pub enum SuAction { Help(SuHelpOptions), Version(SuVersionOptions), Run(SuRunOptions), } impl SuAction { pub fn from_env() -> Result { SuOptions::parse_arguments(std::env::args())?.validate() } #[cfg(test)] pub fn parse_arguments(args: impl IntoIterator) -> Result { SuOptions::parse_arguments(args)?.validate() } #[cfg(test)] #[allow(clippy::result_large_err)] pub fn try_into_run(self) -> Result { if let Self::Run(v) = self { Ok(v) } else { Err(self) } } } #[cfg_attr(test, derive(Debug, PartialEq))] pub struct SuHelpOptions {} impl TryFrom for SuHelpOptions { type Error = String; fn try_from(mut opts: SuOptions) -> Result { let help = mem::take(&mut opts.help); debug_assert!(help); reject_all("--help", opts)?; Ok(Self {}) } } #[cfg_attr(test, derive(Debug, PartialEq))] pub struct SuVersionOptions {} impl TryFrom for SuVersionOptions { type Error = String; fn try_from(mut opts: SuOptions) -> Result { let version = mem::take(&mut opts.version); debug_assert!(version); reject_all("--version", opts)?; Ok(Self {}) } } #[derive(Debug)] #[cfg_attr(test, derive(PartialEq))] pub struct SuRunOptions { // -c pub command: Option, // -g pub group: Vec, // -l pub login: bool, // -p pub preserve_environment: bool, // -s pub shell: Option, // -G pub supp_group: Vec, // -w pub whitelist_environment: Vec, pub user: SudoString, pub arguments: Vec, } #[cfg(test)] impl Default for SuRunOptions { fn default() -> Self { Self { command: None, group: vec![], login: false, preserve_environment: false, shell: None, supp_group: vec![], whitelist_environment: vec![], user: DEFAULT_USER.into(), arguments: vec![], } } } impl TryFrom for SuRunOptions { type Error = String; fn try_from(mut opts: SuOptions) -> Result { let command = mem::take(&mut opts.command); let group = mem::take(&mut opts.group); let login = mem::take(&mut opts.login); let preserve_environment = mem::take(&mut opts.preserve_environment); // always `true`; cannot be disabled via the CLI let _pty = mem::take(&mut opts.pty); let shell = mem::take(&mut opts.shell); let supp_group = mem::take(&mut opts.supp_group); let whitelist_environment = mem::take(&mut opts.whitelist_environment); let mut positional_args = mem::take(&mut opts.positional_args); reject_all("run mode", opts)?; let user = if positional_args.is_empty() { DEFAULT_USER.to_string() } else { positional_args.remove(0) }; let arguments = positional_args; Ok(Self { command, group, login, preserve_environment, shell, supp_group, whitelist_environment, user: SudoString::try_from(user).map_err(|err| err.to_string())?, arguments, }) } } fn reject_all(context: &str, opts: SuOptions) -> Result<(), String> { macro_rules! ensure_options_absent { ($($opt:ident,)*) => { let SuOptions { $($opt),* } = opts; $(if !$opt.is_absent() { let name = concat!("--", stringify!($opt)).replace('_', "-"); return Err(format!("{context} conflicts with {name}")); })* }; } ensure_options_absent! { command, group, help, login, preserve_environment, pty, shell, supp_group, version, whitelist_environment, positional_args, }; if !positional_args.is_absent() { return Err(format!("{context} conflicts with positional argument")); } Ok(()) } trait IsAbsent { fn is_absent(&self) -> bool; } impl IsAbsent for bool { fn is_absent(&self) -> bool { !*self } } impl IsAbsent for Option { fn is_absent(&self) -> bool { self.is_none() } } impl IsAbsent for Vec { fn is_absent(&self) -> bool { self.is_empty() } } #[derive(Debug, Default, PartialEq)] struct SuOptions { // -c command: Option, // -g group: Vec, // -h help: bool, // -l login: bool, // -p preserve_environment: bool, // -P pty: bool, // -s shell: Option, // -G supp_group: Vec, // -V version: bool, // -w whitelist_environment: Vec, positional_args: Vec, } type OptionSetter = fn(&mut SuOptions, Option) -> Result<(), String>; struct SuOption { short: char, long: &'static str, takes_argument: bool, set: OptionSetter, } impl SuOptions { const SU_OPTIONS: &'static [SuOption] = &[ SuOption { short: 'c', long: "command", takes_argument: true, set: |sudo_options, argument| { if argument.is_some() { sudo_options.command = argument; Ok(()) } else { Err("no command provided".into()) } }, }, SuOption { short: 'g', long: "group", takes_argument: true, set: |sudo_options, argument| { if let Some(value) = argument { sudo_options.group.push(SudoString::from_cli_string(value)); Ok(()) } else { Err("no group provided".into()) } }, }, SuOption { short: 'G', long: "supp-group", takes_argument: true, set: |sudo_options, argument| { if let Some(value) = argument { sudo_options .supp_group .push(SudoString::from_cli_string(value)); Ok(()) } else { Err("no supplementary group provided".into()) } }, }, SuOption { short: 'l', long: "login", takes_argument: false, set: |sudo_options, _| { if sudo_options.login { Err(more_than_once("--login")) } else { sudo_options.login = true; Ok(()) } }, }, SuOption { short: 'p', long: "preserve-environment", takes_argument: false, set: |sudo_options, _| { if sudo_options.preserve_environment { Err(more_than_once("--preserve-environment")) } else { sudo_options.preserve_environment = true; Ok(()) } }, }, SuOption { short: 'm', long: "preserve-environment", takes_argument: false, set: |sudo_options, _| { if sudo_options.preserve_environment { Err(more_than_once("--preserve-environment")) } else { sudo_options.preserve_environment = true; Ok(()) } }, }, SuOption { short: 'P', long: "pty", takes_argument: false, set: |sudo_options, _| { if sudo_options.pty { Err(more_than_once("--pty")) } else { sudo_options.pty = true; Ok(()) } }, }, SuOption { short: 's', long: "shell", takes_argument: true, set: |sudo_options, argument| { if let Some(path) = argument { sudo_options.shell = Some(PathBuf::from(path)); Ok(()) } else { Err("no shell provided".into()) } }, }, SuOption { short: 'w', long: "whitelist-environment", takes_argument: true, set: |sudo_options, argument| { if let Some(list) = argument { let values: Vec = list.split(',').map(str::to_string).collect(); sudo_options.whitelist_environment.extend(values); Ok(()) } else { Err("no environment whitelist provided".into()) } }, }, SuOption { short: 'V', long: "version", takes_argument: false, set: |sudo_options, _| { if sudo_options.version { Err(more_than_once("--version")) } else { sudo_options.version = true; Ok(()) } }, }, SuOption { short: 'h', long: "help", takes_argument: false, set: |sudo_options, _| { if sudo_options.help { Err(more_than_once("--help")) } else { sudo_options.help = true; Ok(()) } }, }, ]; /// parse su arguments into SuOptions struct fn parse_arguments(arguments: impl IntoIterator) -> Result { let mut options: SuOptions = SuOptions::default(); let mut arg_iter = arguments.into_iter().skip(1); while let Some(arg) = arg_iter.next() { // - or -l or --login indicates a login shell should be started if arg == "-" { if options.login { return Err(more_than_once("--login")); } else { options.login = true; } } else if arg == "--" { // only positional arguments after this point options.positional_args.extend(arg_iter); break; // if the argument starts with -- it must be a full length option name } else if let Some(unprefixed) = arg.strip_prefix("--") { // parse assignments like '--group=ferris' if let Some((key, value)) = unprefixed.split_once('=') { // lookup the option by name if let Some(option) = Self::SU_OPTIONS.iter().find(|o| o.long == key) { // the value is already present, when the option does not take any arguments this results in an error if option.takes_argument { (option.set)(&mut options, Some(value.to_string()))?; } else { Err(format!("'--{}' does not take any arguments", option.long))?; } } else { Err(format!("unrecognized option '{}'", arg))?; } // lookup the option } else if let Some(option) = Self::SU_OPTIONS.iter().find(|o| o.long == unprefixed) { // try to parse an argument when the option needs an argument if option.takes_argument { let next_arg = arg_iter.next(); (option.set)(&mut options, next_arg)?; } else { (option.set)(&mut options, None)?; } } else { Err(format!("unrecognized option '{}'", arg))?; } } else if let Some(unprefixed) = arg.strip_prefix('-') { // flags can be grouped, so we loop over the the characters let mut chars = unprefixed.chars(); while let Some(curr) = chars.next() { // lookup the option if let Some(option) = Self::SU_OPTIONS.iter().find(|o| o.short == curr) { // try to parse an argument when one is necessary, either the rest of the current flag group or the next argument let rest = chars.as_str(); if option.takes_argument { let next_arg = if rest.is_empty() { arg_iter.next() } else { Some(rest.to_string()) }; (option.set)(&mut options, next_arg)?; // stop looping over flags if the current flag takes an argument break; } else { // parse flag without argument (option.set)(&mut options, None)?; } } else { Err(format!("unrecognized option '{}'", curr))?; } } } else { options.positional_args.push(arg); } } Ok(options) } fn validate(self) -> Result { let action = if self.help { SuAction::Help(self.try_into()?) } else if self.version { SuAction::Version(self.try_into()?) } else { SuAction::Run(self.try_into()?) }; Ok(action) } } fn more_than_once(flag: &str) -> String { format!("argument '{flag}' was provided more than once, but cannot be used multiple times") } #[cfg(test)] mod tests { use std::vec; use super::{SuAction, SuHelpOptions, SuOptions, SuRunOptions, SuVersionOptions}; fn parse(args: &[&str]) -> SuAction { let mut args = args.iter().map(|s| s.to_string()).collect::>(); args.insert(0, "/bin/su".to_string()); SuOptions::parse_arguments(args) .unwrap() .validate() .unwrap() } #[test] fn it_parses_group() { let expected = SuAction::Run(SuRunOptions { group: vec!["ferris".into()], ..<_>::default() }); assert_eq!(expected, parse(&["-g", "ferris"])); assert_eq!(expected, parse(&["-gferris"])); assert_eq!(expected, parse(&["--group", "ferris"])); assert_eq!(expected, parse(&["--group=ferris"])); } #[test] fn it_parses_shell_default() { let result = parse(&["--shell", "/bin/bash"]); assert_eq!( result, SuAction::Run(SuRunOptions { shell: Some("/bin/bash".into()), ..<_>::default() }) ); } #[test] fn it_parses_whitelist() { let result = parse(&["-w", "FOO,BAR"]); assert_eq!( result, SuAction::Run(SuRunOptions { whitelist_environment: vec!["FOO".to_string(), "BAR".to_string()], ..<_>::default() }) ); } #[test] fn it_parses_combined_options() { let expected = SuAction::Run(SuRunOptions { login: true, ..<_>::default() }); assert_eq!(expected, parse(&["-Pl"])); assert_eq!(expected, parse(&["-lP"])); } #[test] fn it_parses_combined_options_and_arguments() { let expected = SuAction::Run(SuRunOptions { login: true, shell: Some("/bin/bash".into()), ..<_>::default() }); assert_eq!(expected, parse(&["-Pls/bin/bash"])); assert_eq!(expected, parse(&["-Pls", "/bin/bash"])); assert_eq!(expected, parse(&["-Pl", "-s/bin/bash"])); assert_eq!(expected, parse(&["-lP", "-s", "/bin/bash"])); assert_eq!(expected, parse(&["-lP", "--shell=/bin/bash"])); assert_eq!(expected, parse(&["-lP", "--shell", "/bin/bash"])); } #[test] fn it_parses_an_user() { let expected = SuAction::Run(SuRunOptions { user: "ferris".into(), ..<_>::default() }); assert_eq!(expected, parse(&["-P", "ferris"])); assert_eq!(expected, parse(&["ferris", "-P"])); } #[test] fn it_parses_arguments() { let expected = SuAction::Run(SuRunOptions { user: "ferris".into(), arguments: vec!["script.sh".to_string()], ..<_>::default() }); assert_eq!(expected, parse(&["-P", "ferris", "script.sh"])); } #[test] fn it_parses_command() { let expected = SuAction::Run(SuRunOptions { command: Some("'echo hi'".to_string()), ..<_>::default() }); assert_eq!(expected, parse(&["-c", "'echo hi'"])); assert_eq!(expected, parse(&["-c'echo hi'"])); assert_eq!(expected, parse(&["--command", "'echo hi'"])); assert_eq!(expected, parse(&["--command='echo hi'"])); let expected = SuAction::Run(SuRunOptions { command: Some("env".to_string()), ..<_>::default() }); assert_eq!(expected, parse(&["-c", "env"])); assert_eq!(expected, parse(&["-cenv"])); assert_eq!(expected, parse(&["--command", "env"])); assert_eq!(expected, parse(&["--command=env"])); } #[test] fn it_parses_supplementary_group() { let expected = SuAction::Run(SuRunOptions { supp_group: vec!["ferris".into()], ..<_>::default() }); assert_eq!(expected, parse(&["-G", "ferris"])); assert_eq!(expected, parse(&["-Gferris"])); assert_eq!(expected, parse(&["--supp-group", "ferris"])); assert_eq!(expected, parse(&["--supp-group=ferris"])); } #[test] fn it_parses_multiple_supplementary_groups() { let expected = SuAction::Run(SuRunOptions { supp_group: vec!["ferris".into(), "krabbetje".into(), "krabbe".into()], ..<_>::default() }); assert_eq!( expected, parse(&["-G", "ferris", "-G", "krabbetje", "--supp-group", "krabbe"]) ); } #[test] fn it_parses_login() { let expected = SuAction::Run(SuRunOptions { login: true, ..<_>::default() }); assert_eq!(expected, parse(&["-"])); assert_eq!(expected, parse(&["-l"])); assert_eq!(expected, parse(&["--login"])); } #[test] fn it_parses_pty() { let expected = SuAction::Run(<_>::default()); assert_eq!(expected, parse(&["-P"])); assert_eq!(expected, parse(&["--pty"])); } #[test] fn it_parses_shell() { let expected = SuAction::Run(SuRunOptions { shell: Some("some-shell".into()), ..<_>::default() }); assert_eq!(expected, parse(&["-s", "some-shell"])); assert_eq!(expected, parse(&["-ssome-shell"])); assert_eq!(expected, parse(&["--shell", "some-shell"])); assert_eq!(expected, parse(&["--shell=some-shell"])); } #[test] fn it_parses_whitelist_environment() { let expected = SuAction::Run(SuRunOptions { whitelist_environment: vec!["FOO".to_string(), "BAR".to_string()], ..<_>::default() }); assert_eq!(expected, parse(&["-w", "FOO,BAR"])); assert_eq!(expected, parse(&["-wFOO,BAR"])); assert_eq!(expected, parse(&["--whitelist-environment", "FOO,BAR"])); assert_eq!(expected, parse(&["--whitelist-environment=FOO,BAR"])); } #[test] fn it_parses_help() { let expected = SuAction::Help(SuHelpOptions {}); assert_eq!(expected, parse(&["-h"])); assert_eq!(expected, parse(&["--help"])); } #[test] fn it_parses_version() { let expected = SuAction::Version(SuVersionOptions {}); assert_eq!(expected, parse(&["-V"])); assert_eq!(expected, parse(&["--version"])); } #[test] fn short_flag_whitespace() { let expected = SuAction::Run(SuRunOptions { group: vec![" ".into()], ..<_>::default() }); assert_eq!(expected, parse(&["-g "])); } #[test] fn short_flag_whitespace_positional_argument() { let expected = SuAction::Run(SuRunOptions { group: vec![" ".into()], user: "ghost".into(), ..<_>::default() }); assert_eq!(expected, parse(&["-g ", "ghost"])); } #[test] fn long_flag_equal_whitespace() { let expected = SuAction::Run(SuRunOptions { group: vec![" ".into()], ..<_>::default() }); assert_eq!(expected, parse(&["--group= "])); } #[test] fn flag_after_positional_argument() { let expected = SuAction::Run(SuRunOptions { arguments: vec![], login: true, user: "ferris".into(), ..<_>::default() }); assert_eq!(expected, parse(&["ferris", "-l"])); } #[test] fn flags_after_dash() { let expected = SuAction::Run(SuRunOptions { command: Some("echo".to_string()), login: true, ..<_>::default() }); assert_eq!(expected, parse(&["-", "-c", "echo"])); } #[test] fn only_positional_args_after_dashdash() { let expected = SuAction::Run(SuRunOptions { user: "ferris".into(), arguments: vec!["-c".to_string(), "echo".to_string()], ..<_>::default() }); assert_eq!(expected, parse(&["--", "ferris", "-c", "echo"])); } #[test] fn repeated_boolean_flag() { let f = |s: &str| s.to_string(); assert!(SuOptions::parse_arguments(["su", "-l", "-l"].map(f)).is_err()); assert!(SuOptions::parse_arguments(["su", "-", "-l"].map(f)).is_err()); assert!(SuOptions::parse_arguments(["su", "--login", "-l"].map(f)).is_err()); assert!(SuOptions::parse_arguments(["su", "-p", "-p"].map(f)).is_err()); assert!(SuOptions::parse_arguments(["su", "-p", "--preserve-environment"].map(f)).is_err()); } } sudo-rs-0.2.5/src/su/context.rs000064400000000000000000000207301046102023000144760ustar 00000000000000use std::{ collections::HashMap, env, ffi::OsString, fs, path::{Path, PathBuf}, }; use crate::common::{error::Error, resolve::CurrentUser}; use crate::exec::RunOptions; use crate::log::user_warn; use crate::system::{Group, Process, User}; use crate::{common::resolve::is_valid_executable, system::interface::UserId}; type Environment = HashMap; use super::cli::SuRunOptions; const VALID_LOGIN_SHELLS_LIST: &str = "/etc/shells"; const FALLBACK_LOGIN_SHELL: &str = "/bin/sh"; const PATH_MAILDIR: &str = env!("PATH_MAILDIR"); const PATH_DEFAULT: &str = env!("SU_PATH_DEFAULT"); const PATH_DEFAULT_ROOT: &str = env!("SU_PATH_DEFAULT_ROOT"); #[derive(Debug)] pub(crate) struct SuContext { command: PathBuf, arguments: Vec, pub(crate) options: SuRunOptions, pub(crate) environment: Environment, pub(crate) user: User, pub(crate) requesting_user: CurrentUser, group: Group, pub(crate) process: Process, } /// check that a shell is not restricted / exists in /etc/shells fn is_restricted(shell: &Path) -> bool { if let Some(pattern) = shell.as_os_str().to_str() { if let Ok(contents) = fs::read_to_string(VALID_LOGIN_SHELLS_LIST) { return !contents.lines().any(|l| l == pattern); } else { return FALLBACK_LOGIN_SHELL != pattern; } } true } impl SuContext { pub(crate) fn from_env(options: SuRunOptions) -> Result { let process = crate::system::Process::new(); // resolve environment, reset if this is a login let mut environment = if options.login { Environment::default() } else { env::vars_os().collect::() }; // Don't reset the environment variables specified in the // comma-separated list when clearing the environment for // --login. The whitelist is ignored for the environment // variables HOME, SHELL, USER, LOGNAME, and PATH. if options.login { if let Some(value) = env::var_os("TERM") { environment.insert("TERM".into(), value); } for name in options.whitelist_environment.iter() { if let Some(value) = env::var_os(name) { environment.insert(name.into(), value); } } } let requesting_user = CurrentUser::resolve()?; // resolve target user let mut user = User::from_name(options.user.as_cstr())? .ok_or_else(|| Error::UserNotFound(options.user.clone().into()))?; // check the current user is root let is_current_root = User::real_uid() == UserId::ROOT; let is_target_root = options.user == "root"; // only root can set a (additional) group if !is_current_root && (!options.supp_group.is_empty() || !options.group.is_empty()) { return Err(Error::Options( "only root can specify alternative groups".to_owned(), )); } // resolve target group let mut group = user.primary_group()?; if !options.supp_group.is_empty() || !options.group.is_empty() { user.groups.clear(); } for group_name in options.group.iter() { let primary_group = Group::from_name(group_name.as_cstr())? .ok_or_else(|| Error::GroupNotFound(group_name.clone().into()))?; // last argument is the primary group group = primary_group.clone(); user.groups.insert(0, primary_group.gid); } // add additional group if current user is root for (index, group_name) in options.supp_group.iter().enumerate() { let supp_group = Group::from_name(group_name.as_cstr())? .ok_or_else(|| Error::GroupNotFound(group_name.clone().into()))?; // set primary group if none was provided if index == 0 && options.group.is_empty() { group = supp_group.clone(); } user.groups.push(supp_group.gid); } // the shell specified with --shell // the shell specified in the environment variable SHELL, if the --preserve-environment option is used // the shell listed in the passwd entry of the target user let user_shell = user.shell.clone(); let mut command = options .shell .as_ref() .cloned() .or_else(|| { if options.preserve_environment && is_current_root { environment.get(&OsString::from("SHELL")).map(|v| v.into()) } else { None } }) .unwrap_or(user_shell.clone()); // If the target user has a restricted shell (i.e. the shell field of // this user's entry in /etc/passwd is not listed in /etc/shells), // then the --shell option or the $SHELL environment variable won't be // taken into account, unless su is called by root. if is_restricted(user_shell.as_path()) && !is_current_root { user_warn!( "using restricted shell {}", user_shell.as_os_str().to_string_lossy() ); command = user_shell; } if !command.exists() { return Err(Error::CommandNotFound(command)); } if !is_valid_executable(&command) { return Err(Error::InvalidCommand(command)); } // pass command to shell let arguments = if let Some(command) = &options.command { vec!["-c".to_owned(), command.to_owned()] } else { options.arguments.clone() }; if options.login { environment.insert( "PATH".into(), if is_target_root { PATH_DEFAULT_ROOT } else { PATH_DEFAULT } .into(), ); } if !options.preserve_environment { // extend environment with fixed variables environment.insert("HOME".into(), user.home.clone().into()); environment.insert("SHELL".into(), command.clone().into()); environment.insert( "MAIL".into(), format!("{PATH_MAILDIR}/{}", user.name).into(), ); if !is_target_root || options.login { environment.insert("USER".into(), options.user.clone().into()); environment.insert("LOGNAME".into(), options.user.clone().into()); } } Ok(SuContext { command, arguments, options, environment, user, requesting_user, group, process, }) } } impl SuContext { pub(crate) fn as_run_options(&self) -> RunOptions<'_> { RunOptions { command: &self.command, arguments: &self.arguments, arg0: None, chdir: None, is_login: self.options.login, user: &self.user, group: &self.group, use_pty: true, } } } #[cfg(test)] mod tests { use std::path::PathBuf; use crate::{ common::Error, su::cli::{SuAction, SuRunOptions}, }; use super::SuContext; fn get_options(args: &[&str]) -> SuRunOptions { let mut args = args.iter().map(|s| s.to_string()).collect::>(); args.insert(0, "/bin/su".to_string()); SuAction::parse_arguments(args) .unwrap() .try_into_run() .unwrap() } #[test] fn su_to_root() { let options = get_options(&["root"]); let context = SuContext::from_env(options).unwrap(); assert_eq!(context.user.name, "root"); } #[test] fn group_as_non_root() { let options = get_options(&["-g", "root"]); let result = SuContext::from_env(options); let expected = Error::Options("only root can specify alternative groups".to_owned()); assert!(result.is_err()); assert_eq!(format!("{}", result.err().unwrap()), format!("{expected}")); } #[test] fn invalid_shell() { let options = get_options(&["-s", "/not/a/shell"]); let result = SuContext::from_env(options); let expected = Error::CommandNotFound(PathBuf::from("/not/a/shell")); assert!(result.is_err()); assert_eq!(format!("{}", result.err().unwrap()), format!("{expected}")); } } sudo-rs-0.2.5/src/su/help.rs000064400000000000000000000017421046102023000137440ustar 00000000000000pub const USAGE_MSG: &str = "Usage: su [options] [-] [ [...]]"; const DESCRIPTOR: &str = "Change the effective user ID and group ID to that of . A mere - implies -l. If is not given, root is assumed."; const HELP_MSG: &str = "Options: -m, -p, --preserve-environment do not reset environment variables -w, --whitelist-environment don't reset specified variables -g, --group specify the primary group -G, --supp-group specify a supplemental group -, -l, --login make the shell a login shell -c, --command pass a single command to the shell with -c -s, --shell run if /etc/shells allows it -P, --pty create a new pseudo-terminal -h, --help display this help -V, --version display version "; pub fn long_help_message() -> String { format!("{USAGE_MSG}\n\n{DESCRIPTOR}\n\n{HELP_MSG}") } sudo-rs-0.2.5/src/su/mod.rs000064400000000000000000000105731046102023000135750ustar 00000000000000#![forbid(unsafe_code)] use crate::common::error::Error; use crate::exec::ExitReason; use crate::log::user_warn; use crate::pam::{PamContext, PamError, PamErrorType}; use crate::system::term::current_tty_name; use std::{env, process}; use cli::SuAction; use context::SuContext; use help::{long_help_message, USAGE_MSG}; use self::cli::SuRunOptions; mod cli; mod context; mod help; const DEFAULT_USER: &str = "root"; const VERSION: &str = env!("CARGO_PKG_VERSION"); fn authenticate(requesting_user: &str, user: &str, login: bool) -> Result { // FIXME make it configurable by the packager let context = if login && cfg!(target_os = "linux") { "su-l" } else { "su" }; let use_stdin = true; let mut pam = PamContext::new_cli("su", context, use_stdin, false, false, false, Some(user))?; pam.set_requesting_user(requesting_user)?; // attempt to set the TTY this session is communicating on if let Ok(pam_tty) = current_tty_name() { pam.set_tty(&pam_tty)?; } pam.mark_silent(true); pam.mark_allow_null_auth_token(false); pam.set_user(user)?; let mut max_tries = 3; let mut current_try = 0; loop { current_try += 1; match pam.authenticate() { // there was no error, so authentication succeeded Ok(_) => break, // maxtries was reached, pam does not allow any more tries Err(PamError::Pam(PamErrorType::MaxTries)) => { return Err(Error::MaxAuthAttempts(current_try)); } // there was an authentication error, we can retry Err(PamError::Pam(PamErrorType::AuthError)) => { max_tries -= 1; if max_tries == 0 { return Err(Error::MaxAuthAttempts(current_try)); } else { user_warn!("Authentication failed, try again."); } } // there was another pam error, return the error Err(e) => { return Err(e.into()); } } } pam.validate_account_or_change_auth_token()?; pam.open_session()?; Ok(pam) } fn run(options: SuRunOptions) -> Result<(), Error> { // lookup user and build context object let context = SuContext::from_env(options)?; // authenticate the target user let mut pam: PamContext = authenticate( &context.requesting_user.name, &context.user.name, context.options.login, )?; // su in all cases uses PAM (pam_getenvlist(3)) to do the // final environment modification. Command-line options such as // --login and --preserve-environment affect the environment before // it is modified by PAM. let mut environment = context.environment.clone(); environment.extend(pam.env()?); let pid = context.process.pid; // run command and return corresponding exit code let command_exit_reason = crate::exec::run_command(context.as_run_options(), environment); pam.close_session(); match command_exit_reason? { ExitReason::Code(code) => process::exit(code), ExitReason::Signal(signal) => { crate::system::kill(pid, signal)?; } } Ok(()) } pub fn main() { crate::log::SudoLogger::new("su: ").into_global_logger(); let action = match SuAction::from_env() { Ok(action) => action, Err(error) => { println_ignore_io_error!("su: {error}\n{USAGE_MSG}"); std::process::exit(1); } }; match action { SuAction::Help(_) => { println_ignore_io_error!("{}", long_help_message()); std::process::exit(0); } SuAction::Version(_) => { eprintln_ignore_io_error!("su-rs {VERSION}"); std::process::exit(0); } SuAction::Run(options) => match run(options) { Err(Error::CommandNotFound(c)) => { eprintln_ignore_io_error!("su: {}", Error::CommandNotFound(c)); std::process::exit(127); } Err(Error::InvalidCommand(c)) => { eprintln_ignore_io_error!("su: {}", Error::InvalidCommand(c)); std::process::exit(126); } Err(error) => { eprintln_ignore_io_error!("su: {error}"); std::process::exit(1); } _ => {} }, }; } sudo-rs-0.2.5/src/sudo/cli/help.rs000064400000000000000000000033451046102023000150370ustar 00000000000000pub const USAGE_MSG: &str = "\ usage: sudo -h | -K | -k | -V usage: sudo -v [-BknS] [-g group] [-u user] usage: sudo -l [-BknS] [-g group] [-U user] [-u user] [command [arg ...]] usage: sudo [-BknS] [-D directory] [-g group] [-u user] [-i | -s] [command [arg ...]] usage: sudo -e [-BknS] [-D directory] [-g group] [-u user] file ..."; const DESCRIPTOR: &str = "sudo - run commands as another user"; const HELP_MSG: &str = "Options: -B, --bell ring bell when prompting -D, --chdir=directory change the working directory before running command -g, --group=group run command as the specified group name or ID -h, --help display help message and exit -i, --login run login shell as the target user; a command may also be specified -K, --remove-timestamp remove timestamp file completely -k, --reset-timestamp invalidate timestamp file -l, --list list user's privileges or check a specific command; use twice for longer format -n, --non-interactive non-interactive mode, no prompts are used -S, --stdin read password from standard input -s, --shell run shell as the target user; a command may also be specified -U, --other-user=user in list mode, display privileges for user -u, --user=user run command (or edit file) as specified user name or ID -V, --version display version information and exit -v, --validate update user's timestamp without running a command -- stop processing command line arguments"; pub fn long_help_message() -> String { format!("{DESCRIPTOR}\n{USAGE_MSG}\n{HELP_MSG}") } sudo-rs-0.2.5/src/sudo/cli/mod.rs000064400000000000000000000610661046102023000146720ustar 00000000000000#![forbid(unsafe_code)] use std::{borrow::Cow, mem}; use crate::common::{SudoPath, SudoString}; pub mod help; #[cfg(test)] mod tests; // remove dead_code when sudoedit has been implemented #[allow(dead_code)] pub enum SudoAction { Edit(SudoEditOptions), Help(SudoHelpOptions), List(SudoListOptions), RemoveTimestamp(SudoRemoveTimestampOptions), ResetTimestamp(SudoResetTimestampOptions), Run(SudoRunOptions), Validate(SudoValidateOptions), Version(SudoVersionOptions), } impl SudoAction { /// try to parse and environment variable assignment /// parse command line arguments from the environment and handle errors pub fn from_env() -> Result { Self::try_parse_from(std::env::args()) } pub fn try_parse_from(iter: I) -> Result where I: IntoIterator, T: Into + Clone, { let opts = SudoOptions::try_parse_from(iter)?; opts.validate() } #[cfg(test)] #[must_use] pub fn is_edit(&self) -> bool { matches!(self, Self::Edit(..)) } #[cfg(test)] #[must_use] pub fn is_help(&self) -> bool { matches!(self, Self::Help(..)) } #[cfg(test)] #[must_use] pub fn is_remove_timestamp(&self) -> bool { matches!(self, Self::RemoveTimestamp(..)) } #[cfg(test)] #[must_use] pub fn is_reset_timestamp(&self) -> bool { matches!(self, Self::ResetTimestamp(..)) } #[cfg(test)] #[must_use] pub fn is_list(&self) -> bool { matches!(self, Self::List(..)) } #[cfg(test)] #[must_use] pub fn is_version(&self) -> bool { matches!(self, Self::Version(..)) } #[cfg(test)] #[must_use] pub fn is_validate(&self) -> bool { matches!(self, Self::Validate(..)) } #[cfg(test)] #[allow(clippy::result_large_err)] pub fn try_into_run(self) -> Result { if let Self::Run(v) = self { Ok(v) } else { Err(self) } } #[cfg(test)] #[must_use] pub fn is_run(&self) -> bool { matches!(self, Self::Run(..)) } } // sudo -h | -K | -k | -V pub struct SudoHelpOptions {} impl TryFrom for SudoHelpOptions { type Error = String; fn try_from(mut opts: SudoOptions) -> Result { // see `SudoOptions::validate` let help = mem::take(&mut opts.help); debug_assert!(help); reject_all("--help", opts)?; Ok(Self {}) } } // sudo -h | -K | -k | -V pub struct SudoVersionOptions {} impl TryFrom for SudoVersionOptions { type Error = String; fn try_from(mut opts: SudoOptions) -> Result { // see `SudoOptions::validate` let version = mem::take(&mut opts.version); debug_assert!(version); reject_all("--version", opts)?; Ok(Self {}) } } // sudo -h | -K | -k | -V pub struct SudoRemoveTimestampOptions {} impl TryFrom for SudoRemoveTimestampOptions { type Error = String; fn try_from(mut opts: SudoOptions) -> Result { // see `SudoOptions::validate` let remove_timestamp = mem::take(&mut opts.remove_timestamp); debug_assert!(remove_timestamp); reject_all("--remove-timestamp", opts)?; Ok(Self {}) } } // sudo -h | -K | -k | -V pub struct SudoResetTimestampOptions {} impl TryFrom for SudoResetTimestampOptions { type Error = String; fn try_from(mut opts: SudoOptions) -> Result { // see `SudoOptions::validate` let reset_timestamp = mem::take(&mut opts.reset_timestamp); debug_assert!(reset_timestamp); reject_all("--reset-timestamp", opts)?; Ok(Self {}) } } // sudo -v [-ABkNnS] [-g group] [-h host] [-p prompt] [-u user] pub struct SudoValidateOptions { // -B pub bell: bool, // -k pub reset_timestamp: bool, // -n pub non_interactive: bool, // -S pub stdin: bool, // -p pub prompt: Option, // -g pub group: Option, // -u pub user: Option, } impl TryFrom for SudoValidateOptions { type Error = String; fn try_from(mut opts: SudoOptions) -> Result { // see `SudoOptions::validate` let validate = mem::take(&mut opts.validate); debug_assert!(validate); let bell = mem::take(&mut opts.bell); let reset_timestamp = mem::take(&mut opts.reset_timestamp); let non_interactive = mem::take(&mut opts.non_interactive); let stdin = mem::take(&mut opts.stdin); let prompt = mem::take(&mut opts.prompt); let group = mem::take(&mut opts.group); let user = mem::take(&mut opts.user); if bell && stdin { return Err("--bell conflicts with --stdin".into()); } reject_all("--validate", opts)?; Ok(Self { bell, reset_timestamp, non_interactive, stdin, prompt, group, user, }) } } // sudo -e [-ABkNnS] [-r role] [-t type] [-C num] [-D directory] [-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] file ... #[allow(dead_code)] pub struct SudoEditOptions { // -B pub bell: bool, // -k pub reset_timestamp: bool, // -n pub non_interactive: bool, // -S pub stdin: bool, // -p pub prompt: Option, // -D pub chdir: Option, // -g pub group: Option, // -u pub user: Option, pub positional_args: Vec, } impl TryFrom for SudoEditOptions { type Error = String; fn try_from(mut opts: SudoOptions) -> Result { // see `SudoOptions::validate` let edit = mem::take(&mut opts.edit); debug_assert!(edit); let bell = mem::take(&mut opts.bell); let reset_timestamp = mem::take(&mut opts.reset_timestamp); let non_interactive = mem::take(&mut opts.non_interactive); let stdin = mem::take(&mut opts.stdin); let prompt = mem::take(&mut opts.prompt); let chdir = mem::take(&mut opts.chdir); let group = mem::take(&mut opts.group); let user = mem::take(&mut opts.user); let positional_args = mem::take(&mut opts.positional_args); if bell && stdin { return Err("--bell conflicts with --stdin".into()); } reject_all("--edit", opts)?; if positional_args.is_empty() { return Err("must specify at least one file path".into()); } Ok(Self { bell, reset_timestamp, non_interactive, stdin, prompt, chdir, group, user, positional_args, }) } } // sudo -l [-ABkNnS] [-g group] [-h host] [-p prompt] [-U user] [-u user] [command [arg ...]] pub struct SudoListOptions { // -B pub bell: bool, // -l OR -l -l pub list: List, // -k pub reset_timestamp: bool, // -n pub non_interactive: bool, // -S pub stdin: bool, // -p pub prompt: Option, // -g pub group: Option, // -U pub other_user: Option, // -u pub user: Option, pub positional_args: Vec, } impl TryFrom for SudoListOptions { type Error = String; fn try_from(mut opts: SudoOptions) -> Result { let bell = mem::take(&mut opts.bell); let list = opts.list.take().unwrap(); let reset_timestamp = mem::take(&mut opts.reset_timestamp); let non_interactive = mem::take(&mut opts.non_interactive); let stdin = mem::take(&mut opts.stdin); let prompt = mem::take(&mut opts.prompt); let group = mem::take(&mut opts.group); let other_user = mem::take(&mut opts.other_user); let user = mem::take(&mut opts.user); let positional_args = mem::take(&mut opts.positional_args); if bell && stdin { return Err("--bell conflicts with --stdin".into()); } // when present, `-u` must be accompanied by a command let has_command = !positional_args.is_empty(); let valid_user_flag = user.is_none() || has_command; if !valid_user_flag { return Err("'--user' flag must be accompanied by a command".into()); } reject_all("--list", opts)?; Ok(Self { bell, list, reset_timestamp, non_interactive, stdin, prompt, group, other_user, user, positional_args, }) } } // sudo [-ABbEHnPS] [-C num] [-D directory] [-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] [VAR=value] [-i | -s] [command [arg ...]] pub struct SudoRunOptions { // -B pub bell: bool, // -E pub preserve_env: PreserveEnv, // -k pub reset_timestamp: bool, // -n pub non_interactive: bool, // -S pub stdin: bool, // -p pub prompt: Option, // -D pub chdir: Option, // -g pub group: Option, // -u pub user: Option, // VAR=value pub env_var_list: Vec<(String, String)>, // -i pub login: bool, // -s pub shell: bool, pub positional_args: Vec, } impl TryFrom for SudoRunOptions { type Error = String; fn try_from(mut opts: SudoOptions) -> Result { let bell = mem::take(&mut opts.bell); let preserve_env = mem::take(&mut opts.preserve_env); let reset_timestamp = mem::take(&mut opts.reset_timestamp); let non_interactive = mem::take(&mut opts.non_interactive); let stdin = mem::take(&mut opts.stdin); let prompt = mem::take(&mut opts.prompt); let chdir = mem::take(&mut opts.chdir); let group = mem::take(&mut opts.group); let user = mem::take(&mut opts.user); let env_var_list = mem::take(&mut opts.env_var_list); let login = mem::take(&mut opts.login); let shell = mem::take(&mut opts.shell); let positional_args = mem::take(&mut opts.positional_args); if bell && stdin { return Err("--bell conflicts with --stdin".into()); } let context = match (login, shell, positional_args.is_empty()) { (true, false, _) => "--login", (false, true, _) => "--shell", (false, false, false) => "command (positional argument)", (true, true, _) => return Err("--login conflicts with --shell".into()), (false, false, true) => { if cfg!(debug_assertions) { // see `SudoOptions::validate` panic!(); } else { return Err( "expected one of: --login, --shell, a command as a positional argument" .into(), ); } } }; reject_all(context, opts)?; Ok(Self { bell, preserve_env, reset_timestamp, non_interactive, stdin, prompt, chdir, group, user, env_var_list, login, shell, positional_args, }) } } #[derive(Default)] struct SudoOptions { // -B bell: bool, // -D chdir: Option, // -g group: Option, // -i login: bool, // -n non_interactive: bool, // -U other_user: Option, // -E preserve_env: PreserveEnv, // -s shell: bool, // -S stdin: bool, // -p prompt: Option, // -u user: Option, // additional environment env_var_list: Vec<(String, String)>, /* actions */ // -e edit: bool, // -h help: bool, // -l list: Option, // -K remove_timestamp: bool, // -k reset_timestamp: bool, // -v validate: bool, // -V version: bool, // arguments passed straight through, either seperated by -- or just trailing. positional_args: Vec, } #[derive(Default, Debug, Clone, PartialEq)] pub enum PreserveEnv { #[default] Nothing, Everything, Only(Vec), } impl PreserveEnv { #[cfg(test)] pub fn try_into_only(self) -> Result, Self> { if let Self::Only(v) = self { Ok(v) } else { Err(self) } } pub fn is_nothing(&self) -> bool { matches!(self, Self::Nothing) } } #[derive(Debug, Clone, PartialEq)] pub enum List { Once, Verbose, } impl List { #[must_use] pub fn is_verbose(&self) -> bool { matches!(self, Self::Verbose) } } enum SudoArg { Flag(String), Argument(String, String), Environment(String, String), Rest(Vec), } impl SudoArg { const TAKES_ARGUMENT_SHORT: &'static [char] = &['D', 'g', 'h', 'p', 'R', 'U', 'u']; const TAKES_ARGUMENT: &'static [&'static str] = &[ "chdir", "group", "host", "chroot", "other-user", "user", "prompt", ]; /// argument assignments and shorthand options preprocessing fn normalize_arguments(iter: I) -> Result, String> where I: IntoIterator, { // the first argument is the sudo command - so we can skip it let mut arg_iter = iter.into_iter().skip(1); let mut processed = vec![]; while let Some(arg) = arg_iter.next() { if arg == "--" { processed.push(SudoArg::Rest(arg_iter.collect())); break; } else if let Some(unprefixed) = arg.strip_prefix("--") { if let Some((key, value)) = unprefixed.split_once('=') { // convert assignment to normal tokens // only accept arguments when one is expected // `--preserve-env` is special as it only takes an argument using this `key=value` syntax if !Self::TAKES_ARGUMENT.contains(&key) && key != "preserve-env" { Err(format!("'{}' does not take any arguments", key))?; } processed.push(SudoArg::Argument("--".to_string() + key, value.to_string())); } else if Self::TAKES_ARGUMENT.contains(&unprefixed) { if let Some(next) = arg_iter.next() { processed.push(SudoArg::Argument(arg, next)); } else { Err(format!("'{}' expects an argument", &arg))?; } } else { processed.push(SudoArg::Flag(arg)); } } else if let Some(unprefixed) = arg.strip_prefix('-') { // split combined shorthand options let mut chars = unprefixed.chars(); while let Some(curr) = chars.next() { let flag = format!("-{curr}"); // convert option argument to separate segment if Self::TAKES_ARGUMENT_SHORT.contains(&curr) { let rest = chars.as_str(); let next = chars.next(); // assignment syntax is not accepted for shorthand arguments if next == Some('=') { Err("invalid option '='")?; } if next.is_some() { processed.push(SudoArg::Argument(flag, rest.to_string())); } else if let Some(next) = arg_iter.next() { processed.push(SudoArg::Argument(flag, next)); } else if curr == 'h' { // short version of --help has no arguments processed.push(SudoArg::Flag(flag)); } else { Err(format!("'-{}' expects an argument", curr))?; } break; } else { processed.push(SudoArg::Flag(flag)); } } } else if let Some((key, value)) = try_to_env_var(&arg) { processed.push(SudoArg::Environment(key, value)); } else { let mut rest = vec![arg]; rest.extend(arg_iter); processed.push(SudoArg::Rest(rest)); break; } } Ok(processed) } } impl SudoOptions { fn validate(self) -> Result { let action = if self.help { SudoAction::Help(self.try_into()?) } else if self.version { SudoAction::Version(self.try_into()?) } else if self.remove_timestamp { SudoAction::RemoveTimestamp(self.try_into()?) } else if self.validate { SudoAction::Validate(self.try_into()?) } else if self.list.is_some() { SudoAction::List(self.try_into()?) } else if self.edit { SudoAction::Edit(self.try_into()?) } else { let is_run = self.login | self.shell | !self.positional_args.is_empty(); if is_run { SudoAction::Run(self.try_into()?) } else if self.reset_timestamp { SudoAction::ResetTimestamp(self.try_into()?) } else { return Err("expected one of these actions: --help, --version, --remove-timestamp, --validate, --list, --edit, --login, --shell, a command as a positional argument, --reset-timestamp".into()); } }; Ok(action) } /// parse an iterator over command line arguments fn try_parse_from(iter: I) -> Result where I: IntoIterator, T: Into + Clone, { let mut options = Self::default(); let arg_iter = SudoArg::normalize_arguments(iter.into_iter().map(Into::into))? .into_iter() .peekable(); for arg in arg_iter { match arg { SudoArg::Flag(flag) => match flag.as_str() { "-B" | "--bell" => { options.bell = true; } "-E" | "--preserve-env" => { options.preserve_env = PreserveEnv::Everything; } "-e" | "--edit" => { options.edit = true; } "-H" | "--set-home" => { // this option is ignored, since it is the default for sudo-rs; but accept // it for backwards compatibility reasons } "-h" | "--help" => { options.help = true; } "-i" | "--login" => { options.login = true; } "-K" | "--remove-timestamp" => { options.remove_timestamp = true; } "-k" | "--reset-timestamp" => { options.reset_timestamp = true; } "-l" | "--list" => match options.list { None => options.list = Some(List::Once), Some(List::Once) => options.list = Some(List::Verbose), Some(List::Verbose) => {} }, "-n" | "--non-interactive" => { options.non_interactive = true; } "-S" | "--stdin" => { options.stdin = true; } "-s" | "--shell" => { options.shell = true; } "-V" | "--version" => { options.version = true; } "-v" | "--validate" => { options.validate = true; } _option => { Err("invalid option provided")?; } }, SudoArg::Argument(option, value) => match option.as_str() { "-D" | "--chdir" => { options.chdir = Some(SudoPath::from_cli_string(value)); } "-E" | "--preserve-env" => { let split_value = || value.split(',').map(str::to_string); match &mut options.preserve_env { PreserveEnv::Nothing => { options.preserve_env = PreserveEnv::Only(split_value().collect()) } PreserveEnv::Everything => {} PreserveEnv::Only(list) => list.extend(split_value()), } // options.preserve_env = value.split(',').map(str::to_string).collect() } "-g" | "--group" => { options.group = Some(SudoString::from_cli_string(value)); } "-p" | "--prompt" => { options.prompt = Some(value); } "-U" | "--other-user" => { options.other_user = Some(SudoString::from_cli_string(value)); } "-u" | "--user" => { options.user = Some(SudoString::from_cli_string(value)); } _option => { Err("invalid option provided")?; } }, SudoArg::Environment(key, value) => { options.env_var_list.push((key, value)); } SudoArg::Rest(rest) => { options.positional_args = rest; } } } Ok(options) } } fn try_to_env_var(arg: &str) -> Option<(String, String)> { let (name, value) = arg.split_once('=')?; if name.chars().all(|c| c.is_alphanumeric() || c == '_') { Some((name.to_owned(), value.to_owned())) } else { None } } trait IsAbsent { fn is_absent(&self) -> bool; } impl IsAbsent for bool { fn is_absent(&self) -> bool { !*self } } impl IsAbsent for Option { fn is_absent(&self) -> bool { self.is_none() } } impl IsAbsent for Vec { fn is_absent(&self) -> bool { self.is_empty() } } impl IsAbsent for PreserveEnv { fn is_absent(&self) -> bool { self.is_nothing() } } fn ensure_is_absent(context: &str, thing: &dyn IsAbsent, name: &str) -> Result<(), String> { if thing.is_absent() { Ok(()) } else { Err(format!("{context} cannot be used together with {name}")) } } fn reject_all(context: &str, opts: SudoOptions) -> Result<(), String> { macro_rules! tuple { ($expr:expr) => { (&$expr as &dyn IsAbsent, { let name = concat!("--", stringify!($expr)); if name.contains('_') { Cow::Owned(name.replace('_', "-")) } else { Cow::Borrowed(name) } }) }; } let SudoOptions { bell, chdir, group, login, non_interactive, other_user, preserve_env, shell, stdin, prompt, user, env_var_list, edit, help, list, remove_timestamp, reset_timestamp, validate, version, positional_args, } = opts; let flags = [ tuple!(bell), tuple!(chdir), tuple!(edit), tuple!(group), tuple!(help), tuple!(list), tuple!(login), tuple!(non_interactive), tuple!(other_user), tuple!(preserve_env), tuple!(remove_timestamp), tuple!(reset_timestamp), tuple!(shell), tuple!(stdin), tuple!(prompt), tuple!(user), tuple!(validate), tuple!(version), ]; for (value, name) in flags { ensure_is_absent(context, value, &name)?; } ensure_is_absent(context, &env_var_list, "environment variable")?; ensure_is_absent(context, &positional_args, "command")?; Ok(()) } sudo-rs-0.2.5/src/sudo/cli/tests.rs000064400000000000000000000317761046102023000152620ustar 00000000000000use crate::common::SudoPath; use crate::sudo::cli::PreserveEnv; use super::{SudoAction, SudoOptions}; use pretty_assertions::assert_eq; /// Passing '-E' with a variable fails #[test] fn short_preserve_env_with_var_fails() { let argss = [["sudo", "-E=variable"], ["sudo", "-Evariable"]]; for args in argss { let res = SudoOptions::try_parse_from(args); assert!(res.is_err()) } } /// Passing '--preserve-env' with an argument fills 'preserve_env', 'short_preserve_env' stays 'false' #[test] fn preserve_env_with_var() { let cmd = SudoOptions::try_parse_from(["sudo", "--preserve-env=some_argument"]).unwrap(); assert_eq!( ["some_argument"], cmd.preserve_env.try_into_only().unwrap().as_slice(), ); } /// Passing '--preserve-env' with several arguments fills 'preserve_env', 'short_preserve_env' stays 'false' #[test] fn preserve_env_with_several_vars() { let cmd = SudoOptions::try_parse_from([ "sudo", "--preserve-env=some_argument,another_argument,a_third_one", ]) .unwrap(); assert_eq!( ["some_argument", "another_argument", "a_third_one"], cmd.preserve_env.try_into_only().unwrap().as_slice(), ); } #[test] fn preserve_env_boolean() { let cmd = SudoOptions::try_parse_from(["sudo", "--preserve-env"]).unwrap(); assert_eq!(cmd.preserve_env, PreserveEnv::Everything); } #[test] fn preserve_env_boolean_and_list() { let expected = PreserveEnv::Everything; let argss = [ ["sudo", "--preserve-env", "--preserve-env=some_argument"], ["sudo", "--preserve-env=some_argument", "--preserve-env"], ]; for args in argss { let cmd = SudoOptions::try_parse_from(args).unwrap(); assert_eq!(expected, cmd.preserve_env); } } #[test] fn preserve_env_repeated() { let cmd = SudoOptions::try_parse_from([ "sudo", "--preserve-env=some_argument", "--preserve-env=another_argument", ]) .unwrap(); assert_eq!( ["some_argument", "another_argument"], cmd.preserve_env.try_into_only().unwrap().as_slice() ); } // `--preserve-env` only accepts a value with the syntax `--preserve-env=varname` // so this `--preserve-env` is acting like a boolean flag #[test] fn preserve_env_space() { let cmd = SudoOptions::try_parse_from(["sudo", "--preserve-env", "true"]).unwrap(); assert_eq!(PreserveEnv::Everything, cmd.preserve_env); assert_eq!(["true"], cmd.positional_args.as_slice()); } /// Catch env variable that is given without hyphens in 'VAR=value' form in env_var_list. /// external_args stay empty. #[test] fn env_variable() { let cmd = SudoOptions::try_parse_from(["sudo", "ENV=with_a_value"]).unwrap(); assert_eq!( cmd.env_var_list, vec![("ENV".to_owned(), "with_a_value".to_owned())] ); assert!(cmd.positional_args.is_empty()); } /// Catch several env variablse that are given without hyphens in 'VAR=value' form in env_var_list. /// external_args stay empty. #[test] fn several_env_variables() { let cmd = SudoOptions::try_parse_from([ "sudo", "ENV=with_a_value", "another_var=otherval", "more=this_is_a_val", ]) .unwrap(); assert_eq!( cmd.env_var_list, vec![ ("ENV".to_owned(), "with_a_value".to_owned()), ("another_var".to_owned(), "otherval".to_owned()), ("more".to_owned(), "this_is_a_val".to_owned()) ] ); assert!(cmd.positional_args.is_empty()); } /// Mix env variables and trailing arguments that just pass through sudo /// Divided by hyphens. #[test] fn mix_env_variables_with_trailing_args_divided_by_hyphens() { let cmd = SudoOptions::try_parse_from(["sudo", "env=var", "--", "external=args", "something"]) .unwrap(); assert_eq!(cmd.env_var_list, vec![("env".to_owned(), "var".to_owned())]); assert_eq!(cmd.positional_args, vec!["external=args", "something"]); } /// Mix env variables and trailing arguments that just pass through sudo /// Divided by known flag. #[test] fn mix_env_variables_with_trailing_args_divided_by_known_flag() { let cmd = SudoOptions::try_parse_from(["sudo", "-i", "external=args", "something"]).unwrap(); assert_eq!( cmd.env_var_list, vec![("external".to_owned(), "args".to_owned())] ); assert!(cmd.login); assert_eq!(cmd.positional_args, vec!["something"]); } /// Catch trailing arguments that just pass through sudo /// but look like a known flag. #[test] fn trailing_args_followed_by_known_flag() { let cmd = SudoOptions::try_parse_from(["sudo", "args", "followed_by", "known_flag", "-i"]).unwrap(); assert!(!cmd.login); assert_eq!( cmd.positional_args, vec!["args", "followed_by", "known_flag", "-i"] ); } /// Catch trailing arguments that just pass through sudo /// but look like a known flag, divided by hyphens. #[test] fn trailing_args_hyphens_known_flag() { let cmd = SudoOptions::try_parse_from([ "sudo", "--", "trailing", "args", "followed_by", "known_flag", "-i", ]) .unwrap(); assert!(!cmd.login); assert_eq!( cmd.positional_args, vec!["trailing", "args", "followed_by", "known_flag", "-i"] ); } /// Check that the first environment variable declaration before any command is not treated as part /// of the command. #[test] fn first_trailing_env_var_is_not_an_external_arg() { let cmd = SudoAction::try_parse_from(["sudo", "FOO=1", "command", "BAR=2"]).unwrap(); let opts = if let SudoAction::Run(opts) = cmd { opts } else { panic!() }; assert_eq!(opts.env_var_list, vec![("FOO".to_owned(), "1".to_owned()),]); assert_eq!(opts.positional_args, ["command", "BAR=2"],); } #[test] fn trailing_env_vars_are_external_args() { let cmd = SudoOptions::try_parse_from([ "sudo", "FOO=1", "-i", "BAR=2", "command", "BAZ=3", "arg", "FOOBAR=4", "command", "arg", "BARBAZ=5", ]) .unwrap(); assert!(cmd.login); assert_eq!( cmd.env_var_list, vec![ ("FOO".to_owned(), "1".to_owned()), ("BAR".to_owned(), "2".to_owned()) ] ); assert_eq!( cmd.positional_args, ["command", "BAZ=3", "arg", "FOOBAR=4", "command", "arg", "BARBAZ=5"] ); } #[test] fn single_env_var_declaration() { let cmd = SudoOptions::try_parse_from(["sudo", "FOO=1", "command"]).unwrap(); assert_eq!(cmd.env_var_list, vec![("FOO".to_owned(), "1".to_owned())]); assert_eq!(cmd.positional_args, ["command"]); } #[test] fn shorthand_with_argument() { let cmd = SudoOptions::try_parse_from(["sudo", "-u", "ferris"]).unwrap(); assert_eq!(cmd.user.as_deref(), Some("ferris")); } #[test] fn shorthand_with_direct_argument() { let cmd = SudoOptions::try_parse_from(["sudo", "-uferris"]).unwrap(); assert_eq!(cmd.user.as_deref(), Some("ferris")); } #[test] fn shorthand_without_argument() { let cmd = SudoOptions::try_parse_from(["sudo", "-u"]); assert!(cmd.is_err()) } #[test] fn non_interactive() { let cmd = SudoOptions::try_parse_from(["sudo", "-n"]).unwrap(); assert!(cmd.non_interactive); let cmd = SudoOptions::try_parse_from(["sudo", "--non-interactive"]).unwrap(); assert!(cmd.non_interactive); } #[test] fn stdin() { let cmd = SudoOptions::try_parse_from(["sudo", "-S"]).unwrap(); assert!(cmd.stdin); let cmd = SudoOptions::try_parse_from(["sudo", "--stdin"]).unwrap(); assert!(cmd.stdin); } #[test] fn shell() { let cmd = SudoOptions::try_parse_from(["sudo", "-s"]).unwrap(); assert!(cmd.shell); let cmd = SudoOptions::try_parse_from(["sudo", "--shell"]).unwrap(); assert!(cmd.shell); } #[test] fn directory() { let cmd = SudoOptions::try_parse_from(["sudo", "-D/some/path"]).unwrap(); assert_eq!(cmd.chdir, Some(SudoPath::from("/some/path"))); let cmd = SudoOptions::try_parse_from(["sudo", "--chdir", "/some/path"]).unwrap(); assert_eq!(cmd.chdir, Some(SudoPath::from("/some/path"))); let cmd = SudoOptions::try_parse_from(["sudo", "--chdir=/some/path"]).unwrap(); assert_eq!(cmd.chdir, Some(SudoPath::from("/some/path"))); } #[test] fn group() { let cmd = SudoOptions::try_parse_from(["sudo", "-grustaceans"]).unwrap(); assert_eq!(cmd.group.as_deref(), Some("rustaceans")); let cmd = SudoOptions::try_parse_from(["sudo", "--group", "rustaceans"]).unwrap(); assert_eq!(cmd.group.as_deref(), Some("rustaceans")); let cmd = SudoOptions::try_parse_from(["sudo", "--group=rustaceans"]).unwrap(); assert_eq!(cmd.group.as_deref(), Some("rustaceans")); } #[test] fn other_user() { let cmd = SudoOptions::try_parse_from(["sudo", "-Uferris"]).unwrap(); assert_eq!(cmd.other_user.as_deref(), Some("ferris")); let cmd = SudoOptions::try_parse_from(["sudo", "--other-user", "ferris"]).unwrap(); assert_eq!(cmd.other_user.as_deref(), Some("ferris")); let cmd = SudoOptions::try_parse_from(["sudo", "--other-user=ferris"]).unwrap(); assert_eq!(cmd.other_user.as_deref(), Some("ferris")); } #[test] fn invalid_option() { let cmd = SudoOptions::try_parse_from(["sudo", "--wololo"]); assert!(cmd.is_err()) } #[test] fn invalid_option_with_argument() { let cmd = SudoOptions::try_parse_from(["sudo", "--background=yes"]); assert!(cmd.is_err()) } #[test] fn no_argument_provided() { let cmd = SudoOptions::try_parse_from(["sudo", "--user"]); assert!(cmd.is_err()) } #[test] fn login() { let cmd = SudoOptions::try_parse_from(["sudo", "-i"]).unwrap(); assert!(cmd.login); let cmd = SudoOptions::try_parse_from(["sudo", "--login"]).unwrap(); assert!(cmd.login); } #[test] fn edit() { let cmd = SudoAction::try_parse_from(["sudo", "-e", "filepath"]).unwrap(); assert!(cmd.is_edit()); let cmd = SudoAction::try_parse_from(["sudo", "--edit", "filepath"]).unwrap(); assert!(cmd.is_edit()); let res = SudoAction::try_parse_from(["sudo", "--edit"]); assert!(res.is_err()); } #[test] fn help() { let cmd = SudoAction::try_parse_from(["sudo", "-h"]).unwrap(); assert!(cmd.is_help()); let cmd = SudoAction::try_parse_from(["sudo", "-bh"]); assert!(cmd.is_err()); let cmd = SudoAction::try_parse_from(["sudo", "--help"]).unwrap(); assert!(cmd.is_help()); } #[test] fn conflicting_arguments() { let cmd = SudoAction::try_parse_from(["sudo", "-K", "-k"]); assert!(cmd.is_err()); let cmd = SudoAction::try_parse_from(["sudo", "--remove-timestamp", "--reset-timestamp"]); assert!(cmd.is_err()); let cmd = SudoAction::try_parse_from(["sudo", "-K"]).unwrap(); assert!(cmd.is_remove_timestamp()); let cmd = SudoAction::try_parse_from(["sudo", "-k"]).unwrap(); assert!(cmd.is_reset_timestamp()); } #[test] fn list() { let valid: &[&[_]] = &[ &["sudo", "--list"], &["sudo", "-l"], &["sudo", "-l", "true"], &["sudo", "-l", "-U", "ferris"], &["sudo", "-l", "-U", "ferris", "true"], &["sudo", "-l", "-u", "ferris", "true"], &["sudo", "-l", "-u", "ferris", "-U", "root", "true"], ]; for args in valid { let cmd = SudoAction::try_parse_from(args.iter().copied()).unwrap(); assert!(cmd.is_list()); } let invalid: &[&[_]] = &[ &["sudo", "-l", "-u", "ferris"], &["sudo", "-l", "-u", "ferris", "-U", "root"], ]; for args in invalid { let res = SudoAction::try_parse_from(args.iter().copied()); assert!(res.is_err()) } } #[test] fn validate() { let cmd = SudoAction::try_parse_from(["sudo", "-v"]).unwrap(); assert!(cmd.is_validate()); let cmd = SudoAction::try_parse_from(["sudo", "--validate"]).unwrap(); assert!(cmd.is_validate()); } #[test] fn version() { let cmd = SudoAction::try_parse_from(["sudo", "-V"]).unwrap(); assert!(cmd.is_version()); let cmd = SudoAction::try_parse_from(["sudo", "--version"]).unwrap(); assert!(cmd.is_version()); } #[test] fn run_reset_timestamp_command() { let action = SudoAction::try_parse_from(["sudo", "-k", "true"]) .unwrap() .try_into_run() .ok() .unwrap(); assert_eq!(["true"], action.positional_args.as_slice()); assert!(action.reset_timestamp); } #[test] fn run_reset_timestamp_login() { let action = SudoAction::try_parse_from(["sudo", "-k", "-i"]) .unwrap() .try_into_run() .ok() .unwrap(); assert!(action.positional_args.is_empty()); assert!(action.reset_timestamp); assert!(action.login); } #[test] fn run_reset_timestamp_shell() { let action = SudoAction::try_parse_from(["sudo", "-k", "-s"]) .unwrap() .try_into_run() .ok() .unwrap(); assert!(action.positional_args.is_empty()); assert!(action.reset_timestamp); assert!(action.shell); } #[test] fn run_no_command() { assert!(SudoAction::try_parse_from(["sudo", "-u", "root"]).is_err()); } #[test] fn run_login() { assert!(SudoAction::try_parse_from(["sudo", "-i"]).unwrap().is_run()); } #[test] fn run_shell() { assert!(SudoAction::try_parse_from(["sudo", "-s"]).unwrap().is_run()); } sudo-rs-0.2.5/src/sudo/diagnostic.rs000064400000000000000000000030421046102023000154560ustar 00000000000000use std::fs::File; use std::io::{BufRead, BufReader}; use std::path::Path; use crate::sudoers::Span; pub(crate) fn cited_error(message: &str, span: Span, path: impl AsRef) { let path_str = path.as_ref().display(); let Span { start: (line, col), end: (end_line, mut end_col), } = span; eprintln_ignore_io_error!("{path_str}:{line}:{col}: {message}"); // we won't try to "span" errors across multiple lines if line != end_line { end_col = col; } let citation = || { let inp = BufReader::new(File::open(path).ok()?); let line = inp.lines().nth(line - 1)?.ok()?; let padding = line .chars() .take(col - 1) .map(|c| if c.is_whitespace() { c } else { ' ' }) .collect::(); let lineunder = std::iter::repeat('~') .take(end_col - col) .skip(1) .collect::(); eprintln_ignore_io_error!("{line}"); eprintln_ignore_io_error!("{padding}^{lineunder}"); Some(()) }; // we ignore any errors in displaying an error let _ = citation(); } macro_rules! diagnostic { ($str:expr, $path:tt @ $pos:ident) => { if let Some(range) = $pos { $crate::sudo::diagnostic::cited_error(&format!($str), range, $path); } else { eprintln_ignore_io_error!("sudo-rs: {}", format!($str)); } }; ($str:expr) => {{ eprintln_ignore_io_error!("sudo-rs: {}", format!($str)); }}; } pub(crate) use diagnostic; sudo-rs-0.2.5/src/sudo/env/environment.rs000064400000000000000000000305311046102023000164710ustar 00000000000000use std::{ collections::{HashMap, HashSet}, ffi::{OsStr, OsString}, os::unix::prelude::OsStrExt, }; use crate::common::{CommandAndArguments, Context, Error}; use crate::sudoers::Restrictions; use crate::system::PATH_MAX; use super::wildcard_match::wildcard_match; const PATH_MAILDIR: &str = env!("PATH_MAILDIR"); const PATH_ZONEINFO: &str = env!("PATH_ZONEINFO"); const PATH_DEFAULT: &str = env!("SUDO_PATH_DEFAULT"); pub type Environment = HashMap; /// obtain the system environment pub fn system_environment() -> Environment { std::env::vars_os().collect() } /// check byte slice contains with given byte slice fn contains_subsequence(haystack: &[u8], needle: &[u8]) -> bool { haystack .windows(needle.len()) .any(|window| window == needle) } /// Formats the command and arguments passed for the SUDO_COMMAND /// environment variable. Limit the length to 4096 bytes to prevent /// execve failure for very long argument vectors fn format_command(command_and_arguments: &CommandAndArguments) -> OsString { let mut formatted: OsString = command_and_arguments.command.clone().into(); for arg in &command_and_arguments.arguments { if formatted.len() + arg.len() < 4096 { formatted.push(" "); formatted.push(arg); } } formatted } /// Construct sudo-specific environment variables fn add_extra_env( context: &Context, cfg: &Restrictions, sudo_ps1: Option, environment: &mut Environment, ) { // current user environment.insert("SUDO_COMMAND".into(), format_command(&context.command)); environment.insert( "SUDO_UID".into(), context.current_user.uid.to_string().into(), ); environment.insert( "SUDO_GID".into(), context.current_user.gid.to_string().into(), ); environment.insert("SUDO_USER".into(), context.current_user.name.clone().into()); // target user environment .entry("MAIL".into()) .or_insert_with(|| format!("{PATH_MAILDIR}/{}", context.target_user.name).into()); // The current SHELL variable should determine the shell to run when -s is passed, if none set use passwd entry environment .entry("SHELL".into()) .or_insert_with(|| context.target_user.shell.clone().into()); // HOME' Set to the home directory of the target user if -i or -H are specified, env_reset or always_set_home are // set in sudoers, or when the -s option is specified and set_home is set in sudoers. // Since we always want to do env_reset -> always set HOME environment .entry("HOME".into()) .or_insert_with(|| context.target_user.home.clone().into()); match ( environment.get(OsStr::new("LOGNAME")), environment.get(OsStr::new("USER")), ) { // Set to the login name of the target user when the -i option is specified, // when the set_logname option is enabled in sudoers, or when the env_reset option // is enabled in sudoers (unless LOGNAME is present in the env_keep list). // Since we always want to do env_reset -> always set these except when present in env (None, None) => { environment.insert("LOGNAME".into(), context.target_user.name.clone().into()); environment.insert("USER".into(), context.target_user.name.clone().into()); } // LOGNAME should be set to the same value as USER if the latter is preserved. (None, Some(user)) => { environment.insert("LOGNAME".into(), user.clone()); } // USER should be set to the same value as LOGNAME if the latter is preserved. (Some(logname), None) => { environment.insert("USER".into(), logname.clone()); } (Some(_), Some(_)) => {} } // Overwrite PATH when secure_path is set if let Some(secure_path) = &cfg.path { // assign path by env path or secure_path configuration environment.insert("PATH".into(), secure_path.into()); } // If the PATH and TERM variables are not preserved from the user's environment, they will be set to default value environment .entry("PATH".into()) .or_insert_with(|| PATH_DEFAULT.into()); // If the TERM variable is not preserved from the user's environment, it will be set to default value environment .entry("TERM".into()) .or_insert_with(|| "unknown".into()); // The SUDO_PS1 variable requires special treatment as the PS1 variable must be set in the // target environment to the same value of SUDO_PS1 if the latter is set. if let Some(sudo_ps1_value) = sudo_ps1 { // set PS1 to the SUDO_PS1 value in the target environment environment.insert("PS1".into(), sudo_ps1_value); } } /// Check a string only contains printable (non-space) characters fn is_printable(input: &[u8]) -> bool { input .iter() .all(|c| c.is_ascii_alphanumeric() || c.is_ascii_punctuation()) } /// The TZ variable is considered unsafe if any of the following are true: /// It consists of a fully-qualified path name, optionally prefixed with a colon (‘:’), that does not match the location of the zoneinfo directory. /// It contains a .. path element. /// It contains white space or non-printable characters. /// It is longer than the value of PATH_MAX. fn is_safe_tz(value: &[u8]) -> bool { let check_value = if value.starts_with(b":") { &value[1..] } else { value }; if check_value.starts_with(b"/") { // clippy 1.79 wants to us to optimise this check away; but we don't know what this will always // be possible; and the compiler is clever enough to do that for us anyway if it can be. #[allow(clippy::const_is_empty)] if !PATH_ZONEINFO.is_empty() { if !check_value.starts_with(PATH_ZONEINFO.as_bytes()) || check_value.get(PATH_ZONEINFO.len()) != Some(&b'/') { return false; } } else { return false; } } !contains_subsequence(check_value, "..".as_bytes()) && is_printable(check_value) && check_value.len() < PATH_MAX as usize } /// Check whether the needle exists in a haystack, in which the haystack is a list of patterns, possibly containing wildcards fn in_table(needle: (&OsStr, &OsStr), haystack: &HashSet) -> bool { haystack.iter().any(|pattern| { if let Some((key, value)) = pattern.split_once('=') { wildcard_match(needle.0.as_bytes(), key.as_bytes()) && wildcard_match(needle.1.as_bytes(), value.as_bytes()) } else { wildcard_match(needle.0.as_bytes(), pattern.as_bytes()) } }) } /// Determine whether a specific environment variable should be kept fn should_keep(key: &OsStr, value: &OsStr, cfg: &Restrictions) -> bool { if value.as_bytes().starts_with("()".as_bytes()) { return false; } if cfg.path.is_some() && key == "PATH" { return false; } if key == "TZ" { return in_table((key, value), cfg.env_keep) || (in_table((key, value), cfg.env_check) && is_safe_tz(value.as_bytes())); } if in_table((key, value), cfg.env_check) { return !value.as_bytes().iter().any(|c| *c == b'%' || *c == b'/'); } in_table((key, value), cfg.env_keep) } /// Construct the final environment from the current one and a sudo context /// see for the original implementation /// see for the original documentation /// /// The HOME, MAIL, SHELL, LOGNAME and USER environment variables are initialized based on the target user /// and the SUDO_* variables are set based on the invoking user. /// /// Additional variables, such as DISPLAY, PATH and TERM, are preserved from the invoking user's /// environment if permitted by the env_check, or env_keep options /// /// If the PATH and TERM variables are not preserved from the user's environment, they will be set to default value /// /// Environment variables with a value beginning with ‘()’ are removed pub fn get_target_environment( current_env: Environment, additional_env: impl IntoIterator, user_override: Vec<(String, String)>, context: &Context, settings: &Restrictions, ) -> Result { // retrieve SUDO_PS1 value to set a PS1 value as additional environment let sudo_ps1 = current_env.get(OsStr::new("SUDO_PS1")).cloned(); // variables preserved from the invoking user's environment by the // env_keep list take precedence over those in the PAM environment let mut environment: HashMap<_, _> = additional_env.into_iter().collect(); environment.extend( current_env .into_iter() .filter(|(key, value)| should_keep(key, value, settings)), ); add_extra_env(context, settings, sudo_ps1, &mut environment); let mut rejected_vars = Vec::new(); for (key, value) in user_override { if should_keep(OsStr::new(&key), OsStr::new(&value), settings) { environment.insert(key.into(), value.into()); } else { rejected_vars.push(key); } } if !rejected_vars.is_empty() { return Err(Error::EnvironmentVar(rejected_vars)); } Ok(environment) } /// Extend the environment with user-supplied info pub fn dangerous_extend(env: &mut Environment, user_override: impl IntoIterator) where S: Into, { env.extend( user_override .into_iter() .map(|(key, value)| (key.into(), value.into())), ) } #[cfg(test)] mod tests { use super::{is_safe_tz, should_keep, PATH_ZONEINFO}; use std::{collections::HashSet, ffi::OsStr}; struct TestConfiguration { keep: HashSet, check: HashSet, path: Option, } impl TestConfiguration { pub fn check_should_keep(&self, key: &str, value: &str, expected: bool) { assert_eq!( should_keep( OsStr::new(key), OsStr::new(value), &crate::sudoers::Restrictions { env_keep: &self.keep, env_check: &self.check, path: self.path.as_deref(), chdir: crate::sudoers::DirChange::Strict(None), trust_environment: false, use_pty: true, } ), expected, "{} should {}", key, if expected { "be kept" } else { "not be kept" } ); } } #[test] fn test_filtering() { let mut config = TestConfiguration { keep: HashSet::from(["AAP".to_string(), "NOOT".to_string()]), check: HashSet::from(["MIES".to_string(), "TZ".to_string()]), path: Some("/bin".to_string()), }; config.check_should_keep("AAP", "FOO", true); config.check_should_keep("MIES", "BAR", true); config.check_should_keep("AAP", "()=foo", false); config.check_should_keep("TZ", "Europe/Amsterdam", true); config.check_should_keep("TZ", "../Europe/Berlin", false); config.check_should_keep("MIES", "FOO/BAR", false); config.check_should_keep("MIES", "FOO/BAR", false); config.keep.insert("PATH".to_string()); config.check_should_keep("PATH", "FOO", false); config.path = None; config.check_should_keep("PATH", "FOO", true); } #[allow(clippy::useless_format)] #[allow(clippy::bool_assert_comparison)] #[test] fn test_tzinfo() { assert_eq!(is_safe_tz("Europe/Amsterdam".as_bytes()), true); assert_eq!( is_safe_tz(format!("{PATH_ZONEINFO}/Europe/London").as_bytes()), true ); assert_eq!( is_safe_tz(format!(":{PATH_ZONEINFO}/Europe/Amsterdam").as_bytes()), true ); assert_eq!( is_safe_tz(format!("/schaap/Europe/Amsterdam").as_bytes()), false ); assert_eq!( is_safe_tz(format!("{PATH_ZONEINFO}/../Europe/London").as_bytes()), false ); assert_eq!( is_safe_tz(format!("{PATH_ZONEINFO}/../Europe/London").as_bytes()), false ); } } sudo-rs-0.2.5/src/sudo/env/mod.rs000064400000000000000000000001371046102023000147030ustar 00000000000000#![forbid(unsafe_code)] pub mod environment; pub mod wildcard_match; #[cfg(test)] mod tests; sudo-rs-0.2.5/src/sudo/env/tests.rs000064400000000000000000000124121046102023000152650ustar 00000000000000use crate::common::resolve::CurrentUser; use crate::common::{CommandAndArguments, Context}; use crate::sudo::{ cli::{SudoAction, SudoRunOptions}, env::environment::{get_target_environment, Environment}, }; use crate::system::interface::{GroupId, UserId}; use crate::system::{Group, Hostname, Process, User}; use std::collections::{HashMap, HashSet}; const TESTS: &str = " > env FOO=BAR HOME=/home/test HOSTNAME=test-ubuntu LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 LS_COLORS=cd=40;33;01:*.jpg=01;35:*.mp3=00;36: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/home/test SHLVL=0 TERM=xterm _=/usr/bin/sudo > sudo env HOSTNAME=test-ubuntu LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 LS_COLORS=cd=40;33;01:*.jpg=01;35:*.mp3=00;36: MAIL=/var/mail/root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin SHELL=/bin/bash SUDO_COMMAND=/usr/bin/env SUDO_GID=1000 SUDO_UID=1000 SUDO_USER=test HOME=/root LOGNAME=root USER=root TERM=xterm > sudo -u test env HOSTNAME=test-ubuntu LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 LS_COLORS=cd=40;33;01:*.jpg=01;35:*.mp3=00;36: MAIL=/var/mail/test PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin SHELL=/bin/sh SUDO_COMMAND=/usr/bin/env SUDO_GID=1000 SUDO_UID=1000 SUDO_USER=test HOME=/home/test LOGNAME=test USER=test TERM=xterm "; fn parse_env_commands(input: &str) -> Vec<(&str, Environment)> { input .trim() .split("> ") .filter(|l| !l.is_empty()) .map(|e| { let (cmd, vars) = e.split_once('\n').unwrap(); let vars = vars .lines() .map(|line| line.trim().split_once('=').unwrap()) .map(|(k, v)| (k.into(), v.into())) .collect(); (cmd, vars) }) .collect() } fn create_test_context(sudo_options: SudoRunOptions) -> Context { let path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin".to_string(); let command = CommandAndArguments::build_from_args(None, sudo_options.positional_args, &path); let current_user = CurrentUser::fake(User { uid: UserId::new(1000), gid: GroupId::new(1000), name: "test".into(), home: "/home/test".into(), shell: "/bin/sh".into(), groups: vec![], }); let current_group = Group { gid: GroupId::new(1000), name: Some("test".to_string()), }; let root_user = User { uid: UserId::ROOT, gid: GroupId::new(0), name: "root".into(), home: "/root".into(), shell: "/bin/bash".into(), groups: vec![], }; let root_group = Group { gid: GroupId::new(0), name: Some("root".to_string()), }; Context { hostname: Hostname::fake("test-ubuntu"), command, current_user: current_user.clone(), target_user: if sudo_options.user.as_deref() == Some("test") { current_user.into() } else { root_user }, target_group: if sudo_options.user.as_deref() == Some("test") { current_group } else { root_group }, launch: crate::common::context::LaunchType::Direct, chdir: sudo_options.chdir, stdin: sudo_options.stdin, prompt: sudo_options.prompt, non_interactive: sudo_options.non_interactive, process: Process::new(), use_session_records: false, use_pty: true, password_feedback: false, bell: false, } } fn environment_to_set(environment: Environment) -> HashSet { HashSet::from_iter( environment .into_iter() .map(|(k, v)| format!("{}={}", k.to_str().unwrap(), v.to_str().unwrap())), ) } #[test] fn test_environment_variable_filtering() { let mut parts = parse_env_commands(TESTS); let initial_env = parts.remove(0).1; for (cmd, expected_env) in parts { let options = SudoAction::try_parse_from(cmd.split_whitespace()) .unwrap() .try_into_run() .ok() .unwrap(); let settings = crate::defaults::Settings::default(); let context = create_test_context(options); let resulting_env = get_target_environment( initial_env.clone(), HashMap::new(), Vec::new(), &context, &crate::sudoers::Restrictions { env_keep: settings.env_keep(), env_check: settings.env_check(), path: settings.secure_path(), use_pty: true, chdir: crate::sudoers::DirChange::Strict(None), trust_environment: false, }, ) .unwrap(); let resulting_env = environment_to_set(resulting_env); let expected_env = environment_to_set(expected_env); let mut diff = resulting_env .symmetric_difference(&expected_env) .collect::>(); diff.sort(); assert!( diff.is_empty(), "\"{cmd}\" results in an environment mismatch:\n{diff:#?}", ); } } sudo-rs-0.2.5/src/sudo/env/wildcard_match.rs000064400000000000000000000056561046102023000171040ustar 00000000000000/// Match a test input with a pattern /// Only wildcard characters (*) in the pattern string have a special meaning: they match on zero or more characters pub(super) fn wildcard_match(test: &[u8], pattern: &[u8]) -> bool { let mut test_index = 0; let mut pattern_index = 0; let mut last_star = None; loop { match (pattern.get(pattern_index), test.get(test_index)) { (Some(p), Some(t)) => { if *p == b'*' { pattern_index += 1; last_star = Some((test_index, pattern_index)); } else if p == t { pattern_index += 1; test_index += 1; } else if let Some((t_index, p_index)) = last_star { test_index = t_index + 1; pattern_index = p_index; last_star = Some((test_index, pattern_index)); } else { return false; } } (None, Some(_)) => { if let Some((t_index, p_index)) = last_star { test_index = t_index + 1; pattern_index = p_index; last_star = Some((test_index, pattern_index)); } else { return false; } } (Some(b'*'), None) => { pattern_index += 1; } (None, None) => { return true; } _ => { return false; } } } } #[cfg(test)] mod tests { use super::wildcard_match; #[test] fn test_wildcard_match() { let tests = vec![ ("foo bar", "foo *", true), ("foo bar", "foo ba*", true), ("foo bar", "foo *ar", true), ("foo bar", "foo *r", true), ("foo bar", "foo *ab", false), ("foo bar", "foo r*", false), ("foo bar", "*oo bar", true), ("foo bar", "*f* bar", true), ("foo bar", "*f bar", false), ("foo ", "foo *", true), ("foo", "foo *", false), ("foo", "foo*", true), ("foo bar", "f*******r", true), ("foo******bar", "f*r", true), ("foo********bar", "foo bar", false), ("#%^$V@#TYH%&rot13%#@$%#$%", "#%^$V@#*t13%#@$%#$%", true), ("#%^$V@#TYH%&rot13%#@$%#$%", "*%^*%&rot*%#$%", true), ("#%^$V@#TYH%&rot13%#@$%#$%", "#%^$V@#TYH%&r*%#@$#$%", false), ("#%^$V@#TYH%&rot13%#@$%#$%", "#%^$V@#*******@$%#$%", true), ]; for (test, pattern, expected) in tests.into_iter() { assert_eq!( wildcard_match(test.as_bytes(), pattern.as_bytes()), expected, "\"{}\" {} match {}", test, if expected { "should" } else { "should not" }, pattern ); } } } sudo-rs-0.2.5/src/sudo/mod.rs000064400000000000000000000110251046102023000141110ustar 00000000000000#![forbid(unsafe_code)] use crate::common::resolve::CurrentUser; use crate::common::Error; use crate::log::dev_info; use crate::system::interface::UserId; use crate::system::kernel::kernel_check; use crate::system::timestamp::RecordScope; use crate::system::User; use crate::system::{time::Duration, timestamp::SessionRecordFile, Process}; use cli::help; #[cfg(test)] pub(crate) use cli::SudoAction; #[cfg(not(test))] use cli::SudoAction; use std::path::Path; mod cli; pub(crate) use cli::{SudoListOptions, SudoRunOptions, SudoValidateOptions}; pub(crate) mod diagnostic; mod env; mod pam; mod pipeline; #[cfg_attr(not(feature = "dev"), allow(dead_code))] fn unstable_warning() { let check_var = std::env::var("SUDO_RS_IS_UNSTABLE").unwrap_or_else(|_| "".to_string()); if check_var != "I accept that my system may break unexpectedly" { eprintln_ignore_io_error!( "WARNING! Sudo-rs is compiled with development logs on, which means it is less secure and could potentially break your system. We recommend that you do not run this on any production environment. To turn off this warning and use sudo-rs you need to set the environment variable SUDO_RS_IS_UNSTABLE to the value `I accept that my system may break unexpectedly`." ); std::process::exit(1); } } const VERSION: &str = if let Some(version_override) = std::option_env!("SUDO_RS_VERSION") { version_override } else { std::env!("CARGO_PKG_VERSION") }; pub(crate) fn candidate_sudoers_file() -> &'static Path { let pb_rs = Path::new("/etc/sudoers-rs"); let file = if pb_rs.exists() { pb_rs } else if cfg!(target_os = "freebsd") { // FIXME maybe make this configurable by the packager? Path::new("/usr/local/etc/sudoers") } else { Path::new("/etc/sudoers") }; dev_info!("Running with {} file", file.display()); file } fn sudo_process() -> Result<(), Error> { crate::log::SudoLogger::new("sudo: ").into_global_logger(); dev_info!("development logs are enabled"); self_check()?; kernel_check()?; // parse cli options match SudoAction::from_env() { Ok(action) => match action { SudoAction::Help(_) => { eprintln_ignore_io_error!("{}", help::long_help_message()); std::process::exit(0); } SudoAction::Version(_) => { eprintln_ignore_io_error!("sudo-rs {VERSION}"); std::process::exit(0); } SudoAction::RemoveTimestamp(_) => { let user = CurrentUser::resolve()?; let mut record_file = SessionRecordFile::open_for_user(&user, Duration::seconds(0))?; record_file.reset()?; Ok(()) } SudoAction::ResetTimestamp(_) => { if let Some(scope) = RecordScope::for_process(&Process::new()) { let user = CurrentUser::resolve()?; let mut record_file = SessionRecordFile::open_for_user(&user, Duration::seconds(0))?; record_file.disable(scope, None)?; } Ok(()) } SudoAction::Validate(options) => pipeline::run_validate(options), SudoAction::Run(options) => { // special case for when no command is given if options.positional_args.is_empty() && !options.shell && !options.login { eprintln_ignore_io_error!("{}", help::USAGE_MSG); std::process::exit(1); } else { #[cfg(feature = "dev")] unstable_warning(); pipeline::run(options) } } SudoAction::List(options) => pipeline::run_list(options), SudoAction::Edit(_) => { eprintln_ignore_io_error!("error: `--edit` flag has not yet been implemented"); std::process::exit(1); } }, Err(e) => { eprintln_ignore_io_error!("{e}\n{}", help::USAGE_MSG); std::process::exit(1); } } } fn self_check() -> Result<(), Error> { let euid = User::effective_uid(); if euid == UserId::ROOT { Ok(()) } else { Err(Error::SelfCheck) } } pub fn main() { match sudo_process() { Ok(()) => (), Err(error) => { if !error.is_silent() { diagnostic::diagnostic!("{error}"); } std::process::exit(1); } } } sudo-rs-0.2.5/src/sudo/pam.rs000064400000000000000000000115211046102023000141100ustar 00000000000000use std::ffi::OsString; use crate::common::context::LaunchType; use crate::common::error::Error; use crate::log::{dev_info, user_warn}; use crate::pam::{PamContext, PamError, PamErrorType, PamResult}; use crate::system::term::current_tty_name; pub(super) struct InitPamArgs<'a> { pub(super) launch: LaunchType, pub(super) use_stdin: bool, pub(super) bell: bool, pub(super) non_interactive: bool, pub(super) password_feedback: bool, pub(super) auth_prompt: Option, pub(super) auth_user: &'a str, pub(super) requesting_user: &'a str, pub(super) target_user: &'a str, pub(super) hostname: &'a str, } pub(super) fn init_pam( InitPamArgs { launch, use_stdin, bell, non_interactive, password_feedback, auth_prompt, auth_user, requesting_user, target_user, hostname, }: InitPamArgs, ) -> PamResult { let service_name = match launch { LaunchType::Login if cfg!(feature = "pam-login") => "sudo-i", LaunchType::Login | LaunchType::Shell | LaunchType::Direct => "sudo", }; let mut pam = PamContext::new_cli( "sudo", service_name, use_stdin, bell, non_interactive, password_feedback, None, )?; pam.mark_silent(matches!(launch, LaunchType::Direct)); pam.mark_allow_null_auth_token(false); pam.set_requesting_user(requesting_user)?; pam.set_user(auth_user)?; match auth_prompt.as_deref() { None => {} Some("") => pam.set_auth_prompt(None), Some(auth_prompt) => { let mut final_prompt = String::new(); let mut chars = auth_prompt.chars(); while let Some(c) = chars.next() { if c != '%' { final_prompt.push(c); continue; } match chars.next() { Some('H') => final_prompt.push_str(hostname), Some('h') => final_prompt .push_str(hostname.split_once('.').map(|x| x.0).unwrap_or(hostname)), Some('p') => final_prompt.push_str(auth_user), Some('U') => final_prompt.push_str(target_user), Some('u') => final_prompt.push_str(requesting_user), Some('%') | None => final_prompt.push('%'), Some(c) => { final_prompt.push('%'); final_prompt.push(c); } } } pam.set_auth_prompt(Some(final_prompt)); } } // attempt to set the TTY this session is communicating on if let Ok(pam_tty) = current_tty_name() { pam.set_tty(&pam_tty)?; } Ok(pam) } pub(super) fn attempt_authenticate( pam: &mut PamContext, non_interactive: bool, mut max_tries: u16, ) -> Result<(), Error> { let mut current_try = 0; loop { current_try += 1; match pam.authenticate() { // there was no error, so authentication succeeded Ok(_) => break, // maxtries was reached, pam does not allow any more tries Err(PamError::Pam(PamErrorType::MaxTries)) => { return Err(Error::MaxAuthAttempts(current_try)); } // there was an authentication error, we can retry Err(PamError::Pam(PamErrorType::AuthError | PamErrorType::ConversationError)) => { max_tries -= 1; if max_tries == 0 { return Err(Error::MaxAuthAttempts(current_try)); } else if non_interactive { return Err(Error::InteractionRequired); } else { user_warn!("Authentication failed, try again."); } } // there was another pam error, return the error Err(e) => { return Err(e.into()); } } } Ok(()) } pub(super) fn pre_exec( pam: &mut PamContext, target_user: &str, ) -> Result, Error> { // make sure that the user that needed to authenticate has a valid token pam.validate_account_or_change_auth_token()?; // check what the current user in PAM is let user = pam.get_user()?; if user != target_user { // switch pam over to the target user pam.set_user(target_user)?; // make sure that credentials are loaded for the target user // errors are ignored because not all modules support this functionality if let Err(e) = pam.credentials_reinitialize() { dev_info!( "PAM gave an error while trying to re-initialize credentials: {:?}", e ); } } pam.open_session()?; let env_vars = pam.env()?; Ok(env_vars) } sudo-rs-0.2.5/src/sudo/pipeline/list.rs000064400000000000000000000135421046102023000161200ustar 00000000000000use std::{borrow::Cow, ops::ControlFlow, path::Path}; use crate::{ common::{Context, Error}, sudo::cli::SudoListOptions, sudoers::{Authorization, ListRequest, Request, Sudoers}, system::{interface::UserId, User}, }; use super::auth_and_update_record_file; pub(in crate::sudo) fn run_list(cmd_opts: SudoListOptions) -> Result<(), Error> { let verbose_list_mode = cmd_opts.list.is_verbose(); let other_user = cmd_opts .other_user .as_ref() .map(|username| { User::from_name(username.as_cstr())? .ok_or_else(|| Error::UserNotFound(username.clone().into())) }) .transpose()?; let original_command = cmd_opts.positional_args.first().cloned(); let mut sudoers = super::read_sudoers()?; let mut context = Context::from_list_opts(cmd_opts, &mut sudoers)?; if original_command.is_some() && !context.command.resolved { return Err(Error::CommandNotFound(context.command.command)); } if auth_invoking_user(&mut context, &sudoers, &original_command, &other_user)?.is_break() { return Ok(()); } if let Some(other_user) = &other_user { check_other_users_list_perms(other_user, &context, &sudoers, &original_command)?; } if let Some(original_command) = original_command { check_sudo_command_perms(&original_command, &context, &other_user, &mut sudoers)?; } else { let invoking_user = other_user.as_ref().unwrap_or(&context.current_user); println_ignore_io_error!( "User {} may run the following commands on {}:", invoking_user.name, context.hostname ); let matching_entries = sudoers.matching_entries(invoking_user, &context.hostname); for entry in matching_entries { if verbose_list_mode { let entry = entry.verbose(); println_ignore_io_error!("{entry}"); } else { println_ignore_io_error!("{entry}"); } } } Ok(()) } fn auth_invoking_user( context: &mut Context, sudoers: &Sudoers, original_command: &Option, other_user: &Option, ) -> Result, Error> { let list_request = ListRequest { target_user: &context.target_user, target_group: &context.target_group, }; match sudoers.check_list_permission(&*context.current_user, &context.hostname, list_request) { Authorization::Allowed(auth, ()) => { auth_and_update_record_file(context, &auth)?; Ok(ControlFlow::Continue(())) } Authorization::Forbidden => { if context.current_user.uid == UserId::ROOT { if original_command.is_some() { return Err(Error::Silent); } println_ignore_io_error!( "User {} is not allowed to run sudo on {}.", other_user.as_ref().unwrap_or(&context.current_user).name, context.hostname ); // this branch does not result in exit code 1 but no further information should // be printed in this case Ok(ControlFlow::Break(())) } else { let command = if other_user.is_none() { "sudo".into() } else { format_list_command(original_command) }; Err(Error::NotAllowed { username: context.current_user.name.clone(), command, hostname: context.hostname.clone(), other_user: other_user.as_ref().map(|user| &user.name).cloned(), }) } } } } fn check_other_users_list_perms( other_user: &User, context: &Context, sudoers: &Sudoers, original_command: &Option, ) -> Result<(), Error> { let list_request = ListRequest { target_user: &context.target_user, target_group: &context.target_group, }; if let Authorization::Forbidden = sudoers.check_list_permission(other_user, &context.hostname, list_request) { return Err(Error::NotAllowed { username: context.current_user.name.clone(), command: format_list_command(original_command), hostname: context.hostname.clone(), other_user: Some(other_user.name.clone()), }); } Ok(()) } fn check_sudo_command_perms( original_command: &str, context: &Context, other_user: &Option, sudoers: &mut Sudoers, ) -> Result<(), Error> { let user = other_user.as_ref().unwrap_or(&context.current_user); let request = Request { user: &context.target_user, group: &context.target_group, command: &context.command.command, arguments: &context.command.arguments, }; let judgement = sudoers.check(user, &context.hostname, request); if let Authorization::Forbidden = judgement.authorization() { return Err(Error::Silent); } else { let command_is_relative_path = original_command.contains('/') && !Path::new(&original_command).is_absolute(); let command: Cow<_> = if command_is_relative_path { original_command.into() } else { let resolved_command = &context.command.command; resolved_command.display().to_string().into() }; if context.command.arguments.is_empty() { println_ignore_io_error!("{command}"); } else { println_ignore_io_error!("{command} {}", context.command.arguments.join(" ")); } } Ok(()) } fn format_list_command(original_command: &Option) -> Cow<'static, str> { if let Some(original_command) = original_command { format!("list {original_command}").into() } else { "list".into() } } sudo-rs-0.2.5/src/sudo/pipeline.rs000064400000000000000000000232541046102023000151460ustar 00000000000000use std::ffi::OsStr; use std::process::exit; use super::cli::{SudoRunOptions, SudoValidateOptions}; use super::diagnostic; use crate::common::resolve::{AuthUser, CurrentUser}; use crate::common::{Context, Error}; use crate::exec::ExitReason; use crate::log::{auth_info, auth_warn}; use crate::pam::PamContext; use crate::sudo::env::environment; use crate::sudo::pam::{attempt_authenticate, init_pam, pre_exec, InitPamArgs}; use crate::sudo::Duration; use crate::sudoers::{ AuthenticatingUser, Authentication, Authorization, DirChange, Judgement, Restrictions, Sudoers, }; use crate::system::term::current_tty_name; use crate::system::timestamp::{RecordScope, SessionRecordFile, TouchResult}; use crate::system::{escape_os_str_lossy, Process}; mod list; pub(super) use list::run_list; fn read_sudoers() -> Result { let sudoers_path = super::candidate_sudoers_file(); let (sudoers, syntax_errors) = Sudoers::open(sudoers_path).map_err(|e| Error::Configuration(format!("{e}")))?; for crate::sudoers::Error { source, location, message, } in syntax_errors { let path = source.as_deref().unwrap_or(sudoers_path); diagnostic::diagnostic!("{message}", path @ location); } Ok(sudoers) } fn judge(mut policy: Sudoers, context: &Context) -> Result { Ok(policy.check( &*context.current_user, &context.hostname, crate::sudoers::Request { user: &context.target_user, group: &context.target_group, command: &context.command.command, arguments: &context.command.arguments, }, )) } pub fn run(mut cmd_opts: SudoRunOptions) -> Result<(), Error> { let mut policy = read_sudoers()?; let user_requested_env_vars = std::mem::take(&mut cmd_opts.env_var_list); if !cmd_opts.preserve_env.is_nothing() { eprintln_ignore_io_error!( "warning: `--preserve-env` has not yet been implemented and will be ignored" ) } let mut context = Context::from_run_opts(cmd_opts, &mut policy)?; let policy = judge(policy, &context)?; let Authorization::Allowed(auth, controls) = policy.authorization() else { return Err(Error::Authorization(context.current_user.name.to_string())); }; apply_policy_to_context(&mut context, &auth, &controls)?; let mut pam_context = auth_and_update_record_file(&mut context, &auth)?; // build environment let additional_env = pre_exec(&mut pam_context, &context.target_user.name)?; let current_env = environment::system_environment(); let (checked_vars, trusted_vars) = if controls.trust_environment { (vec![], user_requested_env_vars) } else { (user_requested_env_vars, vec![]) }; let mut target_env = environment::get_target_environment( current_env, additional_env, checked_vars, &context, &controls, )?; environment::dangerous_extend(&mut target_env, trusted_vars); let pid = context.process.pid; // run command and return corresponding exit code let command_exit_reason = if context.command.resolved { log_command_execution(&context); crate::exec::run_command( context .try_as_run_options() .map_err(|io_error| Error::Io(Some(context.command.command.clone()), io_error))?, target_env, ) .map_err(|io_error| Error::Io(Some(context.command.command), io_error)) } else { Err(Error::CommandNotFound(context.command.command)) }; pam_context.close_session(); match command_exit_reason? { ExitReason::Code(code) => exit(code), ExitReason::Signal(signal) => { crate::system::kill(pid, signal)?; } } Ok(()) } pub fn run_validate(cmd_opts: SudoValidateOptions) -> Result<(), Error> { let policy = read_sudoers()?; let mut context = Context::from_validate_opts(cmd_opts)?; match policy.check_validate_permission(&*context.current_user, &context.hostname) { Authorization::Forbidden => { return Err(Error::Authorization(context.current_user.name.to_string())); } Authorization::Allowed(auth, ()) => { auth_and_update_record_file(&mut context, &auth)?; } } Ok(()) } fn auth_and_update_record_file( context: &mut Context, &Authentication { must_authenticate, prior_validity, allowed_attempts, ref credential, .. }: &Authentication, ) -> Result { let scope = RecordScope::for_process(&Process::new()); let mut auth_status = determine_auth_status( must_authenticate, context.use_session_records, scope, &context.current_user, prior_validity, ); let auth_user = match credential { AuthenticatingUser::InvokingUser => { AuthUser::from_current_user(context.current_user.clone()) } AuthenticatingUser::Root => AuthUser::resolve_root_for_rootpw()?, AuthenticatingUser::TargetUser => { AuthUser::from_user_for_targetpw(context.target_user.clone()) } }; let mut pam_context = init_pam(InitPamArgs { launch: context.launch, use_stdin: context.stdin, bell: context.bell, non_interactive: context.non_interactive, password_feedback: context.password_feedback, auth_prompt: context.prompt.clone(), auth_user: &auth_user.name, requesting_user: &context.current_user.name, target_user: &context.target_user.name, hostname: &context.hostname, })?; if auth_status.must_authenticate { attempt_authenticate(&mut pam_context, context.non_interactive, allowed_attempts)?; if let (Some(record_file), Some(scope)) = (&mut auth_status.record_file, scope) { match record_file.create(scope, context.current_user.uid) { Ok(_) => (), Err(e) => { auth_warn!("Could not update session record file with new record: {e}"); } } } } Ok(pam_context) } fn apply_policy_to_context( context: &mut Context, auth: &Authentication, controls: &Restrictions, ) -> Result<(), crate::common::Error> { // see if the chdir flag is permitted match controls.chdir { DirChange::Any => {} DirChange::Strict(optdir) => { if let Some(chdir) = &context.chdir { return Err(Error::ChDirNotAllowed { chdir: chdir.clone(), command: context.command.command.clone(), }); } else { context.chdir = optdir.cloned(); } } } // expand tildes in the path with the users home directory if let Some(dir) = context.chdir.take() { context.chdir = Some(dir.expand_tilde_in_path(&context.target_user.name)?) } // in case the user could set these from the commandline, something more fancy // could be needed, but here we copy these -- perhaps we should split up the Context type context.use_pty = controls.use_pty; context.password_feedback = auth.pwfeedback; Ok(()) } /// This should determine what the authentication status for the given record /// match limit and origin/target user from the context is. fn determine_auth_status( must_policy_authenticate: bool, use_session_records: bool, record_for: Option, current_user: &CurrentUser, prior_validity: Duration, ) -> AuthStatus { if !must_policy_authenticate { AuthStatus::new(false, None) } else if let (true, Some(record_for)) = (use_session_records, record_for) { match SessionRecordFile::open_for_user(current_user, prior_validity) { Ok(mut sr) => { match sr.touch(record_for, current_user.uid) { // if a record was found and updated within the timeout, we do not need to authenticate Ok(TouchResult::Updated { .. }) => AuthStatus::new(false, Some(sr)), Ok(TouchResult::NotFound | TouchResult::Outdated { .. }) => { AuthStatus::new(true, Some(sr)) } Err(e) => { auth_warn!("Unexpected error while reading session information: {e}"); AuthStatus::new(true, None) } } } // if we cannot open the session record file we just assume there is none and continue as normal Err(e) => { auth_warn!("Could not use session information: {e}"); AuthStatus::new(true, None) } } } else { AuthStatus::new(true, None) } } struct AuthStatus { must_authenticate: bool, record_file: Option, } impl AuthStatus { fn new(must_authenticate: bool, record_file: Option) -> AuthStatus { AuthStatus { must_authenticate, record_file, } } } fn log_command_execution(context: &Context) { let tty_info = if let Ok(tty_name) = current_tty_name() { format!("TTY={} ;", escape_os_str_lossy(&tty_name)) } else { String::from("") }; let pwd = escape_os_str_lossy( std::env::current_dir() .as_ref() .map(|s| s.as_os_str()) .unwrap_or_else(|_| OsStr::new("unknown")), ); let user = context.target_user.name.escape_debug().collect::(); auth_info!( "{} : {} PWD={} ; USER={} ; COMMAND={}", &context.current_user.name, tty_info, pwd, user, &context.command ); } sudo-rs-0.2.5/src/sudoers/ast.rs000064400000000000000000000724061046102023000146450ustar 00000000000000use super::ast_names::UserFriendly; use super::basic_parser::*; use super::tokens::*; use crate::common::SudoString; use crate::common::{ HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2, HARDENED_ENUM_VALUE_3, HARDENED_ENUM_VALUE_4, }; use crate::defaults; /// The Sudoers file allows negating items with the exclamation mark. #[cfg_attr(test, derive(Debug, Eq))] #[derive(Clone, PartialEq)] #[repr(u32)] pub enum Qualified { Allow(T) = HARDENED_ENUM_VALUE_0, Forbid(T) = HARDENED_ENUM_VALUE_1, } impl Qualified { #[cfg(test)] pub fn as_allow(&self) -> Option<&T> { if let Self::Allow(v) = self { Some(v) } else { None } } } /// Type aliases; many items can be replaced by ALL, aliases, and negated. pub type Spec = Qualified>; pub type SpecList = Vec>; /// A generic mapping function (only used for turning `Spec` into `Spec`) impl Spec { pub fn map(self, f: impl Fn(T) -> U) -> Spec { let transform = |meta| match meta { Meta::All => Meta::All, Meta::Alias(alias) => Meta::Alias(alias), Meta::Only(x) => Meta::Only(f(x)), }; match self { Qualified::Allow(x) => Qualified::Allow(transform(x)), Qualified::Forbid(x) => Qualified::Forbid(transform(x)), } } } /// An identifier is a name or a #number #[cfg_attr(test, derive(Clone, Debug, PartialEq, Eq))] #[repr(u32)] pub enum Identifier { Name(SudoString) = HARDENED_ENUM_VALUE_0, ID(u32) = HARDENED_ENUM_VALUE_1, } /// A userspecifier is either a username, or a (non-unix) group name, or netgroup #[cfg_attr(test, derive(Clone, Debug, PartialEq, Eq))] #[repr(u32)] pub enum UserSpecifier { User(Identifier) = HARDENED_ENUM_VALUE_0, Group(Identifier) = HARDENED_ENUM_VALUE_1, NonunixGroup(Identifier) = HARDENED_ENUM_VALUE_2, } /// The RunAs specification consists of a (possibly empty) list of userspecifiers, followed by a (possibly empty) list of groups. pub struct RunAs { pub users: SpecList, pub groups: SpecList, } // `sudo -l l` calls this the `authenticate` option #[derive(Copy, Clone, Default, PartialEq)] #[cfg_attr(test, derive(Debug, Eq))] #[repr(u32)] pub enum Authenticate { #[default] None = HARDENED_ENUM_VALUE_0, // PASSWD: Passwd = HARDENED_ENUM_VALUE_1, // NOPASSWD: Nopasswd = HARDENED_ENUM_VALUE_2, } #[derive(Copy, Clone, Default, PartialEq)] #[cfg_attr(test, derive(Debug, Eq))] #[repr(u32)] pub enum EnvironmentControl { #[default] Implicit = HARDENED_ENUM_VALUE_0, // PASSWD: Setenv = HARDENED_ENUM_VALUE_1, // NOPASSWD: Nosetenv = HARDENED_ENUM_VALUE_2, } /// Commands in /etc/sudoers can have attributes attached to them, such as NOPASSWD, NOEXEC, ... #[derive(Default, Clone, PartialEq)] #[cfg_attr(test, derive(Debug, Eq))] pub struct Tag { pub(super) authenticate: Authenticate, pub(super) cwd: Option, pub(super) env: EnvironmentControl, } impl Tag { pub fn needs_passwd(&self) -> bool { matches!(self.authenticate, Authenticate::None | Authenticate::Passwd) } pub fn allows_setenv(&self) -> bool { matches!(self.env, EnvironmentControl::Setenv) } } /// Commands with attached attributes. pub struct CommandSpec(pub Vec, pub Spec); /// The main AST object for one sudoer-permission line type PairVec = Vec<(A, Vec)>; pub struct PermissionSpec { pub users: SpecList, pub permissions: PairVec, (Option, CommandSpec)>, } pub type Defs = Vec>; pub struct Def(pub String, pub SpecList); /// AST object for directive specifications (aliases, arguments, etc) #[repr(u32)] pub enum Directive { UserAlias(Defs) = HARDENED_ENUM_VALUE_0, HostAlias(Defs) = HARDENED_ENUM_VALUE_1, CmndAlias(Defs) = HARDENED_ENUM_VALUE_2, RunasAlias(Defs) = HARDENED_ENUM_VALUE_3, Defaults(Vec, ConfigScope) = HARDENED_ENUM_VALUE_4, } /// AST object for the 'context' (host, user, cmnd, runas) of a Defaults directive #[repr(u32)] pub enum ConfigScope { // "Defaults entries are parsed in the following order: // generic, host and user Defaults first, then runas Defaults and finally command defaults." Generic = HARDENED_ENUM_VALUE_0, Host(SpecList) = HARDENED_ENUM_VALUE_1, User(SpecList) = HARDENED_ENUM_VALUE_2, RunAs(SpecList) = HARDENED_ENUM_VALUE_3, Command(SpecList) = HARDENED_ENUM_VALUE_4, } /// The Sudoers file can contain permissions and directives #[repr(u32)] pub enum Sudo { Spec(PermissionSpec) = HARDENED_ENUM_VALUE_0, Decl(Directive) = HARDENED_ENUM_VALUE_1, Include(String, Span) = HARDENED_ENUM_VALUE_2, IncludeDir(String, Span) = HARDENED_ENUM_VALUE_3, LineComment = HARDENED_ENUM_VALUE_4, } impl Sudo { #[cfg(test)] pub fn is_spec(&self) -> bool { matches!(self, Self::Spec(..)) } #[cfg(test)] pub fn is_decl(&self) -> bool { matches!(self, Self::Decl(..)) } #[cfg(test)] pub fn is_line_comment(&self) -> bool { matches!(self, Self::LineComment) } #[cfg(test)] pub fn is_include(&self) -> bool { matches!(self, Self::Include(..)) } #[cfg(test)] pub fn is_include_dir(&self) -> bool { matches!(self, Self::IncludeDir(..)) } #[cfg(test)] pub fn as_include(&self) -> &str { if let Self::Include(v, _) = self { v } else { panic!() } } #[cfg(test)] pub fn as_spec(&self) -> Option<&PermissionSpec> { if let Self::Spec(v) = self { Some(v) } else { None } } } /// grammar: /// ```text /// identifier = name /// | # /// ``` impl Parse for Identifier { fn parse(stream: &mut CharStream) -> Parsed { if stream.eat_char('#') { let Digits(guid) = expect_nonterminal(stream)?; make(Identifier::ID(guid)) } else { let Username(name) = try_nonterminal(stream)?; make(Identifier::Name(name)) } } } impl Many for Identifier {} /// grammar: /// ```text /// qualified = T | "!", qualified /// ``` /// /// This computes the correct negation with multiple exclamation marks in the parsing stage so we /// are not bothered by it later. impl Parse for Qualified { fn parse(stream: &mut CharStream) -> Parsed { if is_syntax('!', stream)? { let mut neg = true; while is_syntax('!', stream)? { neg = !neg; } let ident = expect_nonterminal(stream)?; if neg { make(Qualified::Forbid(ident)) } else { make(Qualified::Allow(ident)) } } else { let ident = try_nonterminal(stream)?; make(Qualified::Allow(ident)) } } } impl Many for Qualified { const SEP: char = T::SEP; const LIMIT: usize = T::LIMIT; } /// Helper function for parsing `Meta` things where T is not a token fn parse_meta( stream: &mut CharStream, embed: impl FnOnce(SudoString) -> T, ) -> Parsed> { if let Some(meta) = try_nonterminal(stream)? { make(match meta { Meta::All => Meta::All, Meta::Alias(alias) => Meta::Alias(alias), Meta::Only(Username(name)) => Meta::Only(embed(name)), }) } else { make(Meta::Only(T::parse(stream)?)) } } /// Since Identifier is not a token, add the parser for `Meta` impl Parse for Meta { fn parse(stream: &mut CharStream) -> Parsed { parse_meta(stream, Identifier::Name) } } /// grammar: /// ```text /// userspec = identifier /// | %identifier /// | %:identifier /// | +netgroup /// ``` impl Parse for UserSpecifier { fn parse(stream: &mut CharStream) -> Parsed { fn parse_user(stream: &mut CharStream) -> Parsed { let userspec = if stream.eat_char('%') { let ctor = if stream.eat_char(':') { UserSpecifier::NonunixGroup } else { UserSpecifier::Group }; // in this case we must fail 'hard', since input has been consumed ctor(expect_nonterminal(stream)?) } else if stream.eat_char('+') { // TODO Netgroups unrecoverable!(stream, "netgroups are not supported yet"); } else { // in this case we must fail 'softly', since no input has been consumed yet UserSpecifier::User(try_nonterminal(stream)?) }; make(userspec) } // if we see a quote, first parse the quoted text as a token and then // re-parse whatever we found inside; this is a lazy solution but it works let begin_pos = stream.get_pos(); if stream.eat_char('"') { let Unquoted(text, _): Unquoted = expect_nonterminal(stream)?; let result = parse_user(&mut CharStream::new(text.chars())); if result.is_err() { unrecoverable!(pos = begin_pos, stream, "invalid user") } expect_syntax('"', stream)?; result } else { parse_user(stream) } } } impl Many for UserSpecifier {} /// UserSpecifier is not a token, implement the parser for `Meta` impl Parse for Meta { fn parse(stream: &mut CharStream) -> Parsed { parse_meta(stream, |name| UserSpecifier::User(Identifier::Name(name))) } } /// grammar: /// ```text /// runas = "(", userlist, (":", grouplist?)?, ")" /// ``` impl Parse for RunAs { fn parse(stream: &mut CharStream) -> Parsed { try_syntax('(', stream)?; let users = try_nonterminal(stream).unwrap_or_default(); let groups = maybe(try_syntax(':', stream).and_then(|_| try_nonterminal(stream)))? .unwrap_or_default(); expect_syntax(')', stream)?; make(RunAs { users, groups }) } } /// Implementing the trait Parse for `Meta`. Wrapped in an own object to avoid /// conflicting with a potential future generic parse definition for [Meta]. /// /// The reason for combining a parser for these two unrelated categories is that this is one spot /// where the sudoer grammar isn't nicely LL(1); so at the same place where "NOPASSWD" can appear, /// we could also see "ALL". struct MetaOrTag(Meta); /// A `Modifier` is something that updates the `Tag`. pub type Modifier = Box; // note: at present, "ALL" can be distinguished from a tag using a lookup of 1, since no tag starts with an "A"; but this feels like hanging onto // the parseability by a thread (although the original sudo also has some ugly parts, like 'sha224' being an illegal user name). // to be more general, we impl Parse for Meta so a future tag like "AFOOBAR" can be added with no problem impl Parse for MetaOrTag { fn parse(stream: &mut CharStream) -> Parsed { use Meta::*; let start_pos = stream.get_pos(); let AliasName(keyword) = try_nonterminal(stream)?; let mut switch = |modifier: fn(&mut Tag)| { expect_syntax(':', stream)?; make(Box::new(modifier)) }; let result: Modifier = match keyword.as_str() { // we do not support these, and that should make sudo-rs "fail safe" "INTERCEPT" | "NOEXEC" => unrecoverable!( pos = start_pos, stream, "NOEXEC and INTERCEPT are not supported by sudo-rs" ), // this is less fatal "LOG_INPUT" | "NOLOG_INPUT" | "LOG_OUTPUT" | "NOLOG_OUTPUT" | "MAIL" | "NOMAIL" => { eprintln_ignore_io_error!( "warning: {} tags are ignored by sudo-rs", keyword.as_str() ); switch(|_| {})? } // 'FOLLOW' and 'NOFOLLOW' are only usable in a sudoedit context, which will result in // a parse error elsewhere. 'EXEC' and 'NOINTERCEPT' are the default behaviour. "FOLLOW" | "NOFOLLOW" | "EXEC" | "NOINTERCEPT" => switch(|_| {})?, "SETENV" => switch(|tag| tag.env = EnvironmentControl::Setenv)?, "NOSETENV" => switch(|tag| tag.env = EnvironmentControl::Nosetenv)?, "PASSWD" => switch(|tag| tag.authenticate = Authenticate::Passwd)?, "NOPASSWD" => switch(|tag| tag.authenticate = Authenticate::Nopasswd)?, "CWD" => { expect_syntax('=', stream)?; let path: ChDir = expect_nonterminal(stream)?; Box::new(move |tag| tag.cwd = Some(path.clone())) } // we do not support these, and that should make sudo-rs "fail safe" spec @ ("CHROOT" | "TIMEOUT" | "NOTBEFORE" | "NOTAFTER") => unrecoverable!( pos = start_pos, stream, "{spec} is not supported by sudo-rs" ), "ROLE" | "TYPE" => unrecoverable!( pos = start_pos, stream, "role based access control is not yet supported by sudo-rs" ), "ALL" => return make(MetaOrTag(All)), alias => return make(MetaOrTag(Alias(alias.to_string()))), }; make(MetaOrTag(Only(result))) } } /// grammar: /// ```text /// commandspec = [tag modifiers]*, command /// ``` impl Parse for CommandSpec { fn parse(stream: &mut CharStream) -> Parsed { let mut tags = vec![]; while let Some(MetaOrTag(keyword)) = try_nonterminal(stream)? { use Qualified::Allow; match keyword { Meta::Only(modifier) => tags.push(modifier), Meta::All => return make(CommandSpec(tags, Allow(Meta::All))), Meta::Alias(name) => return make(CommandSpec(tags, Allow(Meta::Alias(name)))), } if tags.len() > Identifier::LIMIT { unrecoverable!(stream, "too many tags for command specifier") } } let start_pos = stream.get_pos(); if let Some(Username(keyword)) = try_nonterminal(stream)? { if keyword == "sudoedit" { // note: special behaviour of forward slashes in wildcards, tread carefully unrecoverable!(pos = start_pos, stream, "sudoedit is not yet supported"); } else if keyword == "list" { unrecoverable!(pos = start_pos, stream, "list is not yet supported"); } else if keyword.starts_with("sha") { unrecoverable!( pos = start_pos, stream, "digest specifications are not supported" ) } else { unrecoverable!( pos = start_pos, stream, "expected command but found {keyword}" ) }; } let cmd: Spec = expect_nonterminal(stream)?; make(CommandSpec(tags, cmd)) } } /// Parsing for a tuple of hostname, runas specifier and commandspec. /// grammar: /// ```text /// (host,runas,commandspec) = hostlist, "=", [runas?, commandspec]+ /// ``` impl Parse for (SpecList, Vec<(Option, CommandSpec)>) { fn parse(stream: &mut CharStream) -> Parsed { let hosts = try_nonterminal(stream)?; expect_syntax('=', stream)?; let runas_cmds = expect_nonterminal(stream)?; make((hosts, runas_cmds)) } } /// A hostname, runas specifier, commandspec combination can occur multiple times in a single /// sudoer line (seperated by ":") impl Many for (SpecList, Vec<(Option, CommandSpec)>) { const SEP: char = ':'; } /// Parsing for a tuple of hostname, runas specifier and commandspec. /// grammar: /// ```text /// (runas,commandspec) = runas?, commandspec /// ``` impl Parse for (Option, CommandSpec) { fn parse(stream: &mut CharStream) -> Parsed { let runas: Option = try_nonterminal(stream)?; let cmd = if runas.is_some() { expect_nonterminal(stream)? } else { try_nonterminal(stream)? }; make((runas, cmd)) } } /// A runas specifier, commandspec combination can occur multiple times in a single /// sudoer line (seperated by ","); there is some ambiguity in the original grammar: /// commands can also occur multiple times; we parse that here as if they have an omitted /// "runas" specifier (which has to be placed correctly during the AST analysis phase) impl Many for (Option, CommandSpec) {} /// grammar: /// ```text /// sudo = permissionspec /// | Keyword_Alias identifier = identifier_list /// | Defaults (name [+-]?= ...)+ /// ``` /// There is a syntactical ambiguity in the sudoer Directive and Permission specifications, so we /// have to parse them 'together' and do a delayed decision on which category we are in. impl Parse for Sudo { // note: original sudo would reject: // "User_Alias, user machine = command" // but accept: // "user, User_Alias machine = command"; this does the same fn parse(stream: &mut CharStream) -> Parsed { if stream.eat_char('@') { return parse_include(stream); } // the existence of "#include" forces us to handle lines that start with # explicitly if stream.peek() == Some('#') { return if let Ok(ident) = try_nonterminal::(stream) { let first_user = Qualified::Allow(Meta::Only(UserSpecifier::User(ident))); let users = if is_syntax(',', stream)? { // parse the rest of the userlist and add the already-parsed user in front let mut rest = expect_nonterminal::>(stream)?; rest.insert(0, first_user); rest } else { vec![first_user] }; // no need to check get_directive as no other directive starts with # let permissions = expect_nonterminal(stream)?; make(Sudo::Spec(PermissionSpec { users, permissions })) } else { // the failed "try_nonterminal::" will have consumed the '#' // the most ignominious part of sudoers: having to parse bits of comments parse_include(stream).or_else(|_| { stream.skip_to_newline(); make(Sudo::LineComment) }) }; } let start_pos = stream.get_pos(); if let Some(users) = maybe(try_nonterminal::>(stream))? { // element 1 always exists (parse_list fails on an empty list) let key = &users[0]; if let Some(directive) = maybe(get_directive(key, stream, start_pos))? { if users.len() != 1 { unrecoverable!(pos = start_pos, stream, "invalid user name list"); } make(Sudo::Decl(directive)) } else { let permissions = expect_nonterminal(stream)?; make(Sudo::Spec(PermissionSpec { users, permissions })) } } else { // this will leave whatever could not be parsed on the input stream make(Sudo::LineComment) } } } /// Parse the include/include dir part that comes after the '#' or '@' prefix symbol fn parse_include(stream: &mut CharStream) -> Parsed { fn get_path(stream: &mut CharStream, key_pos: (usize, usize)) -> Parsed<(String, Span)> { let path = if stream.eat_char('"') { let QuotedIncludePath(path) = expect_nonterminal(stream)?; expect_syntax('"', stream)?; path } else { let value_pos = stream.get_pos(); let IncludePath(path) = expect_nonterminal(stream)?; if stream.peek() != Some('\n') { unrecoverable!( pos = value_pos, stream, "use quotes around filenames or escape whitespace" ) } path }; make(( path, Span { start: key_pos, end: stream.get_pos(), }, )) } let key_pos = stream.get_pos(); let result = match try_nonterminal(stream)? { Some(Username(key)) if key == "include" => { let (path, span) = get_path(stream, key_pos)?; Sudo::Include(path, span) } Some(Username(key)) if key == "includedir" => { let (path, span) = get_path(stream, key_pos)?; Sudo::IncludeDir(path, span) } _ => unrecoverable!(pos = key_pos, stream, "unknown directive"), }; make(result) } /// grammar: /// ```text /// name = definition [ : name = definiton [ : ... ] ] /// ``` /// impl Parse for Def where T: UserFriendly, Meta: Parse + Many, { fn parse(stream: &mut CharStream) -> Parsed { let begin_pos = stream.get_pos(); let AliasName(name) = try_nonterminal(stream)?; if name == "ALL" { unrecoverable!( pos = begin_pos, stream, "the reserved alias ALL cannot be redefined" ); } expect_syntax('=', stream)?; make(Def(name, expect_nonterminal(stream)?)) } } impl Many for Def { const SEP: char = ':'; } // NOTE: This function is a bit of a hack, since it relies on the fact that all directives // occur in the spot of a username, and are of a form that would otherwise be a legal user name. // I.e. after a valid username has been parsed, we check if it isn't actually a valid start of a // directive. A more robust solution would be to use the approach taken by the `MetaOrTag` above. fn get_directive( perhaps_keyword: &Spec, stream: &mut CharStream, begin_pos: (usize, usize), ) -> Parsed { use super::ast::Directive::*; use super::ast::Meta::*; use super::ast::Qualified::*; use super::ast::UserSpecifier::*; let Allow(Only(User(Identifier::Name(keyword)))) = perhaps_keyword else { return reject(); }; match keyword.as_str() { "User_Alias" => make(UserAlias(expect_nonterminal(stream)?)), "Host_Alias" => make(HostAlias(expect_nonterminal(stream)?)), "Cmnd_Alias" | "Cmd_Alias" => make(CmndAlias(expect_nonterminal(stream)?)), "Runas_Alias" => make(RunasAlias(expect_nonterminal(stream)?)), "Defaults" => { //HACK: this avoids having to add "Defaults@" etc as separate tokens; but relying //on positional information during parsing is of course, cheating. let allow_scope_modifier = stream.get_pos().0 == begin_pos.0 && stream.get_pos().1 - begin_pos.1 == "Defaults".len(); let scope = if allow_scope_modifier { if is_syntax('@', stream)? { ConfigScope::Host(expect_nonterminal(stream)?) } else if is_syntax(':', stream)? { ConfigScope::User(expect_nonterminal(stream)?) } else if is_syntax('!', stream)? { ConfigScope::Command(expect_nonterminal(stream)?) } else if is_syntax('>', stream)? { ConfigScope::RunAs(expect_nonterminal(stream)?) } else { ConfigScope::Generic } } else { ConfigScope::Generic }; make(Defaults(expect_nonterminal(stream)?, scope)) } _ => reject(), } } /// grammar: /// ```text /// parameter = name [+-]?= ... /// ``` impl Parse for defaults::SettingsModifier { fn parse(stream: &mut CharStream) -> Parsed { let id_pos = stream.get_pos(); // Parse multiple entries enclosed in quotes (for list-like Defaults-settings) let parse_vars = |stream: &mut CharStream| -> Parsed> { if stream.eat_char('"') { let mut result = Vec::new(); while let Some(EnvVar(name)) = try_nonterminal(stream)? { if is_syntax('=', stream)? { let StringParameter(value) = expect_nonterminal(stream)?; result.push(name + "=" + &value); } else { result.push(name); } if result.len() > Identifier::LIMIT { unrecoverable!(stream, "environment variable list too long") } } expect_syntax('"', stream)?; if result.is_empty() { unrecoverable!(stream, "empty string not allowed"); } make(result) } else { let EnvVar(name) = expect_nonterminal(stream)?; if is_syntax('=', stream)? { unrecoverable!(stream, "double quotes are required for VAR=value pairs") } else { make(vec![name]) } } }; // Parse the remainder of a list variable let list_items = |mode: defaults::ListMode, name: String, cfg: defaults::SettingKind, stream: &mut _| { expect_syntax('=', stream)?; let defaults::SettingKind::List(checker) = cfg else { unrecoverable!(pos = id_pos, stream, "{name} is not a list parameter"); }; make(checker(mode, parse_vars(stream)?)) }; // Parse a text parameter let text_item = |stream: &mut CharStream| { if stream.eat_char('"') { let QuotedStringParameter(text) = expect_nonterminal(stream)?; expect_syntax('"', stream)?; make(text) } else { let StringParameter(name) = expect_nonterminal(stream)?; make(name) } }; if is_syntax('!', stream)? { let value_pos = stream.get_pos(); let DefaultName(name) = expect_nonterminal(stream)?; let Some(modifier) = defaults::negate(&name) else { if defaults::set(&name).is_some() { unrecoverable!(pos = value_pos, stream, "unknown setting: '{name}'"); } else { unrecoverable!( pos = value_pos, stream, "'{name}' cannot be used in a boolean context" ); } }; make(modifier) } else { let DefaultName(name) = try_nonterminal(stream)?; let Some(cfg) = defaults::set(&name) else { unrecoverable!(pos = id_pos, stream, "unknown setting: '{name}'"); }; if is_syntax('+', stream)? { list_items(defaults::ListMode::Add, name, cfg, stream) } else if is_syntax('-', stream)? { list_items(defaults::ListMode::Del, name, cfg, stream) } else if is_syntax('=', stream)? { let value_pos = stream.get_pos(); match cfg { defaults::SettingKind::Flag(_) => { unrecoverable!(stream, "can't assign to boolean setting '{name}'") } defaults::SettingKind::Integer(checker) => { let Numeric(denotation) = expect_nonterminal(stream)?; if let Some(modifier) = checker(&denotation) { make(modifier) } else { unrecoverable!( pos = value_pos, stream, "'{denotation}' is not a valid value for {name}" ); } } defaults::SettingKind::List(checker) => { let items = parse_vars(stream)?; make(checker(defaults::ListMode::Set, items)) } defaults::SettingKind::Text(checker) => { let text = text_item(stream)?; let Some(modifier) = checker(&text) else { unrecoverable!( pos = value_pos, stream, "'{text}' is not a valid value for {name}" ); }; make(modifier) } } } else { let defaults::SettingKind::Flag(modifier) = cfg else { unrecoverable!(pos = id_pos, stream, "'{name}' is not a boolean setting"); }; make(modifier) } } } } impl Many for defaults::SettingsModifier {} sudo-rs-0.2.5/src/sudoers/ast_names.rs000064400000000000000000000072371046102023000160300ustar 00000000000000//! This module contains user-friendly names for the various items in the AST, to report in case they are missing pub trait UserFriendly { const DESCRIPTION: &'static str; } // this is in a submodule so it can be switched off and replaced by a blanket implementation for test-cases #[cfg(not(test))] mod names { use super::*; use crate::defaults; use crate::sudoers::ast::*; use crate::sudoers::tokens; impl UserFriendly for tokens::Digits { const DESCRIPTION: &'static str = "number"; } impl UserFriendly for tokens::Numeric { const DESCRIPTION: &'static str = "nonnegative number"; } impl UserFriendly for Identifier { const DESCRIPTION: &'static str = "identifier"; } impl UserFriendly for Vec { const DESCRIPTION: &'static str = T::DESCRIPTION; } impl UserFriendly for tokens::Meta { const DESCRIPTION: &'static str = T::DESCRIPTION; } impl UserFriendly for Qualified { const DESCRIPTION: &'static str = T::DESCRIPTION; } impl UserFriendly for tokens::Command { const DESCRIPTION: &'static str = "path to binary (or sudoedit)"; } impl UserFriendly for tokens::SimpleCommand { const DESCRIPTION: &'static str = "path to binary (or sudoedit)"; } impl UserFriendly for ( SpecList, Vec<(Option, CommandSpec)>, ) { const DESCRIPTION: &'static str = tokens::Hostname::DESCRIPTION; } impl UserFriendly for (Option, CommandSpec) { const DESCRIPTION: &'static str = "(users:groups) specification"; } // this can never happen, as parse always succeeds impl UserFriendly for Sudo { const DESCRIPTION: &'static str = "nothing"; } impl UserFriendly for UserSpecifier { const DESCRIPTION: &'static str = "user"; } impl UserFriendly for tokens::Username { const DESCRIPTION: &'static str = "user"; } impl UserFriendly for tokens::Hostname { const DESCRIPTION: &'static str = "host name"; } impl UserFriendly for tokens::QuotedStringParameter { const DESCRIPTION: &'static str = "non-empty string"; } impl UserFriendly for tokens::QuotedIncludePath { const DESCRIPTION: &'static str = "non-empty string"; } impl UserFriendly for tokens::StringParameter { const DESCRIPTION: &'static str = tokens::QuotedStringParameter::DESCRIPTION; } impl UserFriendly for tokens::IncludePath { const DESCRIPTION: &'static str = "path to file"; } impl UserFriendly for tokens::AliasName { const DESCRIPTION: &'static str = "alias name"; } impl UserFriendly for tokens::DefaultName { const DESCRIPTION: &'static str = "configuration option"; } impl UserFriendly for tokens::EnvVar { const DESCRIPTION: &'static str = "environment variable"; } impl UserFriendly for CommandSpec { const DESCRIPTION: &'static str = tokens::Command::DESCRIPTION; } impl UserFriendly for tokens::ChDir { const DESCRIPTION: &'static str = "directory or '*'"; } impl UserFriendly for defaults::SettingsModifier { const DESCRIPTION: &'static str = "parameter"; } impl UserFriendly for Def { const DESCRIPTION: &'static str = "alias definition"; } impl UserFriendly for tokens::Unquoted { const DESCRIPTION: &'static str = T::DESCRIPTION; } } #[cfg(test)] impl UserFriendly for T { const DESCRIPTION: &'static str = "elem"; } sudo-rs-0.2.5/src/sudoers/basic_parser.rs000064400000000000000000000331151046102023000165050ustar 00000000000000//! Building blocks for a recursive descent LL(1) parsing method. //! //! The general idea is that a grammar (without left recursion) is translated to a series of //! conditional and unconditional 'acceptance' methods. //! //! For example, assuming we have a parser for integers: //! //! sum = integer | integer + sum //! //! Can get translated as: (representing a sum as `LinkedList`): //! //! ```ignore //! impl Parse for LinkedList { //! fn parse(stream: &mut CharStream) -> Parsed> { //! let x = try_nonterminal(stream)?; //! let mut tail = if is_syntax('+', stream)? { //! expect_nonterminal(stream)? //! } else { //! LinkedList::new() //! }; //! tail.push_front(x); //! //! make(tail) //! } //! } //! ``` /// Type holding a parsed object (or error information if parsing failed) pub type Parsed = Result; #[derive(Copy, Clone)] #[cfg_attr(test, derive(Debug, PartialEq))] pub struct Span { pub start: (usize, usize), pub end: (usize, usize), } #[cfg_attr(test, derive(Debug, PartialEq))] pub enum Status { Fatal(Span, String), // not recoverable; stream in inconsistent state Reject, // parsing failed by no input consumed } pub fn make(value: T) -> Parsed { Ok(value) } pub fn reject() -> Parsed { Err(Status::Reject) } macro_rules! unrecoverable { (pos=$pos:expr, $stream:ident, $($str:expr),*) => { return Err(crate::sudoers::basic_parser::Status::Fatal(Span { start: $pos, end: CharStream::get_pos($stream)}, format![$($str),*])) }; ($stream:ident, $($str:expr),*) => {{ let pos = CharStream::get_pos($stream); return Err(crate::sudoers::basic_parser::Status::Fatal(Span { start: pos, end: pos }, format![$($str),*])) }}; ($($str:expr),*) => { return Err(crate::basic_parser::Status::Fatal(Default::default(), format![$($str),*])) }; } pub(super) use unrecoverable; /// This recovers from a failed parsing. pub fn maybe(status: Parsed) -> Parsed> { match status { Ok(x) => Ok(Some(x)), Err(Status::Reject) => Ok(None), Err(err) => Err(err), } } pub use super::char_stream::CharStream; /// All implementations of the Parse trait must satisfy this contract: /// If the `parse` method of this trait returns None, the iterator is not advanced; otherwise it is /// advanced beyond the accepted part of the input. i.e. if some input is consumed the method /// *MUST* be producing a `Some` value. pub trait Parse { fn parse(stream: &mut CharStream) -> Parsed where Self: Sized; } /// Structures representing whitespace (trailing whitespace can contain comments) #[cfg_attr(test, derive(PartialEq, Eq))] struct LeadingWhitespace; #[cfg_attr(test, derive(PartialEq, Eq))] struct TrailingWhitespace; #[cfg_attr(test, derive(Debug, PartialEq, Eq))] struct Comment; /// Accept zero or more whitespace characters; fails if the whitespace is not "leading" to something /// (which can be used to detect end-of-input). impl Parse for LeadingWhitespace { fn parse(stream: &mut CharStream) -> Parsed { let eat_space = |stream: &mut CharStream| stream.next_if(|c| "\t ".contains(c)); while eat_space(stream).is_some() {} if stream.peek().is_some() { make(LeadingWhitespace {}) } else { unrecoverable!(stream, "superfluous whitespace") } } } /// Accept zero or more whitespace characters; since this accepts zero characters, it /// always succeeds (unless some serious error occurs). This parser also accepts comments, /// since those can form part of trailing white space. impl Parse for TrailingWhitespace { fn parse(stream: &mut CharStream) -> Parsed { loop { let _ = LeadingWhitespace::parse(stream); // don't propagate any errors // line continuations if stream.eat_char('\\') { // do the equivalent of expect_syntax('\n', stream)?, without recursion if !stream.eat_char('\n') { unrecoverable!(stream, "stray escape sequence") } } else { break; } } make(TrailingWhitespace {}) } } /// Parses a comment impl Parse for Comment { fn parse(stream: &mut CharStream) -> Parsed { if !stream.eat_char('#') { return Err(Status::Reject); } stream.skip_to_newline(); make(Comment {}) } } fn skip_trailing_whitespace(stream: &mut CharStream) -> Parsed<()> { TrailingWhitespace::parse(stream)?; make(()) } /// Adheres to the contract of the [Parse] trait, accepts one character and consumes trailing whitespace. pub fn try_syntax(syntax: char, stream: &mut CharStream) -> Parsed<()> { if !stream.eat_char(syntax) { return Err(Status::Reject); } skip_trailing_whitespace(stream)?; make(()) } /// Similar to [try_syntax], but aborts parsing if the expected character is not found. pub fn expect_syntax(syntax: char, stream: &mut CharStream) -> Parsed<()> { if try_syntax(syntax, stream).is_err() { let str = if let Some(c) = stream.peek() { c.to_string() } else { "EOF".to_string() }; unrecoverable!(stream, "expecting '{syntax}' but found '{str}'") } make(()) } /// Convenience function: usually try_syntax is called as a test criterion; if this returns true, the input was consumed. pub fn is_syntax(syntax: char, stream: &mut CharStream) -> Parsed { let result = maybe(try_syntax(syntax, stream))?; make(result.is_some()) } /// Interface for working with types that implement the [Parse] trait; this allows parsing to use /// type inference. Use this instead of calling [Parse::parse] directly. pub fn try_nonterminal(stream: &mut CharStream) -> Parsed { let result = T::parse(stream)?; skip_trailing_whitespace(stream)?; make(result) } /// Interface for working with types that implement the [Parse] trait; this expects to parse /// the given type or gives a fatal parse error if this did not succeed. use super::ast_names::UserFriendly; pub fn expect_nonterminal(stream: &mut CharStream) -> Parsed { let begin_pos = stream.get_pos(); match try_nonterminal(stream) { Err(Status::Reject) => { unrecoverable!(pos = begin_pos, stream, "expected {}", T::DESCRIPTION) } result => result, } } /// Something that implements the Token trait is a token (i.e. a string of characters defined by a /// maximum length, character classes, and possible escaping). The class for the first character of /// the token can be different than that of the rest. pub trait Token: Sized { const MAX_LEN: usize = 255; fn construct(s: String) -> Result; fn accept(c: char) -> bool; fn accept_1st(c: char) -> bool { Self::accept(c) } const ALLOW_ESCAPE: bool = false; fn escaped(_: char) -> bool { false } } /// Implementation of the [Parse] trait for anything that implements [Token] impl Parse for T { fn parse(stream: &mut CharStream) -> Parsed { fn accept_escaped( pred: fn(char) -> bool, stream: &mut CharStream, ) -> Parsed { const ESCAPE: char = '\\'; if T::ALLOW_ESCAPE && stream.eat_char(ESCAPE) { if let Some(c) = stream.next_if(T::escaped) { Ok(c) } else if pred(ESCAPE) { Ok(ESCAPE) } else if stream.eat_char('\n') { // we've just consumed some whitespace, we should end // parsing the token but not abort it reject() } else { unrecoverable!(stream, "illegal escape sequence") } } else if let Some(c) = stream.next_if(pred) { Ok(c) } else { reject() } } let start_pos = stream.get_pos(); let mut str = accept_escaped::(T::accept_1st, stream)?.to_string(); while let Some(c) = maybe(accept_escaped::(T::accept, stream))? { if str.len() >= T::MAX_LEN { unrecoverable!(stream, "token exceeds maximum length") } str.push(c) } match T::construct(str) { Ok(result) => make(result), Err(msg) => unrecoverable!(pos = start_pos, stream, "{msg}"), } } } /// Parser for `Option` (this can be used to make the code more readable) impl Parse for Option { fn parse(stream: &mut CharStream) -> Parsed { maybe(T::parse(stream)) } } /// Parsing method for lists of items separated by a given character; this adheres to the contract of the [Parse] trait. pub(super) fn parse_list( sep_by: char, max: usize, stream: &mut CharStream, ) -> Parsed> { let mut elems = Vec::new(); elems.push(try_nonterminal(stream)?); while maybe(try_syntax(sep_by, stream))?.is_some() { if elems.len() >= max { unrecoverable!(stream, "too many items in list") } elems.push(expect_nonterminal(stream)?); } make(elems) } /// Types that implement the Many trait can be parsed multiple tokens into a `Vec`; they are /// seperated by `SEP`. There should also be a limit on the number of items. pub trait Many { const SEP: char = ','; const LIMIT: usize = 127; } /// Generic implementation for parsing multiple items of a type `T` that implements the [Parse] and /// [Many] traits. impl Parse for Vec { fn parse(stream: &mut CharStream) -> Parsed { parse_list(T::SEP, T::LIMIT, stream) } } /// Entry point utility function; parse a `Vec` but with fatal error recovery per line pub fn parse_lines(stream: &mut CharStream) -> Vec> where T: Parse + UserFriendly, { let mut result = Vec::new(); // this will terminate; if the inner accept_if is an error, either a character will be consumed // by the second accept_if (making progress), or the end of the stream will have been reacherd // (which will cause the next iteration to fall through) while LeadingWhitespace::parse(stream).is_ok() { let item = expect_nonterminal(stream); let parsed_item_ok = item.is_ok(); result.push(item); let _ = maybe(Comment::parse(stream)); if !stream.eat_char('\n') { if parsed_item_ok { let msg = if stream.peek().is_none() { "missing line terminator at end of file" } else { "garbage at end of line" }; let error = |stream: &mut CharStream| unrecoverable!(stream, "{msg}"); result.push(error(stream)); } stream.skip_to_newline(); } } result } #[cfg(test)] fn expect_complete(stream: &mut CharStream) -> Parsed { let result = expect_nonterminal(stream)?; if let Some(c) = stream.peek() { unrecoverable!(stream, "garbage at end of line: {c}") } make(result) } /// Convenience function (especially useful for writing test cases, to avoid having to write out the /// AST constructors by hand. #[cfg(test)] pub fn parse_string(text: &str) -> Parsed { expect_complete(&mut CharStream::new(text.chars())) } #[cfg(test)] pub fn parse_eval(text: &str) -> T { parse_string(text).unwrap() } #[cfg(test)] mod test { use super::*; impl Token for String { fn construct(val: String) -> Result { Ok(val) } fn accept(c: char) -> bool { c.is_ascii_alphanumeric() } } impl Many for String {} #[test] fn comment_test() { assert_eq!(parse_eval::("# hello"), Comment); } #[test] #[should_panic] fn comment_test_fail() { assert_eq!(parse_eval::("# hello\nsomething"), Comment); } #[test] fn lines_test() { let input = |text: &str| parse_lines(&mut CharStream::new(text.chars())); let s = |text: &str| Ok(text.to_string()); assert_eq!(input("hello\nworld\n"), vec![s("hello"), s("world")]); assert_eq!(input(" hello\nworld\n"), vec![s("hello"), s("world")]); assert_eq!(input("hello \nworld\n"), vec![s("hello"), s("world")]); assert_eq!(input("hello\n world\n"), vec![s("hello"), s("world")]); assert_eq!(input("hello\nworld \n"), vec![s("hello"), s("world")]); assert_eq!(input("hello\nworld")[0..2], vec![s("hello"), s("world")]); let Err(_) = input("hello\nworld")[2] else { panic!() }; let Err(_) = input("hello\nworld:\n")[2] else { panic!() }; } #[test] fn whitespace_test() { assert_eq!( parse_eval::>("hello,something"), vec!["hello", "something"] ); assert_eq!( parse_eval::>("hello , something"), vec!["hello", "something"] ); assert_eq!( parse_eval::>("hello, something"), vec!["hello", "something"] ); assert_eq!( parse_eval::>("hello ,something"), vec!["hello", "something"] ); assert_eq!( parse_eval::>("hello\\\n,something"), vec!["hello", "something"] ); } } sudo-rs-0.2.5/src/sudoers/char_stream.rs000064400000000000000000000030151046102023000163340ustar 00000000000000pub struct CharStream<'a> { iter: std::iter::Peekable>, line: usize, col: usize, } impl<'a> CharStream<'a> { pub fn new(src: std::str::Chars<'a>) -> Self { CharStream { iter: src.peekable(), line: 1, col: 1, } } } impl CharStream<'_> { pub fn next_if(&mut self, f: impl FnOnce(char) -> bool) -> Option { let item = self.iter.next_if(|&c| f(c)); match item { Some('\n') => { self.line += 1; self.col = 1; } Some(_) => self.col += 1, _ => {} } item } pub fn eat_char(&mut self, expect_char: char) -> bool { self.next_if(|c| c == expect_char).is_some() } pub fn skip_to_newline(&mut self) { while self.next_if(|c| c != '\n').is_some() {} } pub fn peek(&mut self) -> Option { self.iter.peek().cloned() } pub fn get_pos(&self) -> (usize, usize) { (self.line, self.col) } } #[cfg(test)] mod test { use super::*; #[test] fn test_iter() { let mut stream = CharStream::new("12\n3\n".chars()); assert_eq!(stream.peek(), Some('1')); assert!(stream.eat_char('1')); assert_eq!(stream.peek(), Some('2')); assert!(stream.eat_char('2')); assert!(stream.eat_char('\n')); assert_eq!(stream.peek(), Some('3')); assert!(stream.eat_char('3')); assert_eq!(stream.get_pos(), (2, 2)); } } sudo-rs-0.2.5/src/sudoers/entry/verbose.rs000064400000000000000000000041561046102023000166610ustar 00000000000000use core::fmt; use crate::sudoers::{ ast::{Authenticate, RunAs, Tag}, tokens::ChDir, }; use super::Entry; pub struct Verbose<'a>(pub Entry<'a>); impl fmt::Display for Verbose<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self(Entry { run_as, cmd_specs, cmd_alias, }) = self; let root_runas = super::root_runas(); let run_as = run_as.unwrap_or(&root_runas); let mut last_tag = None; for (tag, cmd_spec) in cmd_specs { if last_tag != Some(tag) { let is_first_iteration = last_tag.is_none(); if !is_first_iteration { f.write_str("\n")?; } write_entry_header(run_as, f)?; write_tag(f, tag)?; f.write_str("\n Commands:")?; } last_tag = Some(tag); f.write_str("\n\t")?; super::write_spec(f, cmd_spec, cmd_alias.iter().rev(), true, "\n\t")?; } Ok(()) } } fn write_entry_header(run_as: &RunAs, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\nSudoers entry:")?; write_users(run_as, f)?; write_groups(run_as, f) } fn write_users(run_as: &RunAs, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\n RunAsUsers: ")?; super::write_users(run_as, f) } fn write_groups(run_as: &RunAs, f: &mut fmt::Formatter<'_>) -> fmt::Result { if run_as.groups.is_empty() { return Ok(()); } f.write_str("\n RunAsGroups: ")?; super::write_groups(run_as, f) } fn write_tag(f: &mut fmt::Formatter, tag: &Tag) -> fmt::Result { if tag.authenticate != Authenticate::None { f.write_str("\n Options: ")?; if tag.authenticate != Authenticate::Passwd { f.write_str("!")?; } f.write_str("authenticate")?; } if let Some(cwd) = &tag.cwd { f.write_str("\n Cwd: ")?; match cwd { ChDir::Path(path) => write!(f, "{}", path.display())?, ChDir::Any => f.write_str("*")?, } } Ok(()) } sudo-rs-0.2.5/src/sudoers/entry.rs000064400000000000000000000163341046102023000152150ustar 00000000000000use core::fmt; use crate::sudoers::{ ast::{Identifier, Qualified, UserSpecifier}, tokens::{ChDir, Meta}, VecOrd, }; use crate::{ common::{resolve::CurrentUser, SudoString}, system::{interface::UserId, User}, }; use self::verbose::Verbose; use super::{ ast::{Authenticate, Def, RunAs, Tag}, tokens::Command, }; mod verbose; pub struct Entry<'a> { run_as: Option<&'a RunAs>, cmd_specs: Vec<(Tag, &'a Qualified>)>, cmd_alias: &'a VecOrd>, } impl<'a> Entry<'a> { pub(super) fn new( run_as: Option<&'a RunAs>, cmd_specs: Vec<(Tag, &'a Qualified>)>, cmd_alias: &'a VecOrd>, ) -> Self { debug_assert!(!cmd_specs.is_empty()); Self { run_as, cmd_specs, cmd_alias, } } pub fn verbose(self) -> impl fmt::Display + 'a { Verbose(self) } } fn root_runas() -> RunAs { let name = User::from_uid(UserId::ROOT) .ok() .flatten() .map(|u| u.name) .unwrap_or(SudoString::new("root".into()).unwrap()); let name = UserSpecifier::User(Identifier::Name(name)); let name = Qualified::Allow(Meta::Only(name)); RunAs { users: vec![name], groups: vec![], } } impl fmt::Display for Entry<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { run_as, cmd_specs, cmd_alias, } = self; let root_runas = root_runas(); let run_as = run_as.unwrap_or(&root_runas); f.write_str(" (")?; write_users(run_as, f)?; if !run_as.groups.is_empty() { f.write_str(" : ")?; } write_groups(run_as, f)?; f.write_str(") ")?; let mut last_tag = None; for (tag, spec) in cmd_specs { let is_first_iteration = last_tag.is_none(); if !is_first_iteration { f.write_str(", ")?; } write_tag(f, tag, last_tag)?; last_tag = Some(tag); // cmd_alias is to be topologically sorted (dependencies come before dependents), // the argument to write_spec needs to have dependents before dependencies. write_spec(f, spec, cmd_alias.iter().rev(), true, ", ")?; } Ok(()) } } fn write_users(run_as: &RunAs, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { if run_as.users.is_empty() { match CurrentUser::resolve() { Ok(u) => f.write_str(&u.name)?, _ => f.write_str("?")?, }; } let mut is_first_user = true; for user in &run_as.users { if !is_first_user { f.write_str(", ")?; } is_first_user = false; let meta = match user { Qualified::Allow(meta) => meta, Qualified::Forbid(meta) => { f.write_str("!")?; meta } }; match meta { Meta::All => f.write_str("ALL")?, Meta::Only(user) => { let ident = match user { UserSpecifier::User(ident) => ident, UserSpecifier::Group(ident) => { f.write_str("%")?; ident } UserSpecifier::NonunixGroup(ident) => { f.write_str("%:")?; ident } }; match ident { Identifier::Name(name) => f.write_str(name)?, Identifier::ID(id) => write!(f, "#{id}")?, } } Meta::Alias(alias) => f.write_str(alias)?, } } Ok(()) } fn write_groups(run_as: &RunAs, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { let mut is_first_group = true; for group in &run_as.groups { if !is_first_group { f.write_str(", ")?; } is_first_group = false; let meta = match group { Qualified::Allow(meta) => meta, Qualified::Forbid(meta) => { f.write_str("!")?; meta } }; match meta { Meta::All => f.write_str("ALL")?, Meta::Only(ident) => match ident { Identifier::Name(name) => f.write_str(name)?, Identifier::ID(id) => write!(f, "#{id}")?, }, Meta::Alias(alias) => f.write_str(alias)?, } } Ok(()) } fn write_tag(f: &mut fmt::Formatter, tag: &Tag, last_tag: Option<&Tag>) -> fmt::Result { let (cwd, auth) = if let Some(last_tag) = last_tag { let cwd = if last_tag.cwd == tag.cwd { None } else { tag.cwd.as_ref() }; let auth = if last_tag.authenticate == tag.authenticate { None } else { Some(tag.authenticate) }; (cwd, auth) } else { (tag.cwd.as_ref(), Some(tag.authenticate)) }; if let Some(cwd) = cwd { f.write_str("CWD=")?; match cwd { ChDir::Path(path) => write!(f, "{}", path.display())?, ChDir::Any => f.write_str("*")?, } f.write_str(" ")?; } if let Some(auth) = auth { if auth != Authenticate::None { let tag = if auth == Authenticate::Passwd { "PASSWD" } else { "NOPASSWD" }; f.write_str(tag)?; f.write_str(": ")?; } } Ok(()) } fn write_spec<'a>( f: &mut fmt::Formatter, spec: &Qualified>, mut alias_list: impl Iterator> + Clone, mut sign: bool, separator: &str, ) -> fmt::Result { let meta = match spec { Qualified::Allow(meta) => meta, Qualified::Forbid(meta) => { sign = !sign; meta } }; match meta { Meta::All | Meta::Only(_) if !sign => f.write_str("!")?, _ => {} } match meta { Meta::All => f.write_str("ALL")?, Meta::Only((cmd, args)) => { write!(f, "{cmd}")?; if let Some(args) = args { for arg in args.iter() { write!(f, " {arg}")?; } } } Meta::Alias(alias) => { if let Some(Def(_, spec_list)) = alias_list.find(|Def(id, _)| id == alias) { let mut is_first_iteration = true; for spec in spec_list { if !is_first_iteration { f.write_str(separator)?; } // 1) this recursion will terminate, since "alias_list" has become smaller // by the "alias_list.find()" above // 2) to get the correct macro expansion, alias_list has to be (reverse-)topologically // sorted so that "later" definitions do not refer back to "earlier" definitons. write_spec(f, spec, alias_list.clone(), sign, separator)?; is_first_iteration = false; } } else { f.write_str("???")? } } } Ok(()) } sudo-rs-0.2.5/src/sudoers/mod.rs000064400000000000000000000733441046102023000146370ustar 00000000000000#![forbid(unsafe_code)] //! Code that checks (and in the future: lists) permissions in the sudoers file mod ast; mod ast_names; mod basic_parser; mod char_stream; mod entry; mod tokens; use std::collections::{HashMap, HashSet}; use std::io; use std::path::{Path, PathBuf}; use crate::common::resolve::resolve_path; use crate::defaults; use crate::log::auth_warn; use crate::system::interface::{GroupId, UnixGroup, UnixUser, UserId}; use crate::system::{self, can_execute}; use ast::*; use tokens::*; pub type Settings = defaults::Settings; pub use basic_parser::Span; /// How many nested include files do we allow? const INCLUDE_LIMIT: u8 = 128; /// Export some necessary symbols from modules pub struct Error { pub source: Option, pub location: Option, pub message: String, } /// A "Customiser" represents a "Defaults" setting that has 'late binding'; i.e. /// cannot be determined simply by reading a sudoers configuration. This is used /// for Defaults@host, Defaults:user, Defaults>runas and Defaults!cmd. /// /// I.e. the Setting modifications in the second part of the tuple only apply for /// items explicitly matched by the first part of the tuple. type Customiser = (Scope, Vec); #[derive(Default)] pub struct Sudoers { rules: Vec, aliases: AliasTable, settings: Settings, customisers: CustomiserTable, } /// A structure that represents what the user wants to do pub struct Request<'a, User: UnixUser, Group: UnixGroup> { pub user: &'a User, pub group: &'a Group, pub command: &'a Path, pub arguments: &'a [String], } pub struct ListRequest<'a, User: UnixUser, Group: UnixGroup> { pub target_user: &'a User, pub target_group: &'a Group, } #[derive(Default)] #[cfg_attr(test, derive(Clone))] pub struct Judgement { flags: Option, settings: Settings, } mod policy; pub use policy::{AuthenticatingUser, Authentication, Authorization, DirChange, Restrictions}; pub use self::entry::Entry; type MatchedCommand<'a> = (Option<&'a RunAs>, (Tag, &'a Spec)); /// This function takes a file argument for a sudoers file and processes it. impl Sudoers { pub fn open(path: impl AsRef) -> Result<(Sudoers, Vec), io::Error> { let sudoers = open_sudoers(path.as_ref())?; Ok(analyze(path.as_ref(), sudoers)) } pub fn read>( reader: R, path: P, ) -> Result<(Sudoers, Vec), io::Error> { let sudoers = read_sudoers(reader)?; Ok(analyze(path.as_ref(), sudoers)) } fn specify_host_user_runas>( &mut self, hostname: &system::Hostname, requesting_user: &User, target_user: &User, ) { let customisers = std::mem::take(&mut self.customisers.non_cmnd); let host_matcher = &match_token(hostname); let user_matcher = &match_user(requesting_user); let runas_matcher = &match_user(target_user); let host_aliases = get_aliases(&self.aliases.host, host_matcher); let user_aliases = get_aliases(&self.aliases.user, user_matcher); let runas_aliases = get_aliases(&self.aliases.runas, runas_matcher); let match_scope = |scope| match scope { ConfigScope::Generic => true, ConfigScope::Host(list) => find_item(&list, host_matcher, &host_aliases).is_some(), ConfigScope::User(list) => find_item(&list, user_matcher, &user_aliases).is_some(), ConfigScope::RunAs(list) => find_item(&list, runas_matcher, &runas_aliases).is_some(), ConfigScope::Command(_list) => { unreachable!("command-specific defaults are filtered out") } }; for (scope, modifiers) in customisers { if match_scope(scope) { for modifier in modifiers { modifier(&mut self.settings); } } } } fn specify_command(&mut self, command: &Path, arguments: &[String]) { let customisers = std::mem::take(&mut self.customisers.cmnd); let cmnd_matcher = &match_command((command, arguments)); let cmnd_aliases = get_aliases(&self.aliases.cmnd, cmnd_matcher); for (scope, modifiers) in customisers { if find_item(&scope, cmnd_matcher, &cmnd_aliases).is_some() { for modifier in modifiers { modifier(&mut self.settings); } } } } pub fn check, Group: UnixGroup>( &mut self, am_user: &User, on_host: &system::Hostname, request: Request, ) -> Judgement { self.specify_host_user_runas(on_host, am_user, request.user); self.specify_command(request.command, request.arguments); // exception: if user is root or does not switch users, NOPASSWD is implied let skip_passwd = am_user.is_root() || (request.user == am_user && in_group(am_user, request.group)); let mut flags = check_permission(self, am_user, on_host, request); if let Some(Tag { authenticate, .. }) = flags.as_mut() { if skip_passwd { *authenticate = Authenticate::Nopasswd; } } Judgement { flags, settings: self.settings.clone(), } } pub fn check_list_permission, Group: UnixGroup>( &self, invoking_user: &User, hostname: &system::Hostname, request: ListRequest, ) -> Authorization { // exception: if user is root or does not switch users, NOPASSWD is implied let skip_passwd = invoking_user.is_root() || (request.target_user == invoking_user && in_group(invoking_user, request.target_group)); let mut flags = self .matching_user_specs(invoking_user, hostname) .flatten() .map(|(_, (tag, _))| tag) .max_by_key(|tag| !tag.needs_passwd()); if let Some(tag) = flags.as_mut() { if skip_passwd { tag.authenticate = Authenticate::Nopasswd; } Authorization::Allowed(self.settings.to_auth(tag), ()) } else { Authorization::Forbidden } } pub fn check_validate_permission>( &self, invoking_user: &User, hostname: &system::Hostname, ) -> Authorization { // exception: if user is root, NOPASSWD is implied let skip_passwd = invoking_user.is_root(); let mut flags = self .matching_user_specs(invoking_user, hostname) .flatten() .map(|(_, (tag, _))| tag) .max_by_key(|tag| tag.needs_passwd()); if let Some(tag) = flags.as_mut() { if skip_passwd { tag.authenticate = Authenticate::Nopasswd; } Authorization::Allowed(self.settings.to_auth(tag), ()) } else { Authorization::Forbidden } } /// returns `User_Spec`s that match `invoking_user` and `hostname` /// /// it also distributes `Tag_Spec`s across the `Cmnd_Spec` list of each `User_Spec` /// /// the outer iterator are the `User_Spec`s; the inner iterator are the `Cmnd_Spec`s of /// said `User_Spec`s fn matching_user_specs<'a, User: UnixUser + PartialEq>( &'a self, invoking_user: &'a User, hostname: &'a system::Hostname, ) -> impl Iterator>> { let Self { rules, aliases, .. } = self; let user_aliases = get_aliases(&aliases.user, &match_user(invoking_user)); let host_aliases = get_aliases(&aliases.host, &match_token(hostname)); rules .iter() .filter_map(move |sudo| { find_item(&sudo.users, &match_user(invoking_user), &user_aliases)?; Some(&sudo.permissions) }) .flatten() .filter_map(move |(hosts, runas_cmds)| { find_item(hosts, &match_token(hostname), &host_aliases)?; Some(distribute_tags(runas_cmds)) }) } pub fn matching_entries<'a, User: UnixUser + PartialEq>( &'a self, invoking_user: &'a User, hostname: &'a system::Hostname, ) -> impl Iterator> { let user_specs = self.matching_user_specs(invoking_user, hostname); user_specs.flat_map(|cmd_specs| group_cmd_specs_per_runas(cmd_specs, &self.aliases.cmnd)) } pub(crate) fn solve_editor_path>( mut self, on_host: &system::Hostname, am_user: &User, target_user: &User, ) -> Option { self.specify_host_user_runas(on_host, am_user, target_user); if self.settings.env_editor() { for key in ["SUDO_EDITOR", "VISUAL", "EDITOR"] { if let Some(var) = std::env::var_os(key) { let path = Path::new(&var); if can_execute(path) { return Some(path.to_owned()); } let path = resolve_path( path, &std::env::var("PATH").unwrap_or(env!("SUDO_PATH_DEFAULT").to_string()), ); if let Some(path) = path { return Some(path); } } } } None } } // a `take_while` variant that does not consume the first non-matching item fn peeking_take_while<'a, T>( iter: &'a mut std::iter::Peekable>, pred: impl Fn(&T) -> bool + 'a, ) -> impl Iterator + 'a { std::iter::from_fn(move || iter.next_if(&pred)) } fn group_cmd_specs_per_runas<'a>( cmnd_specs: impl Iterator, (Tag, &'a Spec))>, cmnd_aliases: &'a VecOrd>, ) -> impl Iterator> { // `distribute_tags` will have given every spec a reference to the "runas specification" // that applies to it. The output of sudo --list splits the CmndSpec list based on that: // every line only has a single "runas" specifier. So we need to combine them for that. // // But sudo --list also outputs lines that are from different lines in the sudoers file on // different lines in the output of sudo --list, so we cannot compare "by value". Luckily, // once a RunAs is parsed, it will have a unique identifier in the form of its address. let origin = |runas: Option<&RunAs>| runas.map(|r| r as *const _); let mut cmnd_specs = cmnd_specs.peekable(); std::iter::from_fn(move || { if let Some(&(cur_runas, _)) = cmnd_specs.peek() { let specs = peeking_take_while(&mut cmnd_specs, |&(runas, _)| { origin(runas) == origin(cur_runas) }); Some(Entry::new( cur_runas, specs.map(|x| x.1).collect(), cmnd_aliases, )) } else { None } }) } fn read_sudoers(mut reader: R) -> io::Result>> { // it's a bit frustrating that BufReader.chars() does not exist let mut buffer = String::new(); reader.read_to_string(&mut buffer)?; use basic_parser::parse_lines; use char_stream::*; Ok(parse_lines(&mut CharStream::new(buffer.chars()))) } fn open_sudoers(path: &Path) -> io::Result>> { let source = crate::system::secure_open(path, false)?; read_sudoers(source) } fn open_subsudoers(path: &Path) -> io::Result>> { let source = crate::system::secure_open(path, true)?; read_sudoers(source) } // note: trying to DRY using GAT's is tempting but doesn't make the code any shorter #[derive(Default)] struct AliasTable { user: VecOrd>, host: VecOrd>, cmnd: VecOrd>, runas: VecOrd>, } #[derive(Default)] struct CustomiserTable { non_cmnd: Vec>, cmnd: Vec>>, } /// A vector with a list defining the order in which it needs to be processed struct VecOrd(Vec, Vec); impl Default for VecOrd { fn default() -> Self { VecOrd(Vec::default(), Vec::default()) } } impl VecOrd { fn iter(&self) -> impl DoubleEndedIterator + Clone { self.0.iter().map(|&i| &self.1[i]) } } /// Check if the user `am_user` is allowed to run `cmdline` on machine `on_host` as the requested /// user/group. Not that in the sudoers file, later permissions override earlier restrictions. /// The `cmdline` argument should already be ready to essentially feed to an exec() call; or be /// a special command like 'sudoedit'. // This code is structure to allow easily reading the 'happy path'; i.e. as soon as something // doesn't match, we escape using the '?' mechanism. fn check_permission, Group: UnixGroup>( sudoers: &Sudoers, am_user: &User, on_host: &system::Hostname, request: Request, ) -> Option { let cmdline = (request.command, request.arguments); let aliases = &sudoers.aliases; let cmnd_aliases = get_aliases(&aliases.cmnd, &match_command(cmdline)); let runas_user_aliases = get_aliases(&aliases.runas, &match_user(request.user)); let runas_group_aliases = get_aliases(&aliases.runas, &match_group_alias(request.group)); let matching_user_specs = sudoers.matching_user_specs(am_user, on_host).flatten(); let allowed_commands = matching_user_specs.filter_map(|(runas, cmdspec)| { if let Some(RunAs { users, groups }) = runas { let stays_in_group = in_group(request.user, request.group); if request.user != am_user || (stays_in_group && !users.is_empty()) { find_item(users, &match_user(request.user), &runas_user_aliases)? } if !stays_in_group { find_item(groups, &match_group(request.group), &runas_group_aliases)? } } else if !(request.user.is_root() && in_group(request.user, request.group)) { None?; } Some(cmdspec) }); find_item(allowed_commands, &match_command(cmdline), &cmnd_aliases) } /// Process a raw parsed AST bit of RunAs + Command specifications: /// - RunAs specifications distribute over the commands that follow (until overridden) /// - Tags accumulate over the entire line fn distribute_tags( runas_cmds: &[(Option, CommandSpec)], ) -> impl Iterator, (Tag, &Spec))> { runas_cmds.iter().scan( (None, Default::default()), |(last_runas, tag), (runas, CommandSpec(mods, cmd))| { *last_runas = runas.as_ref().or(*last_runas); for f in mods { f(tag); } let this_tag = match cmd { Qualified::Allow(Meta::All) if tag.env != EnvironmentControl::Nosetenv => Tag { // "ALL" has an implicit "SETENV" that doesn't distribute env: EnvironmentControl::Setenv, ..tag.clone() }, _ => tag.clone(), }; Some((*last_runas, (this_tag, cmd))) }, ) } /// A type to represent positive or negative association with an alias; i.e. if a key maps to true, /// the alias affirms membership, if a key maps to false, the alias denies membership; if a key /// isn't present membership is affirmed nor denied type FoundAliases = HashMap; /// Find an item matching a certain predicate in an collection (optionally attributed) list of /// identifiers; identifiers can be directly identifying, wildcards, and can either be positive or /// negative (i.e. preceeded by an even number of exclamation marks in the sudoers file) fn find_item<'a, Predicate, Iter, T: 'a>( items: Iter, matches: &Predicate, aliases: &FoundAliases, ) -> Option<::Info> where Predicate: Fn(&T) -> bool, Iter: IntoIterator, Iter::Item: WithInfo>, { let mut result = None; for item in items { let (judgement, who) = match item.as_inner() { Qualified::Forbid(x) => (false, x), Qualified::Allow(x) => (true, x), }; let info = || item.into_info(); match who { Meta::All => result = judgement.then(info), Meta::Only(ident) if matches(ident) => result = judgement.then(info), Meta::Alias(id) if aliases.contains_key(id) => { result = if aliases[id] { judgement.then(info) } else { // in this case, an explicit negation in the alias applies (!judgement).then(info) } } _ => {} }; } result } /// A interface to access optional "satellite data" trait WithInfo { type Item; type Info; fn as_inner(&self) -> Self::Item; fn into_info(self) -> Self::Info; } /// A specific interface for `Spec` --- we can't make a generic one; /// A `Spec` does not contain any additional information. impl<'a, T> WithInfo for &'a Spec { type Item = &'a Spec; type Info = (); fn as_inner(&self) -> &'a Spec { self } fn into_info(self) {} } /// A commandspec can be "tagged" impl<'a> WithInfo for (Tag, &'a Spec) { type Item = &'a Spec; type Info = Tag; fn as_inner(&self) -> &'a Spec { self.1 } fn into_info(self) -> Tag { self.0 } } /// Now follow a collection of functions used as closures for `find_item` fn match_user(user: &impl UnixUser) -> impl Fn(&UserSpecifier) -> bool + '_ { move |spec| match spec { UserSpecifier::User(id) => match_identifier(user, id), UserSpecifier::Group(Identifier::Name(name)) => user.in_group_by_name(name.as_cstr()), UserSpecifier::Group(Identifier::ID(num)) => user.in_group_by_gid(GroupId::new(*num)), _ => todo!(), // nonunix-groups, netgroups, etc. } } fn in_group(user: &impl UnixUser, group: &impl UnixGroup) -> bool { user.in_group_by_gid(group.as_gid()) } fn match_group(group: &impl UnixGroup) -> impl Fn(&Identifier) -> bool + '_ { move |id| match id { Identifier::ID(num) => group.as_gid() == GroupId::new(*num), Identifier::Name(name) => group.try_as_name().is_some_and(|s| name == s), } } fn match_group_alias(group: &impl UnixGroup) -> impl Fn(&UserSpecifier) -> bool + '_ { move |spec| match spec { UserSpecifier::User(ident) => match_group(group)(ident), /* the parser does not allow this, but can happen due to Runas_Alias, * see https://github.com/trifectatechfoundation/sudo-rs/issues/13 */ _ => { auth_warn!("warning: ignoring %group syntax in runas_alias for checking sudo -g"); false } } } fn match_token>( text: &str, ) -> (impl Fn(&T) -> bool + '_) { move |token| token.as_str() == text } fn match_command<'a>((cmd, args): (&'a Path, &'a [String])) -> (impl Fn(&Command) -> bool + 'a) { let opts = glob::MatchOptions { require_literal_separator: true, ..glob::MatchOptions::new() }; move |(cmdpat, argpat)| { cmdpat.matches_path_with(cmd, opts) && argpat.as_ref().map_or(true, |vec| args == vec.as_ref()) } } /// Find all the aliases that a object is a member of; this requires [sanitize_alias_table] to have run first; /// I.e. this function should not be "pub". fn get_aliases(table: &VecOrd>, pred: &Predicate) -> FoundAliases where Predicate: Fn(&T) -> bool, { use std::iter::once; let all = Qualified::Allow(Meta::All); let mut set = HashMap::new(); for Def(id, list) in table.iter() { if find_item(list, &pred, &set).is_some() { set.insert(id.clone(), true); } else if find_item(once(&all).chain(list), &pred, &set).is_none() { // the item wasn't found even if we prepend ALL to the list of definitions; that means // it is explicitly excluded by the alias definition. set.insert(id.clone(), false); } } set } /// Code to map an ast::Identifier to the UnixUser trait fn match_identifier(user: &impl UnixUser, ident: &ast::Identifier) -> bool { match ident { Identifier::Name(name) => user.has_name(name), Identifier::ID(num) => user.has_uid(UserId::new(*num)), } } /// Process a sudoers-parsing file into a workable AST fn analyze( path: &Path, sudoers: impl IntoIterator>, ) -> (Sudoers, Vec) { use Directive::*; let mut result: Sudoers = Default::default(); fn resolve_relative(base: &Path, path: impl AsRef) -> PathBuf { if path.as_ref().is_relative() { // there should always be a parent since we start with /etc/sudoers, and make every other path // absolute based on previous inputs; not having a parent is therefore a serious bug base.parent() .expect("invalid hardcoded path in sudo-rs") .join(path) } else { path.as_ref().into() } } fn include( cfg: &mut Sudoers, parent: &Path, span: Span, path: &Path, diagnostics: &mut Vec, count: &mut u8, ) { if *count >= INCLUDE_LIMIT { diagnostics.push(Error { source: Some(parent.to_owned()), location: Some(span), message: format!("include file limit reached opening '{}'", path.display()), }) // FIXME: this will cause an error in `visudo` if we open a non-privileged sudoers file // that includes another non-privileged sudoer files. } else { match open_subsudoers(path) { Ok(subsudoer) => { *count += 1; process(cfg, path, subsudoer, diagnostics, count) } Err(e) => { let message = if e.kind() == io::ErrorKind::NotFound { // improve the error message in this case format!("cannot open sudoers file '{}'", path.display()) } else { e.to_string() }; diagnostics.push(Error { source: Some(parent.to_owned()), location: Some(span), message, }) } } } } fn process( cfg: &mut Sudoers, cur_path: &Path, sudoers: impl IntoIterator>, diagnostics: &mut Vec, safety_count: &mut u8, ) { for item in sudoers { match item { Ok(line) => match line { Sudo::LineComment => {} Sudo::Spec(permission) => cfg.rules.push(permission), Sudo::Decl(HostAlias(mut def)) => cfg.aliases.host.1.append(&mut def), Sudo::Decl(UserAlias(mut def)) => cfg.aliases.user.1.append(&mut def), Sudo::Decl(RunasAlias(mut def)) => cfg.aliases.runas.1.append(&mut def), Sudo::Decl(CmndAlias(mut def)) => cfg.aliases.cmnd.1.append(&mut def), Sudo::Decl(Defaults(params, scope)) => { if let ConfigScope::Command(specs) = scope { cfg.customisers.cmnd.push(( specs .into_iter() .map(|spec| spec.map(|simple_command| (simple_command, None))) .collect(), params, )); } else { cfg.customisers.non_cmnd.push((scope, params)); } } Sudo::Include(path, span) => include( cfg, cur_path, span, &resolve_relative(cur_path, path), diagnostics, safety_count, ), Sudo::IncludeDir(path, span) => { if path.contains("%h") { diagnostics.push(Error { source: Some(cur_path.to_owned()), location: Some(span), message: format!( "cannot open sudoers file {path}: \ percent escape %h in includedir is unsupported" ), }); continue; } let path = resolve_relative(cur_path, path); let Ok(files) = std::fs::read_dir(&path) else { diagnostics.push(Error { source: Some(cur_path.to_owned()), location: Some(span), message: format!("cannot open sudoers file {}", path.display()), }); continue; }; let mut safe_files = files .filter_map(|direntry| { let path = direntry.ok()?.path(); let text = path.file_name()?.to_str()?; if text.ends_with('~') || text.contains('.') { None } else { Some(path) } }) .collect::>(); safe_files.sort(); for file in safe_files { include( cfg, cur_path, span, file.as_ref(), diagnostics, safety_count, ) } } }, Err(basic_parser::Status::Fatal(pos, message)) => diagnostics.push(Error { source: Some(cur_path.to_owned()), location: Some(pos), message, }), Err(_) => panic!("internal parser error"), } } } let mut diagnostics = vec![]; process(&mut result, path, sudoers, &mut diagnostics, &mut 0); let alias = &mut result.aliases; alias.user.0 = sanitize_alias_table(&alias.user.1, &mut diagnostics); alias.host.0 = sanitize_alias_table(&alias.host.1, &mut diagnostics); alias.cmnd.0 = sanitize_alias_table(&alias.cmnd.1, &mut diagnostics); alias.runas.0 = sanitize_alias_table(&alias.runas.1, &mut diagnostics); (result, diagnostics) } /// Alias definition inin a Sudoers file can come in any order; and aliases can refer to other aliases, etc. /// It is much easier if they are presented in a "definitional order" (i.e. aliases that use other aliases occur later) /// At the same time, this is a good place to detect problems in the aliases, such as unknown aliases and cycles. fn sanitize_alias_table(table: &Vec>, diagnostics: &mut Vec) -> Vec { fn remqualify(item: &Qualified) -> &U { match item { Qualified::Allow(x) => x, Qualified::Forbid(x) => x, } } // perform a topological sort (hattip david@tweedegolf.com) to produce a derangement struct Visitor<'a, T> { seen: HashSet, table: &'a Vec>, order: Vec, diagnostics: &'a mut Vec, } impl Visitor<'_, T> { fn complain(&mut self, text: String) { self.diagnostics.push(Error { source: None, location: None, message: text, }) } fn visit(&mut self, pos: usize) { if self.seen.insert(pos) { let Def(_, members) = &self.table[pos]; for elem in members { let Meta::Alias(name) = remqualify(elem) else { continue; }; let Some(dependency) = self.table.iter().position(|Def(id, _)| id == name) else { self.complain(format!("undefined alias: '{name}'")); continue; }; self.visit(dependency); } self.order.push(pos); } else if !self.order.contains(&pos) { let Def(id, _) = &self.table[pos]; self.complain(format!("recursive alias: '{id}'")); } } } let mut visitor = Visitor { seen: HashSet::new(), table, order: Vec::with_capacity(table.len()), diagnostics, }; let mut dupe = HashSet::new(); for (i, Def(name, _)) in table.iter().enumerate() { if !dupe.insert(name) { visitor.complain(format!("multiple occurrences of '{name}'")); } else { visitor.visit(i); } } visitor.order } #[cfg(test)] mod test; sudo-rs-0.2.5/src/sudoers/policy.rs000064400000000000000000000135441046102023000153530ustar 00000000000000use super::Sudoers; use super::Judgement; use crate::common::{ SudoPath, HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2, }; use crate::sudoers::ast::Tag; use crate::system::{time::Duration, Hostname, User}; /// Data types and traits that represent what the "terms and conditions" are after a succesful /// permission check. /// /// The trait definitions can be part of some global crate in the future, if we support more /// than just the sudoers file. use std::collections::HashSet; #[must_use] #[cfg_attr(test, derive(Debug, PartialEq))] #[repr(u32)] pub enum Authorization { Allowed(Authentication, T) = HARDENED_ENUM_VALUE_0, Forbidden = HARDENED_ENUM_VALUE_1, } #[cfg_attr(test, derive(Debug, PartialEq))] #[must_use] pub struct Authentication { pub must_authenticate: bool, pub credential: AuthenticatingUser, pub allowed_attempts: u16, pub prior_validity: Duration, pub pwfeedback: bool, } impl super::Settings { pub(super) fn to_auth(&self, tag: &Tag) -> Authentication { Authentication { must_authenticate: tag.needs_passwd(), allowed_attempts: self.passwd_tries().try_into().unwrap(), prior_validity: Duration::seconds(self.timestamp_timeout()), pwfeedback: self.pwfeedback(), credential: if self.rootpw() { AuthenticatingUser::Root } else if self.targetpw() { AuthenticatingUser::TargetUser } else { AuthenticatingUser::InvokingUser }, } } } #[must_use] #[cfg_attr(test, derive(Debug, PartialEq))] pub struct Restrictions<'a> { pub use_pty: bool, pub trust_environment: bool, pub env_keep: &'a HashSet, pub env_check: &'a HashSet, pub chdir: DirChange<'a>, pub path: Option<&'a str>, } #[must_use] #[cfg_attr(test, derive(Debug, PartialEq))] #[repr(u32)] pub enum DirChange<'a> { Strict(Option<&'a SudoPath>) = HARDENED_ENUM_VALUE_0, Any = HARDENED_ENUM_VALUE_1, } #[cfg_attr(test, derive(Debug, PartialEq))] #[repr(u32)] pub enum AuthenticatingUser { InvokingUser = HARDENED_ENUM_VALUE_0, Root = HARDENED_ENUM_VALUE_1, TargetUser = HARDENED_ENUM_VALUE_2, } impl Judgement { pub fn authorization(&self) -> Authorization { if let Some(tag) = &self.flags { Authorization::Allowed( self.settings.to_auth(tag), Restrictions { use_pty: self.settings.use_pty(), trust_environment: tag.allows_setenv(), env_keep: self.settings.env_keep(), env_check: self.settings.env_check(), chdir: match tag.cwd.as_ref() { None => DirChange::Strict(None), Some(super::ChDir::Any) => DirChange::Any, Some(super::ChDir::Path(path)) => DirChange::Strict(Some(path)), }, path: self.settings.secure_path(), }, ) } else { Authorization::Forbidden } } } impl Sudoers { pub fn search_path( &mut self, on_host: &Hostname, current_user: &User, target_user: &User, ) -> Option<&str> { self.specify_host_user_runas(on_host, current_user, target_user); self.settings.secure_path() } } #[cfg(test)] mod test { use super::*; use crate::sudoers::{ ast::{Authenticate, Tag}, tokens::ChDir, }; impl Judgement { fn mod_flag(&mut self, mut modify: impl FnMut(&mut Tag)) { let mut tag: Tag = self.flags.clone().unwrap_or_default(); modify(&mut tag); self.flags = Some(tag); } } #[test] fn authority_xlat_test() { let mut judge: Judgement = Default::default(); assert_eq!(judge.authorization(), Authorization::Forbidden); judge.mod_flag(|tag| tag.authenticate = Authenticate::Passwd); let Authorization::Allowed(auth, restrictions) = judge.authorization() else { panic!(); }; assert_eq!( auth, Authentication { must_authenticate: true, allowed_attempts: 3, prior_validity: Duration::minutes(15), credential: AuthenticatingUser::InvokingUser, pwfeedback: false, }, ); let mut judge = judge.clone(); judge.mod_flag(|tag| tag.authenticate = Authenticate::Nopasswd); let Authorization::Allowed(auth, restrictions2) = judge.authorization() else { panic!(); }; assert_eq!( auth, Authentication { must_authenticate: false, allowed_attempts: 3, prior_validity: Duration::minutes(15), credential: AuthenticatingUser::InvokingUser, pwfeedback: false, }, ); assert_eq!(restrictions, restrictions2); } #[test] fn chdir_test() { let mut judge = Judgement { flags: Some(Tag::default()), ..Default::default() }; fn chdir(judge: &mut Judgement) -> DirChange { let Authorization::Allowed(_, ctl) = judge.authorization() else { panic!() }; ctl.chdir } assert_eq!(chdir(&mut judge), DirChange::Strict(None)); judge.mod_flag(|tag| tag.cwd = Some(ChDir::Any)); assert_eq!(chdir(&mut judge), DirChange::Any); judge.mod_flag(|tag| tag.cwd = Some(ChDir::Path("/usr".into()))); assert_eq!(chdir(&mut judge), (DirChange::Strict(Some(&"/usr".into())))); judge.mod_flag(|tag| tag.cwd = Some(ChDir::Path("/bin".into()))); assert_eq!(chdir(&mut judge), (DirChange::Strict(Some(&"/bin".into())))); } } sudo-rs-0.2.5/src/sudoers/test/mod.rs000064400000000000000000000666641046102023000156250ustar 00000000000000use std::ffi::CStr; use super::ast; use super::char_stream::CharStream; use super::*; use basic_parser::{parse_eval, parse_lines, parse_string}; #[derive(PartialEq)] struct Named(&'static str); fn dummy_cksum(name: &str) -> u32 { if name == "root" { 0 } else { 1000 + name.chars().fold(0, |x, y| (x * 97 + y as u32) % 1361) } } impl UnixUser for Named { fn has_name(&self, name: &str) -> bool { self.0 == name } fn has_uid(&self, uid: UserId) -> bool { UserId::new(dummy_cksum(self.0)) == uid } fn in_group_by_name(&self, name: &CStr) -> bool { self.has_name(name.to_str().unwrap()) } fn in_group_by_gid(&self, gid: GroupId) -> bool { GroupId::new(dummy_cksum(self.0)) == gid } fn is_root(&self) -> bool { self.0 == "root" } } impl UnixGroup for Named { fn as_gid(&self) -> GroupId { GroupId::new(dummy_cksum(self.0)) } fn try_as_name(&self) -> Option<&str> { Some(self.0) } } macro_rules! request { ($user:ident) => { (&Named(stringify!($user)), &Named(stringify!($user))) }; ($user:ident, $group:ident) => { (&Named(stringify!($user)), &Named(stringify!($group))) }; } macro_rules! sudoer { ($($e:expr),*) => { parse_lines(&mut CharStream::new([$($e),*, ""].join("\n").chars())) .into_iter() .map(|x| Ok::<_,basic_parser::Status>(x.unwrap())) } } // alternative to parse_eval, but goes through sudoer! directly #[must_use] fn parse_line(s: &str) -> Sudo { sudoer![s].next().unwrap().unwrap() } /// Returns `None` if a syntax error is encountered fn try_parse_line(s: &str) -> Option { parse_lines(&mut CharStream::new([s, ""].join("").chars())) .into_iter() .next()? .ok() } #[test] fn ambiguous_spec() { assert!(parse_eval::("marc, User_Alias ALL = ALL").is_spec()); } #[test] fn permission_test() { let root = || (&Named("root"), &Named("root")); let realpath = |path: &Path| crate::common::resolve::canonicalize(path).unwrap_or(path.to_path_buf()); macro_rules! FAIL { ([$($sudo:expr),*], $user:expr => $req:expr, $server:expr; $command:expr) => { let (Sudoers { rules,aliases,settings, customisers }, _) = analyze(Path::new("/etc/fakesudoers"), sudoer![$($sudo),*]); let cmdvec = $command.split_whitespace().map(String::from).collect::>(); let req = Request { user: $req.0, group: $req.1, command: &realpath(cmdvec[0].as_ref()), arguments: &cmdvec[1..].to_vec() }; assert_eq!(Sudoers { rules, aliases, settings, customisers }.check(&Named($user), &system::Hostname::fake($server), req).flags, None); } } macro_rules! pass { ([$($sudo:expr),*], $user:expr => $req:expr, $server:expr; $command:expr $(=> [$($key:ident : $val:expr),*])?) => { let (Sudoers { rules,aliases,settings, customisers }, _) = analyze(Path::new("/etc/fakesudoers"), sudoer![$($sudo),*]); let cmdvec = $command.split_whitespace().map(String::from).collect::>(); let req = Request { user: $req.0, group: $req.1, command: &realpath(cmdvec[0].as_ref()), arguments: &cmdvec[1..].to_vec() }; let result = Sudoers { rules, aliases, settings, customisers }.check(&Named($user), &system::Hostname::fake($server), req).flags; assert!(!result.is_none()); $( let result = result.unwrap(); $(assert_eq!(result.$key, $val);)* )? } } macro_rules! SYNTAX { ([$sudo:expr]) => { assert!(parse_string::($sudo).is_err()) }; } SYNTAX!(["ALL ALL = (;) ALL"]); FAIL!(["user ALL=(ALL:ALL) ALL"], "nobody" => root(), "server"; "/bin/hello"); pass!(["user ALL=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); pass!(["user ALL=(ALL:ALL) /bin/foo"], "user" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::None]); FAIL!(["user ALL=(ALL:ALL) /bin/foo"], "user" => root(), "server"; "/bin/hello"); pass!(["user ALL=(ALL:ALL) PASSWD: /bin/foo"], "user" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::Passwd]); pass!(["user ALL=(ALL:ALL) NOPASSWD: PASSWD: /bin/foo"], "user" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::Passwd]); pass!(["user ALL=(ALL:ALL) PASSWD: NOPASSWD: /bin/foo"], "user" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::Nopasswd]); pass!(["user ALL=(ALL:ALL) /bin/foo, NOPASSWD: /bin/bar"], "user" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::None]); pass!(["user ALL=(ALL:ALL) /bin/foo, NOPASSWD: /bin/bar"], "user" => root(), "server"; "/bin/bar" => [authenticate: Authenticate::Nopasswd]); pass!(["user ALL=(ALL:ALL) NOPASSWD: /bin/foo, /bin/bar"], "user" => root(), "server"; "/bin/bar" => [authenticate: Authenticate::Nopasswd]); pass!(["user ALL=(ALL:ALL) CWD=/ /bin/foo, /bin/bar"], "user" => root(), "server"; "/bin/bar" => [cwd: Some(ChDir::Path("/".into()))]); pass!(["user ALL=(ALL:ALL) CWD=/ /bin/foo, CWD=* /bin/bar"], "user" => root(), "server"; "/bin/bar" => [cwd: Some(ChDir::Any)]); pass!(["user ALL=(ALL:ALL) CWD=/bin CWD=* /bin/foo"], "user" => root(), "server"; "/bin/foo" => [cwd: Some(ChDir::Any)]); pass!(["user ALL=(ALL:ALL) CWD=/usr/bin NOPASSWD: /bin/foo"], "user" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::Nopasswd, cwd: Some(ChDir::Path("/usr/bin".into()))]); //note: original sudo does not allow the below pass!(["user ALL=(ALL:ALL) NOPASSWD: CWD=/usr/bin /bin/foo"], "user" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::Nopasswd, cwd: Some(ChDir::Path("/usr/bin".into()))]); pass!(["user ALL=/bin/e##o"], "user" => root(), "vm"; "/bin/e"); SYNTAX!(["ALL ALL=(ALL) /bin/\n/echo"]); pass!(["user server=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); FAIL!(["user laptop=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); pass!(["user ALL=!/bin/hello", "user ALL=/bin/hello"], "user" => root(), "server"; "/bin/hello"); FAIL!(["user ALL=/bin/hello", "user ALL=!/bin/hello"], "user" => root(), "server"; "/bin/hello"); for alias in [ "User_Alias GROUP=user1, user2", "User_Alias GROUP=ALL,!user3", ] { pass!([alias,"GROUP ALL=/bin/hello"], "user1" => root(), "server"; "/bin/hello"); pass!([alias,"GROUP ALL=/bin/hello"], "user2" => root(), "server"; "/bin/hello"); FAIL!([alias,"GROUP ALL=/bin/hello"], "user3" => root(), "server"; "/bin/hello"); } pass!(["user ALL=/bin/hello arg"], "user" => root(), "server"; "/bin/hello arg"); pass!(["user ALL=/bin/hello arg"], "user" => root(), "server"; "/bin/hello arg"); pass!(["user ALL=/bin/hello arg"], "user" => root(), "server"; "/bin/hello arg"); FAIL!(["user ALL=/bin/hello arg"], "user" => root(), "server"; "/bin/hello boo"); // several test cases with globbing in the arguments are explicitly not supported by sudo-rs //pass!(["user ALL=/bin/hello a*g"], "user" => root(), "server"; "/bin/hello aaaarg"); //FAIL!(["user ALL=/bin/hello a*g"], "user" => root(), "server"; "/bin/hello boo"); pass!(["user ALL=/bin/hello"], "user" => root(), "server"; "/bin/hello boo"); FAIL!(["user ALL=/bin/hello \"\""], "user" => root(), "server"; "/bin/hello boo"); pass!(["user ALL=/bin/hello \"\""], "user" => root(), "server"; "/bin/hello"); pass!(["user ALL=/bin/hel*"], "user" => root(), "server"; "/bin/hello"); pass!(["user ALL=/bin/hel*"], "user" => root(), "server"; "/bin/help"); pass!(["user ALL=/bin/hel*"], "user" => root(), "server"; "/bin/help me"); //pass!(["user ALL=/bin/hel* *"], "user" => root(), "server"; "/bin/help"); FAIL!(["user ALL=/bin/hel* me"], "user" => root(), "server"; "/bin/help"); pass!(["user ALL=/bin/hel* me"], "user" => root(), "server"; "/bin/help me"); FAIL!(["user ALL=/bin/hel* me"], "user" => root(), "server"; "/bin/help me please"); pass!(["user ALL=(ALL:ALL) /bin/foo"], "user" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::None]); pass!(["root ALL=(ALL:ALL) /bin/foo"], "root" => root(), "server"; "/bin/foo" => [authenticate: Authenticate::Nopasswd]); pass!(["user ALL=(ALL:ALL) /bin/foo"], "user" => request! { user, user }, "server"; "/bin/foo" => [authenticate: Authenticate::Nopasswd]); pass!(["user ALL=(ALL:ALL) /bin/foo"], "user" => request! { user, root }, "server"; "/bin/foo" => [authenticate: Authenticate::None]); assert_eq!(Named("user").as_gid(), GroupId::new(1466)); pass!(["#1466 server=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); pass!(["%#1466 server=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); FAIL!(["#1466 server=(ALL:ALL) ALL"], "root" => root(), "server"; "/bin/hello"); FAIL!(["%#1466 server=(ALL:ALL) ALL"], "root" => root(), "server"; "/bin/hello"); pass!(["#1466,#1234,foo server=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); pass!(["#1234,foo,#1466 server=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); pass!(["foo,#1234,#1466 server=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); FAIL!(["foo,#1234,#1366 server=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); FAIL!(["#1366,#1234,foo server=(ALL:ALL) ALL"], "user" => root(), "server"; "/bin/hello"); pass!(["user ALL=(ALL:#1466) /bin/foo"], "user" => request! { root, root }, "server"; "/bin/foo"); FAIL!(["user ALL=(ALL:#1466) /bin/foo"], "user" => request! { root, other }, "server"; "/bin/foo"); pass!(["user ALL=(ALL:#1466) /bin/foo"], "user" => request! { root, user }, "server"; "/bin/foo"); pass!(["user ALL=(root,user:ALL) /bin/foo"], "user" => request! { root, wheel }, "server"; "/bin/foo"); pass!(["user ALL=(root,user:ALL) /bin/foo"], "user" => request! { user, wheel }, "server"; "/bin/foo"); FAIL!(["user ALL=(root,user:ALL) /bin/foo"], "user" => request! { sudo, wheel }, "server"; "/bin/foo"); FAIL!(["user ALL=(#0:wheel) /bin/foo"], "user" => request! { sudo, wheel }, "server"; "/bin/foo"); pass!(["user ALL=(#0:wheel) /bin/foo"], "user" => request! { root, root }, "server"; "/bin/foo"); FAIL!(["user ALL=(%#1466:wheel) /bin/foo"], "user" => request! { root, root }, "server"; "/bin/foo"); pass!(["user ALL=(%#1466:wheel) /bin/foo"], "user" => request! { user, user }, "server"; "/bin/foo"); // tests with a 'singular' runas spec FAIL!(["user ALL=(ALL) /bin/foo"], "user" => request! { sudo, wheel }, "server"; "/bin/foo"); pass!(["user ALL=(ALL) /bin/foo"], "user" => request! { sudo, sudo }, "server"; "/bin/foo"); // tests without a runas spec FAIL!(["user ALL=/bin/foo"], "user" => request! { sudo, sudo }, "server"; "/bin/foo"); FAIL!(["user ALL=/bin/foo"], "user" => request! { sudo, root }, "server"; "/bin/foo"); FAIL!(["user ALL=/bin/foo"], "user" => request! { root, sudo }, "server"; "/bin/foo"); pass!(["user ALL=/bin/foo"], "user" => request! { root, root }, "server"; "/bin/foo"); // slightly counterintuitive test which simulates only -g being passed pass!(["user ALL=(sudo:sudo) /bin/foo"], "user" => request! { user, sudo }, "server"; "/bin/foo"); // tests with multiple runas specs pass!(["user ALL=(root) /bin/ls, (sudo) /bin/true"], "user" => request! { root }, "server"; "/bin/ls"); pass!(["user ALL=(root) NOPASSWD: /bin/ls, (sudo) /bin/true"], "user" => request! { sudo }, "server"; "/bin/true" => [authenticate: Authenticate::Nopasswd]); FAIL!(["user ALL=(root) /bin/ls, (sudo) /bin/true"], "user" => request! { sudo }, "server"; "/bin/ls"); FAIL!(["user ALL=(root) /bin/ls, (sudo) /bin/true"], "user" => request! { root }, "server"; "/bin/true"); pass!(["user ALL=(root) NOPASSWD: /bin/ls, (sudo) /bin/ls, /bin/true"], "user" => request! { sudo }, "server"; "/bin/true"); SYNTAX!(["User_Alias, marc ALL = ALL"]); pass!(["User_Alias FULLTIME=ALL,!marc","FULLTIME ALL=ALL"], "user" => root(), "server"; "/bin/bash"); FAIL!(["User_Alias FULLTIME=ALL,!marc","FULLTIME ALL=ALL"], "marc" => root(), "server"; "/bin/bash"); FAIL!(["User_Alias FULLTIME=ALL,!marc","ALL,!FULLTIME ALL=ALL"], "user" => root(), "server"; "/bin/bash"); pass!(["User_Alias FULLTIME=ALL,!!!marc","ALL,!FULLTIME ALL=ALL"], "marc" => root(), "server"; "/bin/bash"); pass!(["Host_Alias MACHINE=laptop,server","user MACHINE=ALL"], "user" => root(), "server"; "/bin/bash"); pass!(["Host_Alias MACHINE=laptop,server","user MACHINE=ALL"], "user" => root(), "laptop"; "/bin/bash"); FAIL!(["Host_Alias MACHINE=laptop,server","user MACHINE=ALL"], "user" => root(), "desktop"; "/bin/bash"); pass!(["Cmnd_Alias WHAT=/bin/dd, /bin/rm","user ALL=WHAT"], "user" => root(), "server"; "/bin/rm"); pass!(["Cmd_Alias WHAT=/bin/dd,/bin/rm","user ALL=WHAT"], "user" => root(), "laptop"; "/bin/dd"); FAIL!(["Cmnd_Alias WHAT=/bin/dd,/bin/rm","user ALL=WHAT"], "user" => root(), "desktop"; "/bin/bash"); pass!(["User_Alias A=B","User_Alias B=user","A ALL=ALL"], "user" => root(), "vm"; "/bin/ls"); pass!(["Host_Alias A=B","Host_Alias B=vm","ALL A=ALL"], "user" => root(), "vm"; "/bin/ls"); pass!(["Cmnd_Alias A=B","Cmnd_Alias B=/bin/ls","ALL ALL=A"], "user" => root(), "vm"; "/bin/ls"); FAIL!(["Runas_Alias TIME=%wheel,!!sudo","user ALL=() ALL"], "user" => request!{ sudo, sudo }, "vm"; "/bin/ls"); pass!(["Runas_Alias TIME=%wheel,!!sudo","user ALL=(TIME) ALL"], "user" => request! { sudo, sudo }, "vm"; "/bin/ls"); FAIL!(["Runas_Alias TIME=%wheel,!!sudo","user ALL=(:TIME) ALL"], "user" => request! { sudo, sudo }, "vm"; "/bin/ls"); pass!(["Runas_Alias TIME=%wheel,!!sudo","user ALL=(:TIME) ALL"], "user" => request! { user, sudo }, "vm"; "/bin/ls"); pass!(["Runas_Alias TIME=%wheel,!!sudo","user ALL=(TIME) ALL"], "user" => request! { wheel, wheel }, "vm"; "/bin/ls"); pass!(["Runas_Alias \\"," TIME=%wheel \\",",sudo # hallo","user ALL \\","=(TIME) ALL"], "user" => request! { wheel, wheel }, "vm"; "/bin/ls"); // test the less-intuitive "substition-like" alias mechanism FAIL!(["User_Alias FOO=!user", "ALL, FOO ALL=ALL"], "user" => root(), "vm"; "/bin/ls"); pass!(["User_Alias FOO=!user", "!FOO ALL=ALL"], "user" => root(), "vm"; "/bin/ls"); // quoting pass!(["a\\,b ALL=ALL"], "a,b" => request! { root, root }, "server"; "/bin/foo"); pass!(["\"a,b\" ALL=ALL"], "a,b" => request! { root, root }, "server"; "/bin/foo"); pass!(["\"a\\b\" ALL=ALL"], "a\\b" => request! { root, root }, "server"; "/bin/foo"); } #[test] fn default_bool_test() { let (mut sudoers, _) = analyze( Path::new("/etc/fakesudoers"), sudoer![ "Defaults env_editor", "Defaults !use_pty", "Defaults use_pty", "Defaults !env_keep", "Defaults !secure_path", "Defaults !env_editor" ], ); sudoers.specify_host_user_runas( &system::Hostname::fake("host"), &Named("user"), &Named("root"), ); assert!(!sudoers.settings.env_editor()); assert!(sudoers.settings.use_pty()); assert!(sudoers.settings.env_keep().is_empty()); assert_eq!(sudoers.settings.secure_path(), None); assert!(!sudoers.settings.env_editor()); } #[test] fn default_set_test() { let (mut sudoers, _) = analyze( Path::new("/etc/fakesudoers"), sudoer![ "Defaults env_keep = \"FOO HUK BAR\"", "Defaults env_keep -= HUK", "Defaults !env_check", "Defaults env_check += \"FOO\"", "Defaults env_check += \"XYZZY\"", "Defaults passwd_tries = 5", "Defaults secure_path = /etc" ], ); sudoers.specify_host_user_runas( &system::Hostname::fake("host"), &Named("user"), &Named("root"), ); assert_eq!( sudoers.settings.env_keep(), &["FOO", "BAR"].into_iter().map(|x| x.to_string()).collect() ); assert_eq!( sudoers.settings.env_check(), &["FOO", "XYZZY"] .into_iter() .map(|x| x.to_string()) .collect() ); assert_eq!(sudoers.settings.secure_path(), Some("/etc")); assert_eq!(sudoers.settings.passwd_tries(), 5); assert!(parse_string::("Defaults verifypw = \"sometimes\"").is_err()); assert!(parse_string::("Defaults verifypw = sometimes").is_err()); assert!(parse_string::("Defaults verifypw = never").is_ok()); } #[test] fn default_multi_test() { let (mut sudoers, _) = analyze( Path::new("/etc/fakesudoers"), sudoer![ "Defaults !env_editor, use_pty, secure_path=/etc, env_keep = \"FOO BAR\", env_keep -= BAR" ], ); sudoers.specify_host_user_runas( &system::Hostname::fake("host"), &Named("user"), &Named("root"), ); assert!(!sudoers.settings.env_editor()); assert!(sudoers.settings.use_pty()); assert_eq!(sudoers.settings.secure_path(), Some("/etc")); assert_eq!( sudoers.settings.env_keep(), &["FOO".to_string()].into_iter().collect() ); } #[test] #[should_panic] fn invalid_directive() { parse_eval::("User_Alias, user Alias = user1, user2"); } #[test] fn directive_test() { let y = parse_eval::>; match parse_eval::("User_Alias HENK = user1, user2") { Sudo::Decl(Directive::UserAlias(defs)) => { let [Def(name, list)] = &defs[..] else { panic!("incorrectly parsed") }; assert_eq!(name, "HENK"); assert_eq!(*list, vec![y("user1"), y("user2")]); } _ => panic!("incorrectly parsed"), } match parse_eval::("Runas_Alias FOO = foo : BAR = bar") { Sudo::Decl(Directive::RunasAlias(defs)) => { let [Def(name1, list1), Def(name2, list2)] = &defs[..] else { panic!("incorrectly parsed") }; assert_eq!(name1, "FOO"); assert_eq!(*list1, vec![y("foo")]); assert_eq!(name2, "BAR"); assert_eq!(*list2, vec![y("bar")]); } _ => panic!("incorrectly parsed"), } } #[test] // the overloading of '#' causes a lot of issues fn hashsign_test() { assert!(parse_line("#42 ALL=ALL").is_spec()); assert!(parse_line("ALL ALL=(#42) ALL").is_spec()); assert!(parse_line("ALL ALL=(%#42) ALL").is_spec()); assert!(parse_line("ALL ALL=(:#42) ALL").is_spec()); assert!(parse_line("User_Alias FOO=#42, %#0, #3").is_decl()); assert!(parse_line("").is_line_comment()); assert!(parse_line("#this is a comment").is_line_comment()); assert!(parse_line("#include foo").is_include()); assert!(parse_line("#includedir foo").is_include_dir()); assert_eq!("foo bar", parse_line("#include \"foo bar\"").as_include()); // this is fine assert!(parse_line("#inlcudedir foo").is_line_comment()); assert!(parse_line("@include foo").is_include()); assert!(parse_line("@includedir foo").is_include_dir()); assert_eq!("foo bar", parse_line("@include \"foo bar\"").as_include()); } #[test] fn gh674_at_include_quoted_backslash() { assert!(parse_line(r#"@include "/etc/sudo\ers" "#).is_include()); assert!(parse_line(r#"@includedir "/etc/sudo\ers.d" "#).is_include_dir()); } #[test] fn gh676_percent_h_escape_unsupported() { let (_, errs) = analyze( Path::new("/etc/fakesudoers"), sudoer!(r#"@includedir "/etc/%h" "#), ); assert_eq!(errs.len(), 1); assert_eq!( errs[0].message, "cannot open sudoers file /etc/%h: percent escape %h in includedir is unsupported" ); assert_eq!( errs[0].location, Some(Span { start: (1, 2), end: (1, 23) }) ); } #[test] fn hashsign_error() { assert!(parse_line("#include foo bar").is_line_comment()); } #[test] fn include_regression() { assert!(try_parse_line("#4,#include foo").is_none()); } #[test] fn nullbyte_regression() { assert!(try_parse_line("ferris ALL=(ALL:ferris\0) ALL").is_none()); } #[test] fn alias_all_regression() { assert!(try_parse_line("User_Alias ALL = sudouser").is_none()) } #[test] fn defaults_regression() { assert!(try_parse_line("Defaults .mymachine=ALL").is_none()) } #[test] fn specific_defaults() { assert!(parse_line("Defaults !use_pty").is_decl()); assert!(try_parse_line("Defaults!use_pty").is_none()); assert!(parse_line("Defaults!/bin/bash !use_pty").is_decl()); assert!(try_parse_line("Defaults!/bin/bash!use_pty").is_none()); assert!(try_parse_line("Defaults !/bin/bash !use_pty").is_none()); assert!(try_parse_line("Defaults !/bin/bash").is_none()); assert!(parse_line("Defaults@host !use_pty").is_decl()); assert!(parse_line("Defaults@host!use_pty").is_decl()); assert!(try_parse_line("Defaults @host!use_pty").is_none()); assert!(try_parse_line("Defaults @host !use_pty").is_none()); assert!(parse_line("Defaults:user !use_pty").is_decl()); assert!(parse_line("Defaults:user!use_pty").is_decl()); assert!(try_parse_line("Defaults :user!use_pty").is_none()); assert!(try_parse_line("Defaults :user !use_pty").is_none()); assert!(parse_line("Defaults>user !use_pty").is_decl()); assert!(parse_line("Defaults>user!use_pty").is_decl()); assert!(try_parse_line("Defaults >user!use_pty").is_none()); assert!(try_parse_line("Defaults >user !use_pty").is_none()); } #[test] fn default_specific_test() { let sudoers = || { analyze( Path::new("/etc/fakesudoers"), sudoer![ "Defaults!RR use_pty", "Defaults env_editor", "Defaults@host !env_editor", "Defaults !use_pty", "Defaults:user use_pty", "Defaults !secure_path", "Defaults>runas secure_path=\"/bin\"", "Defaults!/bin/foo !env_keep", "Cmnd_Alias RR=/usr/bin/rr twice" ], ) }; let (mut base_sudoers, _) = sudoers(); base_sudoers.specify_host_user_runas( &system::Hostname::fake("generic"), &Named("generic"), &Named("generic"), ); assert!(base_sudoers.settings.env_editor()); assert!(!base_sudoers.settings.use_pty()); assert!(base_sudoers.settings.env_keep().contains("COLORS")); assert_eq!(base_sudoers.settings.secure_path(), None); let (mut mod_sudoers, _) = sudoers(); mod_sudoers.specify_host_user_runas( &system::Hostname::fake("host"), &Named("user"), &Named("root"), ); assert!(!mod_sudoers.settings.env_editor()); assert!(mod_sudoers.settings.use_pty()); assert!(mod_sudoers.settings.env_keep().contains("COLORS")); assert_eq!(mod_sudoers.settings.secure_path(), None); let (mut mod_sudoers, _) = sudoers(); mod_sudoers.specify_host_user_runas( &system::Hostname::fake("machine"), &Named("admin"), &Named("runas"), ); assert!(mod_sudoers.settings.env_editor()); assert!(!mod_sudoers.settings.use_pty()); assert!(mod_sudoers.settings.env_keep().contains("COLORS")); assert_eq!(mod_sudoers.settings.secure_path(), Some("/bin")); mod_sudoers.specify_command(Path::new("/bin/foo"), &["".to_string(), "a".to_string()]); assert!(mod_sudoers.settings.env_keep().is_empty()); let (mut mod_sudoers, _) = sudoers(); mod_sudoers.specify_host_user_runas( &system::Hostname::fake("machine"), &Named("admin"), &Named("self"), ); mod_sudoers.specify_command(Path::new("/usr/bin/rr"), &["thrice".to_string()]); assert!(mod_sudoers.settings.env_editor()); assert!(!mod_sudoers.settings.use_pty()); assert!(mod_sudoers.settings.env_keep().contains("COLORS")); assert_eq!(mod_sudoers.settings.secure_path(), None); let (mut mod_sudoers, _) = sudoers(); mod_sudoers.specify_command(Path::new("/usr/bin/rr"), &["twice".to_string()]); assert!(mod_sudoers.settings.use_pty()); } #[test] fn useralias_underscore_regression() { let sudo = parse_line("FOO_BAR ALL=ALL"); let spec = sudo.as_spec().expect("`Sudo::Spec`"); assert!(spec.users[0] .as_allow() .expect("`Qualified::Allow`") .is_alias()); } #[test] fn regression_check_recursion() { let (_, error) = analyze( Path::new("/etc/fakesudoers"), sudoer!["User_Alias A=user, B", "User_Alias B=A"], ); assert!(!error.is_empty()); } fn test_topo_sort(n: usize) { let alias = |s: &str| Qualified::Allow(Meta::::Alias(s.to_string())); let stop = || Qualified::Allow(Meta::::All); type Elem = Spec; let test_case = |x1: Elem, x2: Elem, x3: Elem| { let table = vec![ Def("AAP".to_string(), vec![x1]), Def("NOOT".to_string(), vec![x2]), Def("MIES".to_string(), vec![x3]), ]; let mut err = vec![]; let order = sanitize_alias_table(&table, &mut err); assert!(err.is_empty()); let mut seen = HashSet::new(); for Def(id, defns) in order.iter().map(|&i| &table[i]) { if defns.iter().any(|spec| { let Qualified::Allow(Meta::Alias(id2)) = spec else { return false; }; !seen.contains(id2) }) { panic!("forward reference encountered after sorting"); } seen.insert(id); } }; match n { 0 => test_case(alias("AAP"), alias("NOOT"), stop()), 1 => test_case(alias("AAP"), stop(), alias("NOOT")), 2 => test_case(alias("NOOT"), alias("AAP"), stop()), 3 => test_case(alias("NOOT"), stop(), alias("AAP")), 4 => test_case(stop(), alias("AAP"), alias("NOOT")), 5 => test_case(stop(), alias("NOOT"), alias("AAP")), _ => panic!("error in test case"), } } #[test] fn test_topo_positive() { test_topo_sort(3); test_topo_sort(4); } #[test] #[should_panic] fn test_topo_fail0() { test_topo_sort(0); } #[test] #[should_panic] fn test_topo_fail1() { test_topo_sort(1); } #[test] #[should_panic] fn test_topo_fail2() { test_topo_sort(2); } #[test] #[should_panic] fn test_topo_fail5() { test_topo_sort(5); } fn fuzz_topo_sort(siz: usize) { for mut n in 0..(1..siz).reduce(|x, y| x * y).unwrap() { let name = |s: u8| std::str::from_utf8(&[65 + s]).unwrap().to_string(); let alias = |s: String| Qualified::Allow(Meta::::Alias(s)); let stop = || Qualified::Allow(Meta::::All); let mut data = (0..siz - 1) .map(|i| alias(name(i as u8))) .collect::>(); data.push(stop()); for i in (1..=siz).rev() { data.swap(i - 1, n % i); n /= i; } let table = data .into_iter() .enumerate() .map(|(i, x)| Def(name(i as u8), vec![x])) .collect(); let mut err = vec![]; let order = sanitize_alias_table(&table, &mut err); if !err.is_empty() { return; } let mut seen = HashSet::new(); for Def(id, defns) in order.iter().map(|&i| &table[i]) { if defns.iter().any(|spec| { let Qualified::Allow(Meta::Alias(id2)) = spec else { return false; }; !seen.contains(id2) }) { panic!("forward reference encountered after sorting"); } seen.insert(id); } assert!(seen.len() == siz); } } #[test] fn fuzz_topo_sort7() { fuzz_topo_sort(7) } sudo-rs-0.2.5/src/sudoers/tokens.rs000064400000000000000000000251511046102023000153540ustar 00000000000000//! Various tokens use crate::common::{SudoPath, SudoString}; use super::basic_parser::{Many, Token}; use crate::common::{HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2}; #[cfg_attr(test, derive(Clone, PartialEq, Eq))] pub struct Username(pub SudoString); /// A username consists of alphanumeric characters as well as "." and "-", but does not start with an underscore. impl Token for Username { fn construct(text: String) -> Result { SudoString::new(text) .map_err(|e| e.to_string()) .map(Username) } fn accept(c: char) -> bool { c.is_ascii_alphanumeric() || ".-_".contains(c) } fn accept_1st(c: char) -> bool { c != '_' && Self::accept(c) } const ALLOW_ESCAPE: bool = true; fn escaped(c: char) -> bool { matches!(c, '\\' | '"' | ',' | ':' | '=' | '!' | '(' | ')') } } impl Many for Username {} pub struct Digits(pub u32); impl Token for Digits { const MAX_LEN: usize = 9; fn construct(s: String) -> Result { Ok(Digits(s.parse().unwrap())) } fn accept(c: char) -> bool { c.is_ascii_digit() } } pub struct Numeric(pub String); impl Token for Numeric { const MAX_LEN: usize = 18; fn construct(s: String) -> Result { Ok(Numeric(s)) } fn accept(c: char) -> bool { c.is_ascii_hexdigit() || c == '.' } } /// A hostname consists of alphanumeric characters and ".", "-", "_" pub struct Hostname(pub String); impl std::ops::Deref for Hostname { type Target = String; fn deref(&self) -> &Self::Target { &self.0 } } impl Token for Hostname { fn construct(text: String) -> Result { Ok(Hostname(text)) } fn accept(c: char) -> bool { c.is_ascii_alphanumeric() || ".-_".contains(c) } } impl Many for Hostname {} /// This enum allows items to use the ALL wildcard or be specified with aliases, or directly. /// (Maybe this is better defined not as a Token but simply directly as an implementation of [crate::sudoers::basic_parser::Parse]) #[cfg_attr(test, derive(Debug, PartialEq, Eq))] #[repr(u32)] pub enum Meta { All = HARDENED_ENUM_VALUE_0, Only(T) = HARDENED_ENUM_VALUE_1, Alias(String) = HARDENED_ENUM_VALUE_2, } impl Meta { #[cfg(test)] pub fn is_alias(&self) -> bool { matches!(self, Self::Alias(..)) } } impl Token for Meta { fn construct(raw: String) -> Result { // `T` may accept whitespace resulting in `raw` having trailing whitespace which would make // the first two checks below fail. this `cooked` version has no trailing whitespace let cooked = raw.trim_end().to_string(); Ok(if cooked == "ALL" { Meta::All } else if cooked.starts_with(AliasName::accept_1st) && cooked.chars().skip(1).all(AliasName::accept) { Meta::Alias(cooked) } else { Meta::Only(T::construct(raw)?) }) } const MAX_LEN: usize = T::MAX_LEN; fn accept(c: char) -> bool { T::accept(c) || c.is_uppercase() } fn accept_1st(c: char) -> bool { T::accept_1st(c) || c.is_uppercase() } const ALLOW_ESCAPE: bool = T::ALLOW_ESCAPE; fn escaped(c: char) -> bool { T::escaped(c) } } impl Many for Meta { const SEP: char = T::SEP; const LIMIT: usize = T::LIMIT; } /// An identifier that consits of only uppercase characters. pub struct AliasName(pub String); impl Token for AliasName { fn construct(s: String) -> Result { Ok(AliasName(s)) } fn accept_1st(c: char) -> bool { c.is_ascii_uppercase() } fn accept(c: char) -> bool { Self::accept_1st(c) || c.is_ascii_digit() || c == '_' } } /// A struct that represents valid command strings; this can contain escape sequences and are /// limited to 1024 characters. pub type Command = (SimpleCommand, Option>); /// A type that is specific to 'only commands', that can only happen in "Defaults!command" contexts; /// which is essentially a subset of "Command" pub type SimpleCommand = glob::Pattern; impl Token for Command { const MAX_LEN: usize = 1024; fn construct(s: String) -> Result { // the tokenizer should not give us a token that consists of only whitespace let mut cmd_iter = s.split_whitespace(); let cmd = cmd_iter.next().unwrap().to_string(); let mut args = cmd_iter.map(String::from).collect::>(); let command = SimpleCommand::construct(cmd)?; let argpat = if args.is_empty() { // if no arguments are mentioned, anything is allowed None } else { if args.last().map(|x| -> &str { x }) == Some("\"\"") { // if the magic "" appears, no (further) arguments are allowed args.pop(); } Some(args.into_boxed_slice()) }; Ok((command, argpat)) } // all commands start with "/" except "sudoedit" fn accept_1st(c: char) -> bool { SimpleCommand::accept_1st(c) } fn accept(c: char) -> bool { SimpleCommand::accept(c) || c == ' ' } const ALLOW_ESCAPE: bool = SimpleCommand::ALLOW_ESCAPE; fn escaped(c: char) -> bool { SimpleCommand::escaped(c) } } impl Token for SimpleCommand { const MAX_LEN: usize = 1024; fn construct(mut cmd: String) -> Result { let cvt_err = |pat: Result<_, glob::PatternError>| { pat.map_err(|err| format!("wildcard pattern error {err}")) }; // record if the cmd ends in a slash and remove it if it does let is_dir = cmd.ends_with('/') && { cmd.pop(); true }; // canonicalize path (if possible) if let Ok(real_cmd) = crate::common::resolve::canonicalize(&cmd) { cmd = real_cmd .to_str() .ok_or("non-UTF8 characters in filesystem")? .to_string(); } // if the cmd ends with a slash, any command in that directory is allowed if is_dir { cmd.push_str("/*"); } cvt_err(glob::Pattern::new(&cmd)) } // all commands start with "/" except "sudoedit" fn accept_1st(c: char) -> bool { c == '/' || c == 's' } fn accept(c: char) -> bool { !Self::escaped(c) && !c.is_control() } const ALLOW_ESCAPE: bool = true; fn escaped(c: char) -> bool { matches!(c, '\\' | ',' | ':' | '=' | '#' | ' ') } } impl Many for Command {} impl Many for SimpleCommand {} pub struct DefaultName(pub String); impl Token for DefaultName { fn construct(text: String) -> Result { Ok(DefaultName(text)) } fn accept(c: char) -> bool { c.is_ascii_alphanumeric() || c == '_' } } pub struct EnvVar(pub String); impl Token for EnvVar { fn construct(text: String) -> Result { Ok(EnvVar(text)) } fn accept(c: char) -> bool { !c.is_control() && !c.is_whitespace() && !Self::escaped(c) } const ALLOW_ESCAPE: bool = true; fn escaped(c: char) -> bool { matches!(c, '\\' | '=' | '#' | '"') } } /// A token with a very liberal inner tokenizer; compare StringParameter below pub struct QuotedStringParameter(pub String); impl Token for QuotedStringParameter { const MAX_LEN: usize = 1024; fn construct(s: String) -> Result { Ok(Self(s)) } fn accept(c: char) -> bool { !Self::escaped(c) } const ALLOW_ESCAPE: bool = true; fn escaped(c: char) -> bool { matches!(c, '\\' | '"') || c.is_control() } } /// Similar to QuotedStringParameter but treats backslashes differently /// Compare IncludePath below. // `@include "some/path"` // ^^^^^^^^^^^ pub struct QuotedIncludePath(pub String); impl Token for QuotedIncludePath { const MAX_LEN: usize = 1024; fn construct(s: String) -> Result { Ok(Self(s)) } fn accept(c: char) -> bool { !Self::escaped(c) } const ALLOW_ESCAPE: bool = true; fn escaped(c: char) -> bool { matches!(c, '"') || c.is_control() } } pub struct IncludePath(pub String); impl Token for IncludePath { const MAX_LEN: usize = 1024; fn construct(s: String) -> Result { Ok(IncludePath(s)) } fn accept(c: char) -> bool { !c.is_control() && !Self::escaped(c) } const ALLOW_ESCAPE: bool = true; fn escaped(c: char) -> bool { matches!(c, '\\' | '"' | ' ') } } // used for Defaults where quotes around some items are optional pub struct StringParameter(pub String); impl Token for StringParameter { const MAX_LEN: usize = QuotedStringParameter::MAX_LEN; fn construct(s: String) -> Result { Ok(StringParameter(s)) } fn accept(c: char) -> bool { !c.is_control() && !Self::escaped(c) } const ALLOW_ESCAPE: bool = true; fn escaped(c: char) -> bool { matches!(c, '\\' | '"' | ' ' | '#' | ',') } } // a path used for in CWD and CHROOT specs #[derive(Clone, PartialEq)] #[cfg_attr(test, derive(Debug, Eq))] #[repr(u32)] pub enum ChDir { Path(SudoPath) = HARDENED_ENUM_VALUE_0, Any = HARDENED_ENUM_VALUE_1, } impl Token for ChDir { const MAX_LEN: usize = 1024; fn construct(s: String) -> Result { if s == "*" { Ok(ChDir::Any) } else if s.contains('*') { Err("path cannot contain '*'".to_string()) } else { Ok(ChDir::Path( SudoPath::try_from(s).map_err(|e| e.to_string())?, )) } } fn accept(c: char) -> bool { !c.is_control() && !Self::escaped(c) } fn accept_1st(c: char) -> bool { "~/*".contains(c) } const ALLOW_ESCAPE: bool = true; fn escaped(c: char) -> bool { matches!(c, '\\' | '"' | ' ') } } /// Some tokens that support escape characters also support being surrounded by quotes to avoid escaping directly. pub struct Unquoted(pub String, pub std::marker::PhantomData); impl Token for Unquoted { const MAX_LEN: usize = 1024; fn construct(text: String) -> Result { let mut quoted = String::new(); for ch in text.chars() { if T::escaped(ch) { quoted.push('\\'); } quoted.push(ch); } Ok(Self(quoted, std::marker::PhantomData)) } fn accept(c: char) -> bool { c != '"' && !c.is_control() } } sudo-rs-0.2.5/src/system/audit.rs000064400000000000000000000111431046102023000150130ustar 00000000000000use std::fs::{DirBuilder, File, Metadata, OpenOptions}; use std::io::{self, Error, ErrorKind}; use std::os::unix::fs::{DirBuilderExt, MetadataExt, PermissionsExt}; use std::os::unix::prelude::OpenOptionsExt; use std::path::Path; // of course we can also write "file & 0o040 != 0", but this makes the intent explicit enum Op { Read = 4, Write = 2, Exec = 1, } enum Category { Owner = 2, Group = 1, World = 0, } fn mode(who: Category, what: Op) -> u32 { (what as u32) << (3 * who as u32) } pub fn secure_open(path: impl AsRef, check_parent_dir: bool) -> io::Result { let mut open_options = OpenOptions::new(); open_options.read(true); secure_open_impl(path.as_ref(), &mut open_options, check_parent_dir, false) } pub fn secure_open_cookie_file(path: impl AsRef) -> io::Result { let mut open_options = OpenOptions::new(); open_options .read(true) .write(true) .create(true) .mode(mode(Category::Owner, Op::Write) | mode(Category::Owner, Op::Read)); secure_open_impl(path.as_ref(), &mut open_options, true, true) } fn checks(path: &Path, meta: Metadata) -> io::Result<()> { let error = |msg| Error::new(ErrorKind::PermissionDenied, msg); let path_mode = meta.permissions().mode(); if meta.uid() != 0 { Err(error(format!("{} must be owned by root", path.display()))) } else if meta.gid() != 0 && (path_mode & mode(Category::Group, Op::Write) != 0) { Err(error(format!( "{} cannot be group-writable", path.display() ))) } else if path_mode & mode(Category::World, Op::Write) != 0 { Err(error(format!( "{} cannot be world-writable", path.display() ))) } else { Ok(()) } } // Open `path` with options `open_options`, provided that it is "secure". // "Secure" means that it passes the `checks` function above. // If `check_parent_dir` is set, also check that the parent directory is "secure" also. // If `create_parent_dirs` is set, create the path to the file if it does not already exist. fn secure_open_impl( path: &Path, open_options: &mut OpenOptions, check_parent_dir: bool, create_parent_dirs: bool, ) -> io::Result { let error = |msg| Error::new(ErrorKind::PermissionDenied, msg); if check_parent_dir || create_parent_dirs { if let Some(parent_dir) = path.parent() { // if we should create parent dirs and it does not yet exist, create it if create_parent_dirs && !parent_dir.exists() { DirBuilder::new() .recursive(true) .mode( mode(Category::Owner, Op::Write) | mode(Category::Owner, Op::Read) | mode(Category::Owner, Op::Exec) | mode(Category::Group, Op::Exec) | mode(Category::World, Op::Exec), ) .create(parent_dir)?; } if check_parent_dir { let parent_meta = std::fs::metadata(parent_dir)?; checks(parent_dir, parent_meta)?; } } else { return Err(error(format!( "{} has no valid parent directory", path.display() ))); } } let file = open_options.open(path)?; let meta = file.metadata()?; checks(path, meta)?; Ok(file) } #[cfg(test)] mod test { use super::*; #[test] fn secure_open_is_predictable() { // /etc/hosts should be readable and "secure" (if this test fails, you have been compromised) assert!(std::fs::File::open("/etc/hosts").is_ok()); assert!(secure_open("/etc/hosts", false).is_ok()); // /tmp should be readable, but not secure (writeable by group other than root) assert!(std::fs::File::open("/tmp").is_ok()); assert!(secure_open("/tmp", false).is_err()); #[cfg(target_os = "linux")] { // /var/log/wtmp should be readable, but not secure (writeable by group other than root) // It doesn't exist on many non-Linux systems however. if std::fs::File::open("/var/log/wtmp").is_ok() { assert!(secure_open("/var/log/wtmp", false).is_err()); } } // /etc/shadow should not be readable assert!(std::fs::File::open("/etc/shadow").is_err()); assert!(secure_open("/etc/shadow", false).is_err()); } #[test] fn test_secure_open_cookie_file() { assert!(secure_open_cookie_file("/etc/hosts").is_err()); } } sudo-rs-0.2.5/src/system/file/chown.rs000064400000000000000000000010761046102023000157460ustar 00000000000000use std::{fs::File, io, os::fd::AsRawFd}; use crate::{ cutils::cerr, system::interface::{GroupId, UserId}, }; pub(crate) trait Chown { fn chown(&self, uid: UserId, gid: GroupId) -> io::Result<()>; } impl Chown for File { fn chown(&self, owner: UserId, group: GroupId) -> io::Result<()> { let fd = self.as_raw_fd(); // SAFETY: `fchown` is passed a proper file descriptor; and even if the user/group id // is invalid, it will not cause UB. cerr(unsafe { libc::fchown(fd, owner.inner(), group.inner()) }).map(|_| ()) } } sudo-rs-0.2.5/src/system/file/lock.rs000064400000000000000000000031531046102023000155560ustar 00000000000000use std::{ fs::File, io::Result, os::fd::{AsRawFd, RawFd}, }; use crate::cutils::cerr; pub(crate) struct FileLock { fd: RawFd, } impl FileLock { /// Get an exclusive lock on the file, waits if there is currently a lock /// on the file if `nonblocking` is true. pub(crate) fn exclusive(file: &File, nonblocking: bool) -> Result { let fd = file.as_raw_fd(); flock(fd, LockOp::LockExclusive, nonblocking)?; Ok(Self { fd }) } /// Release the lock on the file. pub(crate) fn unlock(self) -> Result<()> { flock(self.fd, LockOp::Unlock, false) } } impl Drop for FileLock { fn drop(&mut self) { flock(self.fd, LockOp::Unlock, false).ok(); } } #[derive(Clone, Copy, Debug)] enum LockOp { LockExclusive, Unlock, } impl LockOp { fn as_flock_operation(self) -> libc::c_int { match self { LockOp::LockExclusive => libc::LOCK_EX, LockOp::Unlock => libc::LOCK_UN, } } } fn flock(fd: RawFd, action: LockOp, nonblocking: bool) -> Result<()> { let mut operation = action.as_flock_operation(); if nonblocking { operation |= libc::LOCK_NB; } // SAFETY: even if `fd` would not be a valid file descriptor, that would merely // result in an error condition, not UB cerr(unsafe { libc::flock(fd, operation) })?; Ok(()) } #[cfg(test)] mod tests { use crate::system::tests::tempfile; use super::*; #[test] fn test_locking_of_tmp_file() { let f = tempfile().unwrap(); FileLock::exclusive(&f, false).unwrap().unlock().unwrap(); } } sudo-rs-0.2.5/src/system/file/mod.rs000064400000000000000000000002131046102023000153770ustar 00000000000000mod chown; mod lock; mod tmpdir; pub(crate) use chown::Chown; pub(crate) use lock::FileLock; pub(crate) use tmpdir::create_temporary_dir; sudo-rs-0.2.5/src/system/file/tmpdir.rs000064400000000000000000000012441046102023000161240ustar 00000000000000use std::ffi::{CString, OsString}; use std::io; use std::os::unix::ffi::OsStringExt; use std::path::PathBuf; pub(crate) fn create_temporary_dir() -> io::Result { let template = cstr!("/tmp/sudoers-XXXXXX").to_owned(); // SAFETY: mkdtemp is passed a valid null-terminated C string let ptr = unsafe { libc::mkdtemp(template.into_raw()) }; if ptr.is_null() { return Err(io::Error::last_os_error()); } // SAFETY: ptr is the same pointer produced by into_raw() above, and it // is still pointing to a zero-terminated C string let path = OsString::from_vec(unsafe { CString::from_raw(ptr) }.into_bytes()).into(); Ok(path) } sudo-rs-0.2.5/src/system/interface.rs000064400000000000000000000121531046102023000156470ustar 00000000000000use std::{ffi::CStr, fmt::Display, num::ParseIntError, str::FromStr}; /// Represents a group ID in the system. /// /// `GroupId` is transparent because the memory mapping should stay the same as the underlying /// type, so we can safely cast as a pointer. /// See the implementation in `system::mod::set_target_user`. #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct GroupId(libc::gid_t); /// Represents a user ID in the system. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct UserId(libc::uid_t); /// Represents a process ID in the system. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ProcessId(libc::pid_t); /// Represents a device ID in the system. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId(libc::dev_t); impl GroupId { pub fn new(id: libc::gid_t) -> Self { Self(id) } pub fn inner(&self) -> libc::gid_t { self.0 } } impl UserId { pub fn new(id: libc::uid_t) -> Self { Self(id) } pub fn inner(&self) -> libc::uid_t { self.0 } pub const ROOT: Self = Self(0); } impl ProcessId { pub fn new(id: libc::pid_t) -> Self { Self(id) } pub fn inner(&self) -> libc::pid_t { self.0 } pub fn is_valid(&self) -> bool { self.0 != 0 } } impl DeviceId { pub fn new(id: libc::dev_t) -> Self { Self(id) } pub fn inner(&self) -> libc::dev_t { self.0 } } impl Display for GroupId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Display for UserId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Display for ProcessId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Display for DeviceId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl FromStr for GroupId { type Err = ParseIntError; fn from_str(s: &str) -> Result { s.parse::().map(GroupId::new) } } impl FromStr for UserId { type Err = ParseIntError; fn from_str(s: &str) -> Result { s.parse::().map(UserId::new) } } /// This trait/module is here to not make this crate independent (at the present time) in the idiosyncracies of user representation details /// (which we may decide over time), as well as to make explicit what functionality a user-representation must have; this /// interface is not set in stone and "easy" to change. pub trait UnixUser { fn has_name(&self, _name: &str) -> bool; fn has_uid(&self, _uid: UserId) -> bool; fn is_root(&self) -> bool; fn in_group_by_name(&self, _name: &CStr) -> bool; fn in_group_by_gid(&self, _gid: GroupId) -> bool; } pub trait UnixGroup { fn as_gid(&self) -> GroupId; fn try_as_name(&self) -> Option<&str>; } impl UnixUser for super::User { fn has_name(&self, name: &str) -> bool { self.name == name } fn has_uid(&self, uid: UserId) -> bool { self.uid == uid } fn is_root(&self) -> bool { self.has_uid(UserId::ROOT) } fn in_group_by_name(&self, name_c: &CStr) -> bool { if let Ok(Some(group)) = super::Group::from_name(name_c) { self.in_group_by_gid(group.gid) } else { false } } fn in_group_by_gid(&self, gid: GroupId) -> bool { self.groups.contains(&gid) } } impl UnixGroup for super::Group { fn as_gid(&self) -> GroupId { self.gid } fn try_as_name(&self) -> Option<&str> { self.name.as_deref() } } #[cfg(test)] mod test { use super::*; use crate::system::{Group, User, ROOT_GROUP_NAME}; use std::ffi::CString; fn test_user(user: impl UnixUser, name_c: &CStr) { let name = name_c.to_str().unwrap(); assert!(user.has_name(name)); if user.has_name("root") { assert!(user.in_group_by_name(CString::new(ROOT_GROUP_NAME).unwrap().as_c_str())); assert!(user.is_root()); } else { assert!(user.in_group_by_name(name_c)); assert!(!user.is_root()); } assert_eq!(user.is_root(), name == "root"); } fn test_group(group: impl UnixGroup, name: &str) { assert_eq!(name == ROOT_GROUP_NAME, group.as_gid() == GroupId::new(0)); assert_eq!(group.try_as_name(), Some(name)); } #[test] fn test_unix_user() { let user = |name| User::from_name(name).unwrap().unwrap(); test_user(user(cstr!("root")), cstr!("root")); test_user(user(cstr!("daemon")), cstr!("daemon")); } #[test] fn test_unix_group() { let group = |name| Group::from_name(name).unwrap().unwrap(); let root_group_cstr = CString::new(ROOT_GROUP_NAME).unwrap(); test_group(group(root_group_cstr.as_c_str()), ROOT_GROUP_NAME); test_group(group(cstr!("daemon")), "daemon"); } } sudo-rs-0.2.5/src/system/kernel.rs000064400000000000000000000033161046102023000151700ustar 00000000000000use crate::common::Error; #[cfg(target_os = "linux")] pub fn kernel_check() -> Result<(), Error> { use crate::cutils::cerr; use std::ffi::CStr; use std::mem::MaybeUninit; // On Linux, we need kernel version 5.9 to have access to `close_range()` const TARGET_VERSION: (u32, u32) = (5, 9); let mut utsname = MaybeUninit::uninit(); // SAFETY: uname is passed a correct pointer cerr(unsafe { libc::uname(utsname.as_mut_ptr()) })?; // SAFETY: since uname exited normally, the struct is now initialized let utsname = unsafe { utsname.assume_init() }; // SAFETY: utsname.release will hold a null-terminated C string let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) }.to_string_lossy(); // Parse the major and minor version numbers let mut version_parts = release.split('.').map_while(|x| x.parse::().ok()); match (version_parts.next(), version_parts.next()) { (Some(major), Some(minor)) if (major, minor) < TARGET_VERSION => { // We have determined that this Linux kernel is too old. Err(Error::KernelCheck) } _ => { // We have not been able to prove that sudo-rs is incompatible with this kernel // and are giving the benefit of the doubt. Ok(()) } } } #[cfg(target_os = "freebsd")] pub fn kernel_check() -> Result<(), Error> { // the kernel check doesn't make much sense on FreeBSD (we need FreeBSD 8.0 or newer, // which is comparatively ancient compared to Linux 5.9) Ok(()) } #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] pub fn kernel_check() -> Result<(), Error> { compile_error!("sudo-rs only works on Linux and FreeBSD") } sudo-rs-0.2.5/src/system/mod.rs000064400000000000000000001176721046102023000145020ustar 00000000000000use core::fmt; // TODO: remove unused attribute when system is cleaned up #[cfg(target_os = "linux")] use std::str::FromStr; use std::{ collections::BTreeSet, ffi::{c_uint, CStr, CString}, io, mem::MaybeUninit, ops, os::{ fd::{AsFd, AsRawFd}, unix::{self, prelude::OsStrExt}, }, path::{Path, PathBuf}, }; use crate::{ common::{Error, SudoPath, SudoString}, cutils::*, }; pub use audit::secure_open; use interface::{DeviceId, GroupId, ProcessId, UserId}; pub use libc::PATH_MAX; use libc::STDERR_FILENO; use time::ProcessCreateTime; use self::signal::SignalNumber; mod audit; // generalized traits for when we want to hide implementations pub mod interface; pub mod kernel; pub mod file; pub mod time; pub mod timestamp; pub mod signal; pub mod term; pub mod wait; pub(crate) fn can_execute>(path: P) -> bool { let Ok(path) = CString::new(path.as_ref().as_os_str().as_bytes()) else { return false; }; // SAFETY: we are passing a proper pointer to access unsafe { libc::access(path.as_ptr(), libc::X_OK) == 0 } } pub(crate) fn _exit(status: libc::c_int) -> ! { // SAFETY: this function is safe to call unsafe { libc::_exit(status) } } /// A type able to close every file descriptor except for the ones pased via [`FileCloser::except`] /// and the IO streams. pub(crate) struct FileCloser { fds: BTreeSet, } impl FileCloser { pub(crate) const fn new() -> Self { Self { fds: BTreeSet::new(), } } pub(crate) fn except(&mut self, fd: &F) { self.fds.insert(fd.as_fd().as_raw_fd() as c_uint); } /// Close every file descriptor that is not one of the IO streams or one of the file /// descriptors passed via [`FileCloser::except`]. /// /// # Safety /// /// Incorrect use of this method can violate [I/O Safety](https://doc.rust-lang.org/std/io/index.html#io-safety) /// by closing fds that are not owned by `FileCloser`. The caller needs to ensure that none of /// the closed fds are ever accessed again. // FIXME do not return a Result. This makes it way to easy to accidentally violate the safety conditions. pub(crate) unsafe fn close_the_universe(self) -> io::Result<()> { let mut fds = self.fds.into_iter(); let Some(mut curr_fd) = fds.next() else { return close_range(STDERR_FILENO as c_uint + 1, c_uint::MAX); }; if let Some(max_fd) = curr_fd.checked_sub(1) { close_range(STDERR_FILENO as c_uint + 1, max_fd)?; } for next_fd in fds { if let Some(min_fd) = curr_fd.checked_add(1) { if let Some(max_fd) = next_fd.checked_sub(1) { close_range(min_fd, max_fd)?; } } curr_fd = next_fd; } if let Some(min_fd) = curr_fd.checked_add(1) { close_range(min_fd, c_uint::MAX)?; } Ok(()) } } fn close_range(min_fd: c_uint, max_fd: c_uint) -> io::Result<()> { if min_fd <= max_fd { // SAFETY: this function is safe to call: // - any errors while closing a specific fd will be effectively ignored // - if the provided range or flags are invalid, that will be reported // as an error but will not cause undefined behaviour cerr(unsafe { libc::close_range(min_fd, max_fd, 0) })?; } Ok(()) } pub(crate) enum ForkResult { // Parent process branch with the child process' PID. Parent(ProcessId), // Child process branch. Child, } /// Create a new process. /// /// # Safety /// /// Must not be called in multithreaded programs. pub(crate) unsafe fn fork() -> io::Result { // FIXME add debug assertion that we are not currently using multiple threads. // SAFETY: Calling async-signal-unsafe functions after fork is safe as the program is single // threaded at this point according to the safety invariant of this function. let pid = cerr(unsafe { libc::fork() })?; if pid == 0 { Ok(ForkResult::Child) } else { Ok(ForkResult::Parent(ProcessId::new(pid))) } } /// Create a new process with extra precautions for usage in tests. /// /// # Safety /// /// In a multithreaded program, only async-signal-safe functions are guaranteed to work in the /// child process until a call to `execve` or a similar function is done. #[cfg(test)] unsafe fn fork_for_test(child_func: impl FnOnce() -> std::convert::Infallible) -> ProcessId { use std::io::Write; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::process::exit; // SAFETY: Not really safe, but this is test only code. match unsafe { fork() }.unwrap() { ForkResult::Child => { // Make sure that panics in the child always abort the process. let err = match catch_unwind(AssertUnwindSafe(child_func)) { Ok(res) => match res {}, Err(err) => err, }; let s = if let Some(s) = err.downcast_ref::<&str>() { s } else if let Some(s) = err.downcast_ref::() { s } else { "Box" }; let _ = writeln!(std::io::stderr(), "{s}"); exit(101); } ForkResult::Parent(pid) => pid, } } pub fn setsid() -> io::Result { // SAFETY: this function is memory-safe to call Ok(ProcessId::new(cerr(unsafe { libc::setsid() })?)) } #[derive(Clone)] #[cfg_attr(test, derive(PartialEq))] pub struct Hostname { inner: String, } impl fmt::Debug for Hostname { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Hostname").field(&self.inner).finish() } } impl fmt::Display for Hostname { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.inner) } } impl ops::Deref for Hostname { type Target = str; fn deref(&self) -> &str { &self.inner } } impl Hostname { #[cfg(test)] pub fn fake(hostname: &str) -> Self { Self { inner: hostname.to_string(), } } pub fn resolve() -> Self { // see `man 2 gethostname` const MAX_HOST_NAME_SIZE_ACCORDING_TO_SUSV2: libc::c_long = 255; // POSIX.1 systems limit hostnames to `HOST_NAME_MAX` bytes // not including null-byte in the count let max_hostname_size = sysconf(libc::_SC_HOST_NAME_MAX) .unwrap_or(MAX_HOST_NAME_SIZE_ACCORDING_TO_SUSV2) as usize; let buffer_size = max_hostname_size + 1 /* null byte delimiter */ ; let mut buf = vec![0; buffer_size]; // SAFETY: we are passing a valid pointer to gethostname match cerr(unsafe { libc::gethostname(buf.as_mut_ptr(), buffer_size) }) { Ok(_) => Self { // SAFETY: gethostname succeeded, so `buf` will hold a null-terminated C string inner: unsafe { string_from_ptr(buf.as_ptr()) }, }, // ENAMETOOLONG is returned when hostname is greater than `buffer_size` Err(_) => { // but we have chosen a `buffer_size` larger than `max_hostname_size` so no truncation error is possible panic!("Unexpected error while retrieving hostname, this should not happen"); } } } } pub fn syslog(priority: libc::c_int, facility: libc::c_int, message: &CStr) { const MSG: *const libc::c_char = match CStr::from_bytes_until_nul(b"%s\0") { Ok(cstr) => cstr.as_ptr(), Err(_) => panic!("syslog formatting string is not null-terminated"), }; // SAFETY: // - "MSG" is a constant expression that is a null-terminated C string that represents "%s"; // this also means that to achieve safety we MUST pass one more argument to syslog that is a proper // pointer to a null-terminated C string // - message.as_ptr() is a pointer to a proper null-terminated C string (message being a &CStr) // for more info: read the manpage for syslog(2) unsafe { libc::syslog(priority | facility, MSG, message.as_ptr()); } } /// set target user and groups (uid, gid, additional groups) for a command pub fn set_target_user( cmd: &mut std::process::Command, mut target_user: User, target_group: Group, ) { use std::os::unix::process::CommandExt; if let Some(index) = target_user .groups .iter() .position(|id| id == &target_group.gid) { // make sure the requested group id is the first in the list (necessary on FreeBSD) target_user.groups.swap(0, index) } else { // add target group to list of additional groups if not present target_user.groups.insert(0, target_group.gid); } // we need to do this in a `pre_exec` call since the `groups` method in `process::Command` is unstable // see https://github.com/rust-lang/rust/blob/a01b4cc9f375f1b95fa8195daeea938d3d9c4c34/library/std/src/sys/unix/process/process_unix.rs#L329-L352 // for the std implementation of the libc calls to `setgroups`, `setgid` and `setuid` // SAFETY: Setuid, setgid and setgroups are async-signal-safe. unsafe { cmd.pre_exec(move || { cerr(libc::setgroups( target_user.groups.len() as _, // We can cast to gid_t because `GroupId` is marked as transparent target_user.groups.as_ptr().cast::(), ))?; // setgid and setuid set the real, effective and saved version of the gid and uid // respectively rather than just the real gid and uid. The original sudo uses setresgid // and setresuid instead with all three arguments equal, but as this does the same as // setgid and setuid using the latter is fine too. cerr(libc::setgid(target_group.gid.inner()))?; cerr(libc::setuid(target_user.uid.inner()))?; Ok(()) }); } } /// Send a signal to a process with the specified ID. pub fn kill(pid: ProcessId, signal: SignalNumber) -> io::Result<()> { // SAFETY: This function cannot cause UB even if `pid` is not a valid process ID or if // `signal` is not a valid signal code. cerr(unsafe { libc::kill(pid.inner(), signal) }).map(|_| ()) } /// Send a signal to a process group with the specified ID. pub fn killpg(pgid: ProcessId, signal: SignalNumber) -> io::Result<()> { // SAFETY: This function cannot cause UB even if `pgid` is not a valid process ID or if // `signal` is not a valid signal code. cerr(unsafe { libc::killpg(pgid.inner(), signal) }).map(|_| ()) } /// Get the process group ID of the current process. pub fn getpgrp() -> ProcessId { // SAFETY: This function is always safe to call ProcessId::new(unsafe { libc::getpgrp() }) } /// Get a process group ID. pub fn getpgid(pid: ProcessId) -> io::Result { // SAFETY: This function cannot cause UB even if `pid` is not a valid process ID Ok(ProcessId::new(cerr(unsafe { libc::getpgid(pid.inner()) })?)) } /// Set a process group ID. pub fn setpgid(pid: ProcessId, pgid: ProcessId) -> io::Result<()> { // SAFETY: This function cannot cause UB even if `pid` or `pgid` are not a valid process IDs: // https://pubs.opengroup.org/onlinepubs/007904975/functions/setpgid.html cerr(unsafe { libc::setpgid(pid.inner(), pgid.inner()) }).map(|_| ()) } pub fn chown>( path: &S, uid: impl Into, gid: impl Into, ) -> io::Result<()> { let path = path.as_ref().as_ptr(); let uid = uid.into(); let gid = gid.into(); // SAFETY: path is a valid pointer to a null-terminated C string; chown cannot cause safety // issues even if uid and/or gid would be invalid identifiers. cerr(unsafe { libc::chown(path, uid.inner(), gid.inner()) }).map(|_| ()) } #[derive(Debug, Clone, PartialEq)] pub struct User { pub uid: UserId, pub gid: GroupId, pub name: SudoString, pub home: SudoPath, pub shell: PathBuf, pub groups: Vec, } impl User { /// # Safety /// This function expects `pwd` to be a result from a successful call to `getpwXXX_r`. /// (It can cause UB if any of `pwd`'s pointed-to strings does not have a null-terminator.) unsafe fn from_libc(pwd: &libc::passwd) -> Result { let mut buf_len: libc::c_int = 32; let mut groups_buffer: Vec; while { groups_buffer = vec![0; buf_len as usize]; // SAFETY: getgrouplist is passed valid pointers // in particular `groups_buffer` is an array of `buf.len()` bytes, as required let result = unsafe { libc::getgrouplist( pwd.pw_name, pwd.pw_gid, groups_buffer.as_mut_ptr(), &mut buf_len, ) }; result == -1 } { if buf_len >= 65536 { panic!("user has too many groups (> 65536), this should not happen"); } buf_len *= 2; } groups_buffer.resize_with(buf_len as usize, || { panic!("invalid groups count returned from getgrouplist, this should not happen") }); // SAFETY: All pointers were initialized by a successful call to `getpwXXX_r` as per the // safety invariant of this function. unsafe { Ok(User { uid: UserId::new(pwd.pw_uid), gid: GroupId::new(pwd.pw_gid), name: SudoString::new(string_from_ptr(pwd.pw_name))?, home: SudoPath::new(os_string_from_ptr(pwd.pw_dir).into())?, shell: os_string_from_ptr(pwd.pw_shell).into(), groups: groups_buffer .iter() .map(|id| GroupId::new(*id)) .collect::>(), }) } } pub fn from_uid(uid: UserId) -> Result, Error> { let max_pw_size = sysconf(libc::_SC_GETPW_R_SIZE_MAX).unwrap_or(16_384); let mut buf = vec![0; max_pw_size as usize]; let mut pwd = MaybeUninit::uninit(); let mut pwd_ptr = std::ptr::null_mut(); // SAFETY: getpwuid_r is passed valid (although partly uninitialized) pointers to memory, // in particular `buf` points to an array of `buf.len()` bytes, as required. // After this call, if `pwd_ptr` is not NULL, `*pwd_ptr` and `pwd` will be aliased; // but we never dereference `pwd_ptr`. cerr(unsafe { libc::getpwuid_r( uid.inner(), pwd.as_mut_ptr(), buf.as_mut_ptr(), buf.len(), &mut pwd_ptr, ) })?; if pwd_ptr.is_null() { Ok(None) } else { // SAFETY: pwd_ptr was not null, and getpwuid_r succeeded, so we have assurances that // the `pwd` structure was written to by getpwuid_r let pwd = unsafe { pwd.assume_init() }; // SAFETY: `pwd` was obtained by a call to getpwXXX_r, as required. unsafe { Self::from_libc(&pwd).map(Some) } } } pub fn effective_uid() -> UserId { // SAFETY: this function cannot cause memory safety issues UserId::new(unsafe { libc::geteuid() }) } pub fn effective_gid() -> GroupId { // SAFETY: this function cannot cause memory safety issues GroupId::new(unsafe { libc::getegid() }) } pub fn real_uid() -> UserId { // SAFETY: this function cannot cause memory safety issues UserId::new(unsafe { libc::getuid() }) } pub fn real_gid() -> GroupId { // SAFETY: this function cannot cause memory safety issues GroupId::new(unsafe { libc::getgid() }) } pub fn real() -> Result, Error> { Self::from_uid(Self::real_uid()) } pub fn primary_group(&self) -> std::io::Result { // Use from_gid_unchecked here to ensure that we can still resolve when // the /etc/group entry for the primary group is missing. Group::from_gid_unchecked(self.gid) } pub fn from_name(name_c: &CStr) -> Result, Error> { let max_pw_size = sysconf(libc::_SC_GETPW_R_SIZE_MAX).unwrap_or(16_384); let mut buf = vec![0; max_pw_size as usize]; let mut pwd = MaybeUninit::uninit(); let mut pwd_ptr = std::ptr::null_mut(); // SAFETY: analogous to getpwuid_r above cerr(unsafe { libc::getpwnam_r( name_c.as_ptr(), pwd.as_mut_ptr(), buf.as_mut_ptr(), buf.len(), &mut pwd_ptr, ) })?; if pwd_ptr.is_null() { Ok(None) } else { // SAFETY: pwd_ptr was not null, and getpwnam_r succeeded, so we have assurances that // the `pwd` structure was written to by getpwnam_r let pwd = unsafe { pwd.assume_init() }; // SAFETY: `pwd` was obtained by a call to getpwXXX_r, as required. unsafe { Self::from_libc(&pwd).map(Some) } } } } #[derive(Debug, Clone)] #[cfg_attr(test, derive(PartialEq))] pub struct Group { pub gid: GroupId, pub name: Option, } impl Group { /// # Safety /// This function expects `grp` to be a result from a successful call to `getgrXXX_r`. /// In particular the grp.gr_mem pointer is assumed to be non-null, and pointing to a /// null-terminated list; the pointed-to strings are expected to be null-terminated. unsafe fn from_libc(grp: &libc::group) -> Group { // SAFETY: The name pointer is initialized by a successful call to `getgrXXX_r` as per the // safety invariant of this function. let name = unsafe { string_from_ptr(grp.gr_name) }; Group { gid: GroupId::new(grp.gr_gid), name: Some(name), } } /// Lookup group for gid without returning an error when a /etc/group entry is missing. fn from_gid_unchecked(gid: GroupId) -> std::io::Result { let max_gr_size = sysconf(libc::_SC_GETGR_R_SIZE_MAX).unwrap_or(16_384); let mut buf = vec![0; max_gr_size as usize]; let mut grp = MaybeUninit::uninit(); let mut grp_ptr = std::ptr::null_mut(); // SAFETY: analogous to getpwuid_r above cerr(unsafe { libc::getgrgid_r( gid.inner(), grp.as_mut_ptr(), buf.as_mut_ptr(), buf.len(), &mut grp_ptr, ) })?; if grp_ptr.is_null() { Ok(Group { gid, name: None }) } else { // SAFETY: grp_ptr was not null, and getgrgid_r succeeded, so we have assurances that // the `grp` structure was written to by getgrgid_r let grp = unsafe { grp.assume_init() }; // SAFETY: `pwd` was obtained by a call to getgrXXX_r, as required. Ok(unsafe { Group::from_libc(&grp) }) } } pub fn from_gid(gid: GroupId) -> std::io::Result> { let group = Self::from_gid_unchecked(gid)?; if group.name.is_none() { // No entry in /etc/group Ok(None) } else { Ok(Some(group)) } } pub fn from_name(name_c: &CStr) -> std::io::Result> { let max_gr_size = sysconf(libc::_SC_GETGR_R_SIZE_MAX).unwrap_or(16_384); let mut buf = vec![0; max_gr_size as usize]; let mut grp = MaybeUninit::uninit(); let mut grp_ptr = std::ptr::null_mut(); // SAFETY: analogous to getpwuid_r above cerr(unsafe { libc::getgrnam_r( name_c.as_ptr(), grp.as_mut_ptr(), buf.as_mut_ptr(), buf.len(), &mut grp_ptr, ) })?; if grp_ptr.is_null() { Ok(None) } else { // SAFETY: grp_ptr was not null, and getgrgid_r succeeded, so we have assurances that // the `grp` structure was written to by getgrgid_r let grp = unsafe { grp.assume_init() }; // SAFETY: `pwd` was obtained by a call to getgrXXX_r, as required. Ok(Some(unsafe { Group::from_libc(&grp) })) } } } pub enum WithProcess { Current, Other(ProcessId), } impl WithProcess { #[cfg(target_os = "linux")] fn to_proc_string(&self) -> String { match self { WithProcess::Current => "self".into(), WithProcess::Other(pid) => pid.to_string(), } } } #[derive(Debug, Clone)] pub struct Process { pub pid: ProcessId, pub parent_pid: Option, pub session_id: ProcessId, } impl Default for Process { fn default() -> Self { Self::new() } } impl Process { pub fn new() -> Process { Process { pid: Self::process_id(), parent_pid: Self::parent_id(), session_id: Self::session_id(), } } /// Return the process identifier for the current process pub fn process_id() -> ProcessId { // NOTE libstd casts the `i32` that `libc::getpid` returns into `u32` // here we cast it back into `i32` (`ProcessId`) ProcessId::new(std::process::id() as i32) } /// Return the parent process identifier for the current process pub fn parent_id() -> Option { // NOTE libstd casts the `i32` that `libc::getppid` returns into `u32` // here we cast it back into `i32` (`ProcessId`) let pid = ProcessId::new(unix::process::parent_id() as i32); if !pid.is_valid() { None } else { Some(pid) } } /// Get the session id for the current process pub fn session_id() -> ProcessId { // SAFETY: this function is explicitly safe to call with argument 0, // and more generally getsid will never cause memory safety issues. ProcessId::new(unsafe { libc::getsid(0) }) } /// Returns the device identifier of the TTY device that is currently /// attached to the given process #[cfg(target_os = "linux")] pub fn tty_device_id(pid: WithProcess) -> std::io::Result> { // device id of tty is displayed as a signed integer of 32 bits let data: i32 = read_proc_stat(pid, 6 /* tty_nr */)?; if data == 0 { Ok(None) } else { // While the integer was displayed as signed in the proc stat file, // we actually need to interpret the bits of that integer as an unsigned // int. We convert via u32 because a direct conversion to DeviceId // would use sign extension, which would result in a different bit // representation Ok(Some(DeviceId::new(data as u64))) } } /// Returns the device identifier of the TTY device that is currently /// attached to the given process #[cfg(target_os = "freebsd")] pub fn tty_device_id(pid: WithProcess) -> std::io::Result> { use std::ffi::c_void; use std::ptr; let mut ki_proc: Vec = Vec::with_capacity(1); let pid = match pid { WithProcess::Current => std::process::id() as i32, WithProcess::Other(pid) => pid.inner(), }; loop { let mut size = ki_proc.capacity() * size_of::(); match cerr(unsafe { libc::sysctl( [ libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid, size_of::() as i32, 1, ] .as_ptr(), 4, ki_proc.as_mut_ptr().cast::(), &mut size, ptr::null(), 0, ) }) { Ok(_) => { assert!(size >= size_of::()); // SAFETY: The above sysctl has initialized at least `size` bytes. We have // asserted that this is at least a single element. unsafe { ki_proc.set_len(1); } break; } Err(e) if e.raw_os_error() == Some(libc::ENOMEM) => { // Vector not big enough. Grow it by 10% and try again. ki_proc.reserve(ki_proc.capacity() + (ki_proc.capacity() + 9) / 10); } Err(e) => return Err(e), } } if ki_proc[0].ki_tdev == !0 { Ok(None) } else { Ok(Some(DeviceId::new(ki_proc[0].ki_tdev))) } } /// Get the process starting time of a specific process #[cfg(target_os = "linux")] pub fn starting_time(pid: WithProcess) -> io::Result { let process_start: u64 = read_proc_stat(pid, 21 /* start_time */)?; // the startime field is stored in ticks since the system start, so we need to know how many // ticks go into a second let ticks_per_second = crate::cutils::sysconf(libc::_SC_CLK_TCK).ok_or_else(|| { io::Error::new( io::ErrorKind::Other, "Could not retrieve system config variable for ticks per second", ) })? as u64; // finally compute the system time at which the process was started Ok(ProcessCreateTime::new( (process_start / ticks_per_second) as i64, ((process_start % ticks_per_second) * (1_000_000_000 / ticks_per_second)) as i64, )) } /// Get the process starting time of a specific process #[cfg(target_os = "freebsd")] pub fn starting_time(pid: WithProcess) -> io::Result { use std::ffi::c_void; use std::ptr; let mut ki_proc: Vec = Vec::with_capacity(1); let pid = match pid { WithProcess::Current => std::process::id() as i32, WithProcess::Other(pid) => pid.inner(), }; loop { let mut size = ki_proc.capacity() * size_of::(); match cerr(unsafe { libc::sysctl( [ libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid, size_of::() as i32, 1, ] .as_ptr(), 4, ki_proc.as_mut_ptr().cast::(), &mut size, ptr::null(), 0, ) }) { Ok(_) => { assert!(size >= size_of::()); // SAFETY: The above sysctl has initialized at least `size` bytes. We have // asserted that this is at least a single element. unsafe { ki_proc.set_len(1); } break; } Err(e) if e.raw_os_error() == Some(libc::ENOMEM) => { // Vector not big enough. Grow it by 10% and try again. ki_proc.reserve(ki_proc.capacity() + (ki_proc.capacity() + 9) / 10); } Err(e) => return Err(e), } } let ki_start = ki_proc[0].ki_start; Ok(ProcessCreateTime::new( ki_start.tv_sec, ki_start.tv_usec * 1000, )) } } /// Read the n-th field (with 0-based indexing) from `/proc//self`. /// /// See ["Table 1-4: Contents of the stat fields" of "The /proc /// Filesystem"][proc_stat_fields] in the Linux docs for all available fields. /// /// IMPORTANT: the first two fields are not accessible with this routine. /// /// [proc_stat_fields]: https://www.kernel.org/doc/html/latest/filesystems/proc.html#id10 #[cfg(target_os = "linux")] fn read_proc_stat(pid: WithProcess, field_idx: isize) -> io::Result { // the first two fields are skipped by the code below, and we never need them, // so no point in implementing code for it in this private function. debug_assert!(field_idx >= 2); // read from a specific pid file, or use `self` to refer to our own process let pidref = pid.to_proc_string(); // read the data from the stat file for the process with the given pid let path = PathBuf::from_iter(&["/proc", &pidref, "stat"]); let proc_stat = std::fs::read(path)?; // first get the part of the stat file past the second argument, we then reverse // search for a ')' character and start the search for the starttime field from there on let skip_past_second_arg = proc_stat.iter().rposition(|b| *b == b')').ok_or_else(|| { io::Error::new( io::ErrorKind::InvalidInput, "Could not find position of 'comm' field in process stat", ) })?; let mut stat = &proc_stat[skip_past_second_arg..]; // we've now passed the first two fields, so we are at index 1, now we skip over // fields until we arrive at the field we are searching for let mut curr_field = 1; while curr_field < field_idx && !stat.is_empty() { if stat[0] == b' ' { curr_field += 1; } stat = &stat[1..]; } // The expected field cannot be in the file anymore when we are at EOF if stat.is_empty() { return Err(io::Error::new( io::ErrorKind::InvalidData, "Stat file was not of the expected format", )); } // we've now arrived at the field we are looking for, we now check how // long this field is by finding where the next space is let mut idx = 0; while stat[idx] != b' ' && idx < stat.len() { idx += 1; } let field = &stat[0..idx]; // we first convert the data to a string slice, this should not fail with a normal /proc filesystem let fielddata = std::str::from_utf8(field).map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, "Could not interpret byte slice as string", ) })?; // then we convert the string slice to whatever the requested type was fielddata.parse().map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, "Could not interpret string as number", ) }) } pub fn escape_os_str_lossy(s: &std::ffi::OsStr) -> String { s.to_string_lossy().escape_default().collect() } pub fn make_zeroed_sigaction() -> libc::sigaction { // SAFETY: since sigaction is a C struct, all-zeroes is a valid representation // We cannot use a "literal struct" initialization method since the exact representation // of libc::sigaction is not fixed, see e.g. https://github.com/trifectatechfoundation/sudo-rs/issues/829 unsafe { std::mem::zeroed() } } #[cfg(all(test, target_os = "linux"))] pub(crate) const ROOT_GROUP_NAME: &str = "root"; #[cfg(all(test, not(target_os = "linux")))] pub(crate) const ROOT_GROUP_NAME: &str = "wheel"; #[allow(clippy::undocumented_unsafe_blocks)] #[cfg(test)] mod tests { use std::{ io::{self, Read, Write}, os::{ fd::{AsFd, AsRawFd}, unix::net::UnixStream, }, process::exit, }; use libc::SIGKILL; use crate::system::interface::{GroupId, ProcessId, UserId}; use super::{ fork_for_test, getpgrp, setpgid, wait::{Wait, WaitOptions}, Group, User, WithProcess, ROOT_GROUP_NAME, }; pub(super) fn tempfile() -> std::io::Result { let timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .expect("Failed to get system time") .as_nanos(); let pid = std::process::id(); let filename = format!("sudo_rs_test_{}_{}", pid, timestamp); let path = std::path::PathBuf::from("/tmp").join(filename); std::fs::File::options() .read(true) .write(true) .create_new(true) .open(path) } #[test] fn test_get_user_and_group_by_id() { let fixed_users = &[ (UserId::ROOT, "root"), ( User::from_name(cstr!("daemon")).unwrap().unwrap().uid, "daemon", ), ]; for &(id, name) in fixed_users { let root = User::from_uid(id).unwrap().unwrap(); assert_eq!(root.uid, id); assert_eq!(root.name, name); } let fixed_groups = &[ (GroupId::new(0), ROOT_GROUP_NAME), ( Group::from_name(cstr!("daemon")).unwrap().unwrap().gid, "daemon", ), ]; for &(id, name) in fixed_groups { let root = Group::from_gid(id).unwrap().unwrap(); assert_eq!(root.gid, id); assert_eq!(root.name.unwrap(), name); } } #[test] fn miri_test_group_impl() { use super::Group; use std::ffi::CString; fn test(name: &str, passwd: &str, gid: libc::gid_t, mem: &[&str]) { assert_eq!( { let c_mem: Vec = mem.iter().map(|&s| CString::new(s).unwrap()).collect(); let c_name = CString::new(name).unwrap(); let c_passwd = CString::new(passwd).unwrap(); unsafe { Group::from_libc(&libc::group { gr_name: c_name.as_ptr() as *mut _, gr_passwd: c_passwd.as_ptr() as *mut _, gr_gid: gid, gr_mem: c_mem .iter() .map(|cs| cs.as_ptr() as *mut _) .chain(std::iter::once(std::ptr::null_mut())) .collect::>() .as_mut_ptr(), }) } }, Group { name: Some(name.to_string()), gid: GroupId::new(gid), } ) } test("dr. bill", "fidelio", 1999, &["eyes", "wide", "shut"]); test("eris", "fnord", 5, &[]); test("abc", "password123", 42, &[""]); } #[test] fn get_process_tty_device() { assert!(super::Process::tty_device_id(WithProcess::Current).is_ok()); } #[test] fn get_process_start_time() { let time = super::Process::starting_time(WithProcess::Current).unwrap(); let now = super::ProcessCreateTime::now().unwrap(); assert!(time.secs() > now.secs() - 24 * 60 * 60); assert!(time < now); } #[test] fn pgid_test() { use super::{getpgid, setpgid}; let pgrp = getpgrp(); assert_eq!(getpgid(ProcessId::new(0)).unwrap(), pgrp); assert_eq!( getpgid(ProcessId::new(std::process::id() as i32)).unwrap(), pgrp ); let child_pid = unsafe { super::fork_for_test(|| { // wait for the parent. std::thread::sleep(std::time::Duration::from_secs(1)); exit(0); }) }; // The child should be in our process group. assert_eq!( getpgid(child_pid).unwrap(), getpgid(ProcessId::new(0)).unwrap(), ); // Move the child to its own process group setpgid(child_pid, child_pid).unwrap(); // The process group of the child should have changed. assert_eq!(getpgid(child_pid).unwrap(), child_pid); } #[test] fn kill_test() { let mut child = std::process::Command::new("/bin/sleep") .arg("1") .spawn() .unwrap(); super::kill(ProcessId::new(child.id() as i32), SIGKILL).unwrap(); assert!(!child.wait().unwrap().success()); } #[test] fn killpg_test() { // Create a socket so the children write to it if they aren't terminated by `killpg`. let (mut rx, mut tx) = UnixStream::pair().unwrap(); let pid1 = unsafe { fork_for_test(|| { std::thread::sleep(std::time::Duration::from_secs(1)); tx.write_all(&[42]).unwrap(); exit(0); }) }; let pid2 = unsafe { fork_for_test(|| { std::thread::sleep(std::time::Duration::from_secs(1)); tx.write_all(&[42]).unwrap(); exit(0); }) }; drop(tx); let pgid = pid1; // Move the children to their own process group. setpgid(pid1, pgid).unwrap(); setpgid(pid2, pgid).unwrap(); // Send `SIGKILL` to the children process group. super::killpg(pgid, SIGKILL).unwrap(); // Ensure that the child were terminated before writing. assert_eq!( rx.read_exact(&mut [0; 2]).unwrap_err().kind(), std::io::ErrorKind::UnexpectedEof ); } fn is_closed(fd: &F) -> bool { crate::cutils::cerr(unsafe { libc::fcntl(fd.as_fd().as_raw_fd(), libc::F_GETFD) }) .is_err_and(|err| err.raw_os_error() == Some(libc::EBADF)) } #[test] fn close_the_universe() { let child_pid = unsafe { fork_for_test(|| { let should_close = std::fs::File::create(std::env::temp_dir().join("should_close.txt")).unwrap(); assert!(!is_closed(&should_close)); let should_not_close = std::fs::File::create(std::env::temp_dir().join("should_not_close.txt")) .unwrap(); assert!(!is_closed(&should_not_close)); let mut closer = super::FileCloser::new(); closer.except(&should_not_close); closer.close_the_universe().unwrap(); assert!(is_closed(&should_close)); assert!(!is_closed(&io::stdin())); assert!(!is_closed(&io::stdout())); assert!(!is_closed(&io::stderr())); assert!(!is_closed(&should_not_close)); exit(0) }) }; let (_, status) = child_pid.wait(WaitOptions::new()).unwrap(); assert_eq!(status.exit_status(), Some(0)); } #[test] fn except_stdio_is_fine() { let child_pid = unsafe { fork_for_test(|| { let mut closer = super::FileCloser::new(); closer.except(&io::stdin()); closer.except(&io::stdout()); closer.except(&io::stderr()); closer.close_the_universe().unwrap(); assert!(!is_closed(&io::stdin())); assert!(!is_closed(&io::stdout())); assert!(!is_closed(&io::stderr())); exit(0) }) }; let (_, status) = child_pid.wait(WaitOptions::new()).unwrap(); assert_eq!(status.exit_status(), Some(0)); } #[cfg(target_os = "linux")] #[test] fn proc_stat_test() { use super::{read_proc_stat, Process, WithProcess::Current}; // The process can be '(uninterruptible) sleeping' or 'running': it looks like the state // field of /proc/pid/stat will show the state for the main thread of the process rather // than for the process as a whole. let state = read_proc_stat::(Current, 2).unwrap(); assert!("SDR".contains(state), "{state} is not S, D or R"); let parent = Process::parent_id().unwrap(); // field 3 is always the parent process assert_eq!( parent, ProcessId::new(read_proc_stat::(Current, 3).unwrap()) ); // this next field should always be 0 (which precedes an important bit of info for us!) assert_eq!(0, read_proc_stat::(Current, 20).unwrap()); } } sudo-rs-0.2.5/src/system/signal/handler.rs000064400000000000000000000040011046102023000165720ustar 00000000000000use std::io; use crate::log::dev_warn; use super::{consts::*, set::SignalAction, signal_name, SignalNumber}; /// A handler for a signal. /// /// When a value of this type is dropped, it will try to restore the action that was registered for /// the signal prior to calling [`SignalHandler::register`]. pub(crate) struct SignalHandler { signal: SignalNumber, original_action: SignalAction, } impl SignalHandler { const FORBIDDEN: &'static [SignalNumber] = &[SIGKILL, SIGSTOP]; /// Register a new handler for the given signal with the provided behavior. /// /// # Panics /// /// If it is not possible to override the action for the provided signal. pub(crate) fn register( signal: SignalNumber, behavior: SignalHandlerBehavior, ) -> io::Result { if Self::FORBIDDEN.contains(&signal) { panic!( "the {} signal action cannot be overriden", signal_name(signal) ); } let action = SignalAction::new(behavior)?; let original_action = action.register(signal)?; Ok(Self { signal, original_action, }) } /// Forget this signal handler. /// /// This can be used to avoid restoring the original action for the signal. pub(crate) fn forget(self) { std::mem::forget(self) } } impl Drop for SignalHandler { #[track_caller] fn drop(&mut self) { let signal = self.signal; if let Err(err) = self.original_action.register(signal) { dev_warn!( "cannot restore original action for {}: {err}", signal_name(signal), ) } } } /// The possible behaviors for a [`SignalHandler`]. pub(crate) enum SignalHandlerBehavior { /// Execute the default action for the signal. Default, /// Ignore the arrival of the signal. Ignore, /// Stream the signal information into the latest initialized instance of [`super::SignalStream`]. Stream, } sudo-rs-0.2.5/src/system/signal/info.rs000064400000000000000000000026661046102023000161270ustar 00000000000000use std::fmt; use crate::system::interface::ProcessId; use super::SignalNumber; /// Information related to the arrival of a signal. #[repr(transparent)] pub(crate) struct SignalInfo { info: libc::siginfo_t, } impl SignalInfo { pub(super) const SIZE: usize = std::mem::size_of::(); /// Returns whether the signal was sent by the user or not. fn is_user_signaled(&self) -> bool { // This matches the definition of the SI_FROMUSER macro. self.info.si_code <= 0 } /// Gets the PID that sent the signal. pub(crate) fn signaler_pid(&self) -> Option { if self.is_user_signaled() { // SAFETY: si_pid is always initialized if the signal is user signaled. let id = unsafe { self.info.si_pid() }; Some(ProcessId::new(id)) } else { None } } /// Gets the signal number. pub(crate) fn signal(&self) -> SignalNumber { self.info.si_signo } } impl fmt::Display for SignalInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{} {} from ", if self.is_user_signaled() { " user signaled" } else { "" }, self.signal(), )?; if let Some(pid) = self.signaler_pid() { write!(f, "{pid}") } else { write!(f, "") } } } sudo-rs-0.2.5/src/system/signal/mod.rs000064400000000000000000000016661046102023000157520ustar 00000000000000//! Utilities to handle signals. mod handler; mod info; mod set; mod stream; pub(crate) use handler::{SignalHandler, SignalHandlerBehavior}; pub(crate) use set::SignalSet; pub(crate) use stream::{register_handlers, SignalStream}; use std::borrow::Cow; pub(crate) type SignalNumber = libc::c_int; macro_rules! define_consts { ($($signal:ident,)*) => { pub(crate) mod consts { pub(crate) use libc::{$($signal,)*}; } pub(crate) fn signal_name(signal: SignalNumber) -> Cow<'static, str> { match signal { $(consts::$signal => stringify!($signal).into(),)* _ => format!("unknown signal ({signal})").into(), } } }; } define_consts! { SIGINT, SIGQUIT, SIGTSTP, SIGTERM, SIGHUP, SIGALRM, SIGPIPE, SIGUSR1, SIGUSR2, SIGCHLD, SIGCONT, SIGWINCH, SIGTTIN, SIGTTOU, SIGKILL, SIGSTOP, } sudo-rs-0.2.5/src/system/signal/set.rs000064400000000000000000000075421046102023000157650ustar 00000000000000use crate::{cutils::cerr, system::make_zeroed_sigaction}; use super::{handler::SignalHandlerBehavior, SignalNumber}; use std::{io, mem::MaybeUninit}; #[repr(transparent)] pub(super) struct SignalAction { raw: libc::sigaction, } impl SignalAction { pub(super) fn new(behavior: SignalHandlerBehavior) -> io::Result { // This guarantees that functions won't be interrupted by this signal as long as the // handler is alive. let mut sa_flags = libc::SA_RESTART; // We only need a full `sa_mask` if we are going to stream the signal information as we // don't want to be interrupted by any signals while executing `send_siginfo`. let (sa_sigaction, sa_mask) = match behavior { SignalHandlerBehavior::Default => (libc::SIG_DFL, SignalSet::empty()?), SignalHandlerBehavior::Ignore => (libc::SIG_IGN, SignalSet::empty()?), SignalHandlerBehavior::Stream => { // Specify that we want to pass a signal-catching function in `sa_sigaction`. sa_flags |= libc::SA_SIGINFO; ( super::stream::send_siginfo as libc::sighandler_t, SignalSet::full()?, ) } }; let mut raw: libc::sigaction = make_zeroed_sigaction(); raw.sa_sigaction = sa_sigaction; raw.sa_mask = sa_mask.raw; raw.sa_flags = sa_flags; Ok(Self { raw }) } pub(super) fn register(&self, signal: SignalNumber) -> io::Result { let mut original_action = MaybeUninit::::zeroed(); // SAFETY: `sigaction` expects a valid pointer, which we provide; the typecast is valid // since SignalAction is a repr(transparent) newtype struct. cerr(unsafe { libc::sigaction(signal, &self.raw, original_action.as_mut_ptr().cast()) })?; // SAFETY: `sigaction` will have properly initialized `original_action`. Ok(unsafe { original_action.assume_init() }) } } // A signal set that can be used to mask signals. #[repr(transparent)] pub(crate) struct SignalSet { raw: libc::sigset_t, } impl SignalSet { /// Create an empty set. pub(crate) fn empty() -> io::Result { let mut set = MaybeUninit::::zeroed(); // SAFETY: same as above cerr(unsafe { libc::sigemptyset(set.as_mut_ptr().cast()) })?; // SAFETY: `sigemptyset` will have initialized `set` Ok(unsafe { set.assume_init() }) } /// Create a set containing all the signals. pub(crate) fn full() -> io::Result { let mut set = MaybeUninit::::zeroed(); // SAFETY: same as above cerr(unsafe { libc::sigfillset(set.as_mut_ptr().cast()) })?; // SAFETY: `sigfillset` will have initialized `set` Ok(unsafe { set.assume_init() }) } fn sigprocmask(&self, how: libc::c_int) -> io::Result { let mut original_set = MaybeUninit::::zeroed(); // SAFETY: same as above cerr(unsafe { libc::sigprocmask(how, &self.raw, original_set.as_mut_ptr().cast()) })?; // SAFETY: `sigprocmask` will have initialized `set` Ok(unsafe { original_set.assume_init() }) } /// Block all the signals in this set and return the previous set of blocked signals. /// /// After calling this function successfully, the set of blocked signals will be the union of /// the previous set of blocked signals and this set. pub(crate) fn block(&self) -> io::Result { self.sigprocmask(libc::SIG_BLOCK) } /// Block only the signals that are in this set and return the previous set of blocked signals. /// /// After calling this function successfully, the set of blocked signals will be the exactly /// this set. pub(crate) fn set_mask(&self) -> io::Result { self.sigprocmask(libc::SIG_SETMASK) } } sudo-rs-0.2.5/src/system/signal/stream.rs000064400000000000000000000072111046102023000164560ustar 00000000000000use std::{ io, mem::MaybeUninit, os::{ fd::{AsFd, AsRawFd, BorrowedFd}, unix::net::UnixStream, }, sync::OnceLock, }; use crate::{cutils::cerr, log::dev_error}; use super::{ handler::{SignalHandler, SignalHandlerBehavior}, info::SignalInfo, signal_name, SignalNumber, }; static STREAM: OnceLock = OnceLock::new(); /// # Safety /// /// The `info` parameters has to point to a valid instance of SignalInfo pub(super) unsafe fn send_siginfo( _signal: SignalNumber, info: *const SignalInfo, _context: *const libc::c_void, ) { if let Some(tx) = STREAM.get().map(|stream| stream.tx.as_raw_fd()) { // SAFETY: called ensures that info is a valid pointer; any instance of SignalInfo will // consists of SignalInfo::SIZE bytes unsafe { libc::send(tx, info.cast(), SignalInfo::SIZE, libc::MSG_DONTWAIT) }; } } /// A type able to receive signal information from any [`super::SignalHandler`] with the /// [`super::SignalHandlerBehavior::Stream`] behavior. /// /// This is a singleton type. Meaning that there will be only one value of this type during the /// execution of a program. pub(crate) struct SignalStream { rx: UnixStream, tx: UnixStream, } impl SignalStream { /// Create a new [`SignalStream`]. /// /// # Panics /// /// If this function has been called before. #[track_caller] pub(crate) fn init() -> io::Result<&'static Self> { let (rx, tx) = UnixStream::pair().map_err(|err| { dev_error!("cannot create socket pair for `SignalStream`: {err}"); err })?; if STREAM.set(Self { rx, tx }).is_err() { panic!("`SignalStream` has already been initialized"); }; Ok(STREAM.get().unwrap()) } /// Receives the information related to the arrival of a signal. pub(crate) fn recv(&self) -> io::Result { let mut info = MaybeUninit::::uninit(); let fd = self.rx.as_raw_fd(); // SAFETY: type invariant for `SignalStream` ensures that `fd` is a valid file descriptor; // furthermore, `info` is a valid pointer to `siginfo_t` (by virtue of `SignalInfo` being a // transparent newtype for it), which has room for `SignalInfo::SIZE` bytes. let bytes = cerr(unsafe { libc::recv(fd, info.as_mut_ptr().cast(), SignalInfo::SIZE, 0) })?; if bytes as usize != SignalInfo::SIZE { return Err(io::Error::new( io::ErrorKind::UnexpectedEof, "Not enough bytes when receiving `siginfo_t`", )); } // SAFETY: we can assume `info` is initialized because `recv` wrote enough bytes to fill // the value and `siginfo_t` is POD. Ok(unsafe { info.assume_init() }) } } #[track_caller] pub(crate) fn register_handlers( signals: [SignalNumber; N], ) -> io::Result<[SignalHandler; N]> { let mut handlers = signals.map(|signal| (signal, MaybeUninit::uninit())); for (signal, handler) in &mut handlers { *handler = SignalHandler::register(*signal, SignalHandlerBehavior::Stream) .map(MaybeUninit::new) .map_err(|err| { let name = signal_name(*signal); dev_error!("cannot setup handler for {name}: {err}"); err })?; } // SAFETY: if the above for-loop has terminated, every handler will have // been written to via "MaybeUnit::new", and so is initialized. Ok(handlers.map(|(_, handler)| unsafe { handler.assume_init() })) } impl AsFd for SignalStream { fn as_fd(&self) -> BorrowedFd { self.rx.as_fd() } } sudo-rs-0.2.5/src/system/term/mod.rs000064400000000000000000000224021046102023000154330ustar 00000000000000mod user_term; use std::{ ffi::{c_uchar, CString, OsString}, fmt, fs::File, io, os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd}, ptr::null_mut, }; use libc::{ioctl, winsize, TIOCSWINSZ}; use crate::cutils::{cerr, os_string_from_ptr, safe_isatty}; use super::interface::ProcessId; pub(crate) use user_term::UserTerm; pub(crate) struct Pty { /// The file path of the leader side of the pty. pub(crate) path: CString, /// The leader side of the pty. pub(crate) leader: PtyLeader, /// The follower side of the pty. pub(crate) follower: PtyFollower, } impl Pty { pub(crate) fn open() -> io::Result { const PATH_MAX: usize = libc::PATH_MAX as _; // Allocate a buffer to hold the path to the pty. let mut path = vec![0 as c_uchar; PATH_MAX]; // Create two integers to hold the file descriptors for each side of the pty. let (mut leader, mut follower) = (0, 0); // SAFETY: // - openpty is passed two valid pointers as its first two arguments // - path is a valid array that can hold PATH_MAX characters; and casting `u8` to `i8` is // valid since all values are initialized to zero. // - the last two arguments are allowed to be NULL cerr(unsafe { libc::openpty( &mut leader, &mut follower, path.as_mut_ptr().cast(), null_mut::(), null_mut::(), ) })?; // Get the index of the first null byte and truncate `path` so it doesn't have any null // bytes. If there are no null bytes the path is left as it is. if let Some(index) = path .iter() .enumerate() .find_map(|(index, &byte)| (byte == 0).then_some(index)) { path.truncate(index); } // This will not panic because `path` was truncated to not have any null bytes. let path = CString::new(path).unwrap(); Ok(Self { path, leader: PtyLeader { // SAFETY: `openpty` has set `leader` to an open fd suitable for assuming ownership by `OwnedFd`. file: unsafe { OwnedFd::from_raw_fd(leader) }.into(), }, follower: PtyFollower { // SAFETY: `openpty` has set `follower` to an open fd suitable for assuming ownership by `OwnedFd`. file: unsafe { OwnedFd::from_raw_fd(follower) }.into(), }, }) } } pub(crate) struct PtyLeader { file: File, } impl PtyLeader { pub(crate) fn set_size(&self, term_size: &TermSize) -> io::Result<()> { // SAFETY: the TIOCSWINSZ expects an initialized pointer of type `winsize` // https://www.man7.org/linux/man-pages/man2/TIOCSWINSZ.2const.html // // An object of type TermSize is safe to cast to `winsize` since it is a // repr(transparent) "newtype" struct. cerr(unsafe { ioctl( self.file.as_raw_fd(), TIOCSWINSZ, (term_size as *const TermSize).cast::(), ) })?; Ok(()) } } impl io::Read for PtyLeader { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.file.read(buf) } } impl io::Write for PtyLeader { fn write(&mut self, buf: &[u8]) -> io::Result { self.file.write(buf) } fn flush(&mut self) -> io::Result<()> { self.file.flush() } } impl AsFd for PtyLeader { fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> { self.file.as_fd() } } pub(crate) struct PtyFollower { file: File, } impl PtyFollower { pub(crate) fn try_clone(&self) -> io::Result { self.file.try_clone().map(|file| Self { file }) } } impl AsFd for PtyFollower { fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> { self.file.as_fd() } } impl From for std::process::Stdio { fn from(follower: PtyFollower) -> Self { follower.file.into() } } mod sealed { use std::os::fd::AsFd; pub(crate) trait Sealed {} impl Sealed for F {} } pub(crate) trait Terminal: sealed::Sealed { fn tcgetpgrp(&self) -> io::Result; fn tcsetpgrp(&self, pgrp: ProcessId) -> io::Result<()>; fn make_controlling_terminal(&self) -> io::Result<()>; fn ttyname(&self) -> io::Result; fn is_terminal(&self) -> bool; fn tcgetsid(&self) -> io::Result; } impl Terminal for F { /// Get the foreground process group ID associated with this terminal. fn tcgetpgrp(&self) -> io::Result { // SAFETY: tcgetpgrp cannot cause UB let id = cerr(unsafe { libc::tcgetpgrp(self.as_fd().as_raw_fd()) })?; Ok(ProcessId::new(id)) } /// Set the foreground process group ID associated with this terminal to `pgrp`. fn tcsetpgrp(&self, pgrp: ProcessId) -> io::Result<()> { // SAFETY: tcsetpgrp cannot cause UB cerr(unsafe { libc::tcsetpgrp(self.as_fd().as_raw_fd(), pgrp.inner()) }).map(|_| ()) } /// Make the given terminal the controlling terminal of the calling process. fn make_controlling_terminal(&self) -> io::Result<()> { // SAFETY: this is a correct way to call the TIOCSCTTY ioctl, see: // https://www.man7.org/linux/man-pages/man2/TIOCNOTTY.2const.html cerr(unsafe { libc::ioctl(self.as_fd().as_raw_fd(), libc::TIOCSCTTY, 0) })?; Ok(()) } /// Get the filename of the tty fn ttyname(&self) -> io::Result { let mut buf: [libc::c_char; 1024] = [0; 1024]; if !safe_isatty(self.as_fd()) { return Err(io::ErrorKind::Unsupported.into()); } // SAFETY: `buf` is a valid and initialized pointer, and its correct length is passed cerr(unsafe { libc::ttyname_r(self.as_fd().as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; // SAFETY: `buf` will have been initialized by the `ttyname_r` call, if it succeeded Ok(unsafe { os_string_from_ptr(buf.as_ptr()) }) } /// Rust standard library "IsTerminal" is not secure for setuid programs (CVE-2023-2002) fn is_terminal(&self) -> bool { safe_isatty(self.as_fd()) } fn tcgetsid(&self) -> io::Result { // SAFETY: tcgetsid cannot cause UB let id = cerr(unsafe { libc::tcgetsid(self.as_fd().as_raw_fd()) })?; Ok(ProcessId::new(id)) } } /// Try to get the path of the current TTY pub fn current_tty_name() -> io::Result { std::io::stdin().ttyname() } #[repr(transparent)] pub(crate) struct TermSize { raw: winsize, } impl PartialEq for TermSize { fn eq(&self, other: &Self) -> bool { self.raw.ws_col == other.raw.ws_col && self.raw.ws_row == other.raw.ws_row } } impl fmt::Display for TermSize { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} x {}", self.raw.ws_row, self.raw.ws_col) } } #[allow(clippy::undocumented_unsafe_blocks)] #[cfg(test)] mod tests { use std::{ ffi::OsString, io::{Read, Write}, os::unix::{net::UnixStream, prelude::OsStringExt}, path::PathBuf, process::exit, }; use crate::system::{fork_for_test, getpgid, setsid, term::*}; #[test] fn open_pty() { let pty = Pty::open().unwrap(); assert!(pty.leader.file.is_terminal()); assert!(pty.follower.file.is_terminal()); let path = PathBuf::from(OsString::from_vec(pty.path.into_bytes())); assert!(path.try_exists().unwrap()); assert!(path.starts_with("/dev/pts/")); } #[test] fn tcsetpgrp_and_tcgetpgrp_are_consistent() { // Create a socket so the child can send us a byte if successful. let (mut rx, mut tx) = UnixStream::pair().unwrap(); unsafe { fork_for_test(|| { // Open a new pseudoterminal. let leader = Pty::open().unwrap().leader; // On FreeBSD this returns an unspecified PID when there is no foreground process // group, so skip this check on FreeBSD. if cfg!(not(target_os = "freebsd")) { // The pty leader should not have a foreground process group yet. assert_eq!(leader.tcgetpgrp().unwrap().inner(), 0); } // Create a new session so we can change the controlling terminal. setsid().unwrap(); // Set the pty leader as the controlling terminal. leader.make_controlling_terminal().unwrap(); // Set us as the foreground process group of the pty leader. let pgid = getpgid(ProcessId::new(0)).unwrap(); leader.tcsetpgrp(pgid).unwrap(); // Check that we are in fact the foreground process group of the pty leader. assert_eq!(pgid, leader.tcgetpgrp().unwrap()); // If we haven't panicked yet, send a byte to the parent. tx.write_all(&[42]).unwrap(); exit(0); }) }; drop(tx); // Read one byte from the children to comfirm that it did not panic. let mut buf = [0]; rx.read_exact(&mut buf).unwrap(); assert_eq!(buf[0], 42); } } sudo-rs-0.2.5/src/system/term/user_term.rs000064400000000000000000000257201046102023000166670ustar 00000000000000//! This module is a port of ogsudo's `lib/util/term.c` with some minor changes to make it //! rust-like. use std::{ ffi::c_int, fs::{File, OpenOptions}, io::{self, Read, Write}, mem::MaybeUninit, os::fd::{AsFd, AsRawFd, BorrowedFd}, sync::atomic::{AtomicBool, Ordering}, }; use libc::{ c_void, cfgetispeed, cfgetospeed, cfmakeraw, cfsetispeed, cfsetospeed, ioctl, sigaction, sigemptyset, sighandler_t, siginfo_t, sigset_t, tcflag_t, tcgetattr, tcsetattr, termios, winsize, CS7, CS8, ECHO, ECHOCTL, ECHOE, ECHOK, ECHOKE, ECHONL, ICANON, ICRNL, IEXTEN, IGNCR, IGNPAR, IMAXBEL, INLCR, INPCK, ISIG, ISTRIP, IXANY, IXOFF, IXON, NOFLSH, OCRNL, ONLCR, ONLRET, ONOCR, OPOST, PARENB, PARMRK, PARODD, PENDIN, SIGTTOU, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, TOSTOP, }; #[cfg(target_os = "linux")] use libc::{IUTF8, OLCUC}; #[cfg(not(target_os = "linux"))] const IUTF8: libc::tcflag_t = 0; #[cfg(not(target_os = "linux"))] const OLCUC: libc::tcflag_t = 0; use super::{TermSize, Terminal}; use crate::{ cutils::cerr, system::{interface::ProcessId, make_zeroed_sigaction}, }; const INPUT_FLAGS: tcflag_t = IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL // | IUCLC /* FIXME: not in libc */ | IXON | IXANY | IXOFF | IMAXBEL | IUTF8; const OUTPUT_FLAGS: tcflag_t = OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET; const CONTROL_FLAGS: tcflag_t = CS7 | CS8 | PARENB | PARODD; const LOCAL_FLAGS: tcflag_t = ISIG | ICANON // | XCASE /* FIXME: not in libc */ | ECHO | ECHOE | ECHOK | ECHONL | NOFLSH | TOSTOP | IEXTEN | ECHOCTL | ECHOKE | PENDIN; static GOT_SIGTTOU: AtomicBool = AtomicBool::new(false); /// This is like `tcsetattr` but it only succeeds if we are in the foreground process group. /// # Safety /// /// The arguments to this function have to be valid arguments to `tcsetattr`. unsafe fn tcsetattr_nobg(fd: c_int, flags: c_int, tp: *const termios) -> io::Result<()> { // This function is based around the fact that we receive `SIGTTOU` if we call `tcsetattr` and // we are not in the foreground process group. // SAFETY: is the responsibility of the caller of `tcsetattr_nobg` let setattr = || cerr(unsafe { tcsetattr(fd, flags, tp) }).map(|_| ()); catching_sigttou(setattr) } fn catching_sigttou(mut function: impl FnMut() -> io::Result<()>) -> io::Result<()> { extern "C" fn on_sigttou(_signal: c_int, _info: *mut siginfo_t, _: *mut c_void) { GOT_SIGTTOU.store(true, Ordering::SeqCst); } let action = { let mut raw: libc::sigaction = make_zeroed_sigaction(); // Call `on_sigttou` if `SIGTTOU` arrives. raw.sa_sigaction = on_sigttou as sighandler_t; // Exclude any other signals from the set raw.sa_mask = { let mut sa_mask = MaybeUninit::::uninit(); // SAFETY: sa_mask is a valid and dereferenceble pointer; it will // become initialized by `sigemptyset` unsafe { sigemptyset(sa_mask.as_mut_ptr()); sa_mask.assume_init() } }; raw.sa_flags = 0; raw }; // Reset `GOT_SIGTTOU`. GOT_SIGTTOU.store(false, Ordering::SeqCst); // Set `action` as the action for `SIGTTOU` and store the original action in `original_action` // to restore it later. // // SAFETY: `original_action` is a valid pointer; second, the `action` installed (on_sigttou): // - is itself a safe function // - only updates an atomic variable, so cannot violate memory unsafety that way // - doesn't call any async-unsafe functions (refer to signal-safety(7)) // Therefore it can safely be installed as a signal handler. // Furthermore, `sigaction` will initialize `original_action`. let original_action = unsafe { let mut original_action = MaybeUninit::::uninit(); sigaction(SIGTTOU, &action, original_action.as_mut_ptr()); original_action.assume_init() }; // Call `tcsetattr` until it suceeds and ignore interruptions if we did not receive `SIGTTOU`. let result = loop { match function() { Ok(_) => break Ok(()), Err(err) => { let got_sigttou = GOT_SIGTTOU.load(Ordering::SeqCst); if got_sigttou || err.kind() != io::ErrorKind::Interrupted { break Err(err); } } } }; // Restore the original action. // // SAFETY: `original_action` is a valid pointer, and was initialized by the preceding // call to `sigaction` (and not subsequently altered, since it is not mut). The third parameter // is allowed to be NULL (this means we ignore the previously-installed handler) unsafe { sigaction(SIGTTOU, &original_action, std::ptr::null_mut()) }; result } /// Type to manipulate the settings of the user's terminal. pub struct UserTerm { tty: File, original_termios: Option, } impl UserTerm { /// Open the user's terminal. pub fn open() -> io::Result { Ok(Self { tty: OpenOptions::new().read(true).write(true).open("/dev/tty")?, original_termios: None, }) } pub(crate) fn get_size(&self) -> io::Result { let mut term_size = MaybeUninit::::uninit(); // SAFETY: This passes a valid file descriptor and valid pointer (of // the correct type) to the TIOCGWINSZ ioctl; see: // https://man7.org/linux/man-pages/man2/TIOCGWINSZ.2const.html cerr(unsafe { ioctl( self.tty.as_raw_fd(), TIOCGWINSZ, term_size.as_mut_ptr().cast::(), ) })?; // SAFETY: if we arrived at this point, `term_size` was initialized. Ok(unsafe { term_size.assume_init() }) } /// Copy the settings of the user's terminal to the `dst` terminal. pub fn copy_to(&self, dst: &D) -> io::Result<()> { let src = self.tty.as_raw_fd(); let dst = dst.as_fd().as_raw_fd(); // SAFETY: tt_src and tt_dst will be initialized by `tcgetattr`. let (tt_src, mut tt_dst) = unsafe { let mut tt_src = MaybeUninit::::uninit(); let mut tt_dst = MaybeUninit::::uninit(); cerr(tcgetattr(src, tt_src.as_mut_ptr()))?; cerr(tcgetattr(dst, tt_dst.as_mut_ptr()))?; (tt_src.assume_init(), tt_dst.assume_init()) }; // Clear select input, output, control and local flags. tt_dst.c_iflag &= !INPUT_FLAGS; tt_dst.c_oflag &= !OUTPUT_FLAGS; tt_dst.c_cflag &= !CONTROL_FLAGS; tt_dst.c_lflag &= !LOCAL_FLAGS; // Copy select input, output, control and local flags. tt_dst.c_iflag |= tt_src.c_iflag & INPUT_FLAGS; tt_dst.c_oflag |= tt_src.c_oflag & OUTPUT_FLAGS; tt_dst.c_cflag |= tt_src.c_cflag & CONTROL_FLAGS; tt_dst.c_lflag |= tt_src.c_lflag & LOCAL_FLAGS; // Copy special chars from src verbatim. tt_dst.c_cc.copy_from_slice(&tt_src.c_cc); // Copy speed from `src`. // // SAFETY: the cfXXXXspeed calls are passed valid pointers and // cannot cause UB even if the speed would be incorrect. unsafe { let mut speed = cfgetospeed(&tt_src); // Zero output speed closes the connection. if speed == libc::B0 { speed = libc::B38400; } cfsetospeed(&mut tt_dst, speed); speed = cfgetispeed(&tt_src); cfsetispeed(&mut tt_dst, speed); } // SAFETY: dst is a valid file descriptor and `tt_dst` is an // initialized struct obtained through tcgetattr; so this is safe to // pass to `tcsetattr`. unsafe { tcsetattr_nobg(dst, TCSAFLUSH, &tt_dst) }?; let mut wsize = MaybeUninit::::uninit(); // SAFETY: TIOCGWINSZ ioctl expects one argument of type *mut winsize cerr(unsafe { ioctl(src, TIOCGWINSZ, wsize.as_mut_ptr()) })?; // SAFETY: wsize has been initialized by the TIOCGWINSZ ioctl cerr(unsafe { ioctl(dst, TIOCSWINSZ, wsize.as_ptr()) })?; Ok(()) } /// Set the user's terminal to raw mode. Enable terminal signals if `with_signals` is set to /// `true`. pub fn set_raw_mode(&mut self, with_signals: bool) -> io::Result<()> { let fd = self.tty.as_raw_fd(); // Retrieve the original terminal (if we haven't done so already) let mut term = if let Some(termios) = self.original_termios { termios } else { // SAFETY: `termios` is a valid pointer to pass to tcgetattr; if that calls succeeds, // it will have initialized the `termios` structure *self.original_termios.insert(unsafe { let mut termios = MaybeUninit::uninit(); cerr(tcgetattr(fd, termios.as_mut_ptr()))?; termios.assume_init() }) }; // Set terminal to raw mode. // SAFETY: `term` is a valid, initialized struct of type `termios`, which // was previously obtained through `tcgetattr`. unsafe { cfmakeraw(&mut term) }; // Enable terminal signals. if with_signals { term.c_cflag |= ISIG; } // SAFETY: `fd` is a valid file descriptor for the tty; for `term`: same as above. unsafe { tcsetattr_nobg(fd, TCSADRAIN, &term) }?; Ok(()) } /// Restore the saved terminal settings if we are in the foreground process group. /// /// This change is done after waiting for all the queued output to be written. To discard the /// queued input `flush` must be set to `true`. pub fn restore(&mut self, flush: bool) -> io::Result<()> { if let Some(termios) = self.original_termios.take() { let fd = self.tty.as_raw_fd(); let flags = if flush { TCSAFLUSH } else { TCSADRAIN }; // SAFETY: `fd` is a valid file descriptor for the tty; and `termios` is a valid pointer // that was obtained through `tcgetattr`. unsafe { tcsetattr_nobg(fd, flags, &termios) }?; } Ok(()) } /// This is like `tcsetpgrp` but it only suceeds if we are in the foreground process group. pub fn tcsetpgrp_nobg(&self, pgrp: ProcessId) -> io::Result<()> { // This function is based around the fact that we receive `SIGTTOU` if we call `tcsetpgrp` and // we are not in the foreground process group. catching_sigttou(|| self.tty.tcsetpgrp(pgrp)) } } impl AsFd for UserTerm { fn as_fd(&self) -> BorrowedFd { self.tty.as_fd() } } impl Read for UserTerm { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.tty.read(buf) } } impl Write for UserTerm { fn write(&mut self, buf: &[u8]) -> io::Result { self.tty.write(buf) } fn flush(&mut self) -> io::Result<()> { self.tty.flush() } } sudo-rs-0.2.5/src/system/time.rs000064400000000000000000000156211046102023000146500ustar 00000000000000use std::{ io::{Read, Write}, mem::MaybeUninit, ops::{Add, Sub}, }; /// A timestamp relative to `CLOCK_BOOTTIME`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct SystemTime { secs: i64, nsecs: i64, } impl SystemTime { pub(super) fn new(secs: i64, nsecs: i64) -> SystemTime { SystemTime { secs: secs + nsecs.div_euclid(1_000_000_000), nsecs: nsecs.rem_euclid(1_000_000_000), } } pub fn now() -> std::io::Result { let mut spec = MaybeUninit::::uninit(); // SAFETY: valid pointer is passed to clock_gettime crate::cutils::cerr(unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, spec.as_mut_ptr()) })?; // SAFETY: The `libc::clock_gettime` will correctly initialize `spec`, // otherwise it will return early with the `?` operator. let spec = unsafe { spec.assume_init() }; Ok(spec.into()) } pub(super) fn encode(&self, target: &mut impl Write) -> std::io::Result<()> { let secs = self.secs.to_ne_bytes(); let nsecs = self.nsecs.to_ne_bytes(); target.write_all(&secs)?; target.write_all(&nsecs)?; Ok(()) } pub(super) fn decode(from: &mut impl Read) -> std::io::Result { let mut sec_bytes = [0; 8]; let mut nsec_bytes = [0; 8]; from.read_exact(&mut sec_bytes)?; from.read_exact(&mut nsec_bytes)?; Ok(SystemTime::new( i64::from_ne_bytes(sec_bytes), i64::from_ne_bytes(nsec_bytes), )) } } impl Sub for SystemTime { type Output = Duration; fn sub(self, rhs: SystemTime) -> Self::Output { Duration::new(self.secs - rhs.secs, self.nsecs - rhs.nsecs) } } impl Add for SystemTime { type Output = SystemTime; fn add(self, rhs: Duration) -> Self::Output { SystemTime::new(self.secs + rhs.secs, self.nsecs + rhs.nsecs) } } impl Sub for SystemTime { type Output = SystemTime; fn sub(self, rhs: Duration) -> Self::Output { SystemTime::new(self.secs - rhs.secs, self.nsecs - rhs.nsecs) } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub struct Duration { secs: i64, nsecs: i64, } impl Duration { pub fn new(secs: i64, nsecs: i64) -> Duration { Duration { secs: secs + nsecs.div_euclid(1_000_000_000), nsecs: nsecs.rem_euclid(1_000_000_000), } } pub fn seconds(secs: i64) -> Duration { Duration::new(secs, 0) } #[cfg(test)] pub fn minutes(minutes: i64) -> Duration { Duration::seconds(minutes * 60) } #[cfg(test)] pub fn milliseconds(ms: i64) -> Duration { let secs = ms / 1000; let ms = ms % 1000; Duration::new(secs, ms * 1_000_000) } } impl Add for Duration { type Output = Duration; fn add(self, rhs: Duration) -> Self::Output { Duration::new(self.secs + rhs.secs, self.nsecs + rhs.nsecs) } } impl Sub for Duration { type Output = Duration; fn sub(self, rhs: Duration) -> Self::Output { Duration::new(self.secs - rhs.secs, self.nsecs - rhs.nsecs) } } impl From for SystemTime { fn from(value: libc::timespec) -> Self { SystemTime::new(value.tv_sec as _, value.tv_nsec as _) } } /// A timestamp relative to `CLOCK_BOOTTIME` on Linux and relative to `CLOCK_REALTIME` on FreeBSD. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct ProcessCreateTime { secs: i64, nsecs: i64, } impl ProcessCreateTime { pub fn new(secs: i64, nsecs: i64) -> ProcessCreateTime { ProcessCreateTime { secs: secs + nsecs.div_euclid(1_000_000_000), nsecs: nsecs.rem_euclid(1_000_000_000), } } #[cfg(test)] pub(super) fn now() -> std::io::Result { let mut spec = MaybeUninit::::uninit(); // SAFETY: valid pointer is passed to clock_gettime crate::cutils::cerr(unsafe { libc::clock_gettime( if cfg!(target_os = "freebsd") { libc::CLOCK_REALTIME } else { libc::CLOCK_BOOTTIME }, spec.as_mut_ptr(), ) })?; // SAFETY: The `libc::clock_gettime` will correctly initialize `spec`, // otherwise it will return early with the `?` operator. let spec = unsafe { spec.assume_init() }; Ok(ProcessCreateTime::new(spec.tv_sec, spec.tv_nsec)) } #[cfg(test)] pub(super) fn secs(&self) -> i64 { self.secs } pub(super) fn encode(&self, target: &mut impl Write) -> std::io::Result<()> { let secs = self.secs.to_ne_bytes(); let nsecs = self.nsecs.to_ne_bytes(); target.write_all(&secs)?; target.write_all(&nsecs)?; Ok(()) } pub(super) fn decode(from: &mut impl Read) -> std::io::Result { let mut sec_bytes = [0; 8]; let mut nsec_bytes = [0; 8]; from.read_exact(&mut sec_bytes)?; from.read_exact(&mut nsec_bytes)?; Ok(ProcessCreateTime::new( i64::from_ne_bytes(sec_bytes), i64::from_ne_bytes(nsec_bytes), )) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_new_durations_and_times() { assert_eq!(Duration::new(1, 1_000_000_000), Duration::seconds(2)); assert_eq!( Duration::new(-2, 500_000_000), Duration::seconds(-1) + Duration::milliseconds(-500) ); assert_eq!(SystemTime::new(-1, 2_000_000_000), SystemTime::new(1, 0)); assert_eq!( SystemTime::new(2, -500_000_000), SystemTime::new(1, 500_000_000) ); } #[test] fn test_time_ops() { assert_eq!( Duration::seconds(2) + Duration::seconds(3), Duration::seconds(5) ); assert_eq!( Duration::seconds(3) - Duration::seconds(1), Duration::seconds(2) ); assert_eq!( Duration::seconds(-10) + Duration::seconds(-5), Duration::seconds(-15) ); assert_eq!( Duration::milliseconds(5555) + Duration::milliseconds(5555), Duration::seconds(11) + Duration::milliseconds(110) ); assert_eq!( Duration::milliseconds(-5555) + Duration::milliseconds(-1111), Duration::milliseconds(-6666) ); assert_eq!( Duration::seconds(10) - Duration::seconds(-5), Duration::seconds(15) ); assert_eq!( SystemTime::new(0, 0) + Duration::seconds(3), SystemTime::new(3, 0) ); assert_eq!( SystemTime::new(10, 0) - Duration::seconds(4), SystemTime::new(6, 0) ); } } sudo-rs-0.2.5/src/system/timestamp.rs000064400000000000000000000654031046102023000157200ustar 00000000000000use std::{ fs::File, io::{self, Cursor, Read, Seek, Write}, path::PathBuf, }; use crate::{ common::resolve::CurrentUser, log::{auth_info, auth_warn}, }; use super::{ audit::secure_open_cookie_file, file::FileLock, interface::{DeviceId, ProcessId, UserId}, time::{Duration, ProcessCreateTime, SystemTime}, Process, WithProcess, }; type BoolStorage = u8; const SIZE_OF_TS: i64 = std::mem::size_of::() as i64; const SIZE_OF_BOOL: i64 = std::mem::size_of::() as i64; const MOD_OFFSET: i64 = SIZE_OF_TS + SIZE_OF_BOOL; #[derive(Debug)] pub struct SessionRecordFile { file: File, timeout: Duration, for_user: UserId, } impl SessionRecordFile { const BASE_PATH: &'static str = "/var/run/sudo-rs/ts"; pub fn open_for_user(user: &CurrentUser, timeout: Duration) -> io::Result { let uid = user.uid; let mut path = PathBuf::from(Self::BASE_PATH); path.push(uid.to_string()); SessionRecordFile::new(uid, secure_open_cookie_file(&path)?, timeout) } const FILE_VERSION: u16 = 1; const MAGIC_NUM: u16 = 0x50D0; const VERSION_OFFSET: u64 = Self::MAGIC_NUM.to_le_bytes().len() as u64; const FIRST_RECORD_OFFSET: u64 = Self::VERSION_OFFSET + Self::FILE_VERSION.to_le_bytes().len() as u64; /// Create a new SessionRecordFile from the given i/o stream. /// Timestamps in this file are considered valid if they were created or /// updated at most `timeout` time ago. pub fn new(for_user: UserId, io: File, timeout: Duration) -> io::Result { let mut session_records = SessionRecordFile { file: io, timeout, for_user, }; // match the magic number, otherwise reset the file match session_records.read_magic()? { Some(magic) if magic == Self::MAGIC_NUM => (), x => { if let Some(_magic) = x { auth_info!("Session records file for user '{for_user}' is invalid, resetting"); } session_records.init(Self::VERSION_OFFSET)?; } } // match the file version match session_records.read_version()? { Some(v) if v == Self::FILE_VERSION => (), x => { if let Some(v) = x { auth_info!("Session records file for user '{for_user}' has invalid version {v}, only file version {} is supported, resetting", Self::FILE_VERSION); } else { auth_info!( "Session records file did not contain file version information, resetting" ); } session_records.init(Self::FIRST_RECORD_OFFSET)?; } } // we are ready to read records Ok(session_records) } /// Read the magic number from the input stream fn read_magic(&mut self) -> io::Result> { let mut magic_bytes = [0; std::mem::size_of::()]; match self.file.read_exact(&mut magic_bytes) { Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => Ok(None), Err(e) => Err(e), Ok(()) => Ok(Some(u16::from_le_bytes(magic_bytes))), } } /// Read the version number from the input stream fn read_version(&mut self) -> io::Result> { let mut version_bytes = [0; std::mem::size_of::()]; match self.file.read_exact(&mut version_bytes) { Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => Ok(None), Err(e) => Err(e), Ok(()) => Ok(Some(u16::from_le_bytes(version_bytes))), } } /// Initialize a new empty stream. If the stream/file was already filled /// before it will be truncated. fn init(&mut self, offset: u64) -> io::Result<()> { // lock the file to indicate that we are currently writing to it let lock = FileLock::exclusive(&self.file, false)?; self.file.set_len(0)?; self.file.rewind()?; self.file.write_all(&Self::MAGIC_NUM.to_le_bytes())?; self.file.write_all(&Self::FILE_VERSION.to_le_bytes())?; self.file.seek(io::SeekFrom::Start(offset))?; lock.unlock()?; Ok(()) } /// Read the next record and keep note of the start and end positions in the file of that record /// /// This method assumes that the file is already exclusively locked. fn next_record(&mut self) -> io::Result> { // record the position at which this record starts (including size bytes) let mut record_length_bytes = [0; std::mem::size_of::()]; let curr_pos = self.file.stream_position()?; // if eof occurs here we assume we reached the end of the file let record_length = match self.file.read_exact(&mut record_length_bytes) { Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None), Err(e) => return Err(e), Ok(()) => u16::from_le_bytes(record_length_bytes), }; // special case when record_length is zero if record_length == 0 { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Found empty record", )); } let mut buf = vec![0; record_length as usize]; match self.file.read_exact(&mut buf) { Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => { // there was half a record here, we clear the rest of the file auth_info!("Found incomplete record in session records file for {}, clearing rest of the file", self.for_user); self.file.set_len(curr_pos)?; return Ok(None); } Err(e) => return Err(e), Ok(()) => (), } // we now try and decode the data read into a session record match SessionRecord::from_bytes(&buf) { Err(_) => { // any error assumes that this file is nonsense from this point // onwards, so we clear the file up to the start of this record auth_info!("Found invalid record in session records file for {}, clearing rest of the file", self.for_user); self.file.set_len(curr_pos)?; Ok(None) } Ok(record) => Ok(Some(record)), } } /// Try and find a record for the given scope and auth user id and update /// that record time to the current time. This will not create a new record /// when one is not found. A record will only be updated if it is still /// valid at this time. pub fn touch(&mut self, scope: RecordScope, auth_user: UserId) -> io::Result { // lock the file to indicate that we are currently in a writing operation let lock = FileLock::exclusive(&self.file, false)?; self.seek_to_first_record()?; while let Some(record) = self.next_record()? { // only touch if record is enabled if record.enabled && record.matches(&scope, auth_user) { let now = SystemTime::now()?; if record.written_between(now - self.timeout, now) { // move back to where the timestamp is and overwrite with the latest time self.file.seek(io::SeekFrom::Current(-MOD_OFFSET))?; let new_time = SystemTime::now()?; new_time.encode(&mut self.file)?; // make sure we can still go to the end of the record self.file.seek(io::SeekFrom::Current(SIZE_OF_BOOL))?; // writing is done, unlock and return lock.unlock()?; return Ok(TouchResult::Updated { old_time: record.timestamp, new_time, }); } else { lock.unlock()?; return Ok(TouchResult::Outdated { time: record.timestamp, }); } } } lock.unlock()?; Ok(TouchResult::NotFound) } /// Disable all records that match the given scope. If an auth user id is /// given then only records with the given scope that are targetting that /// specific user will be disabled. pub fn disable(&mut self, scope: RecordScope, auth_user: Option) -> io::Result<()> { let lock = FileLock::exclusive(&self.file, false)?; self.seek_to_first_record()?; while let Some(record) = self.next_record()? { let must_disable = auth_user .map(|tu| record.matches(&scope, tu)) .unwrap_or_else(|| record.scope == scope); if must_disable { self.file.seek(io::SeekFrom::Current(-SIZE_OF_BOOL))?; write_bool(false, &mut self.file)?; } } lock.unlock()?; Ok(()) } /// Create a new record for the given scope and auth user id. /// If there is an existing record that matches the scope and auth user, /// then that record will be updated. pub fn create(&mut self, scope: RecordScope, auth_user: UserId) -> io::Result { // lock the file to indicate that we are currently writing to it let lock = FileLock::exclusive(&self.file, false)?; self.seek_to_first_record()?; while let Some(record) = self.next_record()? { if record.matches(&scope, auth_user) { self.file.seek(io::SeekFrom::Current(-MOD_OFFSET))?; let new_time = SystemTime::now()?; new_time.encode(&mut self.file)?; write_bool(true, &mut self.file)?; lock.unlock()?; return Ok(CreateResult::Updated { old_time: record.timestamp, new_time, }); } } // record was not found in the list so far, create a new one let record = SessionRecord::new(scope, auth_user)?; // make sure we really are at the end of the file self.file.seek(io::SeekFrom::End(0))?; self.write_record(&record)?; lock.unlock()?; Ok(CreateResult::Created { time: record.timestamp, }) } /// Completely resets the entire file and removes all records. pub fn reset(&mut self) -> io::Result<()> { self.init(0) } /// Write a new record at the current position in the file. fn write_record(&mut self, record: &SessionRecord) -> io::Result<()> { // convert the new record to byte representation and make sure that it fits let bytes = record.as_bytes()?; let record_length = bytes.len(); if record_length > u16::MAX as usize { return Err(io::Error::new( io::ErrorKind::InvalidInput, "A record with an unexpectedly large size was created", )); } let record_length = record_length as u16; // store as u16 // write the record self.file.write_all(&record_length.to_le_bytes())?; self.file.write_all(&bytes)?; Ok(()) } /// Move to where the first record starts. fn seek_to_first_record(&mut self) -> io::Result<()> { self.file .seek(io::SeekFrom::Start(Self::FIRST_RECORD_OFFSET))?; Ok(()) } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum TouchResult { /// The record was found and within the timeout, and it was refreshed Updated { old_time: SystemTime, new_time: SystemTime, }, /// A record was found, but it was no longer valid Outdated { time: SystemTime }, /// A record was not found that matches the input NotFound, } #[cfg_attr(not(test), allow(dead_code))] pub enum CreateResult { /// The record was found and it was refreshed Updated { old_time: SystemTime, new_time: SystemTime, }, /// A new record was created and was set to the time returned Created { time: SystemTime }, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RecordScope { Tty { tty_device: DeviceId, session_pid: ProcessId, init_time: ProcessCreateTime, }, Ppid { group_pid: ProcessId, init_time: ProcessCreateTime, }, } impl RecordScope { fn encode(&self, target: &mut impl Write) -> std::io::Result<()> { match self { RecordScope::Tty { tty_device, session_pid, init_time, } => { target.write_all(&[1u8])?; let b = tty_device.inner().to_le_bytes(); target.write_all(&b)?; let b = session_pid.inner().to_le_bytes(); target.write_all(&b)?; init_time.encode(target)?; } RecordScope::Ppid { group_pid, init_time, } => { target.write_all(&[2u8])?; let b = group_pid.inner().to_le_bytes(); target.write_all(&b)?; init_time.encode(target)?; } } Ok(()) } fn decode(from: &mut impl Read) -> std::io::Result { let mut buf = [0; 1]; from.read_exact(&mut buf)?; match buf[0] { 1 => { let mut buf = [0; std::mem::size_of::()]; from.read_exact(&mut buf)?; let tty_device = libc::dev_t::from_le_bytes(buf); let mut buf = [0; std::mem::size_of::()]; from.read_exact(&mut buf)?; let session_pid = libc::pid_t::from_le_bytes(buf); let init_time = ProcessCreateTime::decode(from)?; Ok(RecordScope::Tty { tty_device: DeviceId::new(tty_device), session_pid: ProcessId::new(session_pid), init_time, }) } 2 => { let mut buf = [0; std::mem::size_of::()]; from.read_exact(&mut buf)?; let group_pid = libc::pid_t::from_le_bytes(buf); let init_time = ProcessCreateTime::decode(from)?; Ok(RecordScope::Ppid { group_pid: ProcessId::new(group_pid), init_time, }) } x => Err(io::Error::new( io::ErrorKind::InvalidInput, format!("Unexpected scope variant discriminator: {x}"), )), } } /// Tries to determine a record match scope for the current context. /// This should never produce an error since any actual error should just be /// ignored and no session record file should be used in that case. pub fn for_process(process: &Process) -> Option { let tty = Process::tty_device_id(WithProcess::Current); if let Ok(Some(tty_device)) = tty { if let Ok(init_time) = Process::starting_time(WithProcess::Other(process.session_id)) { Some(RecordScope::Tty { tty_device, session_pid: process.session_id, init_time, }) } else { auth_warn!("Could not get terminal foreground process starting time"); None } } else if let Some(parent_pid) = process.parent_pid { if let Ok(init_time) = Process::starting_time(WithProcess::Other(parent_pid)) { Some(RecordScope::Ppid { group_pid: parent_pid, init_time, }) } else { auth_warn!("Could not get parent process starting time"); None } } else { None } } } fn write_bool(b: bool, target: &mut impl Write) -> io::Result<()> { let s: BoolStorage = if b { 0xFF } else { 0x00 }; let bytes = s.to_le_bytes(); target.write_all(&bytes)?; Ok(()) } /// A record in the session record file #[derive(Debug, PartialEq, Eq)] pub struct SessionRecord { /// The scope for which the current record applies, i.e. what process group /// or which TTY for interactive sessions scope: RecordScope, /// The user that needs to be authenticated against auth_user: UserId, /// The timestamp at which the time was created. This must always be a time /// originating from a monotonic clock that continues counting during system /// sleep. timestamp: SystemTime, /// Disabled records act as if they do not exist, but their storage can /// be re-used when recreating for the same scope and auth user enabled: bool, } impl SessionRecord { /// Create a new record that is scoped to the specified scope and has `auth_user` as /// the target for authentication for the session. fn new(scope: RecordScope, auth_user: UserId) -> io::Result { Ok(Self::init(scope, auth_user, true, SystemTime::now()?)) } /// Initialize a new record with the given parameters fn init( scope: RecordScope, auth_user: UserId, enabled: bool, timestamp: SystemTime, ) -> SessionRecord { SessionRecord { scope, auth_user, timestamp, enabled, } } /// Encode a record into the given stream fn encode(&self, target: &mut impl Write) -> std::io::Result<()> { self.scope.encode(target)?; // write user id let buf = self.auth_user.inner().to_le_bytes(); target.write_all(&buf)?; // write timestamp self.timestamp.encode(target)?; // write enabled boolean write_bool(self.enabled, target)?; Ok(()) } /// Decode a record from the given stream fn decode(from: &mut impl Read) -> std::io::Result { let scope = RecordScope::decode(from)?; // auth user id let mut buf = [0; std::mem::size_of::()]; from.read_exact(&mut buf)?; let auth_user = libc::uid_t::from_le_bytes(buf); let auth_user = UserId::new(auth_user); // timestamp let timestamp = SystemTime::decode(from)?; // enabled boolean let mut buf = [0; std::mem::size_of::()]; from.read_exact(&mut buf)?; let enabled = match BoolStorage::from_le_bytes(buf) { 0xFF => true, 0x00 => false, _ => { return Err(io::Error::new( io::ErrorKind::InvalidData, "Invalid boolean value detected in input stream", )) } }; Ok(SessionRecord::init(scope, auth_user, enabled, timestamp)) } /// Convert the record to a vector of bytes for storage. pub fn as_bytes(&self) -> std::io::Result> { let mut v = vec![]; self.encode(&mut v)?; Ok(v) } /// Convert the given byte slice to a session record, the byte slice must /// be fully consumed for this conversion to be valid. pub fn from_bytes(data: &[u8]) -> std::io::Result { let mut cursor = Cursor::new(data); let record = SessionRecord::decode(&mut cursor)?; if cursor.position() != data.len() as u64 { Err(io::Error::new( io::ErrorKind::InvalidInput, "Record size and record length did not match", )) } else { Ok(record) } } /// Returns true if this record matches the specified scope and is for the /// specified target auth user. pub fn matches(&self, scope: &RecordScope, auth_user: UserId) -> bool { self.scope == *scope && self.auth_user == auth_user } /// Returns true if this record was written somewhere in the time range /// between `early_time` (inclusive) and `later_time` (inclusive), where /// early timestamp may not be later than the later timestamp. pub fn written_between(&self, early_time: SystemTime, later_time: SystemTime) -> bool { early_time <= later_time && self.timestamp >= early_time && self.timestamp <= later_time } } #[cfg(test)] mod tests { use super::*; use crate::system::tests::tempfile; static TEST_USER_ID: UserId = UserId::ROOT; #[test] fn can_encode_and_decode() { let tty_sample = SessionRecord::new( RecordScope::Tty { tty_device: DeviceId::new(10), session_pid: ProcessId::new(42), init_time: ProcessCreateTime::new(1, 0), }, UserId::new(999), ) .unwrap(); let mut bytes = tty_sample.as_bytes().unwrap(); let decoded = SessionRecord::from_bytes(&bytes).unwrap(); assert_eq!(tty_sample, decoded); // we provide some invalid input assert!(SessionRecord::from_bytes(&bytes[1..]).is_err()); // we have remaining input after decoding bytes.push(0); assert!(SessionRecord::from_bytes(&bytes).is_err()); let ppid_sample = SessionRecord::new( RecordScope::Ppid { group_pid: ProcessId::new(42), init_time: ProcessCreateTime::new(151, 0), }, UserId::new(123), ) .unwrap(); let bytes = ppid_sample.as_bytes().unwrap(); let decoded = SessionRecord::from_bytes(&bytes).unwrap(); assert_eq!(ppid_sample, decoded); } #[test] fn timestamp_record_matches_works() { let init_time = ProcessCreateTime::new(1, 0); let scope = RecordScope::Tty { tty_device: DeviceId::new(12), session_pid: ProcessId::new(1234), init_time, }; let tty_sample = SessionRecord::new(scope, UserId::new(675)).unwrap(); assert!(tty_sample.matches(&scope, UserId::new(675))); assert!(!tty_sample.matches(&scope, UserId::new(789))); assert!(!tty_sample.matches( &RecordScope::Tty { tty_device: DeviceId::new(20), session_pid: ProcessId::new(1234), init_time }, UserId::new(675), )); assert!(!tty_sample.matches( &RecordScope::Ppid { group_pid: ProcessId::new(42), init_time }, UserId::new(675), )); // make sure time is different std::thread::sleep(std::time::Duration::from_millis(1)); assert!(!tty_sample.matches( &RecordScope::Tty { tty_device: DeviceId::new(12), session_pid: ProcessId::new(1234), init_time: ProcessCreateTime::new(1, 1) }, UserId::new(675), )); } #[test] fn timestamp_record_written_between_works() { let some_time = SystemTime::now().unwrap() + Duration::minutes(100); let scope = RecordScope::Tty { tty_device: DeviceId::new(12), session_pid: ProcessId::new(1234), init_time: ProcessCreateTime::new(0, 0), }; let sample = SessionRecord::init(scope, UserId::new(1234), true, some_time); let dur = Duration::seconds(30); assert!(sample.written_between(some_time, some_time)); assert!(sample.written_between(some_time, some_time + dur)); assert!(sample.written_between(some_time - dur, some_time)); assert!(!sample.written_between(some_time + dur, some_time - dur)); assert!(!sample.written_between(some_time + dur, some_time + dur + dur)); assert!(!sample.written_between(some_time - dur - dur, some_time - dur)); } fn tempfile_with_data(data: &[u8]) -> io::Result { let mut file = tempfile()?; file.write_all(data)?; file.rewind()?; Ok(file) } fn data_from_tempfile(mut f: File) -> io::Result> { let mut v = vec![]; f.rewind()?; f.read_to_end(&mut v)?; Ok(v) } #[test] fn session_record_file_header_checks() { // valid header should remain valid let c = tempfile_with_data(&[0xD0, 0x50, 0x01, 0x00]).unwrap(); let timeout = Duration::seconds(30); assert!(SessionRecordFile::new(TEST_USER_ID, c.try_clone().unwrap(), timeout).is_ok()); let v = data_from_tempfile(c).unwrap(); assert_eq!(&v[..], &[0xD0, 0x50, 0x01, 0x00]); // invalid headers should be corrected let c = tempfile_with_data(&[0xAB, 0xBA]).unwrap(); assert!(SessionRecordFile::new(TEST_USER_ID, c.try_clone().unwrap(), timeout).is_ok()); let v = data_from_tempfile(c).unwrap(); assert_eq!(&v[..], &[0xD0, 0x50, 0x01, 0x00]); // empty header should be filled in let c = tempfile_with_data(&[]).unwrap(); assert!(SessionRecordFile::new(TEST_USER_ID, c.try_clone().unwrap(), timeout).is_ok()); let v = data_from_tempfile(c).unwrap(); assert_eq!(&v[..], &[0xD0, 0x50, 0x01, 0x00]); // invalid version should reset file let c = tempfile_with_data(&[0xD0, 0x50, 0xAB, 0xBA, 0x0, 0x0]).unwrap(); assert!(SessionRecordFile::new(TEST_USER_ID, c.try_clone().unwrap(), timeout).is_ok()); let v = data_from_tempfile(c).unwrap(); assert_eq!(&v[..], &[0xD0, 0x50, 0x01, 0x00]); } #[test] fn can_create_and_update_valid_file() { let timeout = Duration::seconds(30); let c = tempfile_with_data(&[]).unwrap(); let mut srf = SessionRecordFile::new(TEST_USER_ID, c.try_clone().unwrap(), timeout).unwrap(); let tty_scope = RecordScope::Tty { tty_device: DeviceId::new(0), session_pid: ProcessId::new(0), init_time: ProcessCreateTime::new(0, 0), }; let auth_user = UserId::new(2424); let res = srf.create(tty_scope, auth_user).unwrap(); let CreateResult::Created { time } = res else { panic!("Expected record to be created"); }; std::thread::sleep(std::time::Duration::from_millis(1)); let second = srf.touch(tty_scope, auth_user).unwrap(); let TouchResult::Updated { old_time, new_time } = second else { panic!("Expected record to be updated"); }; assert_eq!(time, old_time); assert_ne!(old_time, new_time); std::thread::sleep(std::time::Duration::from_millis(1)); let res = srf.create(tty_scope, auth_user).unwrap(); let CreateResult::Updated { old_time, new_time } = res else { panic!("Expected record to be updated"); }; assert_ne!(old_time, new_time); // reset the file assert!(srf.reset().is_ok()); // after all this the data should be just an empty header let data = data_from_tempfile(c).unwrap(); assert_eq!(&data, &[0xD0, 0x50, 0x01, 0x00]); } } sudo-rs-0.2.5/src/system/wait.rs000064400000000000000000000171061046102023000146560ustar 00000000000000use std::io; #[cfg(target_os = "linux")] use libc::__WALL; use libc::{ c_int, WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WSTOPSIG, WTERMSIG, WUNTRACED, }; use crate::cutils::cerr; use crate::system::signal::signal_name; use crate::{system::interface::ProcessId, system::signal::SignalNumber}; #[cfg(not(target_os = "linux"))] const __WALL: c_int = 0; mod sealed { pub(crate) trait Sealed {} impl Sealed for crate::system::interface::ProcessId {} } pub(crate) trait Wait: sealed::Sealed { /// Wait for a process to change state. /// /// Calling this function will block until a child specified by the given process ID has /// changed state. This can be configured further using [`WaitOptions`]. fn wait(self, options: WaitOptions) -> Result<(ProcessId, WaitStatus), WaitError>; } impl Wait for ProcessId { fn wait(self, options: WaitOptions) -> Result<(ProcessId, WaitStatus), WaitError> { let mut status: c_int = 0; // SAFETY: a valid pointer is passed to `waitpid` let pid = cerr(unsafe { libc::waitpid(self.inner(), &mut status, options.flags) }) .map_err(WaitError::Io)?; if pid == 0 && options.flags & WNOHANG != 0 { return Err(WaitError::NotReady); } Ok((ProcessId::new(pid), WaitStatus { status })) } } /// Error values returned when [`Wait::wait`] fails. #[derive(Debug)] pub enum WaitError { // No children were in a waitable state. // // This is only returned if the [`WaitOptions::no_hang`] option is used. NotReady, // Regular I/O error. Io(io::Error), } /// Options to configure how [`Wait::wait`] waits for children. pub struct WaitOptions { flags: c_int, } impl WaitOptions { /// Only wait for terminated children. pub const fn new() -> Self { Self { flags: 0 } } /// Return immediately if no child has exited. pub const fn no_hang(mut self) -> Self { self.flags |= WNOHANG; self } /// Return immediately if a child has stopped. pub const fn untraced(mut self) -> Self { self.flags |= WUNTRACED; self } /// Wait for all children, regardless of being created using `clone` or not. pub const fn all(mut self) -> Self { self.flags |= __WALL; self } } /// The status of the waited child. pub struct WaitStatus { status: c_int, } impl std::fmt::Debug for WaitStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(exit_status) = self.exit_status() { write!(f, "ExitStatus({exit_status})") } else if let Some(signal) = self.term_signal() { write!(f, "TermSignal({})", signal_name(signal)) } else if let Some(signal) = self.stop_signal() { write!(f, "StopSignal({})", signal_name(signal)) } else if self.did_continue() { write!(f, "Continued") } else { write!(f, "Unknown") } } } impl WaitStatus { /// Return `true` if the child terminated normally, i.e., by calling `exit`. pub const fn did_exit(&self) -> bool { WIFEXITED(self.status) } /// Return the exit status of the child if the child terminated normally. pub const fn exit_status(&self) -> Option { if self.did_exit() { Some(WEXITSTATUS(self.status)) } else { None } } /// Return `true` if the child process was terminated by a signal. pub const fn was_signaled(&self) -> bool { WIFSIGNALED(self.status) } /// Return the signal number which caused the child to terminate if the child was terminated by /// a signal. pub const fn term_signal(&self) -> Option { if self.was_signaled() { Some(WTERMSIG(self.status)) } else { None } } /// Return `true` if the child process was stopped by a signal. pub const fn was_stopped(&self) -> bool { WIFSTOPPED(self.status) } /// Return the signal number which caused the child to stop if the child was stopped by a /// signal. pub const fn stop_signal(&self) -> Option { if self.was_stopped() { Some(WSTOPSIG(self.status)) } else { None } } /// Return `true` if the child process was resumed by receiving `SIGCONT`. pub const fn did_continue(&self) -> bool { WIFCONTINUED(self.status) } } #[cfg(test)] mod tests { use libc::{SIGKILL, SIGSTOP}; use crate::system::{ interface::ProcessId, kill, wait::{Wait, WaitError, WaitOptions}, }; #[test] fn exit_status() { #[allow(clippy::zombie_processes)] let command = std::process::Command::new("sh") .args(["-c", "sleep 0.1; exit 42"]) .spawn() .unwrap(); let command_pid = ProcessId::new(command.id() as i32); let (pid, status) = command_pid.wait(WaitOptions::new()).unwrap(); assert_eq!(command_pid, pid); assert!(status.did_exit()); assert_eq!(status.exit_status(), Some(42)); assert!(!status.was_signaled()); assert!(status.term_signal().is_none()); assert!(!status.was_stopped()); assert!(status.stop_signal().is_none()); assert!(!status.did_continue()); // Waiting when there are no children should fail. let WaitError::Io(err) = command_pid.wait(WaitOptions::new()).unwrap_err() else { panic!("`WaitError::NotReady` should not happens if `WaitOptions::no_hang` was not called."); }; assert_eq!(err.raw_os_error(), Some(libc::ECHILD)); } #[test] fn signals() { #[allow(clippy::zombie_processes)] let command = std::process::Command::new("sh") .args(["-c", "sleep 1; exit 42"]) .spawn() .unwrap(); let command_pid = ProcessId::new(command.id() as i32); kill(command_pid, SIGSTOP).unwrap(); let (pid, status) = command_pid.wait(WaitOptions::new().untraced()).unwrap(); assert_eq!(command_pid, pid); assert_eq!(status.stop_signal(), Some(SIGSTOP)); kill(command_pid, SIGKILL).unwrap(); let (pid, status) = command_pid.wait(WaitOptions::new()).unwrap(); assert_eq!(command_pid, pid); assert!(status.was_signaled()); assert_eq!(status.term_signal(), Some(SIGKILL)); assert!(!status.did_exit()); assert!(status.exit_status().is_none()); assert!(!status.was_stopped()); assert!(status.stop_signal().is_none()); assert!(!status.did_continue()); } #[test] fn no_hang() { #[allow(clippy::zombie_processes)] let command = std::process::Command::new("sh") .args(["-c", "sleep 0.1; exit 42"]) .spawn() .unwrap(); let command_pid = ProcessId::new(command.id() as i32); let mut count = 0; let (pid, status) = loop { match command_pid.wait(WaitOptions::new().no_hang()) { Ok(ok) => break ok, Err(WaitError::NotReady) => count += 1, Err(WaitError::Io(err)) => panic!("{err}"), } }; assert_eq!(command_pid, pid); assert!(status.did_exit()); assert_eq!(status.exit_status(), Some(42)); assert!(count > 0); assert!(!status.was_signaled()); assert!(status.term_signal().is_none()); assert!(!status.was_stopped()); assert!(status.stop_signal().is_none()); assert!(!status.did_continue()); } } sudo-rs-0.2.5/src/visudo/cli.rs000064400000000000000000000154761046102023000144560ustar 00000000000000#[derive(Debug, PartialEq)] pub(crate) struct VisudoOptions { pub(crate) file: Option, pub(crate) includes: bool, pub(crate) quiet: bool, pub(crate) strict: bool, pub(crate) owner: bool, pub(crate) perms: bool, pub(crate) action: VisudoAction, } impl Default for VisudoOptions { fn default() -> Self { Self { file: None, includes: true, quiet: false, strict: false, owner: false, perms: false, action: VisudoAction::Run, } } } #[derive(Debug, PartialEq)] pub(crate) enum VisudoAction { Help, Version, Check, Run, } type OptionSetter = fn(&mut VisudoOptions, Option) -> Result<(), String>; struct VisudoOption { short: char, long: &'static str, takes_argument: bool, set: OptionSetter, } impl VisudoOptions { const VISUDO_OPTIONS: &'static [VisudoOption] = &[ VisudoOption { short: 'c', long: "check", takes_argument: false, set: |options, _| { options.action = VisudoAction::Check; Ok(()) }, }, VisudoOption { short: 'f', long: "file", takes_argument: true, set: |options, argument| { options.file = Some(argument.ok_or("option requires an argument -- 'f'")?); Ok(()) }, }, VisudoOption { short: 'h', long: "help", takes_argument: false, set: |options, _| { options.action = VisudoAction::Help; Ok(()) }, }, VisudoOption { short: 'I', long: "no-includes", takes_argument: false, set: |options, _| { options.includes = true; Ok(()) }, }, VisudoOption { short: 'q', long: "quiet", takes_argument: false, set: |options, _| { options.quiet = true; Ok(()) }, }, VisudoOption { short: 's', long: "strict", takes_argument: false, set: |options, _| { options.strict = true; Ok(()) }, }, VisudoOption { short: 'V', long: "version", takes_argument: false, set: |options, _| { options.action = VisudoAction::Version; Ok(()) }, }, VisudoOption { short: 'O', long: "owner", takes_argument: false, set: |options, _| { options.owner = true; Ok(()) }, }, VisudoOption { short: 'P', long: "perms", takes_argument: false, set: |options, _| { options.perms = true; Ok(()) }, }, ]; pub(crate) fn from_env() -> Result { let args = std::env::args().collect(); Self::parse_arguments(args) } /// parse su arguments into VisudoOptions struct pub(crate) fn parse_arguments(arguments: Vec) -> Result { let mut options: VisudoOptions = VisudoOptions::default(); let mut arg_iter = arguments.into_iter().skip(1); while let Some(arg) = arg_iter.next() { // if the argument starts with -- it must be a full length option name if arg.starts_with("--") { // parse assignments like '--file=/etc/sudoers' if arg.contains('=') { // convert assignment to normal tokens let (key, value) = arg.split_once('=').unwrap(); // lookup the option by name if let Some(option) = Self::VISUDO_OPTIONS.iter().find(|o| o.long == &key[2..]) { // the value is already present, when the option does not take any arguments this results in an error if option.takes_argument { (option.set)(&mut options, Some(value.to_string()))?; } else { Err(format!("'--{}' does not take any arguments", option.long))?; } } else { Err(format!("unrecognized option '{}'", arg))?; } // lookup the option } else if let Some(option) = Self::VISUDO_OPTIONS.iter().find(|o| o.long == &arg[2..]) { // try to parse an argument when the option needs an argument if option.takes_argument { let next_arg = arg_iter.next(); (option.set)(&mut options, next_arg)?; } else { (option.set)(&mut options, None)?; } } else { Err(format!("unrecognized option '{}'", arg))?; } } else if arg.starts_with('-') { // flags can be grouped, so we loop over the characters for (n, char) in arg.trim_start_matches('-').chars().enumerate() { // lookup the option if let Some(option) = Self::VISUDO_OPTIONS.iter().find(|o| o.short == char) { // try to parse an argument when one is necessary, either the rest of the current flag group or the next argument if option.takes_argument { let rest = arg[(n + 2)..].trim().to_string(); let next_arg = if rest.is_empty() { arg_iter.next() } else { Some(rest) }; (option.set)(&mut options, next_arg)?; // stop looping over flags if the current flag takes an argument break; } else { // parse flag without argument (option.set)(&mut options, None)?; } } else { Err(format!("unrecognized option '{}'", char))?; } } } else { // If the arg doesn't start with a `-` it must be a file argument. However `-f` // must take precedence if options.file.is_none() { options.file = Some(arg); } } } Ok(options) } } sudo-rs-0.2.5/src/visudo/help.rs000064400000000000000000000012511046102023000146210ustar 00000000000000pub(crate) const USAGE_MSG: &str = "usage: visudo [-chqsV] [[-f] sudoers ]"; const DESCRIPTOR: &str = "visudo - safely edit the sudoers file"; const HELP_MSG: &str = "Options: -c, --check check-only mode -f, --file=sudoers specify sudoers file location -h, --help display help message and exit -I, --no-includes do not edit include files -q, --quiet less verbose (quiet) syntax error messages -s, --strict strict syntax checking -V, --version display version information and exit "; pub(crate) fn long_help_message() -> String { format!("{USAGE_MSG}\n\n{DESCRIPTOR}\n\n{HELP_MSG}") } sudo-rs-0.2.5/src/visudo/mod.rs000064400000000000000000000301401046102023000144470ustar 00000000000000#![forbid(unsafe_code)] mod cli; mod help; use std::{ env, ffi, fs::{File, Permissions}, io::{self, Read, Seek, Write}, os::unix::prelude::{MetadataExt, PermissionsExt}, path::{Path, PathBuf}, process::Command, str, }; use crate::{ common::resolve::CurrentUser, sudo::{candidate_sudoers_file, diagnostic}, sudoers::{self, Sudoers}, system::{ can_execute, file::{create_temporary_dir, Chown, FileLock}, interface::{GroupId, UserId}, signal::{consts::*, register_handlers, SignalStream}, Hostname, User, }, }; use self::cli::{VisudoAction, VisudoOptions}; use self::help::{long_help_message, USAGE_MSG}; const VERSION: &str = env!("CARGO_PKG_VERSION"); macro_rules! io_msg { ($err:expr, $($tt:tt)*) => { io::Error::new($err.kind(), format!("{}: {}", format_args!($($tt)*), $err)) }; } pub fn main() { if User::effective_uid() != User::real_uid() || User::effective_gid() != User::real_gid() { println_ignore_io_error!( "Visudo must not be installed as setuid binary.\n\ Please notify your packager about this misconfiguration.\n\ To prevent privilege escalation visudo will now abort. " ); std::process::exit(1); } let options = match VisudoOptions::from_env() { Ok(options) => options, Err(error) => { println_ignore_io_error!("visudo: {error}\n{USAGE_MSG}"); std::process::exit(1); } }; let cmd = match options.action { VisudoAction::Help => { println_ignore_io_error!("{}", long_help_message()); std::process::exit(0); } VisudoAction::Version => { println_ignore_io_error!("visudo version {VERSION}"); std::process::exit(0); } VisudoAction::Check => check, VisudoAction::Run => run, }; match cmd(options.file.as_deref(), options.perms, options.owner) { Ok(()) => {} Err(error) => { eprintln_ignore_io_error!("visudo: {error}"); std::process::exit(1); } } } fn check(file_arg: Option<&str>, perms: bool, owner: bool) -> io::Result<()> { let sudoers_path = file_arg .map(Path::new) .unwrap_or_else(|| candidate_sudoers_file()); let sudoers_file = File::open(sudoers_path) .map_err(|err| io_msg!(err, "unable to open {}", sudoers_path.display()))?; let metadata = sudoers_file.metadata()?; if file_arg.is_none() || perms { // For some reason, the MSB of the mode is on so we need to mask it. let mode = metadata.permissions().mode() & 0o777; if mode != 0o440 { return Err(io::Error::new( io::ErrorKind::Other, format!( "{}: bad permissions, should be mode 0440, but found {mode:04o}", sudoers_path.display() ), )); } } if file_arg.is_none() || owner { let owner = (metadata.uid(), metadata.gid()); if owner != (0, 0) { return Err(io::Error::new( io::ErrorKind::Other, format!( "{}: wrong owner (uid, gid) should be (0, 0), but found {owner:?}", sudoers_path.display() ), )); } } let (_sudoers, errors) = Sudoers::read(&sudoers_file, sudoers_path)?; if errors.is_empty() { writeln!(io::stdout(), "{}: parsed OK", sudoers_path.display())?; return Ok(()); } for crate::sudoers::Error { message, source, location, } in errors { let path = source.as_deref().unwrap_or(sudoers_path); diagnostic::diagnostic!("syntax error: {message}", path @ location); } Err(io::Error::new(io::ErrorKind::Other, "invalid sudoers file")) } fn run(file_arg: Option<&str>, perms: bool, owner: bool) -> io::Result<()> { let sudoers_path = file_arg .map(Path::new) .unwrap_or_else(|| candidate_sudoers_file()); let (sudoers_file, existed) = if sudoers_path.exists() { let file = File::options().read(true).write(true).open(sudoers_path)?; (file, true) } else { // Create a sudoers file if it doesn't exist. let file = File::create(sudoers_path)?; // ogvisudo sets the permissions of the file so it can be read and written by the user and // read by the group if the `-f` argument was passed. if file_arg.is_some() { file.set_permissions(Permissions::from_mode(0o640))?; } (file, false) }; let lock = FileLock::exclusive(&sudoers_file, true).map_err(|err| { if err.kind() == io::ErrorKind::WouldBlock { io_msg!(err, "{} busy, try again later", sudoers_path.display()) } else { err } })?; if perms || file_arg.is_none() { sudoers_file.set_permissions(Permissions::from_mode(0o440))?; } if owner || file_arg.is_none() { sudoers_file.chown(UserId::ROOT, GroupId::new(0))?; } let signal_stream = SignalStream::init()?; let handlers = register_handlers([SIGTERM, SIGHUP, SIGINT, SIGQUIT])?; let tmp_dir = create_temporary_dir()?; let tmp_path = tmp_dir.join("sudoers"); { let tmp_dir = tmp_dir.clone(); std::thread::spawn(|| -> io::Result<()> { signal_stream.recv()?; let _ = std::fs::remove_dir_all(tmp_dir); drop(handlers); std::process::exit(1) }); } let tmp_file = File::options() .read(true) .write(true) .create(true) .truncate(true) .open(&tmp_path)?; tmp_file.set_permissions(Permissions::from_mode(0o700))?; let result = edit_sudoers_file( existed, sudoers_file, sudoers_path, lock, tmp_file, &tmp_path, ); std::fs::remove_dir_all(tmp_dir)?; result } fn edit_sudoers_file( existed: bool, mut sudoers_file: File, sudoers_path: &Path, lock: FileLock, mut tmp_file: File, tmp_path: &Path, ) -> io::Result<()> { let mut stderr = io::stderr(); let mut editor_path = None; let mut sudoers_contents = Vec::new(); // Since visudo is meant to run as root, resolve shouldn't fail let current_user: User = match CurrentUser::resolve() { Ok(user) => user.into(), Err(err) => { writeln!(stderr, "visudo: cannot resolve : {err}")?; return Ok(()); } }; let host_name = Hostname::resolve(); if existed { // If the sudoers file existed, read its contents and write them into the temporary file. sudoers_file.read_to_end(&mut sudoers_contents)?; // Rewind the sudoers file so it can be written later. sudoers_file.rewind()?; // Write to the temporary file. tmp_file.write_all(&sudoers_contents)?; let (sudoers, errors) = Sudoers::read(sudoers_contents.as_slice(), sudoers_path)?; if errors.is_empty() { editor_path = sudoers.solve_editor_path(&host_name, ¤t_user, ¤t_user); } } let editor_path = match editor_path { Some(path) => path, None => editor_path_fallback()?, }; loop { Command::new(&editor_path) .arg("--") .arg(tmp_path) .spawn()? .wait_with_output()?; let (sudoers, errors) = File::open(tmp_path) .and_then(|reader| Sudoers::read(reader, tmp_path)) .map_err(|err| { io_msg!( err, "unable to re-open temporary file ({}), {} unchanged", tmp_path.display(), sudoers_path.display() ) })?; if !errors.is_empty() { writeln!(stderr, "The provided sudoers file format is not recognized or contains syntax errors. Please review:\n")?; for crate::sudoers::Error { message, source, location, } in errors { let path = source.as_deref().unwrap_or(sudoers_path); diagnostic::diagnostic!("syntax error: {message}", path @ location); } writeln!(stderr)?; match ask_response(b"What now? e(x)it without saving / (e)dit again: ", b"xe")? { b'x' => return Ok(()), _ => continue, } } else { if sudo_visudo_is_allowed(sudoers, &host_name) == Some(false) { writeln!( stderr, "It looks like you have removed your ability to run 'sudo visudo' again.\n" )?; match ask_response( b"What now? e(x)it without saving / (e)dit again / lock me out and (S)ave: ", b"xeS", )? { b'x' => return Ok(()), b'S' => {} _ => continue, } } break; } } let tmp_contents = std::fs::read(tmp_path)?; // Only write to the sudoers file if the contents changed. if tmp_contents == sudoers_contents { writeln!(stderr, "visudo: {} unchanged", tmp_path.display())?; } else { sudoers_file.write_all(&tmp_contents)?; let new_size = sudoers_file.stream_position()?; sudoers_file.set_len(new_size)?; } lock.unlock()?; Ok(()) } fn editor_path_fallback() -> io::Result { let editors = if cfg!(target_os = "linux") { &["/usr/bin/editor"][..] } else if cfg!(target_os = "freebsd") { &["/usr/bin/vi"] } else { unimplemented!("unknown default editor for target") }; for &editor in editors { let path = Path::new(editor); if can_execute(path) { return Ok(path.to_owned()); } } Err(io::Error::new( io::ErrorKind::NotFound, "cannot find text editor", )) } // To detect potential lock-outs if the user called "sudo visudo". // Note that SUDO_USER will normally be set by sudo. // // This returns Some(false) if visudo is forbidden under the given config; // Some(true) if it is allowed; and None if it cannot be determined, which // will be the case if e.g. visudo was simply run as root. fn sudo_visudo_is_allowed(mut sudoers: Sudoers, host_name: &Hostname) -> Option { let sudo_user = User::from_name(&ffi::CString::new(env::var("SUDO_USER").ok()?).ok()?).ok()??; let super_user = User::from_uid(UserId::ROOT).ok()??; let request = sudoers::Request { user: &super_user, group: &super_user.primary_group().ok()?, command: &env::current_exe().ok()?, arguments: &[], }; Some(matches!( sudoers .check(&sudo_user, host_name, request) .authorization(), sudoers::Authorization::Allowed { .. } )) } // Make sure that the first valid response is the "safest" choice fn ask_response(prompt: &[u8], valid_responses: &[u8]) -> io::Result { let stdin = io::stdin(); let stdout = io::stdout(); let mut stderr = io::stderr(); let mut stdin_handle = stdin.lock(); let mut stdout_handle = stdout.lock(); loop { stdout_handle.write_all(prompt)?; stdout_handle.flush()?; let mut input = [0u8]; if let Err(err) = stdin_handle.read_exact(&mut input) { writeln!(stderr, "visudo: cannot read user input: {err}")?; return Ok(valid_responses[0]); } // read the trailing newline loop { let mut skipped = [0u8]; match stdin_handle.read_exact(&mut skipped) { Ok(()) if &skipped != b"\n" => continue, _ => break, } } if valid_responses.contains(&input[0]) { return Ok(input[0]); } else { writeln!( stderr, "Invalid option: '{}'\n", str::from_utf8(&input).unwrap_or("") )?; } } } sudo-rs-0.2.5/util/Dockerfile-release000064400000000000000000000002611046102023000156100ustar 00000000000000FROM rust:1.84-slim-bookworm@sha256:69fbd6ab81b514580bc14f35323fecb09feba9e74c5944ece9a70d9a2a369df0 RUN apt-get update -y && apt-get install -y clang libclang-dev libpam0g-dev sudo-rs-0.2.5/util/build-release.sh000075500000000000000000000073661046102023000152710ustar 00000000000000#!/usr/bin/env bash SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) PROJECT_DIR=$(dirname "$SCRIPT_DIR") SUDO_RS_VERSION="$(cargo metadata --format-version 1 --manifest-path "$PROJECT_DIR/Cargo.toml" | jq '.packages[] | select(.name=="sudo-rs") | .version' -r)" BUILDER_IMAGE_TAG="sudo-rs-release-builder:latest" TARGET_DIR_BASE="$PROJECT_DIR/target/pkg" set -eo pipefail # Fetch the date from the changelog DATE=$(grep -m1 '^##' "$PROJECT_DIR"/CHANGELOG.md | grep -o '[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}') # Build binaries docker build --pull --tag "$BUILDER_IMAGE_TAG" --file "$SCRIPT_DIR/Dockerfile-release" "$SCRIPT_DIR" docker run --rm --user "$(id -u):$(id -g)" -v "$PROJECT_DIR:/build" -w "/build" "$BUILDER_IMAGE_TAG" cargo clean docker run --rm --user "$(id -u):$(id -g)" -v "$PROJECT_DIR:/build" -w "/build" "$BUILDER_IMAGE_TAG" cargo build --release --features pam-login # Generate man pages "$PROJECT_DIR/util/generate-docs.sh" # Set target directories and clear any previous builds target_dir_sudo="$TARGET_DIR_BASE/sudo" target_dir_su="$TARGET_DIR_BASE/su" target_sudo="$TARGET_DIR_BASE/sudo-$SUDO_RS_VERSION.tar.gz" target_su="$TARGET_DIR_BASE/su-$SUDO_RS_VERSION.tar.gz" rm -rf "$target_dir_sudo" rm -rf "$target_dir_su" rm -rf "$target_su" rm -rf "$target_sudo" # Show what is happening set -x # Build sudo umask u=rwx,g=rx,o=rx mkdir -p "$target_dir_sudo/bin" mkdir -p "$target_dir_sudo/share/man/man8" mkdir -p "$target_dir_sudo/share/man/man5" cp "$PROJECT_DIR/target/release/sudo" "$target_dir_sudo/bin/sudo" cp "$PROJECT_DIR/target/release/visudo" "$target_dir_sudo/bin/visudo" cp "$PROJECT_DIR/target/docs/man/sudo.8" "$target_dir_sudo/share/man/man8/sudo.8" cp "$PROJECT_DIR/target/docs/man/visudo.8" "$target_dir_sudo/share/man/man8/visudo.8" cp "$PROJECT_DIR/target/docs/man/sudoers.5" "$target_dir_sudo/share/man/man5/sudoers.5" mkdir -p "$target_dir_sudo/share/doc/sudo-rs/sudo" cp "$PROJECT_DIR/README.md" "$target_dir_sudo/share/doc/sudo-rs/sudo/README.md" cp "$PROJECT_DIR/CHANGELOG.md" "$target_dir_sudo/share/doc/sudo-rs/sudo/CHANGELOG.md" cp "$PROJECT_DIR/SECURITY.md" "$target_dir_sudo/share/doc/sudo-rs/sudo/SECURITY.md" cp "$PROJECT_DIR/COPYRIGHT" "$target_dir_sudo/share/doc/sudo-rs/sudo/COPYRIGHT" cp "$PROJECT_DIR/LICENSE-APACHE" "$target_dir_sudo/share/doc/sudo-rs/sudo/LICENSE-APACHE" cp "$PROJECT_DIR/LICENSE-MIT" "$target_dir_sudo/share/doc/sudo-rs/sudo/LICENSE-MIT" fakeroot -- bash < "$TARGET_DIR_BASE/SHA256SUMS") sudo-rs-0.2.5/util/generate-docs.sh000075500000000000000000000005741046102023000152660ustar 00000000000000#!/usr/bin/env bash docs_dir="docs/man" output_dir="target/docs/man" files=("sudo.8" "visudo.8" "sudoers.5" "su.1") mkdir -p "$output_dir" for f in "${files[@]}"; do origin_file="$docs_dir/$f.md" target_file="$output_dir/$f" echo "Generating man page for $f from '$origin_file' to '$target_file'" util/pandoc.sh -s -t man "$origin_file" -o "$target_file" done sudo-rs-0.2.5/util/pandoc.sh000075500000000000000000000002611046102023000140030ustar 00000000000000#!/usr/bin/env bash exec docker run --rm -i -v "$(pwd):/data" -u "$(id -u):$(id -g)" "pandoc/core@sha256:668f5ced9d99ed0fd8b0efda93d6cead066565bb400fc1fb165e77ddbb586a16" "$@" sudo-rs-0.2.5/util/update-version.sh000075500000000000000000000027161046102023000155130ustar 00000000000000#!/usr/bin/env bash SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) PROJECT_DIR=$(dirname "$SCRIPT_DIR") # Fetch current version CURRENT_VERSION=$(sed -n '/^version\s*=\s*"\([0-9.]*\)"/{s//\1/p;q}' "$PROJECT_DIR/Cargo.toml") # Fetch new version from changelog NEW_VERSION=$(grep -m1 '^##' "$PROJECT_DIR"/CHANGELOG.md | grep -o "\[[0-9]\+.[0-9]\+.[0-9]\+\]" | tr -d '[]') if [ -z "$NEW_VERSION" ]; then echo "Could not fetch version from CHANGELOG.md; you probably made a mistake." exit 1 fi if [ "$CURRENT_VERSION" \> "$NEW_VERSION" ]; then echo "New version number must be higher than current version: $CURRENT_VERSION" echo "Create a new changelog entry before running this script!" exit 1 fi if [ "$CURRENT_VERSION" == "$NEW_VERSION" ]; then echo "Cargo.toml was already at $NEW_VERSION" else echo "Updating version in Cargo.toml to $NEW_VERSION" sed -i 's/^version\s*=\s*".*"/version = "'"$NEW_VERSION"'"/' "$PROJECT_DIR/Cargo.toml" fi echo "Updating version in man pages" sed -i 's/^title: SU(1) sudo-rs .*/title: SU(1) sudo-rs '"$NEW_VERSION"' | sudo-rs/' "$PROJECT_DIR"/docs/man/su.1.md sed -i 's/^title: SUDO(8) sudo-rs .*/title: SUDO(8) sudo-rs '"$NEW_VERSION"' | sudo-rs/' "$PROJECT_DIR"/docs/man/sudo.8.md sed -i 's/^title: VISUDO(8) sudo-rs .*/title: VISUDO(8) sudo-rs '"$NEW_VERSION"' | sudo-rs/' "$PROJECT_DIR"/docs/man/visudo.8.md echo "Rebuilding project" (cd $PROJECT_DIR && cargo build --release)