pax_global_header00006660000000000000000000000064147412244360014521gustar00rootroot0000000000000052 comment=d627b0d1e1108563658dabe3fb8d2a065e64df10 polkit-126/000077500000000000000000000000001474122443600126755ustar00rootroot00000000000000polkit-126/.dir-locals.el000066400000000000000000000000511474122443600153220ustar00rootroot00000000000000((c-mode . ((indent-tabs-mode . nil))))) polkit-126/.editorconfig000066400000000000000000000011101474122443600153430ustar00rootroot00000000000000# EditorConfig configuration for polkit # http://EditorConfig.org # NOTE: If you update this file make sure to update .dir-locals.el and .vimrc, # too. # Top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file, utf-8 charset [*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 [NEWS] indent_style = space indent_size = 2 [*.{c,h}] indent_style = space indent_size = 2 max_line_length = 109 [meson.build] indent_style = space indent_size = 2 [*.{html,xml}] indent_size = 2 indent_style = space polkit-126/.github/000077500000000000000000000000001474122443600142355ustar00rootroot00000000000000polkit-126/.github/ISSUE_TEMPLATE/000077500000000000000000000000001474122443600164205ustar00rootroot00000000000000polkit-126/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000016101474122443600211100ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS (including version): - Desktop Environment [Gnome, KDE, ...]: - Version of polkit: **Additional context** Add any other context about the problem here. **Please...** If possible, provide information from journal/logs. If using systemd, this can be retrieved with `journalctl -u polkit.service` command. This will speed up the process of helping you a lot! Thank you. polkit-126/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011231474122443600221420ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. polkit-126/.github/dependabot.yml000066400000000000000000000001611474122443600170630ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: monthly polkit-126/.github/pull_request_template.md000066400000000000000000000003451474122443600212000ustar00rootroot00000000000000## Summary [short description of the problem and the change]: # ## Detailed description and/or reproducer [Please be more descriptive yet concise here. This text will help us with testing, urgency and severity assessment.]: # polkit-126/.github/workflows/000077500000000000000000000000001474122443600162725ustar00rootroot00000000000000polkit-126/.github/workflows/ci.sh000077500000000000000000000043031474122443600172240ustar00rootroot00000000000000#!/bin/bash set -eux set -o pipefail # TODO # - drop -Wno-deprecated-declarations PHASE="${1:?}" COMMON_BUILD_OPTS=( -Dauthfw=pam -Dexamples=true -Dgtk_doc=true -Dintrospection=true -Dsession_tracking=logind -Dtests=true ) if [[ "$PHASE" =~ ^CLANG_ ]]; then export CC=clang export CXX=clang++ fi case "$PHASE" in BUILD_GCC|BUILD_CLANG) # Build test with various levels of optimization and other flags affecting the build BUILD_TEST_FLAGS=( --optimization=0 --optimization=3 --optimization=s -Db_ndebug=true ) for opt in "${BUILD_TEST_FLAGS[@]}"; do COMPILER_FLAGS=(-Wno-deprecated-declarations) if [[ "$opt" != --optimization=0 ]]; then COMPILER_FLAGS+=(-D_FORTIFY_SOURCE=2) fi meson setup build \ -Dman=true \ --werror \ -Dc_args="${COMPILER_FLAGS[*]}" \ -Dcpp_args="${COMPILER_FLAGS[*]}" \ "${COMMON_BUILD_OPTS[@]}" \ "$opt" meson compile -C build -v rm -rf build done ;; GCC|CLANG) # Build + unit tests meson setup build \ -Dman=true \ -Dc_args="-D_FORTIFY_SOURCE=2" \ -Dcpp_args="-D_FORTIFY_SOURCE=2" \ "${COMMON_BUILD_OPTS[@]}" meson compile -C build -v meson test -C build --print-errorlogs DESTDIR="$PWD/install-test" meson install -C build ;; GCC_ASAN_UBSAN|CLANG_ASAN_UBSAN) # Build + unit tests with ASan and UBSan meson setup build \ -Dman=false \ -Db_sanitize=address,undefined \ --optimization=1 \ -Db_lundef=false \ "${COMMON_BUILD_OPTS[@]}" export ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 export UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 meson compile -C build -v meson test -C build --print-errorlogs ;; *) echo >&2 "Unknown phase '$PHASE'" exit 1 esac polkit-126/.github/workflows/ci.yml000066400000000000000000000016761474122443600174220ustar00rootroot00000000000000--- # vi: ts=2 sw=2 et: name: Build & test on: pull_request: branches: - main push: branches: - main permissions: contents: read jobs: build: runs-on: ubuntu-latest container: image: registry.fedoraproject.org/fedora:latest options: "--privileged" concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.phase }} cancel-in-progress: true strategy: fail-fast: false matrix: phase: [BUILD_GCC, GCC, GCC_ASAN_UBSAN, BUILD_CLANG, CLANG] steps: - name: Repository checkout uses: actions/checkout@v4 - name: Install build & test dependencies run: | sudo dnf install -y dnf-plugins-core python3-dbusmock clang compiler-rt libasan libubsan mozjs115-devel sudo dnf builddep -y polkit - name: Build & test run: sudo --preserve-env=GITHUB_ACTIONS,CI .github/workflows/ci.sh ${{ matrix.phase }} polkit-126/.github/workflows/codeql.yml000066400000000000000000000024531474122443600202700ustar00rootroot00000000000000--- # vi: ts=2 sw=2 et: name: "CodeQL" on: push: branches: - main pull_request: branches: - main jobs: analyze: name: Analyze runs-on: ubuntu-24.04 permissions: security-events: write concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.language }} cancel-in-progress: true strategy: fail-fast: false matrix: language: ['c-cpp', 'javascript-typescript', 'python'] steps: - name: Checkout repository uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} queries: +security-extended,security-and-quality - name: Install dependencies if: ${{ matrix.language == 'c-cpp' }} run: | sudo sed -i 's/^Types: deb/Types: deb deb-src/g' /etc/apt/sources.list.d/*.sources sudo apt update sudo apt build-dep -y policykit-1 # polkit in Ubuntu Jammy (ATTOW) doesn't have the latest build dependencies yet sudo apt install -y duktape-dev meson - name: Autobuild uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" polkit-126/.github/workflows/coverity.sh000077500000000000000000000043131474122443600204760ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: LGPL-2.0-or-later set -eux COVERITY_SCAN_TOOL_BASE="/tmp/coverity-scan-analysis" COVERITY_SCAN_PROJECT_NAME="polkit" coverity_install_script() { local platform tool_url tool_archive platform=$(uname) tool_url="https://scan.coverity.com/download/${platform}" tool_archive="/tmp/cov-analysis-${platform}.tgz" set +x # this is supposed to hide COVERITY_SCAN_TOKEN echo -e "\033[33;1mDownloading Coverity Scan Analysis Tool...\033[0m" wget -nv -O "$tool_archive" "$tool_url" --post-data "project=$COVERITY_SCAN_PROJECT_NAME&token=${COVERITY_SCAN_TOKEN:?}" set -x mkdir -p "$COVERITY_SCAN_TOOL_BASE" pushd "$COVERITY_SCAN_TOOL_BASE" tar xzf "$tool_archive" popd } run_coverity() { local results_dir tool_dir results_archive sha response status_code results_dir="cov-int" tool_dir=$(find "$COVERITY_SCAN_TOOL_BASE" -type d -name 'cov-analysis*') results_archive="analysis-results.tgz" sha=$(git rev-parse --short HEAD) meson setup build -Dtests=true -Dexamples=true COVERITY_UNSUPPORTED=1 "$tool_dir/bin/cov-build" --dir "$results_dir" sh -c "ninja -C ./build -v" "$tool_dir/bin/cov-import-scm" --dir "$results_dir" --scm git --log "$results_dir/scm_log.txt" tar czf "$results_archive" "$results_dir" set +x # this is supposed to hide COVERITY_SCAN_TOKEN echo -e "\033[33;1mUploading Coverity Scan Analysis results...\033[0m" response=$(curl \ --silent --write-out "\n%{http_code}\n" \ --form project="$COVERITY_SCAN_PROJECT_NAME" \ --form token="${COVERITY_SCAN_TOKEN:?}" \ --form email="${COVERITY_SCAN_NOTIFICATION_EMAIL:?}" \ --form file="@$results_archive" \ --form version="$sha" \ --form description="Daily build" \ https://scan.coverity.com/builds) printf "\033[33;1mThe response is\033[0m\n%s\n" "$response" status_code=$(echo "$response" | sed -n '$p') if [ "$status_code" != "200" ]; then echo -e "\033[33;1mCoverity Scan upload failed: $(echo "$response" | sed '$d').\033[0m" return 1 fi set -x } coverity_install_script run_coverity polkit-126/.github/workflows/coverity.yml000066400000000000000000000016721474122443600206670ustar00rootroot00000000000000--- # vi: ts=2 sw=2 et: name: Coverity on: schedule: # Run Coverity daily at midnight - cron: '0 0 * * *' pull_request: paths: - ".github/workflows/coverity.*" permissions: contents: read jobs: build: runs-on: ubuntu-24.04 if: github.repository == 'polkit-org/polkit' env: # Set in repo settings -> Secrets and variables -> Actions -> Repository secrets COVERITY_SCAN_TOKEN: "${{ secrets.COVERITY_SCAN_TOKEN }}" COVERITY_SCAN_NOTIFICATION_EMAIL: "${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}" steps: - name: Repository checkout uses: actions/checkout@v4 - name: Install build dependencies run: | sudo sed -i 's/^Types: deb/Types: deb deb-src/g' /etc/apt/sources.list.d/*.sources sudo apt update sudo apt build-dep -y policykit-1 - name: Build & upload the results run: .github/workflows/coverity.sh polkit-126/.github/workflows/differential-shellcheck.yml000066400000000000000000000014211474122443600235520ustar00rootroot00000000000000name: Differential ShellCheck on: push: branches: [ main ] pull_request: branches: [ main ] permissions: contents: read jobs: lint: runs-on: ubuntu-latest permissions: security-events: write steps: - name: Repository checkout uses: actions/checkout@v4 with: fetch-depth: 0 - id: ShellCheck name: Differential ShellCheck uses: redhat-plumbers-in-action/differential-shellcheck@v5 with: token: ${{ secrets.GITHUB_TOKEN }} - if: ${{ always() }} name: Upload artifact with ShellCheck defects in SARIF format uses: actions/upload-artifact@v4 with: name: Differential ShellCheck SARIF path: ${{ steps.ShellCheck.outputs.sarif }} polkit-126/.gitignore000066400000000000000000000011441474122443600146650ustar00rootroot00000000000000*.o .deps /*.bak /*.gcda /*.gcno /*.orig /*.rej /*.tab.c /*~ /.*.sw[nop] /.dirstamp /.gitignore /GPATH /GRTAGS /GSYMS /GTAGS /ID /Makefile /Makefile.in /TAGS /autom4te.cache /config.cache /config.h /config.log /config.lt /config.status /config.status.lineno /configure /configure.lineno /intltool-extract.in /intltool-merge.in /intltool-update.in /libtool /po/*.gmo /po/*.header /po/*.mo /po/*.sed /po/*.sin /po/.intltool-merge-cache /po/Makefile /po/Makefile.in /po/Makefile.in.in /po/Makefile.in.in~ /po/Makevars.template /po/POTFILES /po/Rules-quot /po/polkit-1.pot /po/stamp-it /so_locations /stamp-h1 /tags polkit-126/.packit.yaml000066400000000000000000000023301474122443600151100ustar00rootroot00000000000000# See the documentation for more information: # https://packit.dev/docs/configuration/ specfile_path: .packit/polkit.spec # add or remove files that should be synced files_to_sync: - .packit/polkit.spec - .packit/polkit.sysusers - packit.yaml # name in upstream package repository or registry (e.g. in PyPI) upstream_package_name: polkit # downstream (Fedora) RPM package name downstream_package_name: polkit # arguments for OpenScanHub # # Note: GCC analyzer in some cases depends on information provided by LTO to # generate accurate results, but the LTO support in GCC analyzer is still # experimental, so it's not enabled by default. In Polkit, however, this # seems to work pretty well, so let's tell OSH to build our code with LTO # when running GCC analyzer. csmock_args: --gcc-add-flag=-flto=auto jobs: - job: copr_build trigger: commit preserve_project: true targets: - fedora-rawhide-x86_64 - job: copr_build trigger: pull_request targets: - fedora-all-aarch64 - fedora-all-ppc64le - fedora-all-s390x - fedora-all-x86_64 - job: tests trigger: pull_request fmf_path: test/integration/ tmt_plan: upstream-ci targets: - fedora-all-aarch64 - fedora-all-x86_64 polkit-126/.packit/000077500000000000000000000000001474122443600142265ustar00rootroot00000000000000polkit-126/.packit/polkit.spec000066400000000000000000000602721474122443600164130ustar00rootroot00000000000000# Only enable if using patches that touches configure.ac, # Makefile.am or other build system related files # Summary: An authorization framework Name: polkit Version: 126 Release: 1%{?dist} License: LGPL-2.0-or-later URL: https://github.com/polkit-org/polkit Source0: https://github.com/polkit-org/polkit/archive/refs/tags/%{version}.tar.gz Source1: polkit.sysusers BuildRequires: gcc-c++ BuildRequires: glib2-devel >= 2.30.0 BuildRequires: expat-devel BuildRequires: pam-devel BuildRequires: gtk-doc BuildRequires: gettext-devel BuildRequires: gobject-introspection-devel BuildRequires: systemd, systemd-devel, systemd-rpm-macros BuildRequires: dbus-devel BuildRequires: pkgconfig(duktape) BuildRequires: meson BuildRequires: git BuildRequires: python3-dbus BuildRequires: python3-dbusmock Requires: dbus, polkit-pkla-compat Requires: %{name}-libs%{?_isa} = %{version}-%{release} %{?systemd_requires} %{?sysusers_requires_compat} Obsoletes: PolicyKit <= 0.10 Provides: PolicyKit = 0.11 # polkit saw some API/ABI changes from 0.96 to 0.97 so require a # sufficiently new polkit-gnome package Conflicts: polkit-gnome < 0.97 Obsoletes: polkit-desktop-policy < 0.103 Provides: polkit-desktop-policy = 0.103 Obsoletes: polkit-js-engine < 0.120-5 Provides: polkit-js-engine = %{version}-%{release} # when -libs was split out, handle multilib upgrade path -- rex Obsoletes: polkit < 0.113-3 %description polkit is a toolkit for defining and handling authorizations. It is used for allowing unprivileged processes to speak to privileged processes. %package devel Summary: Development files for polkit Requires: %{name}-libs%{?_isa} = %{version}-%{release} Requires: %name-docs = %{version}-%{release} Requires: glib2-devel Obsoletes: PolicyKit-devel <= 0.10 Provides: PolicyKit-devel = 0.11 %description devel Development files for polkit. %package docs Summary: Development documentation for polkit Requires: %name-devel = %{version}-%{release} Obsoletes: PolicyKit-docs <= 0.10 Provides: PolicyKit-docs = 0.11 BuildArch: noarch %description docs Development documentation for polkit. %package libs Summary: Libraries for polkit %description libs Libraries files for polkit. %prep %autosetup -S git %build %meson -D authfw=pam \ -D examples=false \ -D gtk_doc=true \ -D introspection=true \ -D man=true \ -D session_tracking=logind \ -D tests=true %meson_build %meson_test %install %meson_install install -Dpm 0644 %{SOURCE1} %{buildroot}%{_sysusersdir}/polkit.conf rm -f $RPM_BUILD_ROOT%{_libdir}/*.la %find_lang polkit-1 %pre %sysusers_create_compat %{SOURCE1} %post # The implied (systemctl preset) will fail and complain, but the macro hides # and ignores the fact. This is in fact what we want, polkit.service does not # have an [Install] section and it is always started on demand. %systemd_post polkit.service %preun %systemd_preun polkit.service %postun %systemd_postun_with_restart polkit.service %files -f polkit-1.lang %doc COPYING NEWS.md README.md %{_datadir}/man/man1/* %{_datadir}/man/man8/* %{_datadir}/dbus-1/system.d/org.freedesktop.PolicyKit1.conf %{_datadir}/dbus-1/system-services/* %{_unitdir}/polkit.service %dir %{_datadir}/polkit-1/ %dir %{_datadir}/polkit-1/actions %attr(0750,root,polkitd) %dir %{_datadir}/polkit-1/rules.d %{_datadir}/polkit-1/actions/org.freedesktop.policykit.policy %{_datadir}/polkit-1/policyconfig-1.dtd %dir %{_sysconfdir}/polkit-1 %{_datadir}/polkit-1/rules.d/50-default.rules %attr(0750,root,polkitd) %dir %{_sysconfdir}/polkit-1/rules.d %{_sysusersdir}/polkit.conf %{_prefix}/lib/pam.d/polkit-1 %{_bindir}/pkaction %{_bindir}/pkcheck %{_bindir}/pkttyagent %dir %{_prefix}/lib/polkit-1 %{_prefix}/lib/polkit-1/polkitd %{_tmpfilesdir}/polkit-tmpfiles.conf # see upstream docs for why these permissions are necessary %attr(4755,root,root) %{_bindir}/pkexec %attr(4755,root,root) %{_prefix}/lib/polkit-1/polkit-agent-helper-1 %files devel %{_libdir}/lib*.so %{_libdir}/pkgconfig/*.pc %{_datadir}/gir-1.0/*.gir %{_includedir}/* %{_datadir}/gettext/its/polkit.its %{_datadir}/gettext/its/polkit.loc %files docs %{_datadir}/gtk-doc %ldconfig_scriptlets libs %files libs %{_libdir}/lib*.so.* %{_libdir}/girepository-1.0/*.typelib %changelog * Thu Jan 25 2024 Fedora Release Engineering - 124-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild * Mon Jan 22 2024 Jan Rybar - 124-1 - rebase to polkit-124 * Sun Jan 21 2024 Fedora Release Engineering - 123-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild * Wed Dec 06 2023 Jan Rybar - 123-4 - align sysusers implementation with Fedora guidelines, fixes upstream CI * Mon Nov 20 2023 Jan Rybar - 123-3 - backport of removal of IPAddressDeny sandboxing option - Resolves: bz#2248838 * Thu Sep 21 2023 Christian Glombek - 123-2 - Provide a sysusers.d file to get user() and group() provides (see https://fedoraproject.org/wiki/Changes/Adopting_sysusers.d_format). * Tue Aug 01 2023 Jan Rybar - 123-1 - Rebase to version 123 * Fri Jul 21 2023 Fedora Release Engineering - 122-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild * Tue Apr 11 2023 Lukáš Zaoral - 122-6 - migrate to SPDX license format * Thu Mar 30 2023 Jan Rybar - 122-5 - config file permission change to increase security of polkitd - Resolves: bz#2182784 * Wed Feb 08 2023 Jan Rybar - 122-4 - Switch 'polkitd' to static UID/GID (fpc#1189) (on behalf of lucab) - Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2104615 * Fri Jan 20 2023 Fedora Release Engineering - 122-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild * Fri Dec 16 2022 František Zatloukal - 122-2 - Rebuilt for duktape 2.7.0 * Wed Oct 26 2022 Vincent Mihalkovic - 122-1 - rebase to 122 * Mon Aug 15 2022 Jan Rybar - 121-4 - duktape re-enabled * Tue Aug 02 2022 Jan Rybar - 121-3 - switched back to mozjs until problems with duktape are fixed - Related: bz#2109145 * Fri Jul 22 2022 Fedora Release Engineering - 121-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild * Tue Jul 12 2022 Jan Rybar - 121-1 - Rebase to v.121 * Sun Feb 20 2022 Frantisek Zatloukal - 0.120-5 - switch to mozjs91 * Wed Feb 16 2022 Jan Rybar - 0.120-4 - file descriptor exhaustion (GHSL-2021-077) - Resolves: CVE-2021-4115 * Wed Jan 26 2022 Timothée Ravier - 0.120-3 - Fix for CVE-2021-4034 * Fri Jan 21 2022 Fedora Release Engineering - 0.120-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild * Wed Oct 06 2021 Pete Walter - 0.120-1 - Update to 0.120 * Fri Jul 23 2021 Fedora Release Engineering - 0.118-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild * Thu May 20 2021 Jan Rybar - 0.118-2 - CVE-2021-3560 mitigation - Resolves: CVE-2021-3560 * Mon Apr 26 2021 Jan Rybar - 0.118-1 - rebase to 0.118 * Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek - 0.117-4 - Rebuilt for updated systemd-rpm-macros See https://pagure.io/fesco/issue/2583. * Wed Jan 27 2021 Fedora Release Engineering - 0.117-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild * Wed Aug 12 2020 Jan Rybar - 0.117-2 - update dependency to mozjs78 * Fri Jul 31 2020 Jan Rybar - 0.117-1 - Rebased to polkit-0.117 * Tue Jul 28 2020 Fedora Release Engineering - 0.116-9 - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild * Wed Jul 22 2020 Tom Stellard - 0.116-8 - Use make macros - https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro * Thu Jan 30 2020 Fedora Release Engineering - 0.116-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild * Fri Oct 25 2019 Jan Rybar - 0.116-6 - jsauthority memleak fix * Fri Sep 27 2019 Jan Rybar - 0.116-5 - pkttyagent: unread input flushed on terminal restore * Sun Sep 08 2019 Kalev Lember - 0.116-4 - Rebuilt for mozjs60 s390x fixes * Fri Aug 02 2019 Jan Rybar - 0.116-3 - pkttyagent: backport patch, get SIGTTOU in background job * Fri Jul 26 2019 Fedora Release Engineering - 0.116-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild * Thu May 02 2019 Pete Walter - 0.116-1 - Update to 0.116 * Thu Feb 14 2019 Jan Rybar - 0.115-11 - pkttyagent: PolkitAgentTextListener leaves echo tty disabled if SIGINT/SIGTERM * Fri Feb 08 2019 Pete Walter - 0.115-10 - Move to mozjs60 * Tue Feb 05 2019 Jan Rybar - 0.115-9 - Allow uid=-1 for PolkitUnixProcess * Sat Feb 02 2019 Fedora Release Engineering - 0.115-8 - Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild * Tue Jan 08 2019 Colin Walters - 0.115-7 - Add security fix for https://bugs.chromium.org/p/project-zero/issues/detail?id=1692 * Fri Dec 07 2018 Jan Rybar - 0.115-6 - Fix of CVE-2018-19788, priv escalation with high UIDs - Resolves: rhbz#1655926 * Thu Sep 27 2018 Owen Taylor - 0.115-5 - Fix installation with prefix != /usr * Mon Aug 13 2018 Jan Rybar - 0.115-4 - Leaking zombie processess started by rules * Fri Jul 20 2018 Jan Rybar - 0.115-3 - Warning raised by polkit when disconnected from ssh - polkitagentlistener: resource leak - pointer to 'server' - Error message raised on every 'systemctl start' in emergency.target * Fri Jul 13 2018 Fedora Release Engineering - 0.115-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild * Tue Jul 10 2018 Miloslav Trmač - 0.115-1 - Update to 0.115 (CVE-2018-1116) * Tue Apr 03 2018 Ray Strode - 0.114-1 - Update to 0.114 * Fri Feb 09 2018 Fedora Release Engineering - 0.113-16 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild * Thu Aug 03 2017 Fedora Release Engineering - 0.113-15 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild * Thu Jul 27 2017 Fedora Release Engineering - 0.113-14 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild * Thu Apr 13 2017 Richard Hughes - 0.113-13 - Add the its files from upstream. * Tue Apr 4 2017 Miloslav Trmač - 0.113-12 - Fix a memory leak in PolkitPermission. Patch by Rui Matos Resolves: #1433915 * Tue Apr 4 2017 Miloslav Trmač - 0.113-11 - Revert back to the state in 0.113-7, undoing the untested changes. * Tue Apr 4 2017 Peter Robinson 0.113-10 - Move to an upstream snapshot, rebase patches * Fri Mar 31 2017 Rex Dieter - 0.113-9 - restore Provides: polkit-desktop-policy polkit-js-engine * Thu Mar 30 2017 Peter Robinson 0.113-8 - Use %%license, license needs to be in -libs as it's the only guaranteed installed package - Move to mozjs38 - Other upstream fixes - Spec cleanups * Mon Feb 13 2017 Miloslav Trmač - 0.113-7 - Fix memory leaks when calling authentication agents Resolves: #1380166 * Sat Feb 11 2017 Fedora Release Engineering - 0.113-6 - Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild * Thu Feb 04 2016 Fedora Release Engineering - 0.113-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild * Tue Jul 14 2015 Miloslav Trmač - 0.113-4 - Bump the Obsoletes: to < 0.113-3 to account for the non-split 0.113-2.fc21 Resolves: #1243004 * Sun Jul 12 2015 Rex Dieter 0.113-3 - Obsoletes: polkit < 0.112-8 (handle multilib upgrade path) * Fri Jul 10 2015 Miloslav Trmač - 0.113-2 - Add a fully versioned dependency from polkit to polkit-libs Resolves: #1241759 - Require polkit-libs, not polkit, in polkit-devel * Thu Jul 2 2015 Miloslav Trmač - 0.113-1 - Update to polkit-0.113 (CVE-2015-3218, CVE-2015-3255, CVE-2015-3256, CVE-2015-4625) Resolves: #910262, #1175061, #1177930, #1194391, #1228739, #1233810 * Fri Jun 19 2015 Miloslav Trmač - 0.112-11 - Add BuildRequires: systemd so that %%{_unitdir} is defined, to fix the build. * Thu Jun 18 2015 Fedora Release Engineering - 0.112-10 - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild * Sun Jan 25 2015 Rex Dieter - 0.112-9 - polkit doesn't release reference counters of GVariant data (#1180886) - fix ldconfig scriptlets (move to -libs subpkg) * Sat Nov 08 2014 Colin Walters - 0.112-8 - Split separate -libs package, so that NetworkManager can just depend on that, without dragging in the daemon (as well as libmozjs17). This allows the creation of more minimal systems that want programs like NM, but do not need the configurability of the daemon; it would be ok if only root is authorized. * Sun Aug 17 2014 Fedora Release Engineering - 0.112-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild * Tue Jul 22 2014 Kalev Lember - 0.112-6 - Rebuilt for gobject-introspection 1.41.4 * Sat Jun 07 2014 Fedora Release Engineering - 0.112-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild * Thu Jun 5 2014 Kay Sievers - 0.112-4 - backport upstream D-Bus "user bus" changes * Mon Feb 10 2014 Miloslav Trmač - 0.112-3 - Fix a PolkitAgentSession race condition Resolves: #1063193 * Sat Dec 7 2013 Miloslav Trmač - 0.112-2 - Workaround pam_systemd setting broken XDG_RUNTIME_DIR Resolves: #1033774 - Always use mozjs-17.0 even if js-devel is installed * Wed Sep 18 2013 Miloslav Trmač - 0.112-1 - Update to polkit-0.112 - Resolves: #1009538, CVE-2013-4288 * Sun Aug 04 2013 Fedora Release Engineering - 0.111-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild * Wed May 29 2013 Tomas Bzatek - 0.111-2 - Fix a race on PolkitSubject type registration (#866718) * Wed May 15 2013 Miloslav Trmač - 0.111-1 - Update to polkit-0.111 Resolves: #917888 - Use SpiderMonkey from mozjs17 instead of js - Ship the signature in the srpm - Try to preserve timestamps in (make install) * Fri May 10 2013 Miloslav Trmač - 0.110-4 - Shut up rpmlint about Summary: - Build with V=1 - Use %%{_unitdir} instead of hard-coding the path - Use the new systemd macros, primarily to run (systemctl daemon-reload) Resolves: #857382 * Fri May 10 2013 Miloslav Trmač - 0.110-4 - Make the JavaScript engine mandatory. The polkit-js-engine package has been removed, main polkit package Provides:polkit-js-engine for compatibility. - Add Requires: polkit-pkla-compat Resolves: #908808 * Wed Feb 13 2013 Miloslav Trmač - 0.110-3 - Don't ship pk-example-frobnicate in the "live" configuration Resolves: #878112 * Fri Feb 8 2013 Miloslav Trmač - 0.110-2 - Own %%{_docdir}/polkit-js-engine-* Resolves: #907668 * Wed Jan 9 2013 David Zeuthen - 0.110-1%{?dist} - Update to upstream release 0.110 * Mon Jan 7 2013 Matthias Clasen - 0.109-2%{?dist} - Build with pie and stuff * Wed Dec 19 2012 David Zeuthen 0.109-1%{?dist} - Update to upstream release 0.109 - Drop upstreamed patches * Thu Nov 15 2012 David Zeuthen 0.108-3%{?dist} - Attempt to open the correct libmozjs185 library, otherwise polkit authz rules will not work unless js-devel is installed (fdo #57146) * Wed Nov 14 2012 David Zeuthen 0.108-2%{?dist} - Include gmodule-2.0 to avoid build error * Wed Nov 14 2012 David Zeuthen 0.108-1%{?dist} - Update to upstream release 0.108 - Drop upstreamed patches - This release dynamically loads the JavaScript interpreter and can cope with it not being available. In this case, polkit authorization rules are not processed and the defaults for an action - as defined in its .policy file - are used for authorization decisions. - Add new meta-package, polkit-js-engine, that pulls in the required JavaScript bits to make polkit authorization rules work. The default install - not the minimal install - should include this package * Wed Oct 10 2012 Adam Jackson 0.107-4 - Don't crash if initializing the server object fails * Tue Sep 18 2012 David Zeuthen 0.107-3%{?dist} - Authenticate as root if e.g. the wheel group is empty (#834494) * Fri Jul 27 2012 Fedora Release Engineering - 0.107-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild * Wed Jul 11 2012 David Zeuthen 0.107-1%{?dist} - Update to upstream release 0.107 * Fri Jun 29 2012 David Zeuthen 0.106-2%{?dist} - Add forgotten Requires(pre): shadow-utils * Thu Jun 07 2012 David Zeuthen 0.106-1%{?dist} - Update to upstream release 0.106 - Authorizations are no longer controlled by .pkla files - from now on, use the new .rules files described in the polkit(8) man page * Tue Apr 24 2012 David Zeuthen 0.105-1%{?dist} - Update to upstream release 0.105 - Nuke patches that are now upstream - Change 'PolicyKit' to 'polkit' in summary and descriptions * Thu Mar 08 2012 David Zeuthen 0.104-6%{?dist} - Don't leak file descriptors (bgo #671486) * Mon Feb 13 2012 Matthias Clasen - 0.104-5%{?dist} - Make the -docs subpackage noarch * Mon Feb 06 2012 David Zeuthen 0.104-4%{?dist} - Set error if we cannot obtain a PolkitUnixSession for a given PID (#787222) * Sat Jan 14 2012 Fedora Release Engineering - 0.104-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild * Tue Jan 03 2012 David Zeuthen 0.104-2%{?dist} - Nuke the ConsoleKit run-time requirement * Tue Jan 03 2012 David Zeuthen 0.104-1%{?dist} - Update to upstream release 0.104 - Force usage of systemd (instead of ConsoleKit) for session tracking * Tue Dec 06 2011 David Zeuthen 0.103-1%{?dist} - Update to upstream release 0.103 - Drop upstreamed patch - Drop Fedora-specific policy, it is now upstream (fdo #41008) * Wed Oct 26 2011 Fedora Release Engineering - 0.102-3 - Rebuilt for glibc bug#747377 * Tue Oct 18 2011 David Zeuthen 0.102-2%{?dist} - Add patch to neuter the annoying systemd behavior where stdout/stderr is sent to the system logs * Thu Aug 04 2011 David Zeuthen 0.102-1 - Update to 0.102 release * Fri May 13 2011 Bastien Nocera 0.101-7 - Allow setting the pretty hostname without a password for wheel, change matches systemd in git * Mon May 2 2011 Matthias Clasen - 0.101-6 - Update the action id of the datetime mechanism * Tue Apr 19 2011 David Zeuthen - 0.101-5 - CVE-2011-1485 (#697951) * Tue Mar 22 2011 Kevin Kofler - 0.101-4 - Also allow org.kde.kcontrol.kcmclock.save without password for wheel * Thu Mar 17 2011 David Zeuthen - 0.101-3 - Fix typo in pkla file (thanks notting) * Thu Mar 17 2011 David Zeuthen - 0.101-2 - Nuke desktop_admin_r and desktop_user_r groups - just use the wheel group instead (#688363) - Update the set of configuration directives that gives users in the wheel group extra privileges * Thu Mar 03 2011 David Zeuthen - 0.101-1 - New upstream version * Mon Feb 21 2011 David Zeuthen - 0.100-1 - New upstream version * Wed Feb 09 2011 Fedora Release Engineering - 0.98-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Fri Jan 28 2011 Matthias Clasen - 0.98-6 - Own /usr/libexec/polkit-1 * Fri Nov 12 2010 Matthias Clasen - 0.98-5 - Enable introspection * Thu Sep 02 2010 David Zeuthen - 0.98-4 - Fix #629515 in a way that doesn't require autoreconf * Thu Sep 02 2010 David Zeuthen - 0.98-2 - Include polkitagentenumtypes.h (#629515) * Mon Aug 23 2010 Matthias Clasen - 0.98-1 - Update to upstream release 0.98 - Co-own /usr/share/gtk-doc (#604410) * Wed Aug 18 2010 Matthias Clasen - 0.97-5 - Rebuid to work around bodhi limitations * Wed Aug 18 2010 Matthias Clasen - 0.97-4 - Fix a ConsoleKit interaction bug * Fri Aug 13 2010 David Zeuthen - 0.97-3 - Add a patch to make pkcheck(1) work the way libvirtd uses it (#623257) - Require GLib >= 2.25.12 instead of 2.25.11 - Ensure polkit-gnome packages earlier than 0.97 are not used with these packages * Mon Aug 09 2010 David Zeuthen - 0.97-2 - Rebuild * Mon Aug 09 2010 David Zeuthen - 0.97-1 - Update to 0.97. This release contains a port from EggDBus to the GDBus code available in recent GLib releases. * Fri Jan 15 2010 David Zeuthen - 0.96-1 - Update to 0.96 - Disable introspection support for the time being * Fri Nov 13 2009 David Zeuthen - 0.95-2 - Rebuild * Fri Nov 13 2009 David Zeuthen - 0.95-1 - Update to 0.95 - Drop upstreamed patches * Tue Oct 20 2009 Matthias Clasen - 0.95-0.git20090913.3 - Fix a typo in pklocalauthority(8) * Mon Sep 14 2009 David Zeuthen - 0.95-0.git20090913.2 - Refine how Obsolete: is used and also add Provides: (thanks Jesse Keating and nim-nim) * Mon Sep 14 2009 David Zeuthen - 0.95-0.git20090913.1 - Add bugfix for polkit_unix_process_new_full() (thanks Bastien Nocera) - Obsolete old PolicyKit packages * Sun Sep 13 2009 David Zeuthen - 0.95-0.git20090913 - Update to git snapshot - Drop upstreamed patches - Turn on GObject introspection - Don't delete desktop_admin_r and desktop_user_r groups when uninstalling polkit-desktop-policy * Fri Sep 11 2009 David Zeuthen - 0.94-4 - Add some patches from git master - Sort pkaction(1) output - Bug 23867 – UnixProcess vs. SystemBusName aliasing * Thu Aug 13 2009 David Zeuthen - 0.94-3 - Add desktop_admin_r and desktop_user_r groups along with a first cut of default authorizations for users in these groups. * Wed Aug 12 2009 David Zeuthen - 0.94-2 - Disable GObject Introspection for now as it breaks the build * Wed Aug 12 2009 David Zeuthen - 0.94-1 - Update to upstream release 0.94 * Sun Jul 26 2009 Fedora Release Engineering - 0.93-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Mon Jul 20 2009 David Zeuthen - 0.93-2 - Rebuild * Mon Jul 20 2009 David Zeuthen - 0.93-1 - Update to 0.93 * Tue Jun 09 2009 David Zeuthen - 0.92-3 - Don't make docs noarch (I *heart* multilib) - Change license to LGPLv2+ * Mon Jun 08 2009 David Zeuthen - 0.92-2 - Rebuild * Mon Jun 08 2009 David Zeuthen - 0.92-1 - Update to 0.92 release * Wed May 27 2009 David Zeuthen - 0.92-0.git20090527 - Update to 0.92 snapshot * Mon Feb 9 2009 David Zeuthen - 0.91-1 - Initial spec file. polkit-126/.packit/polkit.sysusers000066400000000000000000000001741474122443600173540ustar00rootroot00000000000000#Type Name ID GECOS Home directory Shell u polkitd 114 "User for polkitd" / /sbin/nologin polkit-126/AUTHORS000066400000000000000000000000001474122443600137330ustar00rootroot00000000000000polkit-126/COPYING000066400000000000000000000614531474122443600137410ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! polkit-126/ChangeLog000066400000000000000000000000001474122443600144350ustar00rootroot00000000000000polkit-126/HACKING.md000066400000000000000000000061061474122443600142660ustar00rootroot00000000000000SCM === - anonymous checkouts `$ git clone git://git.freedesktop.org/git/PolicyKit.git` - checkouts if you got an ssh account on fd.o (username@ is optional) `$ git clone ssh://[username@]git.freedesktop.org/git/PolicyKit.git` - commit to local repository `$ git commit -a` - push local repository to master repository at fd.o (remember most patches requires review at the mailing list) `$ git push` - pull changes from master repository at fd.o `$ git pull` - diff of working tree versus local repository `$ git diff` - diff of local repository vs. master repository at fd.o synchronize with upstream repo: `$ git pull` (possibly merge changes) generate the diff: `$ git diff origin HEAD` - influential environment variables (set these in e.g. .bash_profile) ```bash export GIT_AUTHOR_NAME='Your Full Name' export GIT_COMMITTER_NAME='Your Full Name' export GIT_COMMITTER_EMAIL=youremail@domain.net export GIT_AUTHOR_EMAIL=youremail@domain.net ``` - see also http://www.kernel.org/pub/software/scm/git/docs/ Committing code === - Commit messages should be of the form (the five lines between the lines starting with ===) > === **begin example commit** === > short explanation of the commit > > > > Longer explanation explaining exactly what's changed, whether any > external or private interfaces changed, what bugs were fixed (with bug > tracker reference if applicable) and so forth. Be concise but not too brief. > === **end example commit** === - Always add a brief description of the commit to the _first_ line of the commit and terminate by two newlines (it will work without the second newline, but that is not nice for the interfaces). - First line (the brief description) must only be one sentence and must not start with a capital letter. Don't use a trailing period either. - The main description (the body) is normal prose and should use normal punctuation and capital letters where appropriate. Normally, for patches sent to a mailing list it's copied from there. - When committing code on behalf of others use the --author option, e.g. ```bash git commit -a --author "Joe Coder " Coding Style === - Please follow the coding style already used. - Write docs for all functions and structs and so on. We use gtkdoc format. - All external interfaces (network protocols, file formats, etc.) should have documented specifications sufficient to allow an alternative implementation to be written. Our implementation should be strict about specification compliance (should not for example heuristically parse a file and accept not-well-formed data). Avoiding heuristics is also important for security reasons; if it looks funny, ignore it (or exit, or disconnect). Code of Conduct === As with other projects hosted on freedesktop.org, Polkit follows its Code of Conduct, based on the Contributor Covenant. Please conduct yourself in a respectful and civilized manner when using the above mailing lists, bug trackers, etc: `https://www.freedesktop.org/wiki/CodeOfConduct` polkit-126/NEWS.md000066400000000000000000001524051474122443600140020ustar00rootroot00000000000000## polkit 126 ### Highlights: - many code fixes detected either by CI or the author himself (Frantisek Sumsal) - shellcheck and dependabot integration (Jan Macku) - search for rules in /usr/local/share rather than /usr/local/lib (Luca Boccassi) - Implement LogControl1 protocol for dynamic log level changes (Luca Boccassi) - read actions also from /etc/, /run/ and /usr/local/share/ (Luca Boccassi) - mozjs dropped in favor of duktape (Xi Ruoyao) - many other fixes in build system and polkit code (Many thanks to all the authors.) ### Build requirements - glib, gobject, gio >= 2.32 - duktape - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd-logind OR elogind - gettext - meson ### People who made this version possible: Bastien Nocera Eli Schwartz Frantisek Sumsal Jan Macku KT-lcz Luca Boccassi Michael Biebl peelz Rafael Fontenelle Robert Ancell Sertonix Vincent Mihalkovic Xi Ruoyao Yoann Congal **Many thanks to the people involved, the contributors and enthusiasts!** The polkit team & polkit community Jan 13, 2025 ## polkit 125 ### Highlights: - introduction of CodeQL and a new integration test suite (Frantisek Sumsal) - dropped mocklibc (Frantisek Sumsal) - syslog-style log-levels introduction (Jan Rybar) - LogControl integration (Luca Boccassi) - pkexec: "No session for cookie" finally fixed (huxiaodong) - resources optimizations: only instances affected by sessions-change recalculate authorizations (Jan Rybar, thanks to Michal Sekletar and Milan Crha) - meson tweaks (Alyssa Ross, Luca Boccassi, Michael Biebl, Michael Olbrich) - build warnings cleanup (peelz) - Packit service configuration for the new upstream platform (Vincent Mihalkovic) - systemd-tmpfiles.d integration (Vincent Mihalkovic) - other fixes and changes (Gleb Popov, heather7283, Tianyu Chen, Tobias Stoeckmann) - internationalization: Slovenian (filmsi), Hindi (Scrambled777) ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-115 OR duktape - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd-logind OR elogind - gettext - meson ### People who made this version possible: Alyssa Ross filmsi Frantisek Sumsal Gleb Popov heather7283 huxiaodong Jan Rybar Luca Boccassi Michael Biebl Michael Olbrich peelz Scrambled777 Tianyu Chen Tobias Stoeckmann Vincent Mihalkovic **Many thanks to the people involved, the contributors and enthusiasts!** The polkit team & polkit community Aug 8, 2024 ## polkit 124 ### Highlights: - PIDFDs are used if available to track processes - pidfd parameter available for CheckAuthorization() - systemd-sysuser enabled for polkit ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-115 OR duktape - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd - gettext - meson ### Changes since polkit 123: * Luca Boccassi * PIDFD implementation and fixes/extensions around it * new systemd_unit and no_new_privileges subject attributes * Ray Strode, Sam James * general and/or buildsystem fixes * Vincent Mihalkovic * uninitialized pointer warning fix * Xi Ruoyao * mozjs 115 dependency upgrade and fixes * Jan Rybar * general fixes * Zbigniew Jędrzejewski-Szmek * systemd-sysuser implementation * Aleksandr Melman, Anders Jonsson, AsciiWolf, Christian K, NorwayFun, Sabri Unal * localization **Many thanks to the people involved, the contributors and enthusiasts!** The polkit team & polkit community Jan 17, 2024 ## polkit 123 ### Highlights: - better safety with deeper resctiction of the configuration files - better safety with restricting the daemon's owner under systemd - better safety with the systemd unit sandboxing - less thread races during upload of the configuration ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-102 OR duktape - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd - gettext - meson ### Changes since polkit 122: * Laurent Gauthier * prevent wrongful termination of runaway thread * Luca Boccassi * Stop installing /usr/share/polkit-1/rules.d as 700/polkitd * set User/Group and don't change uid/gid if already set * bboy_vi, liudun, Marco Trevisan, Marius Bakke, Matej Focko, Olivier Duchateau, Sam James, Val Packett * general and/or buildsystem fixes * Topi Miettinen * systemd service hardening * Vincent Mihalkovic * Packit service integration * pkcheck: manpage and help sync * general fixes * Jan Rybar * Packit service integration * change of ownership of custom configs * general fixes * Aleksandr Melman, Anders Jonsson, Jürgen Benvenuti, Piotr Drąg, Sabri Unal * localization **Thanks to everyone involved for making this possible!** The polkit team & polkit community July 28, 2023 ## polkit 122 ### Highlights: - new Georgian translation - port to mozjs-102 - daemon-less build (support for e.g. flatpak deps) - re-enable of (API) documentation build ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-102 OR duktape - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd - gettext - meson ### Changes since polkit 121: * Anders Jonsson * Swedish translation * NorwayFun * Georgian translation * Martin Kletzander * several pkttyagent fixes and improvements * A. Wilcox * optional netgroup support * Luca Boccassi * move 50-default.rules to /usr/share * Michael Biebl * remove useless ifclause from meson.build * Jordan Petridis * improve error message * Luciano Santos * honour pam_prefix meson option * Xi Ruoyao * meson: lfs autodetection * port to mozjs-102 * Peter Eisenmann * daemonless build option Thanks to everyone involved for making this possible! The polkit team & polkit community October 26, 2022 ## polkit 121 ### Highlights: - new versioning - duktape added as backend JS engine - autotools build system removed - new translations ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-91 OR duktape - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd - gettext - meson ### Changes since polkit 0.120: * Simon McVittie * meson and testsuite fixes * Bastien Nocera * add ability to create policyconfig-1.dtd file * Xi Ruoyao * port to newer mozjs-91, jsauthority tweaks * Wu Xiaotian and Gustavo Lima Chaves * Add duktape as javascript engine * Nathan Follens * Dutch translation * Daniel E * duktape fixup * Fabrice Fontaine * fix build without C++ * Dan Nicholson * fixup in group permision checking (backend) * Phaedrus Leeds * typo fix * Adrian Vovk * add option (--keep-cwd) for pkexec * Matt Turner * Allow --version and --help even if not setuid root * Benedikt Ames * fixes in polkitagent * Vincent Mihalkovic * development support Thanks to everyone involved for making this possible! Jan Rybar & polkit team June 27, 2022 ## polkit 0.120 **WARNING**: This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### NOTICE: This is the LAST version to support AUTOTOOLS build system, as it has been obsoleted by meson build system. The next release of polkit will REQUIRE meson build system. ### Highlights: - tarball fix-ups - re-enabled documentation - Intltool to gettext migration - new translations ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-78 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd - gettext - meson (optional) - autotools (DEPRECATED) ### Changes since polkit 0.119: - Inigo Martinez: transition from Intltool to gettext - Simon McVittie: several tarball, meson and pipeline fixups - Hugo Carvalho: Portuguese translation - Sergiu Bivol: Romanian translation Many thanks to all contributors! Jan Rybar et al., September 30, 2021 ## polkit 0.119 **WARNING**: This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.119**. ### Highlights: - meson build system added - CVE-2021-3560 mitigation ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-78 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.118: - Inigo Martinez: meson build system added alongside autotools (many thanks!!) - Matthias Classen: properties in text listener - René Genz: typos fixups - Balázs Úr: Update Hungarian translation - Hendrik Werner: meson post-install script mod to avoid calling external processes - Kevin Backhouse, Jan Rybar: CVE-2021-3560 mitigation Many thanks to all contributors! Jan Rybar et al., June 3, 2021 ## polkit 0.118 **WARNING**: This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.118**. ### Highlights: - move to mozjs78 - tarball CI fix ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-78 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.117: - Xi Ruoyao: tarball fixup for distcheck - Valentin David: updated dependency to mozjs78 Many thanks to all contributors! Jan Rybar et al., September 8, 2020 ## polkit 0.117 **WARNING**: This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.117**. ### Highlights: Gitlab CI activation - all merge requests are tested before merged New Norwegian translation, several other language updates ### Build requirements - glib, gobject, gio >= 2.32 - mozjs-68 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.116: - Bastien Nocera: Activated Gitlab CI - Xi Ruoyao: Updated dependency to mozjs68 - Kalev Lember, Jan Rybar Memory management fixes - Anders Jonsson, Karl Ove Hufthammer, Andika Triwidada, Yuri Chornoivan: Language updates Many thanks to all contributors! Jan Rybar et al., July 24, 2020 polkit 0.116 ------------ **WARNING**: This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.116**. ### Highlights: - Fix of CVE-2018-19788, high UIDs caused overflow in polkit; - Fix of CVE-2019-6133, kernel vulnerability (Slowfork) allowed local privilege escalation. ### Build requirements: - glib, gobject, gio >= 2.32 - mozjs-60 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.115: - Kyle Walker: Leaking zombie child processes - Jan Rybar: Possible resource leak found by static analyzer Output messages tuneup Sanity fixes pkttyagent tty echo disabled on SIGINT - Ray Strode: HACKING: add link to Code of Conduct - Philip Withnall: polkitbackend: comment typos fix - Zbigniew Jędrzejewski-Szmek: `configure.ac`: fix detection of systemd with cgroups v2 CVE-2018-19788 High UIDs overflow fix - Colin Walters: CVE-2019-6133 Slowfork vulnerability fix - Matthew Leeds: Allow unset process-uid - Emmanuele Bassi Port the JS authority to mozjs-60 - Göran Uddeborg: Use JS_EncodeStringToUTF8 Many thanks to all contributors! Jan Rybar et al., April 25, 2019 --------------- polkit 0.115 -------------- **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.115**. ### Highlights: - Fixes CVE-2018-1116, a local information disclosure and denial of service caused by trusting client-submitted UIDs when referencing processes. Thanks to Matthias Gerstner of the SUSE security team for reporting this issue. ### Build requirements: - glib, gobject, gio >= 2.32 - mozjs-52 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.114: - Miloslav Trmač (1): Fix CVE-2018-1116: Trusting client-supplied UID - Ray Strode (3): Post-release version bump to 0.115 jsauthority: pass "%s" format string to remaining report function NEWS: fix date from 2017 to 2018 for 0.114 entry Thanks to our contributors. Colin Walters and Miloslav Trmač, July 10, 2018 -------------- polkit 0.114 -------------- **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.114**. ### Highlights: Port to mozjs 52, the latest version of the firefox JavaScript engine. Add gettext support for policy files Fixes for various memory leaks ### Build requirements: - glib, gobject, gio >= 2.32 - mozjs-52 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.113: - Anders Jonsson (2): pkcheck: fix man typos Add Swedish translation - Antoine Jacoutot (1): Add support for OpenBSD - Christian Kirbach (1): Add German translation - Colin Walters (3): build: Pull in GCC warning infra from ostree build: Use AC_USE_SYSTEM_EXTENSIONS tests: Correct boundary test for overflow - Dariusz Gadomski (2): Fix multi-line pam text info. Refactor send_to_helper usage - Gabor Kelemen (1): Add initial Hungarian translation, and add hu to LINGUAS - Jeremy Linton (5): change mozjs interface module to c++ Switch to hard requiring mozjs24 Fix warnings caused by building with C++ Replace autocompartment test: Add a test case to handle actions without explicit rules - Jiří Klimeš (1): trivial: fix deprecated indication for polkit_agent_register_listener() - Matthias Clasen (1): Add gettext support for .policy files - Miloslav Trmač (21): Post-release version bump to 0.114 Consistently use HAVE_NETGROUP_H instead of HAVE_OPENBSD Fix a memory leak of PolkitAgentListener's Server object Remove polkitbackendconfigsource.[ch] Add Slovak translation by Dusan Kazik Add Indonesian translation by Andika Triwidada Add Chinese (Taiwan) translation Fix a typo in polkit(8) Simplify GVariant reference counting Fix a memory leak on an error path of lookup_asv (twice) Fix a memory leak in server_handle_register_authentication_agent_with_options Fix a memory leak in server_handle_unregister_authentication_agent Fix a memory leak in server_handle_authentication_agent_response{,2} Fix memory leaks in server_handle_*_temporary_authorizations Fix error handling in polkit_authority_enumerate_temporary_authorizations_finish Fix a memory leak per agent authentication Fix a memory leak on agent authentication cancellation Audit and fix GVariant reference counting Fix help for (pkttyagent -s) Fix a race condition when terminating runaway_killer_thread Move to current GLib - Mingye Wang (Arthur2e5) (1): Add zh_CN translation - Muhammet Kara (1): Added Turkish translation - OBATA Akio (1): Add support for NetBSD - Peter Hutterer (1): gettext: switch to default-translate "no" - Philip Withnall (3): polkit: Add g_autoptr() support for GObject-derived polkit types data: Set GIO_USE_VFS=local in the environment polkitbackend: Fix typos in a couple of initialisation error messages - Piotr Drąg (1): Add Polish translation - Rafael Fontenelle (1): Add Brazilian Portuguese translation - Ray Strode (34): configure: bump mozjs requirement to 52 jsauthority: fix how classes are defined jsauthority: use JS_FN instead of JS_FS jsauthority: get rid of JSRuntime jsauthority: change how setVersion is called jsauthority: call JS_Init jsauthority: call JS_InitSelfHostedCode jsauthority: change how JIT is disabled jsauthority: JS::SetWarningReporter instead of JS_SetErrorReporter jsauthority: add UTF8 suffix to renamed functions jsauthority: pass "%s" format string to report functions jsauthority: s/JSBool/bool/ jsauthority: s/jsval/JS::Value/ jsauthority: s/JSVAL_NULL/JS::NullValue()/ jsauthority: s/JSVAL_VOID/JS::UndefinedValue()/ jsauthority: s/OBJECT_TO_JSVAL/JS::ObjectValue/ jsauthority: s/STRING_TO_JSVAL/JS::StringValue/ jsauthority: s/BOOLEAN_TO_JSVAL/JS::BooleanValue/ jsauthority: JSVAL_TO_OBJECT (o) to o.toObjectOrNull() jsauthority: JSVAL_TO_STRING (s) to s.toString() jsauthority: JSVAL_IS_STRING (s) to s.isString() jsauthority: JSVAL_IS_NULL (o) to o.isNull() jsauthority: Fix up JS_CallFunctionName invocations jsauthority: use InterruptCallback api instead of OperationCallback jsauthority: redo how global objects are set up jsauthority: root some locals to the context jsauthority: adapt arguments for new JS::Compile API jsauthority: adapt arguments for new JS_ExecuteScript API jsauthority: use JS::Evaluate instead of JS_EvaluateScript jsauthority: fix up set_property methods jsauthority: stop using JS_GetStringCharsZ jsauthority: switch from JS_ConvertArguments to JS::CallArgsFromVp jsauthority: re-enable JIT Port JavaScript authority to mozjs52 - Rui Matos (1): polkitpermission: Fix a memory leak on authority changes - Sebastien Bacher (1): Support polkit session agent running outside user session - Stef Walter (2): polkitagent: Fix access after dereference on hashtable polkitagent: No double warnings in polkit_agent_listener_register() - Sven Eden (1): configure: enable elogind support in PolicyKit - Yuri Chornoivan (1): Add Ukrainian translation - enkore (1): Fix abnomal formatting of authentication header lines - muzena (1): Add hr.po Thanks to our contributors. Colin Walters and Miloslav Trmač, April 2, 2018 -------------- polkit 0.113 -------------- NOTE: This release is an important security update, see below. **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.113**. ### Highlights: Fixes CVE-2015-4625, a local privilege escalation due to predictable authentication session cookie values. Thanks to Tavis Ormandy, Google Project Zero for reporting this issue. For the future, authentication agents are encouraged to use PolkitAgentSession instead of using the D-Bus agent response API directly. Fixes CVE-2015-3256, various memory corruption vulnerabilities in use of the JavaScript interpreter, possibly leading to local privilege escalation. Fixes CVE-2015-3255, a memory corruption vulnerability in handling duplicate action IDs, possibly leading to local privilege escalation. Thanks to Laurent Bigonville for reporting this issue. Fixes CVE-2015-3218, which allowed any local user to crash polkitd. Thanks to Tavis Ormandy, Google Project Zero, for reporting this issue. On systemd-213 and later, the “active” state is shared across all sessions of an user, instead of being tracked separately. (pkexec), when not given a program to execute, runs the users’ shell by default. ### Build requirements: - glib, gobject, gio >= 2.30 - mozjs185 or mozjs-17.0 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.112: - Colin Walters (17): PolkitSystemBusName: Add public API to retrieve Unix user examples/cancel: Fix to securely lookup subject sessionmonitor-systemd: Deduplicate code paths PolkitSystemBusName: Retrieve both pid and uid Port internals non-deprecated PolkitProcess API where possible Use G_GNUC_BEGIN_IGNORE_DEPRECATIONS to avoid warning spam pkexec: Work around systemd injecting broken XDG_RUNTIME_DIR pkexec: Support just plain "pkexec" to run shell .dir-locals: Style for Emacs - we don't use tabs authority: Avoid cookie wrapping by using u64 counter CVE-2015-3218: backend: Handle invalid object paths in RegisterAuthenticationAgent build: Start using git.mk Revert "authority: Avoid cookie wrapping by using u64 counter" authority: Add a helper method for checking whether an identity is root CVE-2015-4625: Use unpredictable cookie values, keep them secret CVE-2015-4625: Bind use of cookies to specific uids README: Note to send security reports via DBus's mechanism - Kay Sievers (1): sessionmonitor-systemd: prepare for D-Bus "user bus" model - Lukasz Skalski (1): polkitd: Fix problem with removing non-existent source - Max A. Dednev (1): authority: Fix memory leak in EnumerateActions call results handler - Miloslav Trmač (24): Post-release version bump to 0.113 Don't discard error data returned by polkit_system_bus_name_get_user_sync Fix a memory leak Refuse duplicate --user arguments to pkexec Fix a possible NULL dereference. Remove a redundant assignment. Simplify forced error domain registration Fix a typo, s/Evaluting/Evaluating/g s/INCLUDES/AM_CPPFLAGS/g Fix duplicate GError use when "uid" is missing Fix a crash when two authentication requests are in flight. docs: Update for changes to uid binding/AuthenticationAgentResponse2 Don't pass an uninitialized JS parameter Don't add extra NULL group to subject.groups Don't store unrooted jsvals on heap Fix a per-authorization memory leak Fix a memory leak when registering an authentication agent Wrap all JS usage within “requests” Register heap-based JSObject pointers to GC Prevent builds against SpiderMonkey with exact stack rooting Clear the JS operation callback before invoking JS in the callback Fix spurious timeout exceptions on GC Fix GHashTable usage. Fix use-after-free in polkitagentsession.c - Philip Withnall (1): sessionmonitor-systemd: Use sd_uid_get_state() to check session activity - Rui Matos (1): PolkitAgentSession: fix race between child and io watches - Simon McVittie (1): Use libsystemd instead of older libsystemd-login if possible - Ting-Wei Lan (1): build: Fix several issues on FreeBSD - Xabier Rodriguez Calvar (1): Fixed compilation problem in the backend Thanks to our contributors. Colin Walters and Miloslav Trmač, July 2, 2015 -------------- polkit 0.112 -------------- NOTE: This release is an important security update, see below. **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.112**. ### Highlights: This release fixes CVE-2013-4288: Race condition with process subjects that do not have securely determined uid. pkcheck(1) now supports a new format for the --process argument; all applications need to use the new format to avoid a race condition (or use --system-bus-name to identify the process instead). Similarly, applications using the API should always use polkit_unix_process_new_for_owner(). polkit_unix_process_new() and polkit_unix_process_new_full() are unsafe and have been deprecated. Thanks to Sebastian Krahmer of the SUSE Security Team for reporting this issue. ### Build requirements: - glib, gobject, gio >= 2.30 - mozjs185 or mozjs-17.0 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.111: - Colin Walters (2): polkitunixprocess: Deprecate racy APIs pkcheck: Support --process=pid,start-time,uid syntax too - Miloslav Trmač (1): Post-release version bump to 0.112 - Tomas Bzatek (1): Use GOnce for interface type registration - Tomas Chvatal (2): Add czech translation po file to distribution. Update the czech once more with newest pot file. Thanks to our contributors. Colin Walters and Miloslav Trmač, September 18, 2013 -------------- polkit 0.111 -------------- **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.111**. ### Highlights: The JavaScript interpreter is now mandatory. Both js185 and mozjs17 versions of SpiderMonkey are supported. ### Build requirements: - glib, gobject, gio >= 2.30 - mozjs185 or mozjs-17.0 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.110: - Colin Walters (6): mocklibc: Only require autoconf 2.63 configure: Specify GLib min/max version jsauthority: We can really only handle a string jsauthority: Use JSVAL_NULL rather than {0} struct initialization Revert "Dynamically load libmozjs185.so and cope with it not being available" jsauthority: Work with mozjs-17.0 too - David Zeuthen (1): Post-release version bump to 0.111 - Giovanni Campagna (1): build: try harder to avoid systemd/consolekit misconfiguration - Michael Biebl (1): man: Fix pkaction man page wrt to --action-id option - Miloslav Trmač (28): Clean (git status) after autogen.sh Fix build with srcdir!=builddir Fix DOC_SOURCE_DIR for builddir != srcdir Fix various memory leaks. Add annotation glossary Leave out backend from gtk-doc generation Fix most "undocumented symbol" warnings Move polkit_temporary_authorization_new to private header file. Include documentation of polkit_action_description_get_annotation_keys Document deprecated functions. Fold enum documentation into relevant classes Fix an obvious docstring typo. Add annotations for element types of returned lists Add a FIXME to polkitprivate.h Use auth_admin* instead of auth_self* in examples More warnings about using auth_self* Fix a TypeError when no admin rules are registered Fix handling of null returned from _runRules Refuse non-string parameters to Polkit.spawn() Drop unused variable Fix a memory leak Remove an unused va_start Don't spawn man for --help Fix package version / bug report address mixing Add bug reporting address and home page to --help output Refuse unrecognized command-line operands Exit pkaction with status 0 on success Fix inclusion of COPYING into documentation with srcdir != builddir - Nuno Araujo (1): Fix the build with automake 1.13 - Samuli Suominen (1): Add missing #include - Steve Langasek (1): pkexec: Set process environment from pam_getenvlist() - Vincent Untz (1): polkitagent, pkexec: Respect SUID_CFLAGS and SUID_LDFLAGS - darkxst (1): update types for js188 Thanks to our contributors. Miloslav Trmač, May 15, 2013 -------------- polkit 0.110 -------------- **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.110**. ### Build requirements: - glib, gobject, gio >= 2.30 - mozjs185 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.109: - David Zeuthen (5): Post-release version bump to 0.110 Set XAUTHORITY environment variable if is unset Use mutex and condition variables properly Remove AM_PROG_CC_STDC Update NEWS for release - Emilio Pozuelo Monfort (1): Fix build on GNU Hurd - Michael Biebl (1): build: Remove generated introspection files on "make clean" Thanks to our contributors. David Zeuthen, Jan 9, 2013 -------------- polkit 0.109 -------------- **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.109**. ### Build requirements: - glib, gobject, gio >= 2.30 - mozjs185 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.108: - David Zeuthen (4): Post-release version bump to 0.109 Include gmodule-2.0 to avoid linker errors Don't require libmozjs185 devel packages for polkit rules to work Update NEWS for release Thanks to our contributors. David Zeuthen, December 19, 2012 -------------- polkit 0.108 -------------- **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.108**. ### Build requirements: - glib, gobject, gio >= 2.30 - mozjs185 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.107: - Adam Jackson (1): PolkitAgent: Avoid crashing if initializing the server object fails - David Zeuthen (5): Post-release version bump to 0.108 Fall back to authenticating as uid 0 if the list of admin identities is empty Dynamically load libmozjs185.so and cope with it not being available docs: mention the audience for authorization rules Update NEWS for release - Ryan Lortie (1): build: Fix .gir generation for parallel make Thanks to our contributors. David Zeuthen, November 14, 2012 -------------- polkit 0.107 -------------- **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.10**. ### Build requirements: - glib, gobject, gio >= 2.30 - mozjs185 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.106: - David Zeuthen (7): Post-release version bump to 0.107 Update guidance on situations where there is no polkit authority Nuke leftover polkit-backend-1.pc file Nuke --enable-verbose flag Introduce a polkit.Result enumeration for authorization rules pkexec: add support for argv1 annotation and mention shebang-wrappers Update NEWS for release - Matthias Clasen (1): Try harder to look up the right localization Thanks to our contributors. David Zeuthen, July 11, 2012 -------------- polkit 0.106 -------------- **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. This is polkit **0.106. There's a major change in this release which i**. a switch from .pkla files (keyfile-format) to .rules files (JavaScript), see http://davidz25.blogspot.com/2012/06/authorization-rules-in-polkit.html for more information. ### Build requirements: - glib, gobject, gio >= 2.30 - mozjs185 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.105: - Colin Walters (3): build: Check for mozjs185, not libjs autogen.sh: Fix check for libtool (we only need libtoolize) agenthelper-pam: Fix newline-trimming code - David Zeuthen (65): Post-release version bump to 0.106 Add experimental authority backend using JavaScript rule files Include seat and session in Subject object Pass details to JS functions and simplify how Subject instances are constructed Clean up code a bit Add a couple of more error checks Collect garbage Emit ::Changed signal after reloading rules Reformat init.js and also avoid quoting non-string properties in toString() Make it possible for JS code to change details Add polkit.spawn() to spawn external programs Make polkit.spawn() take an array of arguments instead of a command-line Don't include command-line in spawning error messages docs: add AUTHORIZATION RULES section to the polkit(8) man page Also add an example of polkit.spawn() to polkit(8) man page docs: clarify how rules files work Also load rules from /usr/share/polkit/rules.d Use addRule() and addAdminRule() docs: emphasize that registered functions may actually never be called Add test cases for evaluation order Test that subject.isInGroup() works Add netgroup support Minor doc fixes Mention unix-netgroup:xyz as a valid return value in addAdminRule() functions Add test-cases and 10 second timeout for polkit.spawn() Create rules.d directories Update docs docs: enclose local in to make links work docs: update SEE ALSO to make each man page point to all other man pages Clarify docs a bit polkitd: add reference to polkit(8) from its man page Fix speling Fix a couple typos in the docs Mention details["polkit.message"] and add an example using details Use instead of for Subject attributes Make polkit_details_insert() remove the key if passed value is NULL Add real-world example featuring udisks2 and the drive.* variables it passes Rename --enable-systemd to --enable-libsystemd-login Fix distcheck Add a systemd .service file Nuke polkitbackend library, localauthority backend and extension system Mention systemd(1) in the polkitd(8) man page Store private binaries in /usr/lib/polkit-1 instead of /usr/libexec Add default rules Pass expanded identity list to the AuthenticationSession Use "rules", not "scripts" to refer to files in rules.d Terminate runaway scripts Use a condition variable to signal that runaway killer thread is ready Combine action and details parameters Clarify pkexec(1) variables Use g_unix_signal_add() from GLib 2.30 Move polkitd into src/polkitbackend Ensure polkitd is rebuilt if libpolkit-backend-1.la changes Remove unused DBUS_GLIB_* and GIO_* variables Run polkitd as an unprivileged user Log when the name org.fd.PolicyKit1 has been acquired Rewrite the "Writing polkit applications" chapter Update links to udisks docs Update pkexec(1) man page with example Small updates to the "Writing polkit applications" chapter State that authorization rules must not rely on SpiderMonkey features Make it work when using ConsoleKit instead of libsystemd-login Mention the implications of returning *_keep in an authorization rule docs: add a "make sure your app works when there's no polkitd(8)" note Update NEWS for release Thanks to our contributors. David Zeuthen, June 7, 2012 -------------- polkit 0.105 -------------- This is polkit **0.10**. **WARNING:** This is a prerelease on the road to polkit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.28 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since polkit 0.104: - David Zeuthen (11): Post-release version bump PolkitUnixSession: Set error if we cannot find a session for the given pid PolkitUnixSession: Actually return TRUE if a session exists PolkitAgentSession: Don't leak file descriptors Add pkttyagent(1) helper Make it possible to influence agent registration with an a{sv} parameter Fix type in docs Mention pkttyagent(1) in "Writing PolicyKit applications" chapter Update the docs to use 'polkit' (instead of 'PolicyKit') as the name Add Makefile rules for signing and publishing releases and docs Update NEWS for release - Ryan Lortie (1): Various builddir != srcdir fixes Thanks to our contributors. David Zeuthen, April 24, 2012 -------------- PolicyKit 0.104 -------------- This is polkit **0.104** **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.28 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - ConsoleKit OR systemd ### Changes since PolicyKit 0.103: - David Zeuthen (3): Post-release version bump to 0.104 Detect whether systemd is available and default to use if so Update NEWS for release - Matthias Clasen (1): Add optional systemd support - Nikki VonHollen (2): Bug 43608 – Add unit tests Bug 43610 - Add netgroup support Thanks to our contributors. David Zeuthen, January 3, 2012 -------------- PolicyKit 0.103 -------------- This is polkit **0.103** **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.28 - gobject-introspection >= 0.6.2 (optional) - pam (optional) - IMPORTANT: As of release 0.103, the default Authority backend now - defaults to allowing members of the 'wheel' group to authenticate as - an administator since this is common usage in popular Linux - distributions. Distributors can change this by patching the - 50-localauthority.conf file in /etc/polkit-1/localauthority.conf.d as - needed. ### Changes since PolicyKit 0.102: - Alan Near (1): Mistype in DBus object: PoliycKit1 -> PolicyKit1 - David Zeuthen (7): Post-release version bump to 0.103 Add support for the org.freedesktop.policykit.imply annotation Add --no-debug option and use this for D-Bus activation Bug 41025 – Add org.freedesktop.policykit.owner annotation Default to AdminIdentities=unix-group:wheel for local authority Update NEWS for release Fix typo Thanks to our contributors. David Zeuthen, December 6, 2011 -------------- PolicyKit 0.102 -------------- This is polkit **0.102** **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.28 - gobject-introspection >= 0.6.2 (optional) - pam (optional) ### Changes since PolicyKit 0.101: - Benjamin Otte (1): introspection: Add --c-include to the gir files - David Zeuthen (7): Post-release version bump to 0.102 Don't show diagnostic messages intended for the administrator to the end u PolkitUnixProcess: Clarify that the real uid is returned, not the effectiv Make PolkitUnixProcess also record the uid of the process Use polkit_unix_process_get_uid() to get the owner of a process pkexec: Avoid TOCTTOU problems with parent process Update NEWS for release - Evan Nemerson (1): Specify exported pkg-config files in GIRs - Marc Deslauriers (1): Fix multi-line pam prompt handling - Martin Pitt (3): Ignore .po/ for intltool Fix backend crash if a .policy file does not specify Bug 38769 — pkexec: Support running X11 apps Thanks to our contributors. David Zeuthen, August 1, 2011 -------------- PolicyKit 0.101 -------------- This is polkit **0.101** **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.28 - gobject-introspection >= 0.6.2 (optional) - pam (optional) ### Changes since PolicyKit 0.100: - Adrian Bunk (1): Bug 27253 – Use GOBJECT_INTROSPECTION_CHECK from gobject-introspection - David Zeuthen (16): Post-release version bump to 0.101 Bug 30653 – No way to detect cancellation in pkexec Bug 27081 – pkexec fails to build on non glibc systems Bug 30438 – PolicyKit fails to build on AIX Bug 32334 – Always set polkit.retains_authorization_after_challenge Fix a memory leak Be more specific about what info we want when enumerating files Make pkcheck(1) report if the authentication dialog was dismissed pkcheck: Make it possible to list and revoke temporary authorizations Be a bit more careful parsing the command-line Bug 29712 – Use monotonic for temporary authorizations Allow overriding message shown in authentication dialog Deprecated PolkitBackendActionLookup Fix a couple of warnings triggered by gcc 4.6 Build examples by default and fix compiler warnings Update NEWS for release - Michael Biebl (1): Bug 29871 – Fix build failures with binutils-gold Thanks to our contributors. David Zeuthen, March 3, 2011 -------------- PolicyKit 0.100 -------------- This is polkit **0.100** **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.25.12 - gobject-introspection >= 0.6.2 (optional) - pam (optional) ### Changes since PolicyKit 0.99: - David Zeuthen (12): Post-release version bump to 0.100 Add missing GObject Introspection annotations Build gir/typelib for PolkitAgent-1.0 Fix-up PolkitAgentSession to use GObject properties Improve error reporting for authentication sessions Add some debug info that can be shown with the env var POLKIT_DEBUG Fix up debug and timeouts in agent helper Always pass non-zero value to g_once_init_leave() Add a note about POLKIT_DEBUG Pass caller and subject pid to authentication agent Update NEWS for release Fix 'make distcheck' Thanks to our contributors. David Zeuthen, February 21, 2011 -------------- PolicyKit 0.99 -------------- This is polkit **0.99** **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.25.12 - gobject-introspection >= 0.6.2 (optional) - pam (optional) ### Changes since PolicyKit 0.98: - Colin Walters (3): Remove duplicate definitions of enumeration types Fix (correct) GCC warning about possibly-uninitialized variable Fix another GCC uninitialized variable warning - David Zeuthen (2): Post-release version bump to 0.99 Update NEWS for release - Vincent Untz (1): Bug 29816 – Install polkitagentenumtypes.h Thanks to our contributors. David Zeuthen, September 15, 2010 -------------- PolicyKit 0.98 -------------- This is polkit **0.98**. **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.25.12 - gobject-introspection >= 0.6.2 (optional) - pam (optional) ### Changes since PolicyKit 0.97: - David Zeuthen (11): Post-release version bump to 0.98 Require GLib 2.25.12 Fix scanning of unix-process subjects Add textual authentication agent and use it in pkexec(1) Fix ConsoleKit interaction bug pkexec: add --disable-internal-agent option pkcheck: add --enable-internal-agent option Fix wording in pkexec(1) man page Various doc cleanups Fix dist-check Update NEWS for release Thanks to our contributors. David Zeuthen, August 20, 2010 -------------- PolicyKit 0.97 -------------- This is polkit **0.97**. **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. The main change since the previous version is a port from eggdbus to GLib's new D-Bus implementation. Other changes includes various bug fixes and support for shadow authentication. Support for the AddLockdown() and RemoveLockdown() methods has been removed. You will need an updated version of PolicyKit-gnome to go with this release. ### Build requirements: - glib, gobject, gio >= 2.25.11 - gobject-introspection >= 0.6.2 (optional) - pam (optional) ### Changes since PolicyKit 0.96: - Andrew Psaltis (1): Add shadow support - Dan Rosenberg (1): Bug 26982 – pkexec information disclosure vulnerability - David Zeuthen (23): Post-release version bump to 0.97 Port core bits to gdbus Port CK class to gdbus Port PolkitBackendInteractiveAuthority to gdbus Port PolkitAgent to gdbus Add generated docbook D-Bus API docs to git Nuke eggdbus usage Make polkitd accept --replace and gracefully handle SIGINT Implement polkit_temporary_authorization_new_for_gvariant() Remove Lock Down functionality Make NameOwnerChanged a private impl detail of the interactive authority Update README Merge remote branch 'origin/gdbus' Add a GPermission implementation PolkitAuthority: Implement failable initialization PolkitAuthority: Add g_return_if_fail() checks Add g_return_if_fail() to all public API entry points Use polkit_authority_get_sync() instead of deprecated polkit_authority_get PolkitBackend: Don't export unneeded convenience API Update GI annotations Don't dist org.freedesktop.ConsoleKit.xml; It's dead, Jim Properly reference headers Update NEWS for release - Petr Mrázek (1): Bug 29051 – Configuration reload on every query Thanks to our contributors. David Zeuthen, August 9, 2010 -------------- PolicyKit 0.96 -------------- This is polkit **0.96**. This is supposed to be the last release until 1.0. **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.21.4 - eggdbus-1 >= 0.6 - gobject-introspection >= 0.6.2 (optional) - pam ### Changes since PolicyKit 0.95: - David Zeuthen (15): Bug 25367 — Also read local authority configuration data from /etc Fix logic error in pk-example-frobnicate Run the open_session part of the PAM stack in pkexec(1) Fix up last comment Bug 25594 – System logging Remove trailing whitespace from log messages Properly handle return value from getpwnam_r() Fix error message when no authentication agent is available Make pkexec(1) validate environment variables Make pkexec(1) use the syslogging facilities Save original cwd in pkexec(1) since it will change during the life-time Complain on stderr, not stdout Post-release version bump to 0.96 Don't log authorization checks Update NEWS for release David Zeuthen, January 15, 2010 -------------- PolicyKit 0.95 -------------- This is polkit **0.95**. This is supposed to be the last release until 1.0. **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.21.4 - eggdbus-1 >= 0.6 - gobject-introspection >= 0.6.2 (optional) - pam ### Changes since PolicyKit 0.94: - Alexander Sack (1): Bug 24566 – Properly _ref authority in singleton constructor - Andreas Sandberg (1): Bug 24235 – polkit-agent-helper may call pam_end with a stale pam handle - Bastien Nocera (1): Fix process start time when using polkit_unix_process_new_full() - David Zeuthen (20): Post-release version bump to 0.95 Use correct program name when complaining about not being setuid root Sort by action id in pkaction(1) output Bug 23867 – UnixProcess vs. SystemBusName aliasing Implement lockdown for the Local Authority implementation Remove POLKIT_USER from configuration summary Add missing comma so we're save both LANG and LANGUAGE, not only LANGLANGUAGE Pass --libtool to g-ir-scanner Clarify comment on where to find process start-time on Linux Add properties with information about the currently used authority Clarify when AllowUserInteraction should and shouldn't be used Add methods AddLockdownForAction() and RemoveLockdownForAction() Port lockdown from pklalockdown(1) to D-Bus methods Drop ununsed policykit actions Remove TODO about symbol visibility as this has been fixed for a while Clarify pklocalauthority(8) man page Properly validate all arguments passed via D-Bus Add Python example Fix make distcheck Update NEWS for release - Matthias Clasen (1): Bug 24640 – Typos in pklocalauthority(8) - Michael Biebl (8): Trim the list of exported symbols Use _polkit_agent_marshal prefix Make private symbols accessible to libpolkitagent and libpolkitbackend Make examples optional Enable silent rules Remove POLKIT_USER option Don't include Polkit-1.0.gir in the dist tarball Bug 24176 – Current git master fails to build, GLIB_LDADD -> GLIB_LIBS - Samuel Thibault (1): Bug 24495 – Fails to build on platforms without PATH_MAX (like hurd) David Zeuthen, November 13, 2009 -------------- PolicyKit 0.94 -------------- This is polkit **0.94**. **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.21.4 - eggdbus-1 >= 0.5 - gobject-introspection >= 0.6.2 (optional) - pam ### Changes since PolicyKit 0.93: - David Zeuthen (13): Post-release version bump to 0.94 Require correct versions of glib and eggdbus Ignore .pkla files starting with dot and don't segfault on error path Allow unprivileged callers to check authorizations Don't spawn man(1) from a setuid program Add polkit.retains_authorization_after_challenge to authz result Ensure all fds except stdin/stdout/stderr are closed after exec(2) Be more careful when determining process start time Pass the right struct offset for the ::changed class signal handler Don't set the GError if the process doesn't exist Remove temporary authorization when the subject it applies to vanishes Generate GI gir and typelibs for libpolkit-gobject-1 Update NEWS for release - Joe Marcus Clarke (1): Bug 23093 – FreeBSD portability fixes David Zeuthen, August 12, 2009 -------------- PolicyKit 0.93 -------------- This is polkit **0.93**. **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.21.4 - eggdbus-1 >= 0.5 - pam ### Changes since PolicyKit 0.92: - David Zeuthen (16): Post-release version bump to 0.93 GIO modules need to be prefix with lib Cancel an authentication if the unique name for the subject vanishes Plug a couple of memory leaks Move local authority management to a separate library Rip out polkit-local and refactor local authority to only use tmp authz Move authentication agent bits to separate authority subclass Also pass the identity of the subject we are checking for Actually make the local authority look up authorization files In .pkla files, use Result{Any,Inactive,Active} instead of just Result Rename some man pages and the daemon binary Add docs detailing how the Local Authority works Add support for querying and revoking temporary authorizations Fix make distcheck Update TODO Update NEWS for release - Yanko Kaneti (2): Use unique ids for sections to prevent them being autogenerated More unique ids to get the docs build fully predictable David Zeuthen, July 20th, 2009 -------------- PolicyKit 0.92 -------------- This is polkit **0.92**. **WARNING:** This is a prerelease on the road to PolicyKit 1.0. Public API might change and certain parts of the code still needs some security review. Use at your own risk. ### Build requirements: - glib, gobject, gio >= 2.14 - eggdbus-1 >= 0.4 - pam ### Changes since PolicyKit 0.91: - David Zeuthen (36): post-release version bump to 0.92 install gtkdoc HTML in the proper location Fix D-Bus policy to work with non-permissive D-Bus Only allow privileged apps to check authz and add ActionLookup interface Change the PolkitAuthorizationResult enumeration into an object Port examples and command-line tools to new API Move docs to proper location Add a pkexec(1) command Mention /usr/bin/pkexec in the configure blurb Fix a bug where details were not shown for normal pkexec usage Use an object, not a GHashTable when passing details around Forgot to add source for PolkitDetails Change the defaults for .run-frobnicate to auth_self_keep Require eggdbus-1 >= 0.4 Only free hash table if it's not NULL Avoid returning an error if no authentication agent is available Clarify docs for is_challenge member of the AuthorizationResult struct Add pkcheck(1) command to check for authorizations nullbackend: Catch up with latest API changes Return the icon name instead of a GIcon in PolkitActionDescription Add pkaction(1) and nuke polkit-1(1) commands Update SEE ALSO sections in man pages Add a man page for polkit-1(8) First cut at some high-level docs Improve pkexec(1) man page by adding screenshots of authentication dialogs Add some more API docs Add a "PolicyKit Overview" section to the docs Consolidate all gtk-doc stuff in docs/polkit Expand on the D-Bus docs Use .../extensions instead of ../backends for loading extensions Minor doc fixes Move the doc chapters around a bit Change GNOME to freedesktop.org in the docs Fix make distcheck Update NEWS Also dist polkitd-1.xml - Richard Hughes (2): fix up gtk-doc API markup for a couple of functions add a draft version of the porting guide -- WIP David Zeuthen, June 8, 2009 polkit-126/README.md000066400000000000000000000046471474122443600141670ustar00rootroot00000000000000OVERVIEW ======== polkit is a toolkit for defining and handling authorizations. It is used for allowing unprivileged processes to speak to privileged processes. DOCUMENTATION ============= Latest documentation, reference manual and API description of polkit can be still found on project's previous instance of Gitlab Pages https://polkit.pages.freedesktop.org/polkit Older reference can be found at https://www.freedesktop.org/software/polkit/docs/latest/ RELEASES ======== Latest releases are available at [the Release page](https://github.com/polkit-org/polkit/releases). Older releases are still available as tarballs at https://www.freedesktop.org/software/polkit/releases/ To verify the authenticity of the compressed tarball, use this command ``` bash $ gpg --verify polkit-$(VERSION).tar.gz.sign polkit-$(VERSION).tar.gz $ gpg: Signature made Tue 23 Apr 2019 04:19:29 PM CEST using RSA key ID FFDCE258 $ gpg: Good signature from "Jan Rybar (Red Hat) " ``` Public key available at https://keys.openpgp.org/vks/v1/by-fingerprint/7FFB7D6BD83147D74284E3178CEB3030FFDCE258 BUGS and DEVELOPMENT ==================== Please report non-security bugs via the polkit's GitHub issues at https://github.com/polkit-org/polkit/issues The other way, in case of **non**-security issues, is to contact developers via official polkit's FreeDesktop.org mailing list at polkit-devel@lists.freedesktop.org SECURITY ISSUES =============== Please report any security issues not yet known to public by sending mail to polkit-security@redhat.com or use GitHub's button for reporting a vulnerability when creating an issue. Thank you. BUILD INSTRUCTIONS ================== **polkit** uses [meson build system](https://mesonbuild.com/) for configuration with *ninja* as backend and *gcc* as compiler. To configure and compile your copy of polkit tarball, simply follow meson build instructions in the following manner: ``` $ meson setup [[-D option]...] target_directory $ meson compile -C target_directory ... # meson install -C target_directory ``` List of available configuration options can be obtained with `meson configure` command. I WANT TO CONTRIBUTE ==================== Your pull requests and patch suggestions are welcome! If you want to contribute, a pull request on this GitHub project is a preferred way, yet not the only one. Please consult other options with this upsteam's maintainers. Thank you in advance. polkit-126/actions/000077500000000000000000000000001474122443600143355ustar00rootroot00000000000000polkit-126/actions/meson.build000066400000000000000000000003121474122443600164730ustar00rootroot00000000000000policy = 'org.freedesktop.policykit.policy' i18n.merge_file( input: policy + '.in', output: '@BASENAME@', po_dir: po_dir, data_dirs: its_dir, install: true, install_dir: pk_pkgactiondir, ) polkit-126/actions/org.freedesktop.policykit.policy.in000066400000000000000000000014421474122443600232730ustar00rootroot00000000000000 The polkit project http://www.freedesktop.org/wiki/Software/polkit/ Run a program as another user Authentication is required to run a program as another user auth_admin auth_admin auth_admin polkit-126/data/000077500000000000000000000000001474122443600136065ustar00rootroot00000000000000polkit-126/data/meson.build000066400000000000000000000025531474122443600157550ustar00rootroot00000000000000service_conf = { 'libprivdir': pk_prefix / pk_libprivdir, 'polkitd_user': polkitd_user, 'polkitd_uid': polkitd_uid, } configure_file( input: 'org.freedesktop.PolicyKit1.service.in', output: '@BASENAME@', configuration: service_conf, install: true, install_dir: dbus_system_bus_services_dir, ) configure_file( input: 'org.freedesktop.PolicyKit1.conf.in', output: '@BASENAME@', configuration: {'polkitd_user': polkitd_user}, install: true, install_dir: dbus_policydir, ) if enable_pam if os_type == 'debian' and pam_include == '' install_data( 'polkit-1.debian', rename: 'polkit-1', install_dir: pam_prefix ) else configure_file( input: 'polkit-1.in', output: '@BASENAME@', configuration: pam_conf, install: true, install_dir: pam_prefix, ) endif endif if not get_option('libs-only') configure_file( input: 'polkit.service.in', output: '@BASENAME@', configuration: service_conf, install: true, install_dir: systemdsystemunitdir, ) configure_file( input: 'polkit.conf.in', output: '@BASENAME@', configuration: service_conf, install: true, install_dir: sysusers_dir, ) install_data( 'policyconfig-1.dtd', install_dir: pk_datadir / 'polkit-1' ) install_data( 'polkit-tmpfiles.conf', install_dir: tmpfiles_dir ) endif polkit-126/data/org.freedesktop.PolicyKit1.AuthenticationAgent.xml000066400000000000000000000073551474122443600253670ustar00rootroot00000000000000 polkit-126/data/org.freedesktop.PolicyKit1.Authority.xml000066400000000000000000000666101474122443600234200ustar00rootroot00000000000000 polkit-126/data/org.freedesktop.PolicyKit1.conf.in000066400000000000000000000012141474122443600221500ustar00rootroot00000000000000 polkit-126/data/org.freedesktop.PolicyKit1.service.in000066400000000000000000000001751474122443600226700ustar00rootroot00000000000000[D-BUS Service] Name=org.freedesktop.PolicyKit1 Exec=@libprivdir@/polkitd --no-debug User=root SystemdService=polkit.service polkit-126/data/policyconfig-1.dtd000066400000000000000000000015611474122443600171310ustar00rootroot00000000000000 polkit-126/data/polkit-1.debian000066400000000000000000000004161474122443600164130ustar00rootroot00000000000000#%PAM-1.0 @include common-auth @include common-account @include common-password session required pam_env.so readenv=1 user_readenv=0 session required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0 @include common-session-noninteractive polkit-126/data/polkit-1.in000066400000000000000000000003251474122443600155760ustar00rootroot00000000000000#%PAM-1.0 auth include @PAM_FILE_INCLUDE_AUTH@ account include @PAM_FILE_INCLUDE_ACCOUNT@ password include @PAM_FILE_INCLUDE_PASSWORD@ session include @PAM_FILE_INCLUDE_SESSION@ polkit-126/data/polkit-agent-1.pc.in000066400000000000000000000004121474122443600172700ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: polkit-agent-1 Description: PolicyKit Authentication Agent API Version: @VERSION@ Libs: -L${libdir} -lpolkit-agent-1 Cflags: -I${includedir}/polkit-1 Requires: polkit-gobject-1 polkit-126/data/polkit-gobject-1.pc.in000066400000000000000000000010121474122443600176040ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ rulesdir=@datarootdir@/polkit-1/rules.d/ policydir=@datarootdir@/polkit-1/actions/ actiondir=@datarootdir@/polkit-1/actions/ Name: polkit-gobject-1 Description: PolicyKit Authorization API Version: @VERSION@ Libs: -L${libdir} -lpolkit-gobject-1 Cflags: -I${includedir}/polkit-1 Requires: gio-2.0 >= 2.18 glib-2.0 >= 2.18 # Programs using pkcheck can use this to determine # whether or not it can be passed a uid. pkcheck_supports_uid=true polkit-126/data/polkit-tmpfiles.conf000066400000000000000000000000561474122443600176010ustar00rootroot00000000000000d /etc/polkit-1/rules.d 0750 root polkitd - - polkit-126/data/polkit.conf.in000066400000000000000000000000621474122443600163620ustar00rootroot00000000000000u @polkitd_user@ @polkitd_uid@ "User for polkitd" polkit-126/data/polkit.service.in000066400000000000000000000014351474122443600171020ustar00rootroot00000000000000[Unit] Description=Authorization Manager Documentation=man:polkit(8) [Service] Type=notify-reload BusName=org.freedesktop.PolicyKit1 CapabilityBoundingSet=CAP_SETUID CAP_SETGID DeviceAllow=/dev/null rw DevicePolicy=strict ExecStart=@libprivdir@/polkitd --no-debug --log-level=notice User=@polkitd_user@ LimitMEMLOCK=0 LockPersonality=yes MemoryDenyWriteExecute=yes NoNewPrivileges=yes PrivateDevices=yes PrivateNetwork=yes PrivateTmp=yes ProtectControlGroups=yes ProtectHome=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectKernelTunables=yes ProtectSystem=strict ProtectClock=yes ProtectHostname=yes RemoveIPC=yes RestrictAddressFamilies=AF_UNIX RestrictNamespaces=yes RestrictRealtime=yes RestrictSUIDSGID=yes SystemCallArchitectures=native SystemCallFilter=@system-service UMask=0077 polkit-126/docs/000077500000000000000000000000001474122443600136255ustar00rootroot00000000000000polkit-126/docs/PORTING-GUIDE000066400000000000000000000041111474122443600155220ustar00rootroot00000000000000PolicyKit to polkit1 migration notes (DRAFT) Build files: • Replace polkit-dbus and polkit-grant with polkit-gobject-1 >= 0.91 • There's no polkit-policy-file-validate in 0.91 yet. • The install directory for *.policy files is now $datadir/polkit-1/actions not $datadir/PolicyKit/policy Policy files: • There's no more auth_admin_keep_always authorisation. Use auth_admin_keep instead, and rethink what you're trying to achieve. auth_admin_keep is kept for the lifetime of the PolkitSubject. • If you're getting messages about "action foo is not registered" then check the polkitd-1 daemon output -- it'll print the reason why the policy format is invalid to stdout. Source files: • Don't include , only include • No kit_* OOM handling in the new library • polkit_sysdeps_get_exe_for_pid() doesn't exist anymore, just read contents of /proc//cmdline • PolKitContext is now PolkitAuthority • PolKitError is now GError • polkit_bool_t is now gboolean • PolKitCaller is now PolkitSubject and a lot less powerful, but much quicker to create • polkit_caller_new_from_dbus_name() to polkit_system_bus_name_new() • PolKitAction is no more, just use the action_id everywhere. • polkit_context_is_caller_authorized() to polkit_authority_check_authorization_sync() • No need to do polkit_context_set_io_watch_functions() and icky g_io_add_watch() stuff to get changes from Polkit • polkit_context_new() to polkit_authority_get(), and polkit_context_unref() to g_object_unref() as expected • polkit_caller_get_pid() no longer exists, use http://cgit.freedesktop.org/PolicyKit/tree/src/polkit-dbus/polkit-dbus.c?id=POLICY_KIT_0_9#n417 • polkit_caller_get_uid() no longer exists, use dbus_bus_get_unix_user() as in http://cgit.freedesktop.org/PolicyKit/tree/src/polkit-dbus/polkit-dbus.c?id=POLICY_KIT_0_9#n411 • If you want data about the session from the PolKitContext, you have to ask ConsoleKit directly as in http://cgit.freedesktop.org/PolicyKit/tree/src/polkit-dbus/polkit-dbus.c?id=POLICY_KIT_0_9#n467 polkit-126/docs/TODO000066400000000000000000000011021474122443600143070ustar00rootroot00000000000000 Needed for 1.0 -------------- - check that all public but unstable API is properly guard off with I_KNOW_THIS_API_IS_SUBJECT_TO_CHANGE_ETC - man page review / section review - make sure library API is reasonably MT-safe - avoid watching all name owner changes in PolkitBackendAuthority and PolkitBackendServer; remove the name-owner-changed vfunc GNOME Authentication Agent -------------------------- - maybe expand on the notification icon so it is more detailed what temporary authorizations the session has - and maybe a way to only drop some of them polkit-126/docs/extensiondir.xml.in000066400000000000000000000000351474122443600174650ustar00rootroot00000000000000@libdir@/polkit-1/extensions polkit-126/docs/man/000077500000000000000000000000001474122443600144005ustar00rootroot00000000000000polkit-126/docs/man/meson.build000066400000000000000000000014261474122443600165450ustar00rootroot00000000000000xsltproc = find_program('xsltproc', required: false) assert(xsltproc.found(), 'xsltproc is required for man pages generation') xsltproc_cmd = [ xsltproc, '--output', '@OUTPUT@', '--nonet', '--stringparam', 'man.base.url.for.relative.links', pk_api_docpath + '/', 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl', '@INPUT@', ] mans = [ ['polkit', '8'], ['polkitd', '8'], ['pkexec', '1'], ['pkcheck', '1'], ['pkaction', '1'], ['pkttyagent', '1'], ] foreach man: mans xml = files(man[0] + '.xml') content_files += xml output = '@0@.@1@'.format(man[0], man[1]) custom_target( output, input: xml, output: output, command: xsltproc_cmd, install: true, install_dir: pk_mandir / ('man' + man[1]), ) endforeach polkit-126/docs/man/pkaction.xml000066400000000000000000000065331474122443600167410ustar00rootroot00000000000000 ]> pkaction May 2009 polkit pkaction 1 pkaction Get details about a registered action pkaction pkaction pkaction action DESCRIPTION pkaction is used to obtain information about registered polkit actions. If called without then all actions are displayed. Otherwise the action action. If called without the option only the name of the action is shown. Otherwise details about the actions are shown. RETURN VALUE On success pkaction returns 0. Otherwise a non-zero value is returned and a diagnostic message is printed on standard error. AUTHOR Written by David Zeuthen davidz@redhat.com with a lot of help from many others. BUGS Please send bug reports to either the distribution or the polkit-devel mailing list, see the link on how to subscribe. SEE ALSO polkit8, polkitd8, pkcheck1, pkexec1, pkttyagent1 polkit-126/docs/man/pkcheck.xml000066400000000000000000000203241474122443600165330ustar00rootroot00000000000000 ]> pkcheck May 2009 polkit pkcheck 1 pkcheck Check whether a process is authorized pkcheck pkcheck pkcheck pkcheck action pid pid,pid-start-time pid,pid-start-time,uid busname key value DESCRIPTION pkcheck is used to check whether a process, specified by either (see below) or , is authorized for action. The option can be used zero or more times to pass details about action. If is passed, pkcheck blocks while waiting for authentication. The invocation pkcheck --list-temp will list all temporary authorizations for the current session and pkcheck --revoke-temp will revoke all temporary authorizations for the current session. This command is a simple wrapper around the polkit D-Bus interface; see the D-Bus interface documentation for details. RETURN VALUE If the specified process is authorized, pkcheck exits with a return value of 0. If the authorization result contains any details, these are printed on standard output as key/value pairs using environment style reporting, e.g. first the key followed by a an equal sign, then the value followed by a newline. KEY1=VALUE1 KEY2=VALUE2 KEY3=VALUE3 ... Octets that are not in [a-zA-Z0-9_] are escaped using octal codes prefixed with \. For example, the UTF-8 string føl,你好 will be printed as f\303\270l\54\344\275\240\345\245\275. If the specified process is not authorized, pkcheck exits with a return value of 1 and a diagnostic message is printed on standard error. Details are printed on standard output. If the specified process is not authorized because no suitable authentication agent is available or if the wasn't passed, pkcheck exits with a return value of 2 and a diagnostic message is printed on standard error. Details are printed on standard output. If the specified process is not authorized because the authentication dialog / request was dismissed by the user, pkcheck exits with a return value of 3 and a diagnostic message is printed on standard error. Details are printed on standard output. If an error occurred while checking for authorization, pkcheck exits with a return value of 127 with a diagnostic message printed on standard error. If one or more of the options passed are malformed, pkcheck exits with a return value of 126. If stdin is a tty, then this manual page is also shown. NOTES Do not use either the bare pid or pid,start-time syntax forms for . There are race conditions in both. New code should always use pid,pid-start-time,uid. The value of start-time can be determined by consulting e.g. the proc5 file system depending on the operating system. If fewer than 3 arguments are passed, pkcheck will attempt to look up them up internally, but note that this may be racy. If your program is a daemon with e.g. a custom Unix domain socket, you should determine the uid parameter via operating system mechanisms such as PEERCRED. AUTHENTICATION AGENT pkcheck, like any other polkit application, will use the authentication agent registered for the process in question. However, if no authentication agent is available, then pkcheck can register its own textual authentication agent if the option is passed. AUTHOR Written by David Zeuthen davidz@redhat.com with a lot of help from many others. BUGS Please send bug reports to either the distribution or the polkit-devel mailing list, see the link on how to subscribe. SEE ALSO polkit8, polkitd8, pkaction1, pkexec1, pkttyagent1 polkit-126/docs/man/pkexec.xml000066400000000000000000000253651474122443600164140ustar00rootroot00000000000000 ]> pkexec May 2009 polkit pkexec 1 pkexec Execute a command as another user pkexec pkexec username PROGRAM ARGUMENTS DESCRIPTION pkexec allows an authorized user to execute PROGRAM as another user. If PROGRAM is not specified, the default shell will be run. If username is not specified, then the program will be executed as the administrative super user, root. RETURN VALUE Upon successful completion, the return value is the return value of PROGRAM. If the calling process is not authorized or an authorization could not be obtained through authentication or an error occurred, pkexec exits with a return value of 127. If the authorization could not be obtained because the user dismissed the authentication dialog, pkexec exits with a return value of 126. AUTHENTICATION AGENT pkexec, like any other polkit application, will use the authentication agent registered for the calling process or session. However, if no authentication agent is available, then pkexec will register its own textual authentication agent. This behavior can be turned off by passing the option. SECURITY NOTES Executing a program as another user is a privileged operation. By default the action to check for (see ) requires administrator authentication. In addition, the authentication dialog presented to the user will display the full path to the program to be executed so the user is aware of what will happen. The environment that PROGRAM will run it, will be set to a minimal known and safe environment in order to avoid injecting code through LD_LIBRARY_PATH or similar mechanisms. In addition the PKEXEC_UID environment variable is set to the user id of the process invoking pkexec. As a result, pkexec will not by default allow you to run X11 applications as another user since the $DISPLAY and $XAUTHORITY environment variables are not set. These two variables will be retained if the org.freedesktop.policykit.exec.allow_gui annotation on an action is set to a nonempty value; this is discouraged, though, and should only be used for legacy programs. pkexec will run PROGRAM in username's home directory, unless is used to override this behavior Note that pkexec does no validation of the ARGUMENTS passed to PROGRAM. In the normal case (where administrator authentication is required every time pkexec is used), this is not a problem since if the user is an administrator he might as well just run pkexec bash to get root. However, if an action is used for which the user can retain authorization (or if the user is implicitly authorized) this could be a security hole. Therefore, as a rule of thumb, programs for which the default required authorization is changed, should never implicitly trust user input (e.g. like any other well-written suid program). ACTION AND AUTHORIZATIONS By default, the org.freedesktop.policykit.exec action is used. To use another action, use the org.freedesktop.policykit.exec.path annotation on an action with the value set to the full path of the program. In addition to specifying the program, the authentication message, description, icon and defaults can be specified. If the org.freedesktop.policykit.exec.argv1 annotation is present, the action will only be picked if the first argument to the program matches the value of the annotation. Note that authentication messages may reference variables (see ), for example $(user) will be expanded to the value of the user variable. WRAPPER USAGE To avoid modifying existing software to prefix their command-line invocations with pkexec, it's possible to use pkexec in a she-bang wrapper like this: If this script is installed into /usr/bin/my-pk-test, then the following annotations /usr/bin/python /usr/bin/my-pk-test [...] ]]> can be used to select the appropriate polkit action. Be careful to get the latter annotation right, otherwise it will match any pkexec invocation of /usr/bin/python scripts. VARIABLES The following variables are set by pkexec. They can be used in authorization rules and messages shown in authentication dialogs: program Fully qualified path to the program to be executed. Example: /bin/cat command_line The requested command-line (do not use this for any security checks, it is not secure). Example: cat /srv/xyz/foobar user The user name of the user to execute the program as. Example: davidz user.gecos The full name of the user to execute the program as. Example: David Zeuthen user.display A representation of the user to execute the program as that is suitable for display in an authentication dialog. Is typically set to a combination of the user name and the full name. Example: David Zeuthen (davidz) AUTHOR Written by David Zeuthen davidz@redhat.com with a lot of help from many others. BUGS Please send bug reports to either the distribution or the polkit-devel mailing list, see the link on how to subscribe. SEE ALSO polkit8, polkitd8, pkaction1, pkcheck1, pkttyagent1 polkit-126/docs/man/pkttyagent.xml000066400000000000000000000126501474122443600173200ustar00rootroot00000000000000 ]> pkttyagent May 2009 polkit pkttyagent 1 pkttyagent Textual authentication helper pkttyagent pkttyagent pid pid,pid-start-time busname fd DESCRIPTION pkttyagent is used to start a textual authentication agent for the subject specified by either or . If neither of these options are given, the parent process is used. To get notified when the authentication agent has been registered either listen to the Changed D-Bus signal or use to pass the number of a file descriptor that has been passed to the program. This file descriptor will then be closed when the authentication agent has been successfully registered. If is used, the textual authentication agent will not replace an existing authentication agent. RETURN VALUE If the authentication agent could not be registered, pkttyagent exits with an exit code of 127. Diagnostic messages are printed on standard error. If one or more of the options passed are malformed, pkttyagent exits with an exit code of 126. If stdin is a tty, then this manual page is also shown. If the authentication agent was successfully registered, pkttyagent will keep running, interacting with the user as needed. When its services are no longer needed, the process can be killed. NOTES Since process identifiers can be recycled, the caller should always use pid,pid-start-time when using the option. The value of pid-start-time can be determined by consulting e.g. the proc5 file system depending on the operating system. If only pid is passed to the option, then pkttyagent will look up the start time itself but note that this may be racy. AUTHOR Written by David Zeuthen davidz@redhat.com with a lot of help from many others. BUGS Please send bug reports to either the distribution or the polkit-devel mailing list, see the link on how to subscribe. SEE ALSO polkit8, polkitd8, pkaction1, pkcheck1, pkexec1 polkit-126/docs/man/polkit.xml000066400000000000000000001230611474122443600164270ustar00rootroot00000000000000 ]> polkit February 2021 polkit polkit 8 polkit Authorization Manager OVERVIEW polkit provides an authorization API intended to be used by privileged programs (MECHANISMS) offering service to unprivileged programs (SUBJECTS) often through some form of inter-process communication mechanism. In this scenario, the mechanism typically treats the subject as untrusted. For every request from a subject, the mechanism needs to determine if the request is authorized or if it should refuse to service the subject. Using the polkit APIs, a mechanism can offload this decision to a trusted party: The polkit authority. The polkit authority is implemented as an system daemon, polkitd8, which itself has little privilege as it is running as the polkitd system user. Mechanisms, subjects and authentication agents communicate with the authority using the system message bus. In addition to acting as an authority, polkit allows users to obtain temporary authorization through authenticating either an administrative user or the owner of the session the client belongs to. This is useful for scenarios where a mechanism needs to verify that the operator of the system really is the user or really is an administrative user. SYSTEM ARCHITECTURE The system architecture of polkit is comprised of the Authority (implemented as a service on the system message bus) and an Authentication Agent per user session (provided and started by the user's graphical environment). Actions are defined by applications. Vendors, sites and system administrators can control authorization policy through Authorization Rules. | libpolkit-gobject-1 | +------------------+ +---------------------+ | polkitd(8) | +------------------+ | org.freedesktop. | | PolicyKit1 |<---------+ +------------------+ | ^ | | +--------------------------------------------+ | | /etc/polkit-1/actions/*.policy | | | /run/polkit-1/actions/*.policy | | | /usr/local/share/polkit-1/actions/*.policy | | | /usr/share/polkit-1/actions/*.policy | | +--------------------------------------------+ | +--------------------------------------------+ | /etc/polkit-1/rules.d/*.rules | | /run/polkit-1/rules.d/*.rules | | /usr/local/share/polkit-1/rules.d/*.rules | | /usr/share/polkit-1/rules.d/*.rules | +--------------------------------------------+ ]]> For convenience, the libpolkit-gobject-1 library wraps the polkit D-Bus API and is usable from any C/C++ program as well as higher-level languages supporting GObjectIntrospection such as JavaScript and Python. A mechanism can also use the D-Bus API or the pkcheck1 command to check authorizations. The libpolkit-agent-1 library provides an abstraction of the native authentication system, e.g. pam8 and also facilities for registration and communication with the polkit D-Bus service. See the developer documentation for more information about writing polkit applications. AUTHENTICATION AGENTS An authentication agent is used to make the user of a session prove that the user of the session really is the user (by authenticating as the user) or an administrative user (by authenticating as an administrator). In order to integrate well with the rest of the user session (e.g. match the look and feel), authentication agents are meant to be provided by the user session that the user uses. For example, an authentication agent may look like this: If the system is configured without a root account, it may prompt for a specific user designated as the administrative user: Applications that do not run under a desktop environment (for example, if launched from an ssh1 login) may not have an authentication agent associated with them. Such applications may use the PolkitAgentTextListener type or the pkttyagent1 helper so the user can authenticate using a textual interface. DECLARING ACTIONS A mechanism needs to declare a set of actions in order to use polkit. Actions correspond to operations that clients can request the mechanism to carry out and are defined in XML files that the mechanism installs into the /usr/share/polkit-1/actions directory. polkit actions are namespaced and can only contain the characters "[A-Z][a-z][0-9].-", e.g. ASCII, digits, period and hyphen. Each XML file can contain more than one action but all actions need to be in the same namespace and the file needs to be named after the namespace and have the extension .policy. The XML file must have the following doctype declaration ]]> The policyconfig element must be present exactly once. Elements that can be used inside policyconfig includes: vendor The name of the project or vendor that is supplying the actions in the XML document. Optional. vendor_url A URL to the project or vendor that is supplying the actions in the XML document. Optional. icon_name An icon representing the project or vendor that is supplying the actions in the XML document. The icon name must adhere to the Freedesktop.org Icon Naming Specification. Optional. action Declares an action. The action name is specified using the id attribute and can only contain the characters "[A-Z][a-z][0-9].- ", e.g. ASCII, digits, period and hyphen. Elements that can be used inside action include: description A human readable description of the action, e.g. Install unsigned software. message A human readable message displayed to the user when asking for credentials when authentication is needed, e.g. Installing unsigned software requires authentication. defaults This element is used to specify implicit authorizations for clients. Elements that can be used inside defaults include: allow_any Implicit authorizations that apply to any client. Optional. allow_inactive Implicit authorizations that apply to clients in inactive sessions on local consoles. Optional. allow_active Implicit authorizations that apply to clients in active sessions on local consoles. Optional. Each of the allow_any, allow_inactive and allow_active elements can contain the following values: no Not authorized. yes Authorized. auth_self Authentication by the owner of the session that the client originates from is required. Note that this is not restrictive enough for most uses on multi-user systems; auth_admin* is generally recommended. auth_admin Authentication by an administrative user is required. auth_self_keep Like auth_self but the authorization is kept for a brief period (e.g. five minutes). The warning about auth_self above applies likewise. auth_admin_keep Like auth_admin but the authorization is kept for a brief period (e.g. five minutes). annotate Used for annotating an action with a key/value pair. The key is specified using the key attribute and the value is specified using the value attribute. This element may appear zero or more times. See below for known annotations. vendor Used for overriding the vendor on a per-action basis. Optional. vendor_url Used for overriding the vendor URL on a per-action basis. Optional. icon_name Used for overriding the icon name on a per-action basis. Optional. For localization, description and message elements may occur multiple times with different xml:lang attributes. To list installed polkit actions, use the pkaction1 command. Known annotations The org.freedesktop.policykit.exec.path annotation is used by the pkexec program shipped with polkit - see the pkexec1 man page for details. The org.freedesktop.policykit.imply annotation (its value is a string containing a space-separated list of action identifiers) can be used to define meta actions. The way it works is that if a subject is authorized for an action with this annotation, then it is also authorized for any action specified by the annotation. A typical use of this annotation is when defining an UI shell with a single lock button that should unlock multiple actions from distinct mechanisms. The org.freedesktop.policykit.owner annotation can be used to define a set of users who can query whether a client is authorized to perform this action. If this annotation is not specified, then only root can query whether a client running as a different user is authorized for an action. The value of this annotation is a string containing a space-separated list of PolkitIdentity entries, for example "unix-user:42 unix-user:colord". A typical use of this annotation is for a daemon process that runs as a system user rather than root. AUTHORIZATION RULES polkitd reads .rules files from the following directories in this order: /etc/polkit-1/rules.d /run/polkit-1/rules.d /usr/local/share/polkit-1/rules.d /usr/share/polkit-1/rules.d These directories are processed in lexical order based on the basename of each file. If there's a tie, files in directories earlier in the list are processed first. For example, for the following four files, the order is: /etc/polkit-1/rules.d/10-auth.rules /run/polkit-1/rules.d/10-auth.rules /usr/local/share/polkit-1/rules.d/10-auth.rules /usr/share/polkit-1/rules.d/10-auth.rules All of these directories are monitored, so if a rules file is changed, added, or removed, existing rules are purged and all files are read and processed again. Rules files are written in the JavaScript programming language and interface with polkitd through the global polkit object (of type Polkit). While the JavaScript interpreter used in particular versions of polkit may support non-standard features (such as the let keyword), authorization rules must conform to ECMA-262 edition 5 (in other words, the JavaScript interpreter used may change in future versions of polkit). Authorization rules are intended for two specific audiences System Administrators Special-purpose Operating Systems / Environments and those audiences only. In particular, applications, mechanisms and general-purpose operating systems must never include any authorization rules. The <type>Polkit</type> type The following methods are available on the polkit object: void addRule polkit.Result function(action, subject) {...} void addAdminRule string[] function(action, subject) {...} void log string message string spawn string[] argv The addRule() method is used for adding a function that may be called whenever an authorization check for action and subject is performed. Functions are called in the order they have been added until one of the functions returns a value. Hence, to add an authorization rule that is processed before other rules, put it in a file in /etc/polkit-1/rules.d with a name that sorts before other rules files, for example 00-early-checks.rules. Each function should return a value from polkit.Result corresponding to the values that can be used as defaults. If the function returns polkit.Result.NOT_HANDLED, null, undefined or does not return a value at all, the next user function is tried. Keep in mind that if polkit.Result.AUTH_SELF_KEEP or polkit.Result.AUTH_ADMIN_KEEP is returned, authorization checks for the same action identifier and subject will succeed (that is, return polkit.Result.YES) for the next brief period (e.g. five minutes) even if the variables passed along with the check are different. Therefore, if the result of an authorization rule depend on such variables, it should not use the "*_KEEP" constants (if similar functionality is required, the authorization rule can easily implement temporary authorizations using the Date type for timestamps). The addAdminRule() method is used for adding a function that may be called whenever administrator authentication is required. The function is used to specify what identities may be used for administrator authentication for the authorization check identified by action and subject. Functions added are called in the order they have been added until one of the functions returns a value. Each function should return an array of strings where each string is of the form "unix-group:<group>", "unix-netgroup:<netgroup>" or "unix-user:<user>". If the function returns null, undefined or does not return a value at all, the next function is tried. There is no guarantee that a function registered with addRule() or addAdminRule() is ever called - for example an early rules file could register a function that always returns a value, hence ensuring that functions added later are never called. If user-provided code takes a long time to execute, no exception will be thrown and the script will be killed right away (the current limit is 15 seconds). This is used to catch runaway scripts. The spawn() method spawns an external helper identified by the argument vector argv and waits for it to terminate. If an error occurs or the helper doesn't exit normally with exit code 0, an exception is thrown. If the helper does not exit within 10 seconds, it is killed. Otherwise, the program's standard output is returned as a string. The spawn() method should be used sparingly as helpers may take a very long or indeterminate amount of time to complete and no other authorization check can be handled while the helper is running. Note that the spawned programs will run as the unprivileged polkitd system user. The log() method writes the given message to the system logger prefixed with the JavaScript filename and line number. Log entries are emitted using the LOG_AUTHPRIV flag meaning that the log entries usually ends up in the file /var/log/secure. The log() method is usually only used when debugging rules. The Action and Subject types has suitable toString() methods defined for easy logging, for example, will produce the following when the user runs 'pkexec -u bateman bash -i' from a shell: The <type>Action</type> type The action parameter passed to user functions is an object with information about the action being checked. It is of type Action and has the following attribute: string id The action identifier, for example org.freedesktop.policykit.exec. The following methods are available on the Action type: string lookup string key The lookup() method is used to lookup the polkit variables passed from the mechanism. For example, the pkexec1 mechanism sets the variable program which can be obtained in JavaScript using the expression action.lookup("program"). If there is no value for the given key, then undefined is returned. Consult the documentation for each mechanism for what variables are available for each action. The <type>Subject</type> type The subject parameter passed to user functions is an object with information about the process being checked. It is of type Subject and has the following attributes int pid The process id. string user The user name. string[] groups Array of groups that user user belongs to. string seat The seat that the subject is associated with - blank if not on a local seat. string session The session that the subject is associated with. string system_unit The systemd unit that the subject's process is part of (if any). Note that this can only match on system units, as user units can be created with any name without privileges (unlike system units which require root to create). A process running in a user unit will return the user session unit in this attribute (e.g.: user-1000.service). boolean local Set to true only if seat is local. boolean no_new_privileges Set only if system_unit is not empty, and set to true only if the referenced systemd service unit has the NoNewPrivileges= setting enabled. This ensures that the process cannot gain any new privileges via executing setuid binaries. boolean active Set to true only if the session is active. The following methods are available on the Subject type: boolean isInGroup string groupName boolean isInNetGroup string netGroupName The isInGroup() method can be used to check if the subject is in a given group and isInNetGroup() can be used to check if the subject is in a given netgroup. Authorization Rules Examples Allow all users in the admin group to perform user administration without changing policy for other users: Define administrative users to be the users in the wheel group: Forbid users in group children to change hostname configuration (that is, any action with an identifier starting with org.freedesktop.hostname1.) and allow anyone else to do it after authenticating as themselves: Run an external helper to determine if the current user may reboot the system: The following example shows how the authorization decision can depend on variables passed by the pkexec1 mechanism: The following example shows another use of variables passed from the mechanism. In this case, the mechanism is UDisks which defines a set of actions and variables that is used to match on: Allow all processes running as part of the admin.service systemd system unit to perform user administration, as long as they cannot gain new privileges: AUTHOR Written by David Zeuthen davidz@redhat.com with a lot of help from many others. BUGS Please send bug reports to either the distribution or the polkit-devel mailing list, see the link on how to subscribe. SEE ALSO polkitd8, pkaction1, pkcheck1, pkexec1, pkttyagent1 polkit-126/docs/man/polkitd.xml000066400000000000000000000062701474122443600165750ustar00rootroot00000000000000 ]> polkitd May 2009 polkit polkitd 8 polkitd The polkit system daemon polkitd DESCRIPTION polkitd provides the org.freedesktop.PolicyKit1 D-Bus service on the system message bus. Users or administrators should never need to start this daemon as it will be automatically started by dbus-daemon1 or systemd1 whenever an application calls into the service. polkitd must be started with superuser privileges but drops privileges early by switching to the unprivileged polkitd system user. See the polkit8 man page for more information. AUTHOR Written by David Zeuthen davidz@redhat.com with a lot of help from many others. BUGS Please send bug reports to either the distribution or the polkit-devel mailing list, see the link on how to subscribe. SEE ALSO polkit8, pkaction1, pkcheck1, pkexec1, pkttyagent1, dbus-daemon1, systemd1 polkit-126/docs/meson.build000066400000000000000000000007741474122443600157770ustar00rootroot00000000000000pk_api_docpath = pk_prefix / gnome.gtkdoc_html_dir(pk_api_name) enable_man = get_option('man') if enable_man subdir('man') endif enable_gtk_doc = get_option('gtk_doc') if enable_gtk_doc content_files += configure_file( input: 'extensiondir.xml.in', output: '@BASENAME@', configuration: {'libdir': pk_prefix / pk_libdir}, ) content_files += configure_file( input: 'version.xml.in', output: '@BASENAME@', configuration: {'VERSION': pk_version}, ) subdir('polkit') endif polkit-126/docs/polkit-1-diagrams.svg000066400000000000000000001153701474122443600176020ustar00rootroot00000000000000 image/svg+xml Subject AuthenticationAgent org.freedesktop.PolicyKit1 Mechanism SystemMessageBus User Session System Context libpolkit-gobject-1 libpolkit-agent-1 /usr/share/polkit-1/actions/*.policy /usr/share/polkit-1/rules.d/*.rules /etc/polkit-1/rules.d/*.rules polkitd(8) polkit-126/docs/polkit-architecture.png000066400000000000000000001740171474122443600203270ustar00rootroot00000000000000PNG  IHDRX*PPsBIT|d pHYstEXtSoftwarewww.inkscape.org< IDATxw|Es^P@ A (Ҥ@P !"B"%޻Hzr!$Yngvf.3C'M堠͕+U* zd6=cSMu̚~h}n` :m9WVuݛjvgIV 9N=oҴIcF6! ???K `403[;&2NniLxƍ#,i][F ߾ݷ[R>z4͛NFy҂圌F__|~O$:ݿM9׭\^8k4p%t9,Ƿ@^|q;…08~~pTx[=wB@@h…Sf\qдi/-Cw9K~ݻuꗾYQclws !,9s5+9vѣΖ}6hPu ۢaim֬)!/K?17nVR5%möm>ڷ0*?v \͒>j„~aǡC%\:yVh7ڲLZ_ΙRƠfذVlrXo 9x t=z]h-j7NTcfW{PGk֌?AߵkndǎFFV^e˷b2 i۶m}aO .з !D'_IEu#,̰~F[c}Z6k8_뤌ӫg*ï6o fXMnc0k>̲ٿEuk 0aÆ=;N{nz3 /f,s`[w9eifBΣG ^]2bn]lf8`FY?V+ ͞zĉNmt۶O_~_4sRJJ]_ݹv2q7m+7O?] A;uj/?ㆡܰa^:‚!CN=K!9֜y\+VhY ^΢%K<-=1k7,ϛ֭}BpU W0Vo*9|`-Ӳ^FC&ׯEEd[=lh00xkIukfr/42drU4v),_{j-/7kya..zP|lٳ2I^!2IzDnqqqԲ} prr2K/ѥm{ǘ2|/Z4ݜbhHLJ2ވtc] H|1_;L .>W`uŹy$>@1c_ %JfM)S$L;g%,lN"uqrN'!ѤiC!: Ds.Μq>ujpY~oo{trrPPrttSTtPlٹӻd@@jfOHtOnJ✝͕˕pR 斜^zղe#bYiSn͚]N[e2gΜf ʗ/5јnU{b}nY?2lBLKHյio훐沥K4{Y׍%J$-S&>+2_EKxx:jӷf͟fLɒʕOpqv>]a)))W""\YbiΨԹ/z {is:}P.'Λwyԩ׿xӦ;u:֤F(ј2lʔƽ[:EMoZX%9lVFcJR{5mj]+-[…32Sݺ懄t5kΝf dqaaaLMkT~ҳ[o۹ ~:542*ʩ+ܾm/z܄?\2 )xҤ˖U*Xɒ'~SJ"-'_\=;oƺ:;kPtbBRW\]5xk=۵ۺu5gnѥ [OYꌿzّ#]{NF#Ο?Νy3?d6;uklN@W-go^׬k@O-X0>L-G]ze˾Pi" y{_v2 !DB BdFMwm yjͬRzK;\gJwLFefGj2y ҝ51_|A"'q"za՚5K۴mkff-ZkMlcӖ{LG{v}iMSgǤmMS甩SCCC01wӓBzB!XB!YL,!ŽĤ$l~uV^]իu/hO: B+}{V.kޢU .,w'߶m[/XxҥΣFwYG-Kd=YA!Э\ȔSk]:wbRPӴ,"lؐ!\\\R;w8]pʖɊ#ٳXQ[vj=7]W@Wb}b tr]矿_-o긱cF[߷>d)'%%uϞ;Wx@@ػFmsww`Ν%˔)c Μ9S)QFwzR֝;w2ٕ+WjXwGiӞKvYr *&%CBrubWZuoXXX  RXsEKzxIII!!urϵkIHYOL49r$Uo^7W.]8{5"SWotqvfg7SEZdl0k!-gV; B,Ӽy޽{3gNS>Ul٨z:ٽ{}WQӦbfQÆ7m*ּY+~[(իA3O$0005񧟦߸W_yЏӧ׿uVN}Duv03uӦMhִZzēA,!Yh4#7m pa~>5}ɒC[oʐqw^u4|xS.6nР?F)ؽjʝuSL&.iѣ5lxŋ^v*ܺ^fMsZiSqK+U:ҦnH B#"JFF>"Oyfx$gʕ啰m۶uS'O<߻o_;U*WWjK~ʕڥ˙]׭kt|J*u1::l-TPh|sCzȤs\W3{'O3qEFS* ҳT|EkN;l}}CMVvoXW(Yl^P47Bd/‘#G ֩Sz=k׮϶kwH7Z&Onpĉ wuvv6Y^oP|+oʝjܸ an]C+vڵk~ Lpoٽ{wwnj XBH/otqq .|+ٻ@PPБ/x[~$"][RٲїCC= feD.ǎ=Xx;bc=8]Ry6]ݧ);7Iΰ FAK14ӆ znʕ;ܹs $쳭;tGSn~ ܽgOF\rR{ZjyCBBJ9shƍ1b%]4hLes~>'O9~D… =ױprrӧOn&\)Sl2,_˗W:w\9$ӟ "VnX04,k…ʕ,o GDK6iUx? &[RA͚f.]Zv-y&f-[V{˖ xy%gԎ*T88$k+v';Y( ilY<777o>^{, 3z~f֞];xa6)""¯E8`~f˚f)\F+?-^\h4_w+.έ~]Z:jb7)P 6k6ܣA?>L_|{ַVfgy !Oxx[Ǹ%<ۮGGd/"Ϟ}PK6YZ5ܿp[%wΝOWD@;CW@j=%|}yA %J;wz 'I~ X6~` NV!2:ڵoq`#G/Uݺ$ w8;;kb$dP,t*ї_^iS^^IsǏ_<쥗g|BDzB6lߤIe0aE!` !Bd1 B!XB!YL,!B,&B!DK!"I%B$B!b` !Bd1 B!*G<4M3ܺu `lAy*[4Sn9Xt/k47zAOg4MO3i4MK}ά1Azy<21*~R>f0 43MK}ʯif4U}f9FKe4 zb֟hZɤ5M3͚l֒SR)fb2i)&'$%L&-19YKHNnݹcs;7 !cI%Hqư  hP1X=`DIX;*bK(x2ۤiJ`6M]^>POzqzf9Sc7 z̧j5Mg/R_kv^'tkeu>`}R{~t/B8XT#`fQ&ݻ-9&3lSmlÔ0={v2omN1BǓ9XB!YL,!B,&B!DK!"I%B$9‘G O6lƚUkN<%ߙgJ_tٰp¤辣^_]cۏVVϦ}v6d'{<辧mYwW8_ؘec/߈#zDF3%Ijn;6|̙kA-fT"pk|O/[{1[Ӷ ҭaiir';et*2M9X"vin*\;=$?ֵךĄVR|G~=q))SD.gb!DVK8& /ݥL2)Kݿzj?檽5⭛WN{fe/ oݱ-˱7o:};b-oxUkUL{6\hoJ~d2i~YVaۆ+ E?`uDggӗ}^}[7Wq3wLw}eKzh~:rXJ߱v[6n%Ϭ1j^^RBG%|燫\wӻ񋍷l_K<=lrǒ`ؽm{ `ۦmV(UjV`ⱷb5pJҝ^BQ_:z[%'gϧO\ԙEI,CXx'cw#L&;~r`SPYg,/5%iQ3ktj2uitm갩 QQ>Cm)zF$eQ-jN6yduD,+ (fjը}_fםvӒ@~A#Kt9ınWoe/'wޑ& hQ3^vuߛ ^nw޺>{_ӢS/ݬ_pW<]Z\vNlP*^غr3nV=[]c!ޘ0fKw-q4Iiҭɯ[lyʯ/[쑘|{2}~4xL\h/{5ky9~R|Ruͬ 1􉙩G XBD̜E"9B:[niW1 ch٦eg_|o;r̥BP ;6\)^rO!3/]sruu歘w-cɉ]{sΡVZ=+wTZ1&|wkAT=(n~5n(VXBW;M۫}U~/v,MUXtu}Y\]Ö<3v^VT}~~ݽS`}/=ӵoGiԵbKzP?]X-pI 콭 n{9Ŝ:w͖Sճ'C9 膈Xx| fK!+Е`3JM;{/'vI{zOmښ^_ah`&1aDzt|d=L\6O/:WHGFD9="g!B!r>ʼn='98'݊ {ԬQ;]BVDb X"йC\gj8kꦩK=W< -/υV/ |戜F,,TSǜq'[jeV+99BvѧUc?:(7c읷B{VV MLye.Uhh܂ pk$qf<|O'{[`(jhψm)Q8[/}DMޑ} ~M`"unP`=Tz>P@ї6ॗJ@d 2~܅}!nhlPn}}K =.V@[lߡ~Jz<;C3EDPo(f*Ϧ"6ǟP&_^7l}z6u7_LpD?~lw5Ool j.o0dh{؅Fݶ |M@DfNʷjRztzư03gto{TN#z`05zѲ g'fH#׏ ZJLPt"yjF:!B+P0>ˠ7Gʢd9^BO\L"x0"r*?c/O08f #eܽYefx-^Yhhݛs}uö fdYwWo'_,3a硆 M8?VM{J86K`aMr.yBNL2X?6Qz jxp~tϪ\uݱk"r,ȕzٙna}sLOK:_Ѡ3WFsAN "/aP!Dc}81B"3!nx9jRxoR7] 5ȲgEsyq%䧠jCɔ` j!Kh: "leɸp.P˿"Pmֈj59PkUND~A{V6A\eAJb|<.C] jP]|ZO 6dsiJ{j>x Z][˲ւtޗd۳b=rCݧMbpt[d{7K` z@q`C_ P,NPחI5Y^)CoSqP91c<Ȼi&C psAr^yhSYc|AyC:%uo0!r#"z'6O$a{y~Zڣ"PW}qBܟ>' GG<$"!jWֲ+cZ(D@D P:=#b6Bs*!r'"&ߪLU d\B.ZcaBXB~k&".Dbhh#,+D.Gym"aH+ QGG<9$"oX&"[0aAO'"{ |8wF99" ^O/:p&!03Lh|pvvvtoߎ˗c„ nB nܸ!yl_|WWW5 L";L&(^#_hzs$p8.]:.88#DnI5j%GEdpYKN6[ df$DEDd2n:/HC_!jVz"lADu$B0!"?7I\趈#"]"& [F#⾘̃stȅvMDNn` !2l"ED^n3_ 5Q` !2W D$" ` !3G0 "/H}PэYC,!Ca_<"& XXBrXu;DDm9eP + !U0@>|M"GчЉLzYxjͬCD|"9@s. "0s23 %n9nxt` !3oZgD,(D\` ` !3'2 `/vpxIV.!"[1aA N$"w7K'UH+H"1DtGh3٥_U@ bV Vu̬=Ƌ.!XBF_7 `0o:e~ov^jY":c0C F.ݽn,T4}7bnEtԍII L䢚i5+짟_a$)R sYKDT$?AU \]WUQӢG zlڗpg3k la[{f""b掎nx4` !^0>Շ">`0̷.lr2TT ey(4}5dIˤ;4Mk`&vȉי" cfoRΧ5a^"*'94ANN.?7okJWKݠʈv^bS\衭cfS'D[m&*B"DD[(MIDJDKsv7"ʑ7$~D !`lK{"j8- bnx4NDT@0ԇB-~b(?Ԋ† nr;vN yy!0_[Mys( p7 A0WL{@}kwOJ!W+c!=XTmfBάw1<}F+u*(P.?TO VE`p4ݠ0T=ň6Bu7PF9Akv*̯i,꪿'z=QiԁZ`3oʛ}t}<:h>z8 >ޱS@1 vh ̫ԺAPͦ2M<w+%D4>`u,U_, GIߌVWXo{ue,~ٿdЖg"CDfAYPDGP뾬 gx|ՕNKCTѳDp̯Se{f^\)Io =z  bP|`07Z@ bL'̻2ou\ ߠ朘f]\$n`^e/7L +}o>1~CbE|h CMʮFDc7e aMהtL=*@f &@ P?Vz٥l j^t&0@>ޘ9P6s>P=8OX;>o9W#P[G +zWWquz{{AdGsmܲMvӼyXj 3_A/TpԕY-xXz~ҿlwrr>kzA"Tϕfv{&?@x{u;[5򃟣{g c>^lwD("=|f*ԕ]KDۈ="pRMy}>$[+}̑z3e楖~Xf7PD]y^F 3/CV@{;8'3s>d9ZdfT<~p3Tٟ<9Y@!ugmY`%hѲGg濘٤ ukJyoڏ>Wd= aԺ" f^ h."*`|h`j_^Pd/_pe/hT [{ڋGo@iw ԇ1f>b'rV3ҠdM Prel;i.Q_ogQc ՚ա?ھC}`Ԅ (9 IDATރ^>p/~ l,4AN3h42%S!"Mkf`橬V-jg"*iw ԫV߬Z(`Mг>C|zJk=^ω `(ճ{:})H?z+^LYCDD#"gX9ݹVZPW0*ۏPDYzfڔ -Ujl D3vZ>ٗ6pDÙYe.u9x >Op;zϡ1ov|0s@DCVWkvOb繽 ֓3Vjh,`ojBi>*WkR~WvXN0sz<ԨG7D<ܱ{ Tg/ ZB8wޫu>"a8yN&; ]q^,B*"P>*{FD~2v RWr۵ !/2[Yd&Qif@D[r-۽lF^e`Y]MA^peʠ\[ uzݸγ%uDjٛYeO./kNSq `\o>#eA}*/j{6(E=`4ԽMDd򍤯!خRl+~d*<`,e1D(wC p,Tw?5V Մg4f{NgE꞊"X"3@-p&QK" $D4YKmY&3^j~'zPC7pG(ODc)յZq~!۽ '0u \$ωQu"zj _@wR),DDzޏ&=b}ID9jD[Q}"Nj]2_&i*Њ~IA;zyurcŦ@}2cB:A \\&gA} RO{` +E@"vtCs ]}Xf>LD;_Y ַ DDtx;g9G_7ގ l6y O_OkÄ(L;%(*f,GHqV̴4oj,+-3Q8qFP] ˽2>|1)Az}{UzLfU`>fY @ 568Ndd1Bԙ"rJ>`p2qSvqZ,2[ )j۾K7dg:Pغ4DשʵL?X#ntqo~udr驆nxqFM;݋}xL>LJz~om=qd{[^6]\_kǾju_<1o}>alC6۹et@=!hG߃&X`"=o1VT#]\ܿ \~SXڍ2?jܩ3V޾s\Aǯya._R9emG}}{4 }uZ,wrr{MwFӡ_^,Lrrޝ+dPϙ ^y0%X b_#׍uPTW;ğ=BjZiP/ |-x)@Qģ1 bzi~o W(733l`k*R5${pvNܷUnc)6xȁ4i5CFN] cx{܃.o4}8d2KJ\.}e\'"q ;U/HSn0ErB(*i]B&(_BeȪV҅!^IJ'"'xB|KJc lwhpQ*owׯ9Q z^u2o{Iowdg+׈  Zڍ܌8K؛=jfzdgfjiNDBte5}k0%X2!3B2LK$iK^T7Z8+h,3B |O`1J\6”`INήY( 5}'.הJ>_^*|uL޽C\8@h!ӛ/^䫆~斳tĞwjC6Kw1QA5'l+CAE5k=}+)1N}3V7߄iB&ɔ0VٺؘGPm4/R.pB\U|q+Lcu~Q`J,- y<Ȁ!=qRb\$}ć 5AR)B6rF.b?*H_'ek Nh{Z>&A<WϞqw6)fF'*Ӻt~tjѦA_,ӺTNx- m_pFqPY/+m/4fO`!@~O1֘j,{ !>}鹞0MK3zH}Zu7BmMmco%դwڠ/|'ruAEm $jsL+Ӣ)`]cUccuNND9GXgm"ب}1ƞJyP.P@lY_Z p07GaA;ۛۀ_V%gty\iXAD"@ g 9.xa1 D+ Bݽ wUV•lͧXFlX4~dyk Ver>ۿ )UjYiᲒxWB!D{#,c c$SR|U(7|,.M> ՒU 4χa ȟ`Y9h\I4'bEI%Ss"''4x !D#B 5/{r1VTNLI{ڨ.+iQHk>5emeSu`a͂E=]7ƉYFD))8bkBr2j6`!Jó~H6PpUPU`ɕg*ߠvt26߇x̬ t=Bx2Ncŝ"μf˲2Zn|Mrn&ԃEzWG/}>%/l'c_kl%Z5XMEMH:Z@7XgJBQU`1Ɗ;?@ÔT_蜩<'}+:$1!=k"!ټnS7c.ԉz&OM5\uUP;*6̈́,KYŝc]e!DyS흜\;9O@BŀcI FW  N٤jzg]94,t UnW ոYTQ FӨ*( 諑NhLud7o\t:{#33Mڤs,ΉtTLMw|JάҜDbuO쮦+2up(v;nz@,*.hKD{LY c2Y >^r«}dogR[ئs[}wKQǼoig)޸ݬQ=!ZK޿o7% *gYdP9E'3HLGU_zF}^ >9n-/_(EoK( bJoOw23R'閑\Zk5= :-7_}=W / Өq7/˦~Ыŵ+gܪרYZoK{E1uB7k]Nk̸o'7m\4 =_ټ d DT |Ƭ!D="tt0e 'X.~m75 "}8m;W^:,lؤs6^2L!Y./Ry_@͌s? h̋)-xxV4df9xEӖ6hP*UB>:}bK']4L(JfE5'\;WZXH7uhxmމ#=%`PzZrZ?",]!"x+]Ujuo3;ʢ`l ɤ#,~L&ӷ~:Z)R%:KNP7l)JՐtĩڴp&itmWMnNm_aj81cLѬe;zR:鵚,rW,Xj,sYY2\z=--#=힧[Juۼ׼|Xb 8t{1VTU>>9'VDK{.u (wf{+ <3GIJvu._Nsm>5vR7PnV߰wp]jGg^Lصo){fEFF@BO8;iΝU*~">* $vyo\dq`%>B"*toHp+Z(=pȩw`L,M6r(3u-$O9 <=l m˶/ zv5k|ώ[uOuu@Թr:>YOvMK<`K1ףUCT IBAZjN5ReRkS֚9mpЧZ_8oS})b]ܷ3_ݨY筵4z^[qߝ*MrV72;H/c-'XO'XbKj1+ôHbjr/ɼs/Q'0ڴn(''ၽBή*]\+*k#6TuOuRQ*}x{oT;IZokҬk 'ԋ~vrѵ8rr2/+7[-<kU3rM;3޻{Dj'g5k5{I%@)I`l0׎1V|!Z#(w_}kXW*SZ;:(~Z&qzEg2Fq=J=}R^7q(鯰f@{Mrg pY'mM6 Ml~}絶-SS?"6YY# @y"zѱ<caKD˜4 ɕɺ𦿢Z٪i*(doNTh]e؊fÌ{-Cge)k cB/!l &heN鉂’+:Ud6^f/z{Hu=wƐDȞ{@;p zQzO-}ҁ]f,?{?2(_?Ag6阤P(̬FhJ L>߹ "2OXA.P !(v\{,QK!7b,#%o#l={z_zQjrĄ8'jU^F nhYb[<z]"޺4PD+'J{7yI0Wup(e`1! @kk3f ~?FlERYV٫D0@;9|ղowcwű$IVndqGnX_,RSqپ,j ٚ[znLYry+c5X" ! ! o GDѮG\8𷩯hWR٪ӡ] zㅌ5PD66G*LS[6̯ꟿf=sJGm~WΞ[=)1m7?zpK%<\߬|N5Bna_GQp c !XSDc2Y鋇I6l` ʺ jG$LsB%{\^J$A$$I"=Iɔivl]RnVGMy 'H͓Qh볏^ 4}yʚ['kcE&BLpoP!}9]2bתwPxӚź_\չϿ:얺Ϟ%44d0$`!$Im@`h×u<\soY>?DtQ?_Vq c@ꝅ\P0AD>|I a_Lꋢ%L IDATSEJ+ :h۾OԳF :q.qk}h$I$K2$IzA/ RSh7ʥ.QDK[$C3?='J;n"|B8b% P^"p$U9(O/1h/hk(@6@޼@[߭[ަc41iiw+ &Bâ|rLB79 *G=CZ]ݤBD&J2dfEU6p,X>B6cKE'G[I2;I؜G>WvD$kˆ JJsȃ͕*m{i֪ۙkz._e=?6/(uA{k4ZnKQGge{Vs ĪN%]9D"X'+!8mjL});Hh) c,.ZSBDB^{svCsXMnEIOSP-VۙzG޷"OEB0kMxuO8R,0!Dce88|*ah,=NmZF !*V壗dwoL hj괚`N[ Rt-˻ҥ;ֳWer9* _ereknB[:Ԯۺq|ү%eD#|Bq:k'OY ceu[M 5!+w 02t)(ɒÆNzsCjۖ_<$nҭ)uR$@YH`Ր#vt_<8{0k5?kBVks택W(x259H y߆Ƿ8Y&?N+ń.LD5!whIn=rFW7ύ~nV UX' ߲X~23R{HV#Y҇,J1!<D4ѱXB<`SQ%D&oU;llӾOx3PNjugNd$Ijc"ƁRcM2m.)s?2Ɗ,J)!D [`L`5!.ˉgGbfj c=!D YL.qJ:zAICeI0Αjsϫ㼊mTE ciL$5!DS$,Gc<?R⬖xqSez a5Y/QۃޑCrntKd9M}USBuQJk{k:zupJ2*Ezh4njjq'XL+lOϓ jۄE _U>|K,l]رr^oi4t{{GEy8pΝ;wxhb,Jv/!'Dtl 8UѣϟgU9tPbT[Rgڶ]^\9ca !8:y`wT0%sU^llˁ=ZhqV\YE汻j^7lvZ*UثM<7h4uWv/_|s]sm 7Vӻs9r2335kԸ/.]ݲe˛k׭% (JU*C```B 1b0DZг%XbEkWz9{SҥKtBBzڵAD#G<$eZ`!":`w+\{u NOt?TiѢŽtfΘq2JIIQ :}{CCVZ|ŊeKn7n83[JJkF.;wg͜222\^=z@Ŋ|,Xqus^?,,zZN};vD8{*߱Ʀ͛-Y잋KjJJJUW`^^^7nt[d;YR~ٳ$N>='5k}ї\ݔJeZFFF.7ҁ,J.^ !6ێKRIMOАo ZD._rpp*UdY3s֬z:N{׮5(;+ ;txC ?oP( ۶lY$IRN-PBBB?YӦwtz=:v޽{}۶mW^{ׯ$">\჉ǟQn$:d""ݷl 4p9s}=H2'r+z ! 'MsΉ:vߘQgffFM[s]>7+e"B+0NӹBSDt0Dd^g {ĉ+V?"oo3gTp%.;uuu5S!Ȕؔ/_nf *W;tpmr@{VxRNSd2}TTTysV23iϵ5K*/uݺuC\Gꇆ&Z׬YL&#"BPPPԕWp .:u*Ý+o! Fb߿6::kz`ׯ{ԯ_uyfV$&%9'&%9'%%97nfƍo&;[fʕK\W*zV+?UVh߮ݩw+ ]vvv Nr<JTj:vn֭7\zS~sܸ-===?jBH /5:ߞ,.c%&сCD:!7pt,%'XOtᆳd]痪P(OO?EDcsPR{6t%[/Ȳe GEo_%FlxkD?yɓ'aƭ=w`D!XBDfg;A@ +W#L5*0Ow-FTԾ}ۃwnx^?ˍ^?2]n=O[X>ϼX\x>/xm]?f̜鞚(̱y_F=5F=3dnS0h1m,LSQJ5dYcO$Isr%rΗdWrOknuJH$|d<"rL&G$Hz29ekIy$Ce,։ ]Ϲ%gH$" iDq 왱,Nѯl2$Y,l3/lSrv,"VnZq|s9+X 40X}+8b⋛+8b,J(Ncҁ,J$Nc>XP`1VDʩ dkdZykȖk_ǒAo) )L A0'XGlpy}d&<};ڳɩ سqOU Fj}>c- 9h?_b^߽r371bԅe7pZeϗ{{,9:1OOv1$ǿ/~i=ϟYOө~<zȯ&'%wiK9[w@2juܯ}h`1VBqu/5\ÚN->Y;{쟢OG/TzhC<!Ӓ0JbQaojKW,skqq7>zj0Ϙ6',G_7ڵ蛅~%Mo˾jb.Ÿ7FZ⩋w%ܳ~O=[T|ի6bCDQ-Fkj9;'&?癜c=D|=U JSFJFu>]Fteu? }TϮK@*::"^rkbW8q1PF͘8#vlmv,۵h#WPH&̪W98ru|c%Wk>U3j1v[?wu0gҜU}ol%gi@YY{ I&mC :O>_ g yw",{#FУxf%i[fBJN+!LK=wL8w'y7iۦbh١e\H5\g\QFKتO}L&#?" ˾ήw\ m7.p~+jҵɦ:Fǵy)[U'Ra1VqX V u*~YrHKMSxWrʙJ{j N/wuwo,:Nb^ꓭP)4uJ^i:򘅟.l~/j;睞g%ϫn'lm9'W`N+!sՃg\:w;3#3g;;_&c?mzn698iZ}bܽݓN9Yü:ټް}8V|Ŝ!ܸT)3ꜚ.'WԌ{L Vг(cvh c,JR)U߻}ݴr_nQ]\]tzj/|紷-:z f-ٴhSx3OEjonsS)^95a 7rRv߼UVS{~o1pƳ1cW rԼ[w M5Vb@!OX 40VBx zePSR}wcݰ,|YԷ6W_L<VZ \ɯR+VV_[jP+jpմƘ; ף/D_ =%ݹ۰n}ˇRS,yYvqlDZ ~@&QNMn_A:a.ގ_v]UjUJVM_?}9٣gwi7J'b Dc)ࠠ-{>r IDAT#^HD$%Ӻi/7m3S`9ˉlϝ[n+޼9rc|Lײ[~V<3o<. ޼:^[<+3g' sw~^D{,|f0k \=uյ~>㎭ XMO5~*scc1mYSQeL[?m+7 ڴ Bi cp`1VԮ[;VZ|MDtYq~vtzF1R-j:= @2gdLM9e +79FtDgXyK,b0%Wsӷ&eB,M 9,󳱭c9J47V':tryכZ zy=vJ>߳G :Oj?w{4ŵ\}.O+a]qct$xM.9Beb3'ϨS88WyWϟ=|7c|3H$^Z>#=\\tK"Nc1 Ih`<B,BtB8"0@btu!sƷ3q̌<״ZtΟ>dFN剃'IӋ4c5ѓt3e6[+H͐k47Zǽ@i"6FD[t;Sڬٻgo߯}嶤$7F} 5߯~xz՝wTfR/׭jA\ڕccF6_`#X`'.ZOU`-o !2"J{R`mo 2)*JJINQ|˗^d8sL9V'ܺ4QW?k!ot=zdg*&Κxj7 ̑3Ƽ0cn|>㟎3+,;l]_Ҩ}9m|_uw^|;[{SU+RUyS.jQ_RL,W^?bo_l9$IR$vmz=0 &txݜogenZ{ݏ~tx/_>~U'%' ߈w_{|zLi/YO"Dzl5X1VQ$}BD!0qx@D!f!!ċVh `.x!BVO m6_:{6ݏߍߎSߦ*~NƝowY>[g?l>cWҮޜsG΅˯F^us֟;5al櫄_ynEߪ²^{yߌf0fv9iѤ7l7e.yθ9`ZiݣDw8yik_YnCfϘfݛvQǯJ_5dUƪA \3*8b2NѧDo;Ư#n5+ !P6.S42 oNx3A$?wsyHhHZ;@>ݒkԮy ]vj{arޙNJ2l{m\1`° ?}o|F N/S s➍{jv-zS)R\εn @|ˇjԺ{;-BO;|.Omס]#|}kf."j||T=/,_n \l{p3Ӧ_{t7[[WnҒU Dceiٻ(.. ,w1Xc{,IKc4X(+ *6D.ew~A8$-sfwwfdf=c34`"j2꺥h~UUM|)լYfvZnёz-D"UkVx^jp{hEs锥wbֲ7U"8d&4QL-l," M ՚g;n\;湛۴ݛ\= xbRuz ɖDۍ7~ӟ69&Z(>}]dE%fߗmYG !eDrJ@_?[JfI3#~jxlyJ5+}rp; t^jJ*{艁R.npiL<:xD@E늩f€?xlֹO簼hKKg.m4H-'\H%ٯ_0?kgߞv-/"1/v!/Ty661cl%ipFFo\Tٵ}zj^zB͌2i{[eȌdQ=wCjY%b5<˙}I#냗:l"$[nS-Z7hsb8ث}vB@@q/jsw!a.|~W#]'WpE~JZZ'tה?w8GO~7:qg/)RųWSԢ>C9:eicӑo? go7GF?f~O~SxsMyCDb~i+KN>pu[ֽ'[>5 1k8141P>-/xYGsgQN;O_7 %S3vfp{i)i6:di#}k|iy;H#Xbs,߸Kk(p. `׶gA4oЅpΟ.͐^CA`T[iSE~"\Puژi5$H'ΜE0y<6yh C#C`5EBʞ3C0m; C,ҨqFZ7zB=&Οxpǯ;yUFk7}_@_ kUN;o6n۽RjX??OUy\V%&&[[VM s,ߣEi\cz4SsOMdc%pŕjW:]ٽ5|r~Sl>sOn,_w뎣n~YrޙK), c.,oɍ?58"#"ӧ% u˖ziWJmm8s~V꺹GSfVV)&IGk%IiӚvoYJS瘏^z1LZ y|tUV٤fB8O+fWl 狋ZhrMq޽lmmJNNIӦ,)'&9s,@[˫:9xR4ӳkZF [Z]~ GFJO_XN164T3lX!~HE_5W/ѳg239Vrf꒩S`&-Yҽ]q]:c?#رN jպYks,w8yqҥc͌bm_9{#WwopSg;I))CBm*1]N'_^j9gׂ2T.:pҏIXL|~j8qjYJl<&{YnD[7 ;I}suGQ0PåFJ* 1(313QuQ׾L7C*T:1/D"ްm34S6h VS~mMs6u141jֽYv;{HVriVLna, ``RBϜ)оrώ PoOoM3:p`?y"}P΅M:4(vʕ/0orK׮:Y_we?ą v׮+Wi> d/];jT*J6}O? u؟~2u`}D=cŊcb\Xqce֨QwCBC w8 vAFi2ma-ݯ׃\p[^<8ɵF2 ZL4,ٳM8idN/p;jA=ټyG?y_Λꔿom;/Bt{„C[Lwܟ3{vUM>RuՕ*tG~jԩqʊTVZ&St#JO_/ms> nܸ!sӦ$9:oMS ۶M662))AHly=%K-[2p48gVpj5W~~Xh*ti=9OU(D))oWqT*S'Ѕ pXGq͚wG,XІsι pƘիfv@O/ɓׯÇ_no˗OLR(*o=j~qry}J 'Ob5jTw"}L s?Ç%%5@ V93ٯ4w}&̟pf;%n! ݧt!!%@P Uc)735 jVy/55qaUbCY.ZrQz1$H" ^GGD ujt\H$r7A]^275MK e &&8Z[gޡiiJ$*.4*.4 F*pߍpFm9{v/xSLE,ML͍ZLM3 06VKȔ4:=$˼By[ !$!% $=<miLlf&M悥3&M4HgϚV('0x#7I#FÇײTP"Mp bHժggeޝ+VܚdI//U,mpn :׿nknbqfջ>ᇞ_-X0#:q'\({Ξc$Wwpx`l``ls^C+W0@bk{wȑ@##u wM TF2`e 'N5#}V\tԨ[O#"_GRizq.(cgM25UkW+J.HUr[nj琈JXL")"""|G \K9r ,s4mIֲ,̨Y\F;mw̚,mmmm4[mnfDkoi2!dY#f}(m/*=pޜ\ʲlm9ʄ#L\lCO snv9n3r__P|s׿qĶ#߼~Bǀ,BJkw:ԽСu>m|Wzi;}RW!C:kt{f8xuƍWso\+`Rܸs˗5sKsc]DFGyh} =}jq-QgMW`tϞ+Y[iryl}l;}[*/?y}"] )R0p.><^.mw>l3iԨДTClxaP%npyNWj 3,{nEC,m@NOƭj=<^VwtLԻo޽jfMuxߦLa( +W~%Nݼi`PMڰ9_9f X>zXp.zVGB>"sSHRu\.ڵ  bvmWɋw<~ (D  ˟~t끁N_͛ 6<|ʕm.^su׍^c݁S?loSh-ٽ{Vqr<%۷}ZI !e `R T*#ytZԽ}W"yv &&Jk?8<84>-_^/cGV> Z޽oqcӆ͠ g\L}xLӖ#Gkzz5MœaÂ_dvw sVR!W5jnf/٤n]eF"*^U\qú>}ԮCf晇'*lVnڴhU01̙ɩ1&a3Ƥ?cz!}`R un&2ٳ>+yr:Ue<) RR쭭s+żW7:$Mm:skud2e+cbu)Ojx  13cjƘ1d)ccd Bǯ4Bt֭eZܿoS)1vP6߿Z^?*c-j!pf{z^e:o.[uz5.)5U[_2=Z΋9!yTaH#RŜyNBH:J)C|\9s\]ZMA`SGz\Pǎ16n\ۜi1qځێٮaC_}\LS͑%K:6=z|nIV cck[;] *>^2brϗ/Gu/^zsnqp7TBJJ)ej9;'T[Yc7-Zsv7ߴ58++}R*U4]VX1Z5hfU^FFCgܥ#F^_&mfUݺqG/^RIG,ZPeoidi=ٴS~~m>P*٫7o?q槖##|! ^pS ,jb_1,?LTҀш6!N˜ 5{Epԙ??YNe-Yc#ccMw^,xKFڢ316zd%y>989O(_3BH)D#X?sƮ.ݺht.p7 @;q99 1v161f%%Xըvnm4xGVS<h *{9_9cccccl!c)͡EHB !y2|]qP>1֕1W!B> J!D8穜~ 1 w*F1v33Lu(!PE!:9898Ց~!=Df\$?1IW !EG !|d8w9 8T `O#gMd96JBH~(""ytݠBJ y |+7/@cwcwc?0t%!y!T'`cL .2Ƣps~SaRn!rw$z)/3*XSIH9C !!s |<үQt$cb1{HH@ !Q<d'[h g̳%mM`BH91us)'ǐ>|8cwXsFHHx9p+!CXe218s?b*^spgVdM/v߲l'r;Z[U_mT?'8Ƙ+үiXHʘ-~'m1v1)J!1[FBJ -:X[L3NBJc3\~0E> 1GG98$B@))_# t< 0Ɯt!ŎF!Ŏ1&Emx #~ιb$qpӒs)6yuI!@'=`ps[Ʋ=i1\aD2Ɍ 0:rΓu!ŁF!%1vD]B>>1 |9W8$B]E)Q11? u,9WCHRҜ$s_:ʸ'.ot!,BHIs^BHq!%XV`B7UA !)a\iԢRj,R`=cL_.("; }QB sKt !,BHI s@HW1@)*J!%%kZg!QK*`BJ("(cF:B,BHI,00℔`BJDDn:RzqUױR`BJJK8 ]BJz:B,BHIHq PMAR`BJJ[u)"ᄔs!eLW!*rΓu)cVHt BB>z4E) =G)(iu !E !$ HB IB !Xe<c)ˑ~RA!eN[/8Ot);8t!EA#XtzRQE)n=A !%Xbk%]B!D`BH8J]RXvbsVl S]ϭM(oT?P-mMQUD%UϨQE)wYױL&?9;;s.g$Ms,{(Gh/kӴSϣ&lfPȲl9c\l-r^]~:[x[?,Q{'4Ǭ%XC9ed;~oEZĚ};Ax.YC.e_2AlW 9oZZ?w %Xr/0ι)-[^o OiȺUc}Al[KYVLN#<k9^e_z z}yv/˥]^ZMc|l9s_,BHq` %Xs@W;t !| ("NgBHG !8xNBH&J!1 )hrQBD !}su i!1f`溎B>&4Eyq:BPEy#ABHtNc4OױBdžF!j$C7B>64E)2Ƙ!u,1,BȻ !`B1&0t !|hRTHN!yRT3,s]B!+J!N]B!3J!E1jιRׁBnj.r' c.ҟ98HױBǎF!5&y!`B ?BJ"d{9t.ܻwOtiqIn={_zFt!Ӓ#/G 4~~._b9dذI'{uk޾}|A4 ҤD/]]aC?nu˫ޞ{k*>hKIIGFFʴ?y1cOEbժ>XxqߒS?`՚5-̘YPEc:]Y|޽{%y'6m_Gɢ?9/XPV+!>^/44f˗摑FՕ &ɚ)Sv`A˂1Q^Gtj[A_۶ ^Mh >ةtߛ7+R/}\^(: H8$SZQE)89t.j8qBܷo_ "GRʴ4vmIhhhWF[riʼ7j`= ޽{W_F<XRRΝ;>eOҘJbl%ՔEEEIo߹c{ի OTTLP ,,L#܎[rr_Xiny#))IVk5kNJbLV$y(ӧN-ر㕼9{Q**5k zZ]z&111$6>>^z̀$MV9 RAXV^.\oݾ]BN.KT*ST+v5:s 4_3-+T.pgΜVM͘1o\߃H=Z"""=t91111UT#N:eױQ=v↍]@TtGz׋*S9 ]EcT|ΎWZ7on(3G'6l ;z0qDÇ;99D:vL]~7ouzqk֮5Qcǎu- N:e8eT[}}}.Hׯ_Kҿ=z$_ L"f 0p`,qիdi[l CcggrMsR)g?<|ĉ>>>+VLIHHЛ=k!C5xq-ZQy+&$$^o޴銩X Sڷkb Hr\2n„i d޽k6^re횵k>|H$ l``r`߾szM{y..dkʕ 䩩FjZ2tȐÇj͜3##(J_'wiiiƣnj |Նh={~}`ou/)9ن1J򑞞+t ƌrʗڹ޽ze?]-<<|U:\FR5|@`@o5/XPE;3gΟ??'͙3ƛ7o/]pʕsfezᙣ W^ֵkffVzK}cQQQFWpmk<}˗/^<_]v-[weÿbG,Ӵ˧LR֓͛r`߾6(8حM6VZ7n\v`߾^?{|IZbEfǍ[u`߾<8T+Ν:324?s=c/ok# V,[k|cia'jF-'̝=Rʗ|||zBݺ}=z,k˖q۶njԩ2ۅ&nq)ӧ/\-AM[u'޼yhџ4wn7SS^]P(D Btgɓ;|1lXGXwܹy9W]['b,m֍]#qw?uҤ  {wH$5}]ZPEcL~u,%۷o,ccc.E۶m3x?~|eӧO8;;۫H8y򤉏VVVjGuΝkժO? #\.gggR±c6&ۯWn\N9cױcȈHi@`\&/]l ۷ϼF$=JVZI-[~?q*aСо}(7WW/^q{n:ӳuC۰ᬞThsȓ$r\"%Fۖݺv 'Gd++'NTZrJ≯hU:qVtt~Ttt-ZuZUnogҽ[7o\^^ժU/7k4R$V޼qKII "(Nnno4>nʕ[h~̈́ `ߞw36iW|Ǜ5m`ohѬnR|JOT*7_{5[yx89:NMMm#ګTfvv~i84W/Gn2 ))LP4000dz4DqO:]{.SThԨQ𝜜۷9rDߥvm~Yf^"иq,#3ǎu5IrŊ吡C6lX}kh~XJV7oވ`Νc!]wn#jҴ4fϞ:{9nog'9r]ƍ#DZ,,,-,,2/ rݺu>|4o޼K.uV۴yiBm~QruRd2_=r׍#^^>G| .^h۹cp>bbb 2O!޽kaffVVO<Ԯdean{g"&A26h{ʕE׮\Yx_,! E5;vlR֝?lٺxBƍ=F=|(ש$9f͚qΚuOt朋+99&ݻ?]zuk tqq9rڵ=2咸&];wͰ!C >}FE?xES~Νx#)Μ;4]I^?J*j٢Eja-M("f5u.yyyOp777aǎR{{{g&/^2 5Rrmj*P(zLK޽{^GԦk׮v]֭[s\]|Bll$""Bٳgf=zn9gk׭PL]تU0ٳgXLL?pV~P}ǎbU.իWWcInj q6\.)sQDD,*:Z7sdF,rZ͞=n|,S;4jggOmA ϯ"xl89%2 0Hsz?~ԩSLMLlu~F@EK˴M0TJRw߾z2}5*T6c֬mٺW=xӧ7|}=ٽ{h=B;~n8u.ˡ9;HܵT*:"۷;UC^^snQ*CU*U;; ! `1Pʢ/_Ǐve93gTK.շ>sf+((H2~x A K,kG5ҥKS&,,,T5j(Hƍj޼yUvmP(DÇ {׶mN?o^Ȍ3jtD"Q>yr}nhQ/,4hQqoe^^^.BUA,Ytx}TՒvm۞xYMom׶mw\kW5lx- 7.Ǩ&Lsʕ|?wjacƬD11o>.HAsuue֖vyNE]TTT/c#W2OYZZ^ھc^\$%kD9]v_jHҀ^=z-̶ڵi /u;ws S'M <}zI|PeD)c=Ps*~iWRs>ܮ^Bƭs7ѥKΝ;Ӵ 0\^j5 VjzWAܸI˗Gn&E^dT(66VTF rUۓOV @{дT"NX1|С۵k>fP6~t>y(IJj!%>)xQ*U TŞ爱W#5iqVR%Q&SͱONJZ8:8$888$?g<0Qb <V'&J[hZn~k3wTk? u_͚3ʧҮM\ԱKH$lϜ;gm(h"&;zO.>~£e5WWh޳o|䇟`1g9sFpmT/.OA#XfXAr޽{ /Pr=u'QJMbM6E֫[W}ڪbEP9;;+}W82LpwsK|7cFPՓjSbEeƗi2T\]*וnj9Y=Sjk׎m344T5o,0mnj֬ǣn:qU(|߽[gJ]DummZza-bbb2^Ϝ>]ܮA !$c--E;4hYիWOl_N*%׭[7*V͛G.|<>z=~ &&&+T3.)cjmh`PߋPE6JyQBg=w(_凹rNEvt`E !k 1Ou !v4M!Dc&usy5 !,BczبX!,c᜿u R5Xs1= u,RV!dhB J!B!,B1X}uX!,m45!/ȝr1VMt !54EH5 y!,B!0tq;!J)0pXׁBHYD !ӗ6qUB"J)gc2u,RVQEHc#]B!e%X?ا !,r1&~]B!e%X/?u RQEH hjB)q`RpIABHYG"$`YમcTx/742Rs\SY̹v.s0vTH!|`R>TXABHyA !C PE! %X4E!%X:B)/("| ੮ ,BcB J)(t!`R>HuR^PEHJ!䃡NBD !ecLF!䃡=R0Ĝs ,B!bF !BDB&azWuT<rNG.Ys;by_@9f48)`syk Z]g)E}oX*;E&JѫW9/|grι?k] 9׿]v9}r3_9׎Iӓ6R?sZBf1dݟ쯚nӗ,S!d\m X3* Z{ӂl 傀٧uvSfTlA{rF{M x Wd[Nn$3bȶV3k!\jg.W) \-jyJR %X,B>vfJ*|A닿˹'X94 H *~n V8. 9/뻴=*8!-vvM,Isn-֢y nr߳E)'OB!QE!R("B)f`B!3J!B%XB!Ō,B!bF !e\.gϟ?/!KSRRsY-iYr{ҥ 7lpz(Jv]/^7ܿ$)9Y.+ YZGFFKOHΜ5wllaaF!!!׮ن_|7{xiqMHYB !eի-[V}lliӦVEHHHo9--,___'OV,ٻ&AAAF]`YgM A6nd>pѢzՉӳ_BNiJMcJ\/2*Jǎor[3,(OhjBA !ȇ#G$}-p:iJ R,RmT~333ݣ,1cƼܻwM/L ٳ?6o>ݬYy:rġZjrTqXXk=˧͚?Mcݿc$@)CӧOEڵS@tt46mŋb}I]0\O_> `)JHOOȑHw3gV񑥤0wwwŮ;_q8~I.]`%j5b'O2366{wPS?}v۵,d<3ĘH։a0da03b$Tm[wv뮟ܞO}99=oϹ(VFGO2jӧu9rĘfSbc+.zW_d2aEtLVn߾ҥKiii=qrjt:u ggθʙ?o^@ÇQOuKK1 `֭CGG uszNQٲuJZIwe9{ŋ|>_KGG/RƏap) +Ȩ㧻+оҁdr&&!1юd~?;qW͛6nuO5&555C]b5ڳs篝mXbEJwDZyONH$> -Eii`(rݻsa WWSk]uJXX_ c@ꮌnC_bbCCÜ /~_VVHqcNIusc?wtpS_1c, $%%CCCׯ_:x`<::ܹsJCCC`˗/P8;]GGG9uTj*55ULfff&Wd2ۿFv]۩SN>!\reˉD":slN> sj=sS:T$ŒD֖JX,O]v@`ei)Vmvtuu[YYo/\>|xޢkkkњիp.^rYbCvsy]9<Z\\<4$88%$$bԩ#FdtMWV^2nܸv>|n6m`V:ްm˖?Gxy5899u;՛i66K*mPΖ͛ϐH2 Ht:ҲuA%:0ObGB(>|H2ꪥJP(ۻ˗d{{{W^et*)TWWpTH$r:TXÍ[nu4hoϞ=JHL4<ώy :~tj>|ذZow8nSSAmRب 0c~= %%wS#F4v-X!D xSzʕ47o4#Hrߑ#B'!Xfjf&`b"6111H462*~*JB[{) B`p\c ܆=/Wub?0#qUBSStuO$VH d(HR@R nRRR=ű龚j*P055555ofmcFPYddd/bu6ukז@ ### ΛB~1vF`.Jo|_r\1r=Y|yjzT̞5hE7SRL^omc tMLLeeeoLݹ{wM^6̚9!1Bv^R\9k@W L&<=Ox5EbUUU~5jD*.\Wh=dƅPH^^^"a-Z|>^VVF#vvsoaMODuu$A7D"9΢P(rw7^q\r:@EeF˗=T~!NՒ>O^DPD"zlt%d6JM {u~LI19Ҍ9򹰥ŪNB}O?+( "L dKUєg_t~R$'ǰ0cG0# ͛{m,,,{/\c!..eeD"\ٳg;8fϚ:c͛6"<$}Z͛I/3x„ 60Νd߹{W{ذaC\]ڟLc?9k[[[qg\[7ݻݻC inӦncjR"t]\\v8 3wwNWJT6pk+Z=K ڞүY&}ɒ%N Yfgk[4b$__ߚӣKtzkeUQ-B/v:ir"fLرy3fͲT& \V[[fHpp#}%߼]WWg?cƌg=qwwoy<:mS--DdѢc;vڸ`Ѣs,R$|O&OؘwС:::Y2ѹPDL Luמ=]^TMB7n7l@}Ajj*Iq+kkH)F $'ban.BD*OO϶A#̷ncɒ%FJ`uRS隚 AAB PU&A̚#}|ZT˩|*T͡"mmmr^^cذaPH H"ff254nnjp(sV!}}vw77^W lP Z^ƃBm&DR 4/kj5g)ѵjhhH=<<\:ggD"+== ѽnefjl'H{Wrzk3g:q}G뱞NKնQޱiC`aSR]d2EEEV/JUFGGf͜ x ð{x ð_ba3`aa3`aa3`aa3`aa3`aa3`aG˜L&#Q{KH^fBћO23gZ]pS֬])ݥ=ӧ{|o_oD"z؝YYڧBOaV v$'GZ?ႃWBد\ {8° B|}}555;>>|qoil6SFrdlE_~9//|~feB͔7@mgk<~9S>lؕ11ˏ;6twҌn۶CTS[ل**+*** :^z}eU@kjj4R)Nꯩ5< A"tprrR]vs|``Tի<==ݺukze@,=KP==tRSu%Rɤmڼy^B|7-mObv;1 >2䐐ḯXjDDI'NNXvfZZdnn.?x@t?h \.G 񁁢;v MKc|8woih0 ӛWZՠ/}۶x:-*2V۩wR֮[甛"]yCe&_/YfooCmmիF6iڱssJJ}kk+¢iNwkCYju㺵kܵ˭~/. }ǎP ">lXAʕ]ˠEOAx|Pxxx@ԊY[[WTWW;$rRcc޶-[TyGGb_tع?//ϯ][KKbޜ9G;wNrNDAflbgݕ^* tzin޺5RG[;@__r`qv/}rL~~>=&:ze+tF[֎i=-5+-~{DnELLY|ҥ]ːdolSJ%B2W\^݋UVVEa!p \o)|...d2_~EΞ=[tRݻw5 dVV2>O&6d….^|&\ߏ?hf]v%$$'$tLYYZ8~ E;4d2Dkv՗.^`0YsxJRؑ@MUeVTT76625;6ޞwĉMl6 `.}i z`DcYY}Su[WZ f56mx`_|!-"kĉ/_>Y__Mr8:Gs>.c4+sƎml:]]ꃇPr9!k=;w~7 rv~S~ط/!D3kNNrsswMSSScmm"~^]]Š󴴵 7^v⒒ZZS5rdX,6ikk3N"8tWƆ͛#TL"3Nx>>S\hMzt!I/3E$DB0403 %NWX[[K:0""q_ cӚ=kV/6JIp]___*ؔ)5sfntݪ*S!nl,C@BRkJUZajj*XSθ'^ֿŲeˊ]rQDD @۷mK:+!1tԩ<־}ZIw/c455YYY+W:[ZXQJ6==o7l.qv$#..l@7nmC_H$6ĤmiӓʹzH$rX*Ə/_vmMMM*Myy9]$9O e2r;s]|9friⶶGt '7n<s~LL^6@֛<ٳ.v!Zhw kb)`aG$!!'|t+% .\'U;fLǽZ׮_W|!A{ccZsSsGS!ˉヂF!@$QYSKKv/-dҤIp8Iolmuuu fᑣofffhhȫv`ǎ!#F(Xӗ}nih;kYd2YPթmLSs331B͝3SLmmm@GG@=KٻJM5Bm:*jo!Dܻ[]}+[i4.1W8°˗˗/544X,BA}}}Ǻ˜r_.׬6M \~nn.MTGjۖk\z5zr2ȑ#z~2UѣW._~bkkɓtML;Fttu(!!3x֤I޸שͦw^ojn;::6'Ɉ|>)D,\xףG?^zJוqeVh}yeccZ#m;ohT*U*N[n'!)aafϜ^uGU+Wŷ.{n9{")b0bB[477vNSV^cffvk;bBwd2:DjӁDBa]^ /#"rT}촄m7,,.>rҥG9#<ܷFa;{0#D'v\G-$+۷oSI`eeXbEwo7NZ DtS-].--UˌUIIHD TTT{3fhX,&Fs\ @RRGGGgW=Q__5|8޾Afy[[ ŋf2Ƙckݸ1~r. @]]]NuuuH3?44"44"hdll\b$,f6{Ǯ\bmAR'J;"\*I3gkRURZ8y땫W|}|ʈDbKHU#Te B2V՝c'N =uҘ>Şr#J]ڡ" y2byyl6$'GWeanMTIII1c(8!ooos>lU11鏳xW=v3c֬M:ڵV-M͚x]w1{OFFFOe2iʕΜYmG&A7!}=}歮C}bђ%b)N/WsW5͍><əC"[̞4S?4;ٳ.S(jBI&_}u֣GG312::98;Dں:}&))rvr)J}ɳKBAͭIUGڭ#}|Tu_zղsk+rw{[Μ>+#t:{ƍg)Ž)KD"Q:iR Μ;hmij|dr9:|xT*Myk:ߑ#srrK-V[\ZJOHh&7W׬pi6=(B|9o-߾]K(r? OuaidZZZolWaKU/f!TXTDgbROTz_Ƌ-*Uzժ=\TrTTTh$$&:.<m]ec{x{ d;zO.`a!QQoİU80vף`+B 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~, 0 ð~F@ۀaX55kҮ:>=Qu΀ޕ]VQǗV}ʃЯvOޛ|ZW/y#\ެo*ү@Є}U? IENDB`polkit-126/docs/polkit-authentication-agent-example-wheel.png000066400000000000000000001540461474122443600245130ustar00rootroot00000000000000PNG  IHDRXbKGD pHYs  tIME2 iTXtCommentCreated with GIMPd.e IDATx]wI ,}a]:w){gîO> ^Q,*(J޻,,H2IfdYL==33@ klV(RZZ8~!msd!wY,@`e AJZUU&$ă V& TYaXS` >)qJ5l@r2Y+ 00YAL" @%2/ g6" " `R, 2mqt I&g{L 6Ub !r>d65T.W X`fF\|w6RUU A0qqI.6Pj ApjMжzLj{g6)TUU@b"h5-]O(srPIs62 "baTVXbd$: r .IcxśPUe (/BbBjae;8VG]U{BUU"6b%nV;ڂSFlk &9! aV̈K@^tȫjZ0`Xmm2bEe(--C2 /#E{7=&Af{d&A]l%$ `V@ ijl:v6$`Xm~* d"Ԩm `xvG>*6*j0l[,3 9(`T (%iL rI4㙧 ;q5`g&Bee_X=V l@6aV9MO8 .d^2EI]\IӓA3K/R {a@ |ӹKՀy!i&wŀYDv  r ȒθIΪx#]`k5U^^fEaQo)A9m,9ɡ˪n H|R$W_,+]ωHYA~'|2^jɫn{_5޳GQBnĸ̑ۓCȝ\! dIjb2gom$3- Ra]ICg\'3Ą=Y_0+ XF eрT(pkMQGX`IjCVs/Rp6A 1#Wrg41V XE7ٿJu۫{~ʻLG;\2^{b693 ]H niԁk6]yr!?R]LY>HWm;π=KޕKSXI큔\9ٝ7 fU? v|&34>S J>5; JH8ʢVDǤ2Q"v*)[?ICs Έ &i'Y|`Rv2f6L$wU˼ Tf!I6Q`0I^Rg%"c3G(>#I[܈ !{Sa\]T$X%  iOlٚ?`LKBG}!c;RWy"?ɓ4]k20GKO=вK:=d6]Y "rG^2P JfU{2*w,40dա fHI)-H$X$ (P˯er$3;ާ,#3}(j 2 H=WQA0լpPe_̭#^Z,rVϒBpkWIZI*ܤA%lIe,-*֮XH0r'H4Z5B55*#ҰB:eIJhH˲)W>A:# 6l y'`5IE VSsԐ$.ob%TU vdGHctSzorשҶ^nѤ{OJKeUD.RȐbΗYyBi*I<#4([HqŦ=f}UIb.gzD 04SK&F' s[{mHi1.O`1(Iaz"AQ (xWPj! ڶu %D.:) QUBi\,1d-kRd g]6dbN"(mq$@6 m#|=+ `Y B;WHԇ՞ ށlX!wt($I!)5mM,5%ޖ-dbok@Z5"dWJ[9@& Xh0c]*!+yOjxEx k$Y)E~{]b6i1gV)xL;urf1S$0ˏTJ7@wjz3T䡟 S{G# DHZzH k?Ażf Dn , )2- WgYHmݮXHafH= g%ɱWU ^Tz&]g!\{?R ,߉5Ndسg5o#Ep Iչl5{RQA<UMPJ'EZEÞ@n9eX \O*9dDŽ-]'9@Me֦) fi@RsuR@Nb)qAH}n11Il$fݬlGe0X-YfFz5LR &xBT<G+\ީBKJl;yX5c R# V])I@FZNʷ TkP7t[T6e0z4,#MQJC @Is2X A4qO?Y+mrm*:hЌVՠ`e(F1QZ$g%$i N \Ta&U,=q=H}83`&Z 5hӀ2 0 PYY9r򑛗Nege`B'IWۉjqޡV+mўG[Bĩ,U e|L "ؽI=]Zcp'y37vGceCyO8VS6*ݧ-Af2@P fD1i`*oBy5 -+vه`yaT&P(<$. MurF8Crz3 ~YUłb߁WCN&ՂI͸XE^5K-GeVj3]k14g",X+sv8aV r\SĈ p0Vو}a G3ضc'ƌڵSqe 8ӥ9K"4en5yٗV:vH9[6z6c`n\^ vR̩Y<J(T}䉷JUsx^ϩd ҡG ҳyH+4ٶ% y=ku(x~Tv+,S;qta`P["'Ф[(l8{[@JJ 6iVT1PT&igsZ`2lӠy7]i['-]<.[+Wa;/pދb a1.&G tV> =/QyL3` 4e, a9:`B%,N5BFE9OcS #6]+bޝV*N 9 bց5ms+q7:}e^(TtVGicL `BbE-^B@B)XSѸn8i ^f ? Gq I@$$k)_ԒnTCp")ۣjn>;^B :J#ǎIhi&R"{2 NleWv9Ao#)BK>%Rzu*|l[ihD}qn5TB.#,uuYlo\! z2 3"b~Ø( suawŒPDTSUB5/rP{BƒǞjaJDHx'U8jgEժ/re lU01C龁TjoW `׏D6@ z$U:QDGc7yCXb𪺎D&z$#}~Fuc'zOAOWp 0zgYNJM.d!>ix ]Pn*" -E5Pr zz! z*B.ЫxTJ0">Pw@vx/P8(Z{p3ȇJ;}hZMJF?,r|f|5KPD2PUz}wE&X$"X< HM$T𖣵#av9Q蠝7}vy@JiD鲆NR* ҧMP2 aABsMbKlv,AR–ݎb6JErs(B+N_7$e`@-'e#-ר8hC;`bЉMEPh]¶^ /l)=czM7;I1.$A.SpA':ct9*m[D1$o ,q R(Bb:fg5h$]6JtEH1I! :J!#V78cحSDB!e "zrcGF EP4 ؖDOMS8CPj(~)ǖ:Q42P 9 +`em)ԛ;rea"q`/YVQk±bņ9j1 \S‚t4r<*J̛eogϡNϵ!:”n9ơh@R]8DRJM5ДEtT '-@˹s,;8!jimצ50Z > 0|9{Aԯfw:~MX~40ӧqMX9A7o OV[,/.,;pcd4vҼJqWz+rQ|Sͬ8l7@]ⲋJnp>ͅS0 @-HoXލ>Ç {ff!?a:x~lΞ-¹saXPvm]/eX:<'ovw Ț D#" xԚ_N>m?r=v_y BO +eKR"Bmpi^hۺ5vޣ#2 YRcd>d+T/S(WMvjY.V0>tzꁖ-Z !1`}a&дi:PVV.sm7Lu1nFqگ^Z]1`2 ֛yXjXO@ gSjzWTϛg4EZ+%K]{>&?qhؠ>'OƺQVVzubp;Oӑ⺱a|s*)V-to/.FAA!m܄n]:AU&QRrGǎPQY-iϵ׍65Z;zziiL ]8}@tUl 9yw/㬌fMqI'9w'g[.fognO~7٭7ۦ*\7Yߋ2t-ٹyXfM5ENQ;JJ."3;%6h^ݻn:0Q= {)mꋞ} F\WKjJLLc<X4'߽ШQ#ԩ]ujFzpUcbUXt2~GuPRS +;ZlK++~\.ujl9$'%nB|\*SzZʅ %lSY?/Z\\|\*((<ƍUnDթ] J` *Y{kݪ8hִ }"-- Z4ſRIyΡahݪ%?؆C/jII.O ~Q^O@QQԯoF 3QR Af kQx?{̤ǟaOXa fw^[T7j 8͛lE(@8ʱQp1d4KGFthG EiijHQbԪU#Aٳ(.JK E1f0桬)II(v4/.F51b-ӱӝ{QWO4_g"998z8))ԡ=JJJаAm;vbwObBvmaXu_ץ 6vj-ԭS.ܶdemuyy9QQQag~4lP}z@ĉh# Q }+Q/\@vN.Zh#;'e"]vt`f@} Z@||\_ȌESL ,ex7"ٻLz6opf&= /? 4IOW^tXcu)_pv8+ѶM+cæ-04hP& 3,.Rw57=Q;cҚuQx-2MVPZV#JZl-NIG 'No.GѨa7isϣ ΟDzѵK'nVf!EEHI̵,^'zvV-2`Xqql_)UUXE pIصg/֮y39;.~cXv=\ uQ d2Az0M8db|fAWFSm D29ԁ#Gr:t3ʦ(cfHcSØ5\%5EP kc;ڶO~5zSM44W_V|rj\?f/.U\mI&9rd@0oK2(`,IЁ*sf}t?Om.r|H,$܈c+Gȶd]Y #ecwաv *CRn.M mfVȶ62h7c \] ]tL1RA?{nuwY4?$x@\`y.¸8<Ê: 7Mr1xDwTUUaݺtQSg(B40-дUlQDC JLAp$u,ŽwȠkP^=gLz'I?^nٺMqcA1EtO8]XUz!nG.Y.qŴ.u}yxɒѽr(d=G~(Аj (b(  D(7lR[93;wC~I=..}7`;wi?ŗ_O Wu7ܨ}"BΡhذWv!y&hκR?-\~Jc >߁ٹt>[ju'?4}̌!HN;*2lظQS"NcaDAJJJt!aUKy%rXgsmsFz8qB7=}-{A:&F 4J-{St5E!?f1%#BPyq ]ָ$jZv&̏t;9 XYhPÇ3/ W^ՎZ*OMHLLt|q16oJ4^zI{6Izz㟶K3)Pgu̥X#d@x!Y<0's!y+6nuk/TVV3=vo0~؀vk Qb U6 ɇhuhF $%EpU@"pm]p]+ܧf \ _UF):ZO]',u2Pz`-Zm k),V8G7l܄N)>>C]EчAwWT " Kw+Ѧ(&_.k5d,w+M@Ym1YUfդIv.`-*_;wUOpaы5:Zqڦ hluهO1@:@1DS-.?A^^W_˖>sXz%v:c,|_P0()`iHlXT-eeexOξΝ:a?p\vDh HHH@֭qӍh<`>4h4:[Ƀa"SoYoEbk8AKEtB^/nmlE1>D7%4cKߨ]+x~3ʵa:d;CfɔB>BV(J#.ZFy[#/&))r:(f2kBِQ\K7—_}:O81> _(:)nXC"Ly ݏa}u<Xٸ'ִ\zɳ] ?N=3I_V.S:F`}€zG#8B" `4Ǎ<B}W4DK#釹(N {P=dko 7ߊ#>ܟpA2-13#K[f]OE󲪙bChruEczdX2 HFt2?0 ,\KghM0zxv?pNC>H,CYe$C|*'ıJU݌ȟ@A1vf0hvXK{S0qa{ilՃ (,,}a&.$MDяٶ-2X,Xf-VYv%Pt޸q,%x7zᪿȃQCao Rdx5 _MFv! 8[1d-QuV:ϱQ+9:[!:z5E"T91btY:v9 ,8sbUG`ʌs^lzc&(h a)(t>dN:!CQBa,@a$.ذGkb"[8p0%~G5R`Wc1[M~H[i$w2R~D&$ %0٧V}k/۵Ci񹠰}v(np+yZꖋ۔MpݙEbuzjT~a6қ ;nG Xsh߮uWl6+Sy%$?Q~DͰv̬,4mJ](K$;y+G5$3dffibӶփ%ddd`~u`oKѪM{\HS{>W#gBzF+f YKgDzF+Wݫ B-ğ#DL~"Z#uÇM&''W_;qh>~@!2 F\;~vl]v.:WF Ldڏ̃HUK|^p8>NIa_aݰ䗅8q4Y)jWx嗐}'ګ<Gs'E@bb">> 'hn~ N1-("pqK8s'8>>FgE^w];/LY1}\r,/D~DZlsםYx^{%ĩ"ۍ|3'!/8v$G;n>#ƅsػkFw;R-[Y~O d e}#B“fȮ+ʥ`hjo:i槟k>T$bK}f2 N]{`)ⳗ^x{Ą'bahҸ1xչn+%9%Kp}[>Xf>}[[O[I5ST3,qo;o>;Fמq=PYY XR\{ts~_}8՝X#5}~m4o^}@>iӦ0&%%%%%Эv>5kסeW`1*c'ꁃqWO^z L5Cqx]ߏ?v`ࠡhZI>SrR?FˮpDƃ_|93 w; ;wqzеyڣDjrDxmzd . Bɧ)ǏUw_g7+pU1ԓOd6a1x0ݷE.m)_w9?Y_8sW;jO= 6MEڱU;UFת|&n(),SI TsJGI;f4}KKCNnJ0蚁XҀ4{N@Ӱamd\>S?}_|>3Y6[IJJĭoAΗȑn2`YӦZwBBnFJ3ش~ t?+*~o.FJ 4kj[}a}_Kiִp1E2v<أhٲ֭ w5 ?ʄ hаEQb?uv3}[^!*ƍ!%9kV,S?))33{&ƍ!>>{Bث,6l# #φ  ?& Q٦Q60n:tFJ~ۑ({ImwY;CS aDpd89šo$P׺kkbEߏܼ\>?dd4U?!;s `QU&RRSRr .7_}YttÆ̋vnj,ouo:d06l(TG򑞞yZTVV7_iiiHKKxMp++x o菦.X,:}>:wZЭ[WY33q +߮KJ.^ĂE2v s~Ӧ~дiLuRRmGYBC ==9_/sujF4O]~GVWNc?8;R4@-rH~{Tc*e(kǏk) iqϸ&nIÇ Ŝgaxp?)~׽a͘1cھuM IDATR^x,Y?0'}&}<]۷ӐZ["wpT;ۙr?شi33u }!)]iQӵ@" 7ᣏaߐV/ GU[v -X_3VcGrğ%.t{Gؑ?Tј+&X#'#kc=k>y._W\U\EV;r ,%*@kA=$2e$MXkYRXKGPFG۵ÎiD=v)`u}ǬߢZZɺRrx]9Z4) }$;ZH #9V` FT` Mw&!E)g@lf߁ v4o{ >ykv>99E5Gi$JȈ8*^\Qt솣1O;(r~]D'K2AXm8nVF𱼼;w/f;?[BsD{XJH Y|ypTYҥ !q hDu5qo c#P`5#tcg5f{b=Q!"b M<ERF\̢6E/00tTvF RSؕ"^e) Fdl_Jp>U=KK9:թ7PE`g$g`HVƖ2Qd񘪫')#-9J휢c`G3D^R]PHB( U /]qLD#Mt2BQ"Q r09Q $"\0L>h G i]MA! T`sp*܍`c,۵CBBg<Ѩ l3_|1\vϻ#FJgaͪܩScGBQb(KDSwTp~`;ϝ[$/M&cşKq$7gO믊iy}v(/9'8.>8:MI}ҽ{7]8v\ҹ3}!܁Om[p{eY { jyٌ֡[7Em]SǏ`/ 1d {)֯YO`t@Q0Q2jphغi^e4iԨqpn}vZ>xߏa埿 .F޽eJ.G5kdv&&?l7Ic, T[?8nz<}nS0 SbӫjXX;Sf/rEdFlW_ƕW^|py;GIh4j]t7v J6 w b*t xWHkѢ9‰'0l+`f|6oي[민?\&f8s{ yߤ{1utMm'&+oe@nm7?ϟ/ҙ>{wmYZ!~>u{Ų?d:c zt%,ĉ:NyO|ו rZOهP\t{wnǨeʲeV*]:V+i}1 .tC2d- j6& I OC Ar ۶o~=;#Ay}Sٻoڷo: 8|?{`p߮s[%-`ZoʞYV<ؿq[бCl}7~߰ZغŶZnqF0˕{ "ԯ__;w}/{vn ϿzK=}zoK꩹:u = 6m٢$4k330rp[P1p׽оs;^VK/<^z↛&bࠡhҸ1^eǕW\&btܸ9[#c"2Xv\v%YpJqhYRts~Y_)ÙrIg{r"ƺH!!vFI6L62Q{C6[oc<Z]x*I)؇믻VƎ+W!-- 9y(--s. w))ɘ;g61vu8_\f۶mW+/W^z3۷PϺ -Z!GZ,8s U9'm7E kk/^,wE=N:(xQS}lcUU6׳ÇkXR͛m`l6㞻]LŠ?\.'\b0lff6l܈ _9|8=-۶ړ)5ЬiSÇaF,iغm;Sd":zW_iMjr,==Xm8?(J/<{K > 7oZQ*:v޽<)x]RaCQNغGsp$'͚5M ӄX8go…UZ&".R ,TPPP#-gΜAyy9ڵm+ckvmQ^^.wK`Ƨĉx퍷s^UKa{f䲾}Eܹ I5k#fmtun8T_AAΜ9+/\W@fXƏy ƍ!>>{SqFHIIUq4/ GѠAof~' f?gVXBðQcPUU9 {ƎUq,?Gm 3ʪ` W-*B(Hrr1fߣlr6a&$$&$''Gx/QR7NއHaѻ/ƎvѳO?Ʉy,+Btzw/S*++ǟ0b0#+͚5w_:l[=`_<֭Z.>xԧn~_~ ^d VUCfcnj_W)ǩS$ MHH@5Ŀk׮4^ŋVxͷ0t`Z'xlݶ 5kD=׎)9i)//Ǯ{У{7deg+l5k׉aΏs1cT<'Nu\Ă0/qaԬQҥl|,X >>oѣ=#tعk XPEzphj #I:&N٭ϭ['o.-lJcG6)D;`()q=7v4;pB1ۏ|~Y,yg7ƍG9fƱcq83 &=G\r7~8^EE3nqƌ8|7{%/^sn3/>,xؽ6B푝/Mj=X,Vr[qo="޽{rjCb}cnj_8܊?m@/wm;, nm^#'_4?t:uҋg>dgge0t`yƏ~q[W^_|ZReV$]܇>OOE f ~|[Zg~)))8OxzeMh4~*+1o"3_O>,pJ^ϩu%r2"W+9JKbs:!`̘RL.bܠvf `T6{@b?q]tVfضi#:\t{,[»jb,: X),Z5Y,[W\~~ڴRoYh=˟+ą-sd*J\@rU<-dhsXѺ9Xq֯Y0$f 41D(%PьÒKq]wbBJqAE׮5C܏3> 2z4e4%8r@JaiH?}PmhK~HR̐[.}t*dp'QSfVùF~N= )j7om_M6+^urڝ놘םwǷU?'{'d1aHBB{h)eB ?vP6UސH #2 ;qgoteIָ8lKw>{V _3:!I$jaqskJ,JG[ڢJD'3-*ZA(SS&`) !,E98f2;m)ÖRKqH?[9Tf;"',= &IiN=Es?J4ҊUᨙh]"dPF G8b BVxSdR Yͪ@qZF6`Hfwk͛e~2g.1LRl dyqwl/;&*COƐѐs>0>ldId~| bw;t@dq$?s[, vXdaD?ňlsc; BIX†Xb<=I! (n >6A9C{RW-eZ ռ&F H_ @^,gA)]1RŪd4WXE!ԓN8m;fa IGF9RGIxUgňҢk,.2 [pT鱅El3͑Dq&8\I]&ZZmQ #cnnlp %| E$EJ)"u4 I)PncTPP\`f( ( /{S\U% e*\ *T'|` | *P(p3е ];vE+ @RZ.-.5nd?sn{vI<<}ȻQ=D;pMH ne94ߨ?tm@UF}ɗ*X |`4Tѧ3O]vE?P>`X敀!+ T#n0f(8vvj ߰v&89ʓ%Iq$?ZYpÙʑC$ J`|**0< 0! .<=ۗn@Y8 ݱrۥ 'ˍŅ0YPȅ,F+nrK!一cFIa{wmѭ |MZ/'" +"Eq66B ҂%d]`RR\R$xy.Ċw yDؼ;Fρ}ѹm 6S'~z5rRU!\ ":7WhW '_tmA;d(v ₢,- Y)Hq,ږ`?%cp?|_mN=8m΋PS!V9CV06P0TBOBy>DcU58l\~5سs7\vh&/~zwǨ~ѻg`պMXr-@Epܼbt*뉞: Q55ɿ >!z4)ә$gnZ$P%ňh*8_/ YDRd ?0 nRP_v=dT@QPoo N>n F Yx0 L8*cǕ^|(N@n,X0O89r$Ei ϢEP]e,EI,bl&EͶAF]On6Vt)(t=d,Tx VPYP}ߍlsa˦ V]=;{7gKJxQұ/]Q }:¾*.,7@IAvoB6\P83>/Cp|T>ٍV'1IɼŦخ/6]z!YwE1 |h]j$UrǙF:b`7Рzfag L}F=Z , 49S-uЮ IDAT@wr)(*Íl8\# .}G.4_?zvl75%,p*-O.I_ y4ǵX=HLIdĞ>)Q|H@Rf!Me8f2,(jЈ0#1` h)\H|&ՇU^B6S~_ &6l^7D 7`'31عu0 .:vɂ/[ڝq.PM&jK"r*7qGQI=~'kir^DqŶuZʛ "D1b]K-_m#\<ˍ #NCdcfQH!e*.g`عi:v(7¸1rعmÆm[Qtkƶ;wAtڻahl/^W} @en 6@ V-q~$12n~qd")Gd LC̫w[QmWvYFaV~ߠ&9G} =rk%+|\4x>7?yn)cSPܱ+3ju#ە4IFb _?uEឍ[fV۶S!rCbҊӒz̑QFɞMD4 H6KIbDvc8~qX|5<} z큱Llٲ{vccEhM.RJ܍G@56Ygj :{v{́GidTѦ2I Щb7d;[T͒TR@~F7&IFWU1`P71 1F "PX $`BQlJ X<ᵨw`neJDe!B s︗l#/tW~zس;OE2<02=n 6A#ؼm**aYg oYgL˰5x|0#5ҋh[\F,a)F֢v>UNCYYG<565V.''B>F*F֒Y1Խ,\z"ptl-H̓Nr]lj|u$Ha^>P[x2P F مs#>h+ E57@٧ =(k3? ׬Dnq_d?,_3N?K{ùб/֋طcT7a jy&^x: / eW? IwL[Sbk XHFcb. ~701j[wc渝8w"BV%+rT*PIZG (2*pijŸgD#^^lnD.ӏKIg6WgW_ؽq{0&qSXb-K~\ջp3 \.q꘱(mSO6zKV&(d~xKy/:3a#&~@o|2<1S 5sC)]%*0_.4)mmQ<\s(-tEխbgNSO;-^]6T\tXfc[~.RAXE6cDcuvꎕamXz| 0\l nCa/]O|6  ,6kS&5ȾH,u.q)v^x>(Pᆊ,Vp 3/=>D6"+,ՍlŅ}Hޝ9[U`ʕ袋pxWqOa>=U+6or:bȐVd! 0ǟ0{_'_|ȅ|&䂑"_yKB @#۰ynAVVVڼ8`j{o]FK G[jzСCs$tz.r|>M=zt/9q-7gdH>vm{4M GJQfI=y{yhHmW /V,t.ɧ/=UPY^yWs~k:OpdbL6CF }}AM{ktgU>x8VUT ˗[ Cbd/£|<Ņ[s0x|0{9*7^+7U)c5xp/n#=2/^o٩#Z*))OϿMyy=z) oIr_a*Y`qbIK+Cp/OɉC^"PFLI8ԣCO\u58a:*+b҄w8.~9;10t_}N6qӰx"kW3ǝoM>^ ' SFt9 EOqp&9 t&Νx+Yx[|u9VX6EPpyowgOG`ݹʗ.}zӅk Cӂ{Pe#>5+9S&lGaȿ>EH@n^.lX G§PQ\;m<۷h?5ꇢ;˗- Ur- }cO8U[b{pn 1m7ӻ@y˯믽~Q\rExZ]ӹ aB:tK}w^,Y; ?,phhhK tl&,~W~B޶kZ.tKe>4QڥW-Ko>& =zacP?`Szݾ ?6n߁FJ zF/En1j1_cCet]܈}a̐8攓ѩGw\}xɇ7+bx͗^5-ͷކ j$xq gn38fAdr 3FCC-Όnx}vb6;$Юs.tޅ[ 1c c)->m2ԺIZ>י( m.ǎ;q!U eww~9a᪥h?rⅨS%(nnEuM- .jSju().à7b8Tmu.,\=zڱWQU[Rܨ[/yvRsII :aacAPӶm[ѵkW¾|g~~;wo[o<$~sﰥÆۯ[ 'Q[Ws>/{7mB6ѣ;if\w5ǿB1uʔ?p0m9/S} y@{tܹl&e WO&<ɐ+Q LJ)Q*TTr BNV>N<8o9xeP|x|J* W^wNU0bx'1pHo#c8v)DPХ=|>/P=^T/~k ϡ*Tfp%;1غ̌]vc-o\Lxɧ`9عsW3^y5z]<͎"6r/9eZ='t+~&M; 'СC)*nņ > vލ_~s:~<$:Nh7xxLH6T XUed> qYVlhbga)=́pADzWAMYH3T'ⓕcpI1$9^{ 4lm>G ; "7T(*ON];o>C u*OU&w%w#>vc٘9]iSg{Eشf5VZlX? |*&];ll,܌:*VxJ1\piSw2yLH&DqlCS$2"职RgEn^w(k")E0}Hyi;QtePEt&"_r|*ڸ\z0v tn&^~=cmއ܎qb9g0/_]zư#QPȌ"F+Q?|_.لCȀcT"p QZj:ŏq#/c ^q`^O&3H'r2֥8*3bF`OT /<&9.mउӰ~aUhR<׷aPYXfr>z¼N8aXL>;wFRx*DˮD6mPwԃ.=;`سxQz Ae5 .Lp449eTh5Z;ƍa Nc ?5eV,n^PKE hc ̟nvzx(6[lŽW㫅_7}Yg̝3}{ԜujBav:u_~n-Y3 qS'cW pͯn#J L-,Uc:=6Pd@,w~B ,JCYGvQC: !I5Ŧ^Tj[4@,vy`0|DZW٫ʱrمplnٿPhr0߫?^ vn =54q݁)W߈o]{PAyS6zuBaY'pUJ,/ 'k3+驄-VfWR$3FynM9qJQT,0N& OKY*¥`ŗU_f~n=K;o…_b8u̱byq}*E*6n9n4>7Ͼ> ];wǨcFƒ,l<YqoybSPS)+QdAZ=I1vk'qwNT)(QQ=-ŶʵX͋vv>T0z 6mrdRl;*ޯ/fL. VEGŤQ} t*jDP+۠׀FO- +ږ,pf/u]c1ໜzS}R&c?9Wl0B> seTÒNBp3M{n bTpeCq>3ϝG{/J*W»3˗{GG@Q*1.T7`w DQzٽ ?ex@^pv)|mUq#-&+ Z۽GM,K$+ ]E N ]&Ņ>zb`~h8R:|(_ou?_*Ԧ&۸*Py: T\.?V|BcpPұ"h`*0r1I`x(~ﱸM\a /9Ѵy w=C_ TV2Vffj?~ IDATP9>Fcu#[vlGyzض̀(^.sP(7PIUЯO/l܀{Ý;k֮A]C#\.7z 3:zL Mn>D2 d( *),KeU&LwBء0 $)CuHqE-] bcO,[E%mPخB%W UU> j(fӀyC5{ob*40Ie(fO eeE4b: D&f<]?r(!,:p*O!j72wĈ#Pa|z P$PA.CAh n܃&+9=`[(e%KͅYo$2#of#ROVT SF5+ˉ2EYv_ _CRP}a%f?r )PP\ B{ Udh ZEQw$R9r܀ bƢ8E%ceax)bo1G_,~*Fk bIuX!`OWhS U05% *3}P\}`E+>(n ,֬ cȒ35Ll~;2!ْҵe_p [G$R67 9"dGG-~ (,7[3(o;fuODmNG6^ p'hjE}t/*akgl7r%q^HW?U?2V#Υ!FsܷaĒ=P%ڢLWZO64wvaJQQL::z@?QLx T|$YD௶ĥvI %yh bO$KЕ$묌L/& N %I`9xl:Klzz{^њhFi2EH.,/J2 `TQM;I^|n&3T5bngGdmzMoQ'06w$nA\MwHV삈Y`4Q񬙬 ;G LI.%+ fHө׼EX@ېje>)M*s؉ͪw fy3)h!bldUH?s&H?(Q ЌzF61DUa\%X'rVi'J"6]SQ7ާ4cT8 ~1L`ILE qN sNf Jrb=ciY"㔜ˌ%9_$Be> % uI2e@mHUy SV'g1pEvGFes°[T,A!j$Ay; nʌdK-Z*٦yiMneQ]pL 2I K[疝l[h=/J< ɢS#Q: Wvd$I2|MuhډȎ"ot7Js%k{B1tRA$IGA(}=?YnfQH ,F,*u=m(x.WN#S<)8I$e=\+vO7 ;W8:`e#s 4WW L$IKel|?/|Rҁg8`miZڕ=J%$ 2Y i="B #IIu #Gƍm,Cl($T}6k֦w6(7\$y%I]4=X=!4p4 ف/F0_eaڵTpI$IZpnF]j1 _FFVnk>P\.t\o2 a&Q$b%+X$Iy-^P(z\N

o~cO8 Lk|f~=;f4{BaA&N8 n4B֭Xb%={` 'aqcѭ[7b>Ν>O?O_z gOǞo.Ĺ3f3#'']z ;`λEoF 0wާغEnpuD,Gma?I9E(I$IUPq5M!`qZoݼ.!8S;cq'_ތߛ>}*~5W`g@_8e*^Y9{ ;oϟv`>5L98{DNPl ~P:u9Νː7rPKKN*Q̸%š< %aoݺ 7r+/sf]۶ ߛ=C9 @QQ[lظ3g)S0Ӱ| l߾w>03lO>|سgo̾pnAV|0s8‹[o? />|'aobeaoM&[$I$Ir.N >W}[C-5P5=@"~ eT!P9F)Dgr#-gCC0Pu%?'!7J I7]௶I$IRɷXڍa~=n܍[06&j0gS\Y )l)U)]ͨqrq&ű $I$ep|gE4Cc8(X4(C@V>pK$I2($RH4U8lݒ*,F}\ C$I$pg4i| s6Q*b ^6Aqg嵰HV$Ity=['IÕ.ڊN+'ImCe ],|K{;"aKI6 xlhi "L?/ڷɑ"IG1xvůfHXwhuTVygЄ[fV@ӔGK;Wb摒i< < $IxڷjZmj.|H}WCt`Z#iځAg 'W$I2T8cc?p+rc2щb 0aiֿMǀQG--łqd{CtZ7hA='zis[gaZ"`O(ܕc6I3bgL}{&9B 6ާ(u8ǦcEOC"yq@ AJx0')d`@n\#2d1JZLAlG7V56Qs_ر#JG'FPj5'Bbp*/Dj\-(6f`be9X7I8Xֵi_*aNYڎ]$sG|rg_h,T =4<]3E,rԹM Q|&CwpSo`M4/lRh'A)}[) _B+Xq/MӔH#ҵe0xAXL϶19{t[Q."d.kVW`z=#b:k(Kn-]6BQa:֟F#dkŇJJ XXKw9v]D55y0WiD2*)uqWVlOr"N#39f4$)骜W3f@K*ICüDǙE%Oڀt~(;t4hxN }pbX\B#btR Xrp9mIn/!ЫA?IfG$[ߒitN $e"ucXH[d#Gl]KV\h gL_ b(d:lmrz۞nF#B[)) u% [+ʣF.T86U ɝuP̂`Q,r}{X<)\C`hCaAՈQ3mkRQ`PmLXmW|R"jT*T2$^2J(lS[Kc3+Yp:Q9nSN*Aod9C GR2`,Yv k/9,P'U)ŐΟHWiY' 0,S=5PL˨bu Q3d&:;Ni$ k.6Oq D ecJɳ-o[eQY0İ?JN,M2)y)j4=XI-Ddc YWI͠Ho-eJ`z|"1STIq+B,OD$MuIkΙ lAeh>W"b_psAߴ ]s+|Ⱦ"FZ90xӱ~J-YǰctP@P!u}df49RqXmj]l# #֑I!RZލcdNV {K7GCQmU&T+MfяgdMi¦ذS"*dҮ;U6SNfJPe6$NCqGrf42Q6p8!Q%qӰT4Gʢ>Q(" .Է8JRMRwDm}5 $DKpd%+eRZ(!4a59/z%#7黲)vWHfr%ծ;iAæVKk6* M-`Nl#TT9'YDCrH2i>-c$KoTXh Yo֢\!X(ws֏ b 9*'!֋ ueMB Qz{=(䜫kL Z%0 K2C"foT8bh$@&'[$>aOK"͞Ν41XaQMxfvLM;fRH4:|%@)e=$ʐqF$#$I27o$em㴟 tfwdc(<Z2cԒyI 7Zb1ׄO:Z*XLyl+NJ}ʢOߝq͂sD$3ɁZ[*Xdub J:ڄlLv$oagD M>Ocn]*DbSVR`!;D)99,z@ߣeJ+sۧ4d@TqNYxі2huJY_ԫ^4vp"v?'o-p|<Ҽk䰃F)3Kd6pk@JC9 vX S?/-Dɡ&QE0a @S{p%1K2bolبMO ɺ}f[/3 ,LׂQaW+A'VNa˴#94YbwxFg- lfI-(lj-.:D7|ݰ&K2|wԀF 80;AɍV|ivX&<0oFډ|;[Hq~n&e(q,#K6&Umr>F1?j}?M"`NNJm:v-vN7$Mʹg_Z=t' \Uzk+&4>z+'66鶨D  9 ZlV#G*=R%"ɑmi8ѧ, ޥ( c[嬱D'|3>7LV8Sb)S6o'_c6@z藎ȑ&m%e6KcO'%;sfNbhs((gg$ *cNeIf`9Ӌ?"~,B~O٤Ov5e6;vq%ղ›ll q$G@Mَ`I l4 FXzWyJ GC'(T 6ʌtr7g,O-BKLdۈyEQ8q с"dِ$b*UGYMKqp{AVElVxv6kQhܗi1槇*& y dNt}gQo]JZ )_,ٴP_ݑ,3g;m⸈Dtl鯿c` iK ;K6. Ie%BNDIi r)!4=0V"o^r tm2!qIDY5_YӒ&bJ5f.y.MO S u fG{kJWl':|Q QR2 C$KaJl|`f6y3/2 lV-BinFIN$D&}IEh';3u#3wT5n)~3S((d 9BHcuewM/l{(yŲ O*\`2$Hd|+aԘ4D쬨]X_3,QI*jX3$Yp)RN<<9!Utϑ8RZ:w@V~6p (VɈi 9:L۳nmpۤ:M`82WS$FeKEb g.dI nw>dbQ϶7LN[Ϊ4,c†^kbtJ^$9N@c$B<\ 4Ah۶nyRux^9R}c /߀F}D, ::|Fu mo8zQú8Д^ȿtݛ;̤biM:vNCؾc'VYT^_xFq UB sЧ*.\.梸};;i,fL;k֭×_-d~n0p>@lg)т̖!}/ܷm9i{_}[G o sJ9Pyy(n^8 UEyϠ5dcYgbX>X[3 X,s=/</QUu[!++ ƍ7\~ϿU!= l |d-u֮[Ys>R-X]fU*[* &4$ ncu-u%Kph |4W45?Ђ Vs6&$3Gm+ k֕cӖ 1^S[8z}m+;M][/ OZ)A ˘ 'maixL>hժhFB4n#'V _~PM %Bs2\{Xv=V[7=lf`q,C̒SÎ L4TVvS>RVdgtsd5|'='QlXT^@j=OfDsګštf8f#>g+ᯖ8@dfPǟv2rE ;2#^RR^y-^{&;; 6IFk8|AA SpZ,n*؀~r)z8<߷;GjNJƸEd_c|k:hO7(._ `|`Rk`qK% Hs Vta^4уY ١2V-Gi6%ߚQ2߰` X%J #*X閖B/Œ9ZN)~LdŦ%8nh,^TU3$_jhF mCOH8U,Y#?n ڴiPsÚסquX4.$g=/ ,--qǎ%zbDIwFѬz5-q(Z=wv8\uiy`,JQ$xV5XOXb,enuu(/߈񧝂Ys>J?9xIؓć5;Xk4#jєhbZN9+WV2YIM1pHKWs҇^ZYUt `쏴umuvVԑ1r0|P{z, ] ֮/y~}CR"]&.ݠ$Ky>l(x]o%a<OX`1YkS ead;s4j9]$'1N^"w>74Qӻ7nZ 4={@uu5~\/*,]I^FSꕨ؊ISE ڌ} ط?Uך.+ t"s) ?0^fmwNHB I,4Uwޫ]U]S3̨ën}L(l qމDQ#~+V`mhh,2‹?6(sJT( CN4F=w 懰1WUƜ؄-[^ 6DJzNu6,Y? ͘sV2=o݈d Urܹe6 `T$<c*PXܲeñg~S1bp03.RbKhj:&e_ErA9mmGp8=&h mٺ cNl8aiW"n{?ڰaaen)8;f ~pӍ׾-,\(^mm-T5>F0,ˎ0!ܵ<{5$lqJ|0e>[вvV,Y|t>n.tZnY,4D?ˉO=㬀B%0H=s7uz98X៶ymbnIN2 mР8v%kfcʯOŭ߁_z^WpcBs3N=a ê5k6t 'M,{u||̚˘܌Low݃uc!ڿ f7GuVZ[o6o6 %2Emmm8ztG%DbFW α0yEm4.6=^8?ţxu|+0olٴ]lڼs-ɚ _/у_p]w㗿~ 0@Kddz3Xk[0 rt ew*Bdvzqn2~ 1aYj8r6 rV>~ok67F@pYXY>ƤINj"ؽ{;ǟ| #G _=>&l1瞃| Lhn9g^[Λ 6lx?ctwwcqغm;%G~ZZ&gmm5SOC{gIv|&N0nu4Ʈ{oIȥsLsx_RR%i3n(xmd-+5FWw7jj,"M\(^ǟg4Á;í~P5 'M/qHnF|ӟ/X۶y< uKaM:m~9 ͿjGX6'  vw)k;p<kD ||K䟁#'[;{;yDt /:͕_1s66oޒ/ .^Ah{P( ATS?lt:Va}_CdyN2ӧ[:ӟnS1̳WNp͵NJCH\7(1jFKP$^J(tw, =47nnun|[{ᅬ!CĦ&q=Xf e$̝31wlV`n>4nf<կÇԄ{/_k¼?E/#GmwfFď/y9 ,xŗ]UUO7}8{t|¬\ݴf"w܍{q7[zD8SivPNG+}@>#6~@̀U W#vw2=/0y 8)8{YK_(Ey Kv} n 9%ƃO\W,&͍r{t>^&d|du={as"26H^ױdHy/ĞΒNGlXO .<픓[0}Yx߇[B!)R'֒{sr˾S6!ڄ^+SDc4x t m Jtm 9pUn=>v7ԣέIt"X# zihl!n@<@x(4*gC\oreݿn=.h닋q-rrx`[ֈ[)z+yOi F7|kb [gL= _~D``݊ }^94;-^?$1p$&ɢ$sӧbW?XϾ`JJ,T˨,Yz!J2(+Y,{c9.9|y,؃]c 0,"¬Ų7(͑H E9EA0ČI%u: ݻ^_3y.\I1XF PbʑSWc$/1 ;m 38)al׿:=CZ,=fZ8jsOGmm-4?ZfHQ Jh?PW_sO_\t'"Qz_+z{vܱgr7"|~7\eܹ{V(L,-4Ic1qx<_3EWs;LT5iВ'|7|Zܵ 6+-BS&1Xbbs\('WcW-ÂN<]#ŗ^vvit vJXTxR̜I'BGg')ߗ=$7r %r=R 0;:;o?5F _ZqaGqXlˡk|(zˌx-R#OKE18 ZyE+qcoXr5O;bNo:P]N K/YyEO~nP1~>[͉TJNMH\~* es:BOwWgV*BIDATZVǢbU0}C eEHq\*Y,Pȃe7 [_q TB艹`&>?a>>Ǖf8"ޓ`i\Z2κaN4JANKdP\QHh&"rxIKބiD5$82qak.mbx˾a*JI C+ayUF õ*|'_>$C9윃 mEЛfc,gG*9U , KB"V~tT.+nو\V`>(# k+9c1`1ie@@>UJe|*e(p)ůcnZᴘYWP.w9]wl-/n]fOZa|Œ+]vVәbQu ,<ҭ:LJ!0Y+:6R54lRMMMJ(jsvdp Z1NW$U$!if)ZW[ h(2zkL-,1<= nudsZ~[6br<ŁHDg$Sٴue4yRM֥|1G! U𠨴XJhPUP !@jU7Bգ4$c{7T-tg  k1Vt/{`_`Rpx>ȝuDG̥0@6`4 XF` ˞,u‰`Zt:th_EϾ#N]D|cMTœ`!8)+wxFfLnt#z2 &RA4&lS4ӦyIW<9xq1XH'@aq/qN\$KV%zP[[Q#Gn=%b粏]@mm &4O6`M^0.,W:Yo*u␖j+bh#nrWޢ#mfԤqЫ @|В[apҔ:. 39̹ &]atb`s% b>lmeƐHƝ-,P_&!v~D`^"e,G¡.–)[i/Q{5^ݠ'L fWD㽨f?e1.cP%);K!͟+9u=|LЌ l{ fi&`orG7Fy瞍ӧ֖ v2DpU# d 2#xxHtwPUꌩa9 ٚBGOvNv$hx[^k=-/fS"yU,=6u䕫HF2U)-*& <6spGX٪`Wy f +!vkȨ3Ƥ0{y9|Օ8K,r,#jXrpӮb3sdBԿ hfc[q6,0׮ۭCk *TEGE'[8{b_P(-.664K/qc150m;"䂰~1"ASư- O%LY/yVaݹf@M$M8 ?,睟Ł'G5viUM; _E7*l+fx-S#̻8eUk6 8u;3'=<C qH{JT $7ْI\(Z GhU*bL`&| "yup3(Ʈ,w2v" H4CG% B 6qALBl,>B T/[,/+\ Xin1,Ygv,P$6~Ѳ'W`#I4ǡ$#$kTM';`ۄPո$Ŗ` x*T+"fo,oy~q`9^!Oҟ V5!ڰ@SujW6 A}!R4,YPR@GTZi-.i& ga/r6"31ێMn2d>z26Rf0{ In^1M`u'w6`8+-3X3)D؏bj0WZ/w!Ƕ`[rϖ\Ap>x K%6`}=S3:|f" ,+K:*x,o c=vI.me%Q.{ɤӹ=rVXZ ձ (P~ύ #Sּb󶗎s<}ǐrvr)&1'ӑILuy噜 s}6sF4fxr{<,Z4a^X=3'w)!,A&wQ'bs(zdDiXհp[23 V,ˍ`Tm9}dVxv14;hˆ$*Y֞|ַ` ?ɭHDX^#w6?Ix>sA bу;;{{EaR*X$[@"9O`\~RX'!biS<۱y 'dYN.NnILKQ&[ᒎ1w.Jf!X 1P1 5յ Ȏm'X \(xc`6j'k쪎GD \(1R[ #8TWՂ`X`ӧc"&M1DGǠZK.cVDD3􄤼SRv9gVf &I3lT[EFŅJTU#)" c`1ΘC61Dۘ)*ʦuL6:ill0b]jr\4YaX8eb`eVpd5 Ĥ(;nLQ?*,H:B``saeD)PU T9pxu6v5yP`M؞y56& 5z`8PS]#~NB.5ET0o)`UV68cvȆ;+LfBT4Z@up%"N/ ͸v&&^ԉ? bkyW$TVrE.뻰:!=yȍh\IA<#RLlIndrmn)F( "{gC$Ǘ3sC^85LR2]%bSfT/q-HIS"Y$)ry'ȃ-d$!yL/ )%7!gɻu5rb(Od9& 52%1.xu$aP<# `'2`̏(L ^ryЊ$2OMyYFot8`D(TW$|H^uQ![E2TqnunK|L̻ԗX]s/N4lsMs#hDJt;_`.n'y2'kLJxo JH$1BZufrxάbVq&=99X$H,*!մMF H:w!9G^F]0aC^{&0'vHv"(3((;e L5$bkg*0e )[P7$aʤuTeF:||Ƙ[Ҡ48ϋ+A'2$M2Wo<y^[ݮ<&ӟ$<(p]"r%u| YI7,2z4` ѧ>Ks(>~FǢ&c^ʨڍFQܤwd}\@x.>-sLEq4#:*tdW5y(3O &ՅL2\ Hya1(?*[-ے\Ex vVR L92dndN=Wv2}JIYr3RMd)!t\hyHt%R1'>!SFBR"J!AyIj5B T<{$4RN$)ͻmP:[V")BnM&ɓ@y@.I5p0Qk0M~.v"iE1괟~cFT se!}j&d$:f$ڟEO<,];1 U0I+.^H%׬Ihj&rpKXzb5FH8DR瓹= tĢyL0()EEb璑`9 hp/G$s)b^Yr5rH`){$MJ)E|ZN&KNS3X)b)1'.j''_!\plДOm##$hS0R[̃kA+]HwGV_K@UMњxJCoRVAmnah p| kkJjZjEUT>QIOr f;Dswǩe4R/f4hF358 11@JL[R]fV=>``1Q˞Ί33o Ia0sXee ӧla***U(:" t|P!HqժE &2DLbIe+?E'=֑a +uwz 1}}(8R[ڀ/:ⓘR!\pl:f2*@?&'=/yJbF03&< Me-TgW D>[eo+9CuꖜhF!BS[&cy)/G@&@& *aڽbT!Ur/HrTRH2-4ЗYUUwOsf,A  -p$t*++Q\\GafL&doݺ"h]eZ7!,®=J̤A2j:>s[c0@Cj\CCҹ &UPnͻj%#]}WLJC7` A8(laChKNhdAxgpT#uHv]"TXii"CV f(..% ;70#f-$)ȤQM&O  ."CΜ-ļ_bǮ;j4H•MlT?r ucJ5պm2+'hЫ%m߉GaQ62,T[ g{:=]ҽB1[{|+9 "RRW,LKYN_/s$'殈VbVM'B{_BG)4˕H} A 1(۰ #kJ58 ’Vu&dgј!)(**Ƣ%^- ǣ]A`n׳gKk]ɇB>˃)pȋ ATT*UCgZsq!9mbe ԭ~1m٫F`Bu`_o4|J9A'"L}c.CGX0$O3;+LHadbdêmD,U!> #FGC<afD|3M;|$ 5H",o9]{QRYn^>j-S7D-43Z !WPm$Z58oۜ˹-mjokAHόzvȯL!H:5a ÚT IVN9сe(v3FD`aAb%&y sz2Z ;;̰UA'ϐ;2 oh36(!| l :/RhD7 OO͝;28RL_=qN&kc.lEE%6+JF#` Lh .@ǜiTذ$T0UkQC$.SzΰSZA`NM'ѕ7WmWRuuak=IAB"T,b0Q2cN>̔/҆NW]6@ɯrBWi/}cZ L TtiW)LxG7s]53 \쥮1oIjkmQ]xU:#ýՒ<ծј;ZhʈTHm4݌1"I tL66g<4u~Ņt("ivF &GmNʰ4А${(~i;a#pԷ"#Pj[:Sݹ-2̿޾(w& DO#J.ҫXC>d I[((0<8_r.ָ- |m|컡(CY lJYH !*)x ^}X;YD*S4 [-R$@2nKUrW#CE'X RěX 1ɘPmTO2=XF `MG.|O:UjK)G*#9Af$!TC2A"̐QNj ݟ\svmPpc 6o?EAQL:\酋ڵ#S ~]ha!!yM[†s<@mF( # E=Ѧuk,؟yl/ `׮B 7:o}?oohTVVH-u RSbaÝ/5w\u6i(sN>7Uz t II(P\ٗɿ-7SAƍ@D8u 6lފJ4l#ppGAV]kǍf"]M5Fll Η6oA]иQ#$&G\l,/ص{kjc9c\F$%wHMIgPXXYr<7mEeUbnb!7/sY]zsGwƍRѫ{74lfs|,V+~3֛&ql{vsǚul<вtI(/\ٷ_|ƍѻg74LNlEOT]EPuHaA&HUF' Yd/xqeiӦHn  55ꇧ|׮~IDh׶5qYd#B69y9yégDϵiZi͛KeoA!66ztGn]E}4k-[& -pYjd Cv+X>F8yv݇¢bTUWׯ!;'9ʌN3ЧwOxUNђ1v4kٹGM0j~y^ڷbDG!a2z8+wQxՕv\Buu z.IiCFF(**BiirxϜӧqH 0?bCFѳ{7`hS:Mjfp=4AD ( w,2X94Tx1вe ޻Kd<<(..VRq]=͚">.c 9ӫڷk}\ZV57mVXX~ss/@EE%R&ch޴ 81,_e0q5~,t}PSSZ`1.TTar5Ma݆hۺ,V+n^=#** k7lDNn7nO?zEZxk׍fFI::ճ;L&֬Gn~.=wW癜F@3< 7muu vسǡwO^X IDAT\Ǡsؗy555ѭ+7mFVv.$_,V+~[3W|i)L&M.:`L.X~ݹ%>hU  ~)Ăb!MV) ?˱gR&h_y|)Ο/a?w<, %6%q]&DXLyL>t}_`S th%d2=oG&b9PS[]W^~Q5DE ((8]6ڴik^ڽΐbƣmVز}'V+kjI(.98ٲGB}IOI+++`CQYY"H-,*FMЮMk:Q ޽PVVE/EeejkkqNjBMu͏׶52lx3!4D֭pe`EpB,X0pshҸڵm5| ) |#<ˢxqF68DȎUS= :}1J ,qRicnj>6_c_q&!5%m7,6mC>ͭ۶E<;j_98Zee%=iHo8q [`aDz }?_5b(QZZ(ݥeeC֭0vp売 qqv{5H_# Eqݮp޷M4F޽иQ##>>8vcHHGP^^&Kfyb/vi!O/$%%bEnj`X]IUiV4HJBwe=WeeGVcuUUQ]]ý/4nzmVvu)lÇ % 9yhӺƍ|TUUpص}pFOomW_Ej*F5ȕT> *G^`3YkJRx'/$o~زuiLII deaΜz@|;k4n8(pYV@ٱ(-s=v$;-pQ/-`2qhܸLfd >>͛7]۸IBJw7lFqq ZDvmG7o:8pSڥ6ipyXj-uvmZʘgI ĎMsyl!X7zvaXq lւUkahڤ1. {QT\V-Ÿ>vcDZvFt3&'KѦma2иQ*fd !>^;]q3Hйp@"q S[ac:dvK"*W[ŹJsV}M:69" 8OyeN 6k֌]~lN؟"BL!C򻕫yڇL ȁFn#=ۃƍ|>~8r$ o6ooW^*w}vu|i)np5qNʿ& XgŠ`=p hEsǬ,Q`KSEUFZX?aMxAM.uk&o`˯b˯Vwg ǩ:7C)n!HVvr&,L&3 nʘ3EFSJ~qEH~>dո} Uw=` uBYQfDZ#~g(ԃwP¥W<ҥUqdOo12+H~7UXb9.ǣx^=h 6]nݺdv%( 腩4q%ktaXP;:9 Rg?TP:T'? 9P&IgÜ~y^YYǟzFviKX/,^0z ZjĠ}vXp~[7-WBgBߓ*Ģ"4` kvgg靄, _Ug1sK~kW_$pD6t uǏǠddĝ#y]7Qsy#@(P3/RWٗ _^ ' 2z=lYɍewU9y$N+JSXSWT:LNpp)H]`WUF@M«ICc؟y^TȩtQqXHq#PHc &t+ AAm0N% Ҥuɘ0fH\zI_i8EEE؟yl/ `׮"F/WF@K:!RF -N3sT>ז ,k2XG`Gd'jXvz].B"'J HxjR0$Exa~WQ2tFq6z;өsePp|kdhrV(Xd&6iwBݣ;Q`8k(3sh17k䮠:i˃E2W;`4?,C.jLT4QPVSOdF2(-mpeEGGwFLgLGrr2}ط{'JΞ~q'?;lVCE91e0>>ݍ :Eg=r$ kʲs,y~zg9|yY0ϑ~샰=coqXe*S'Λo Nu0WDӦ|Ɣ/>C%.tw, :/9uo)g4z 4 q-׮fvf+۲{aM`Аhެ{nVB|b O\|5c(֛oFFq1,Z+/Ls<-Z ;'c111+QTTxIزqy!TWzh¶#8uح*\/L&?wa1ȵ/bꗟٌk&LCy`]~z00|d?r }9}܍ACa|̀[Zjj/_]>\O@2et[jWxy 2u:<HKKG`"<<3h2 9ٹ>)Ψ9w<|1;8r$gz8X~ r(54`q04QB(\&G҄tD&f͚"!!V}%eűc%Uc̓OMhذ!8e[ӦMqϲq4iG/'ڕa]hdlذQܛ5kx[BX 0иYLHGmGRhwĶqE♧D69sxc2`!OC,״7ZG1m\n4ύem~'KJLLM7L==/I$$GK1nQ"bb<٬\y:w/$:zԩSFnj :v~_@~r}c ֝kۦ-, N<7GE]bJx!$)Mq ]D\8q$*++ѡC7FAݥ0igς1N;Esw8)#:uʩqZ#:uӿ !gE{b SoIZy`=[:z ʋ䯒skE>de[ߕWww_EK~qEE%<ӧNUE47gϸ-fzA }:2G&;yG{o&7hԔx0))whu2hd/&%J|Y@\ 5cuz=pDزe :?͞/EJJ ;ؾe^#>'N`׶ؾyrrrϪBÇaOgv&_0IeW'=#;\!*ƞbunӂhJ"/Tb',@B#w~\ݔPWG0 F IDAT3+>DKL Cum ՑTڜa?fR骪*޳W/T΁Q+W@8= 7ʱ@0"sNm,E! J. c-VշKg˾,RN2PKN~7u}"D|ٸX_>~}lݶ Dw'dߕW}v9 6t~q%c!xr4ز /N>і"?|O7lV ],|VkX\,왳keOg rp-TSL ޹1 Ib}FL > rUe& ϥ[eV о];Z@E9|駸qq8y$ ~}۽ Ϝ'{ q`(=C.1He9teX۶K?3ѣ[7 2 ЪU:ۃpqhn^a pU?,?CG;=!(-Aޗ0;ڀWןh%^"ƍ… gz(.)3Ͻwqx7ѩkw˅mPU~zu[J/IRO]3aU\‘왿W .{V|8=r#@3Bw+YvgB4G2g'eϿgGDp~8q ;#[e7 +yFUTTÏ'c¸E|degcq =^={`߾}9K/gy 999G e2pU+1ǟD4g. uv|>ck{\)ǗS;C ]{1e4Em܂I/"r1~8p!gll,^yiߋ2REGGwFL/gLGd˓^_~=W'O7 ,٣;.Y}(O21,Ӧ|(-9wbmߎ2rjO $y3o땕9Qift $d@fwݍ;wb/ppnlE޽زmWb-[HVYLL F7v4t]#^F޽p`hެ}M•W\|tq9k6?HxbӰ~J 5~# ˘׊`Yl9&k"ꋹ?Ǭ#Yzq# &- Cv.XIB|Ut348Q^EqI Ai}!FC_xcu^#rƍU %%yo x̟;7Z/-u;3֛7^?b]YCѢu[baTUٖ߯?<Zw/Y]3~q~^ݺuEEiǛ ^zrRN 8˱uY3͸q}`5_e 3Ͻ</`plVV66mތ믻0j;v Y3~r6ovߢ-XwDvvr*lN$`ee!55'W2& 3Ve|s9 +kXh.1݋1'oֹSGdt1Ř񍭿'OƻF dھq47 -[˄XWnDYyq%` T+))Aaaˏ={UUUȸ"2.BUUg?o_ɓ'{ॗ_{NN=p$+]z)ϤwA\$o/derGp@ԟnE8{,r'ڝH لqc`b>{جYSDGGcfd͚5EBB<֯Yc8_ƍ0k6nx=9k6Dُ=X/̟2'X@"b8^cٶ@%8X@|;!N vV)(B+E̠5 Y wN,tcC,3M &6Ưg->>O>~]̩XA70 '!gK1nr /rp\?9ٲeK8x ~j3oO]۶ho-{ƹ?yY?W^P NNh!V+ f; h)))5m_G0mײc{>_$ 4>1cF{4i|.$&&w69ڙ3gTko+ӧ?0b0=d?A0 |Yٻo>x_}M]?+a  ׮'~eӧ< w9z7h))) lԿ 73,ӵ?!k6dV؁zwg?ضu|UU݇=#;'W^q9` 3|L:O< rrsйS] Xx f~-^|>rGzzK,[%iSa&;v$Aѣ(+/bEuM5**`Eu$%ٜ5O-2v˓aiXtuCWAgቚ>% ~$hٻǍ]ee؟y>8?ngX~6c8~dewǏsjG0a<֮[D70wƎ(8s. Ip5v^%z 8}x>x7 11۶oaټe 2sښ,XD'a@abS`y~A>L$ĐEV^3ښZZ$1:/`9U:\:XV^, W!%ݘ>4!fՈ:b!0W8\K([bǖxq7ϰb*|s{O> +Wc ',YQ2̛#gtFS9 w+J,BQ,R%ҙ,:XbLxŘ$a$` ;Xz%ToH>S+'(QQ⩙̍4@W3RewmL;4)I%?c1vµAטڝ/[*i>gJLsCcFuaB5Wcrj0܂L&&ϿPp{|B`yBG ٙE$l89Y:eK`*o+4w~jM>z , lٲUP#B*1az$P$i@% RWbjc8wr0(NHrJ"$"RU]Z ɳٟ9%D/N,X s3!EPE4jxKUa8 !f(ga%tTJg5eR2{1H_dO Du' ιLÞMU\%URz@֚cR&T~@/:xJp-bO1- wY_Lnnd4eLnB, ddBb]GX БAqR(5d i?2,K 򛇬3S,f 4E$B?2!]x,>j`Lޘ6! =YuvtLۨ3Tw>\EG傖""la6<#ُh 1 "}pR~6:CL9ք$HhP}">lC'()]D7""%@JB 餷=gf̙3eHNߵkK*KbUiACWP^}xDa˄I1%x0_]h7Ib>^jA|eI:Uf49#ţ]'1<]8)fGh"-q&I=Xo$ gē쁁l/t5 Rt{Am.Z^i`,t@$+nڢah&Ox؜:ܲ#'ϖPǚ )dIOcdvQ)pI:d[XHn5}. O/)ͩWn :D^K'VQ^0$@H4i@}کK~3gf0 Q"Ȣ YS=XI*,*Sةs3 >T")"VOwЕeL BLCZQTdf9tJfJ#}e21%Q0"yDNm"o`E E;@.%E}8m}U,#4MyjA)px>`))/ӀrU#lpi/>@ovU*tw.ljv\ߋzqv136EUѣv\ۃ;s\=%}<ºv,j{{1ݱ;pB;pEoVuc]g{Y7vcEg;vt]>8q}oںڱ7vc&َ+z}_78nX6 ƂCk&1(HyogqOpO0zpJ46cr@ԪXVL&l#~ߋ ZF\׍0: fc3fċJndp`m;c~(ۀY-e <ZXQpx|u@YOf8ZlBճ2g]׃bطe F3X[2 z9r| Mɳ|uS4q(E 4ueϲYfsxVb3.fm \ils,VA]mE*ܒmD3QyY[xܛ fr cهW3GsG&6EJ#dLQpmC#Z)k֚Pاfl!"\،h}42<M)\y0Εٝs\.Œ<5,-RP'7~w/ӀzphV1W<.ád nlͽ=Չ 6/}-utwb(n.[[g}a~w@)^csE-U$. dT`ӹ/ah Y2R$SJ Q9{:k_ r9pqBeW&l. IDAT ͮ hy$ggRCTP-1(SEZV5Y ȱav,E:h\Kjэt~J @C$^)ߙSl*'))%@(bD165|3+q)BB٪RDD=ҿVc0o "9+@J5 >1E!!ޥ Ix9X.JPQ:yٹR/fZURPՠeKsω\<Ő=hT} T9Y(#wQS$ich %>*#G@''jF"wlE!$&/e,/6bz![ܤ8IƅЬn`8 9dERdk Qyؑ|P|#qv fAPROK]l")[=R׈Ra1gK6)(RVĨY,+T7&[P& 9-k^w~ @.&$(q\YD*d^6ҊLGkY鲅XG z%uM`R܄V3kl닣Wb֞"4pU\@5ȕmV%$!>F7Kq^y@ȎQe@+eWS3]^EZ--oźP @Jo::3X Ϭ5x, zLc2&[Z =`g}0y\P8݆QE'p?aSU6dKu4b0`IظXCd#ǑAcfaؔ(ұqݱ-&[xvZϏ0|ڱڴ ;V-H >G;9%/s=6I8 &lm#viYGNp`ؔRZh@DRL 2=` ;3`֭&[*'?@.{cG-4Gcx! i6G` t{g݉6rAדsO88ܟm ԬA6dKi+L)usW~ܜE6䤞2y2z;_׭Κ)DpN%ŨQ5M6d4DْD^? -6jP2>r.Opϝcꌙʮ1c%3X&lkeEXfw},͞if paGb͚BB+E;J%l)f ;-m.:7^-V}m·q Ǘ﬙aڕhp~[o]p>{)]o t06//U4p bX7^{ 4,ׯ_mKnͪsqO%ki߮뷧};L]>to/4ڔ`FM1Brf] :#F\= nxO3w {௸??܈^~|`9du=gZ}vÈ#퉁`α -$Z=-_*0zh\sUZ[[u8ȣq.K~SN>^pL'znW*l޲E&l2+$6};[Z]8er/ 9iR("뚕م+yo^ZG- յ݀3f4-k1zta~?H2lÆ xgq)'ƛo)'?v;:xqڧ?̌&upA`K ]W_y'xh[C6=G3o6`ʕ:j\5D(lɖԨXO̮Wi`Hwp76m*SN/<ڰf"x)dpr<^zi_|b”xk~ìY31ed 4K><N:yl̟WX~Ə/;~xy[r6m3cԩ`ɗ|ɗ|IXSJٕxCZ)l[R֞ )VapO/}~7aQvGgG'ydgYWW7ã=hk[n{{-Ӄ ~}/1tP :λo Z{pM7\=G9S 6LeM6dsbU^"*i(eLCf(<:wϝ#>+p9?ҥWw݃'(.˄xGq?:__^C>c?_[o/mނAc޼7uܫ[ul&l jdMo\S*" ߟSqʴBˮpA!jS^T,~xзRY3 F~hl1lO3@LfcZ~r@!&lɖvsu'JVAC#|7J,F^OP9Abp_`FfdM6R- 2^BߛCt)n,e1P,dM4nR;٤b6Z uZ&l䪲*: M&h?Rs}PރZ* lɖҶi{S>כtU/YS7#|ݳtλ8ѳu%ZO^] \N2P6dKu{ee:ɚ:yPObt0vDwAD8 y&)#&[^>$؈B"nxqn[SB.cd2&lkjݛcg}Jꜘ{,"jqyΟc* clc۪kݑfȥ«fsmC=Q(j␙u!].W5M+Iu >t3>P"2 s`̕Aݝ=V! ^cmlJfҩeVŬ#et(i\2*fQLJzM^~dR-LHTdE $͆lbWW6_T8PɌJ&e*IOA`1EeE?4 B r༤JxOr5!ḲO IQ.,Aa}/Ő-)a14R|ȟ(~3&-Y%#qއUԠTp'˙ 3f..NAi۸8>t"¢.Lhvpux?9c;9͑ق35H x8AWlc8P--u/$)xLW)4/Ui&qOJ"z  ɪܫĢ 3Ň 3wi>$A(f b:!QTȥlq i;|/KΥI[65Pt[ף5l,0G%L2[e9\ѕG05_)D2qdiRAH%˖nJ߈4⸵}"?ꨪs04&VP\KRJXrUڋx3 d[?.shJ P}󠐥ij&wR.S }8Rme: 6# &' J(R3rfXXoVr :%qcQݢT\l-j+,dlZO ;&/hD&n'qt[ K\c+ft$eYJ<(<3 ?J"кdDT>bh*,YĚ&($qMB#qK^MFJUE6[f3,qE IY\pRdl S>30DfoxE"l1&\LJJ83*Ҁx!PQș|ܣ?fjGᑞ<8}Jsɚruг0X;NHȩ k'9'1ūrU۩xl>Y>1Ha (%" ՠ)a[!zHBa Yz / bt2,'XZ][7 ,+## )Qña"AH)QIے@BD1D|*M%I)Fa9cdJޕ\\-bSL<:.r!m3:xTs2ꌬA95E".•XE+DŸ$CɖX۰6B_wg!9lsޯjVh [8I4qjM`5XP޲X#Ė|xiP[AvI\P}GN0^ ЉawUhX=ZH)*Y(JXp;_ c3%]SVqN:܉O!c,CoQ0r:#E\<3[!E 4q`K b>J zqhcP&&f⚟m:nFQ;pC]㱔PP+:k^9}ClYL0N,C]"Sas,9+xbAUQ_  C17 I%*>,Ƴ8Xΐġ JT]}0KI(NuV8"@R,RA)F&0XwrVn 0C`)zF ov8YUDI{.%{?Y ĉHT1ޣАpr(Xď|2W?Fq۳̰PY)Ƃ@xdj2I7U_f)R7a."`,Ɣ$XT 0[-/b| IُsD „VMa@["L1214%ӻHplu51P[oީE'I@pdDD:s;q~@!JR\p:Iʱ@cVȅ-pDX '1'jX\PU:&w>gaT#$Sn{BJFѠ-2XJBĉe|ן;J-k?1cA@|OhuG*W"娸+u7a/q @i`oNz])J?"B2Ɍw(`&G᱊RTnzQFi"eQ&쌅Tq׍&kSg? 'Y2][Dnr J(U3YIX!xZZR`) IK9BpIsW*'{R&{rϠHhUӗ{bsC^zWJ}p H)=Ӑ^$dajEsa]̜X-)X8]eA\r\Ac+ ڲ- KܤBFRɝ."KtSb @8)-LwܠtDK=PYw9au#boR{H)(a/8y׸.pv-&%V;1'pG,n^e!e?!aл@Loqߚ=Xlzs@nr /±`HщYwG)gŻ֏cwtW-D>ӏL*1(.S %c<1k|bao<=}{~H6$P璨81B{vEQق} dqT#RE͏"2T 7_cmvKm^g|ПL{R=L fq:Nz[qޗ+сH 2 TfJ.#;"Av΃NQp+WxeI?;2hln0GF[bkt/6N')BIv^ڿq/1vAf15A{+\-dzA x[rzUpLv6v'd,9$bzh5%7Z宄E^TW5C'ߣ{#ې*@lJ͔"r罵@IDWQC]FwV k(}K"WCKl$f~chڵ_ Qs /rL YЍBMV9N3eN Z;9xV$(*{ӰQOgStpJ5XTOkiҩUV6)z1 ]C[\To#ɇSԌ6L$?kZԗDU*@3Bޭ@QK6A,I %&wFkv$*JQ0{#քFh$3h"ƽ`x%xqgtҢCrԬ#_aQ66f"ar<_BjA|R) U(9nv#"4ک4gdnM7,<`G79Y!L2̈xE#IRkJ0<J8\rm(J IDATT,'x|=xH_g\T'2Y"d)S:Ԇ=8iU Q-rF =@qPHՋ\V\Ρ2%F!L\|j>ϰIIT}suqz7ۼ!!nĦ. F@#PL'&VDKdrxG:Ao,n[rIxN_rhAbDŐFMHnP+ Ske LT{(B*`";f2 0`*V5Jd"XLֲ?8%IP?rVAdP8&c9m!GA|?rEѱUhL0=jK@$P$M8̄WJ'%UoVV0iae%pU\MI2y* u19nmg)Bb tRb!A,DqℲݍcG,h4%wvJz,j`S `fJ59`NKFe 7 R"ڣ݂ӘBeml4(}`h&2I4G`?(w5~:G&LմVLKW#<*K,wUNNΏ|ӜW>rV:\1fL:Ǐ0x dY47K+CA>ö;aF,]ֆ,AOo⺞AQi1ӯLk6mތUG.@ѬPD"X gg)Efd܌]vC>>Dhelܴ)f_tcZ9ke1{e"3LJFAKs3vy8?Pwݫye ù;%Ԉ9G* fT`r$vC!ןCOO/mێ+Ѐyo~H3 ;J'|h˕_*G-ƣ?\>_ 6ތVⴀ8Y `x\?z{{mv,[ LY4Qo /9wt==i ؑGp3XlF%K>| 8+.'!]#@t aÆK?x'}Mhj]f2,3%[rd]>h-m"#u2pd~GȃlAdYJqM?_5dX`I5=v!#d5ka/?g'`QD ̈́ɨ, _{fS&w8`WK&JDK,\c?X'E?Qx쬏:r6mߎ%-5Yb8)U '*-p)v> zsJk`B Tl'dwQGƶmlktw "9nLAKZ(xַ؇ c]+LLgÆ Ł>(NyT+d)orZc{.rcyoko)7k95 *ׇm[r튭OOt,% W-%v𚠲x'?zƸـ+Ȃهヌn`U@7yy,)B @gW/^#gG"::z[\gv(nT^.1RUKJ[JjK[,S#ã=E Ɲ g9 ܌gLK̞^(GP/|E,3AsSS3.lʼn>(uʶXÑީ|@!^(Cf{JsxSNFcS#^Xr%f~[",2vp |J7.,΢UQHpD[?n/_Q^1V~5jiOyUg6u;ͮ>}xɔgb1e ZP(T 9/*T&iw0q\Ӄ <`.&4c 9<%n|O$){tHŶBdN?TYUp\_X\XeJ'3[\-,yg!Y:43O,J~,`ykd֭ۀ>y1M Ul9).w2d0vw+U^U.`ϡr` ~`B0e,‹/L&ӧ^{u\tϰe˖3:w1urXx .J,^wp0p@=./֦#faor/v\emm8z elI彷7v2Re6]/ނ3#ן+ziap}G>lW/X xᥗd0c^EFv{-Kp//ô)S?r寯 ,.s/|v> ~;8poX`ZC,H1.;u2 8&:PӦӖcaf߰lw܅ѣG睋>|aak?ĴSp1GcM?pcquK>oaXb%2o3f 7ތ|;oGAgiVʸbGES77:%:>׎cL}j$^+־j_ٵAKs3{Xc-g8>HT̸9_ k01cFqI'b%?G6)սgoB{{'~WpcQǖkךZkngUUņ yv]=j.8\|u8ٖd7]_ 7݌v `)~ ܍nHȚaMX+z(ua\p;x6ͦW^{裎İCvݺBնG=0j}.? |;زuy{ϘY3gx 6nڄ+ux+ZU }S pqŰ %:vnJQBLRUU+5  UUqo-døq{bGb5::+~ 8S1d`X G30fh3o}*f͚?~sUnڴ W_+}xNj G5~il<Ϙ^#~~{u3ɒo`s`ep\,t҂pv,U|[>Ir[LdŧO9 cFASS#2 @(E}ڵhԩSq&l,ر{;+-}6jUܣvM dh#Nοa+l83Q+֮H>zhj#k~w籉Ѿjmn3WqsC]}Tfs;dp;+Sͭ2B=~@llFڒ&,T%֭0hr6 Z˛@w_8 u6{hdV*f7~c g}+%?_PUK͖V\)S&c#zݷ gl} r<,yV]zy`"p4.(֝dY3N?pֶh^gN5:B1zZ}}}6ή~\[ީZuql_#g~G~z13e?R'E_o8缺4w8[l@$ P-5k~5{knۺuۮbuĸP㣥K ͯC|G6t(Əy/X1}:s4 hű]BͫefT-yj޻{p\mؾ}Əj̛&,|s=7\;< hmmѣ~ cFw[<̳xGmVL}]c L !#'0bҧl[c횏X'"|3wҥtpG÷.88`0B|ڵk1s}lu1axu1oޛx+'myob]hW[Z1z(e `yƗOcƀVVU}?oyjm7k?=ePQo~6L4ykc}Ͻ8f xGyf;k&ZZZw+~?.>/.`Ux\E?>f͚G,q]1}/f,oD__f~8 3MՊ4qB,A$yJ4Un#emm4ib}ꬱt`{GG '?3{?^ c {{y_@g;/ ,Xf3N? ﷯#[_w܅¯ 0k>ll+~?Ə|l,Z,,sZ|>}2 Q&MڄReb \Wq>l cb6 T¸_[tCT\O%u|l%wcZ>E["4m1/|pjim72Y3jn^ozdH5yV\e׋5,.%)UƑe2ǮV뤴$M .-݁EOƖ~).Fė=SޱGdr5Ƚ|f>m`"#g@ 44*P5`,ML?z^xnFD K~* U>yxUJ3'm8f!i?:l$$,sϿ|&ա~)vAvhm5cjsL9O=nڴ1Gq|1 ˏaZR].VMD#gx(!Eץk\gGeDT$X\զMpG- +‘(!q B_"4v+vL (@iĤoP㩧Ԅ?7yzpѳRCϧ‡ɻ2wnV9㩧܄?uPf2Y5,_,E88=kE,|>;-N9;tuw㎻Wy5>^+p5w`UkbaבÌ_]N%P( ?ȵnMy'@瞊'`pٶ[^pg[-dp@ `ɀ k<0Xd%$\[ׯ߀_w8㴓 :bhXF(f+"yۃ5hN8~1hhh7܌K[a.wQ% E8iq)(=X`Yl&D]Wz`MW Y*rCݍ~7fv?kxxתѲX`O}/^kxŗ}Ks_\*东s1Cr.>+:DP7T `!f ,_e/{d5tTQi^M|>g{|}wᷱfZ,^׬ŖYuk)٥*gEQ0`= S&O¨w;gko۶mt,E ?6x ,q/[u9$Fx0o8#ƶ-Ym[)%BШmaZyjG”N҄tЗ~(gh cr.gŸJ;>ݲ SN 1|0 <Z[( }h ۷ tYt7CWqi%ZxBJ3"d*9S0JҕJ7Juc%wϞ>K:"kii)S0q⸢m,mk@WWm߁ 7bRg~l5޴{'ЄɩTrς D8gk9r7 -;+%B,oǶ3s+Sn'K.Uk%Ij×{x{2`))Fh zǦkh3| Yc+Kc`ݥˮϱyj<,()\+Էou^õ%]WkqR"S6isR*>6B KZMC_;l W(P3BagCf8b/ФQ3u쥾)E&ۿH88ũM6ydDch𲫙FBԎϜf[ %yw4 b r1Zp#; Q@K,aqxz:9M7( UBo$2 򚖠]w?{+3`B8N0;ge螫|Li~ْrhhpad #EeXALg7eXP*p =HL&ڱTK,sz&v*ǦQrE9b`2l6JfTVXAY崞 rVg5C1lIcS8BNg)zsK+l"iToaZSc#:EpU:5tG442af1T_6WIvPb00"(LEH, PF9̟كe@5=e@/Kԉ$gdzIDAT 2(9Yz&8ʕ$f%lѧKv4u fx'nt p|LŊYЇf6QcyV^ ~D!ybC\1֒v5-bx>-``Ɍ-Xi$eL>| (l|F ZWKS4h&HTǛO#ƕ^`ڰc<؀1Gl,!6=9֮ƒNy\u1{c;98٤M`a c<b@Rޭ iS!m ֧YR7j5 ϴJޢtB=>Nr4C gN9PR]%ȍ,IyXvTSF!` >- L4Y%FO?sjhf ɳeRcrO!zGť8*rI#vnHK v#1q8XCWq7R j,0Лn(!e2(*Rp4 軹9= AY$ ]$\cв- zrW3j`15`:<0}i4i+ 7'M]ƒY$N 'V/-sg0tsFOp[8 \\U޷۸ȣʖ8=eL:9X%SAOTPSLJO[S؋ucXz))Jx<%;-E_˦nM8~7*Un+@eqI'H&P)Y Lq- .3%"9"-RHCD8C|k~sie UxiI}9`i8ýIRL,@7tkUkFTi ?"%qC&y߷[aX$is4 g([r"&Ofj;tzwEP M rP<ŧGyɓp9gcܸ k2;β( EX$ډ!nORTerNhdI>0l^ϖw2M0}fLknnBKK ]ÞccƔT`6KdГӆ\LUePρUT WܘvݩG6aN"dM6dMb[/au{+֫P3.wSp!3#@65@7ĄlRhUICx@_>m+6HV !#al&lN˩@ }@{m=ͽg1TF&cP0;WWwW{U>:TU=o܆zdUCors+7a|݇5GŊKl )PMfd/fF>d)mP*@ fd 'P{ *ԓң6U*"Es 2} V PC{;:v3RP8ݝ}P2(z;:+M3 Q*&=߉h)H#\"0"}T46eTU-I_,=g *OEcs-q yV@/#ӐA1SufE }}P2s"0Y;T R@V =SռZ kZiNV*dl6|._}cCCA*M46gT iQ5V$.T`ET^,|2:vR@J}R7vN @og7 0#PڌFLX+fd&#EQW|lWr52 =cBcc|&LPYEsK^ 28`ӍlVc x9!ۘE!>uh (Zf qQ نF )L6/Vd 6 bZ5Ղ~ -N j9G>f 5B B(T>0 e@KA.X Sw1 & .9 A@e|0ɤՐl#T΃2Z) @_A *FZr d(PUU"^*I_YEy@LVA__FR/-*XQ:0(bx2)KLNr /8-2[nt\O,ΒWd(WCuAZY/r@fM7K2杖k;U4ʊ80frԋ3U d$ۊ-Mg}ߠЙ:6c#T}#b[2Vjncg'ЩkecEuu|vYQ$c#c-Vd~6W5CI1kOd) \i\$4VAU! i^ΙyuڎMh!6د:.2vL< jDU I-bhZP̶J@n2zҗBRSݜlJUaݭ7dgB3v,5x6㳉+ M_J Y>!gƵ! q?;6[';[+2W9%*qi0N6#͑PU֛)K:ĺV' egKR{  -~psZI5b'ΐ\6ffM6ie&ؤ8ȉ(sa@ k!tbibm&H0\rAt+xU.B+ɎZ?[!Y(VR:UژXɖT%YbFl*QĬF" ) ]%w+<لZL֔ g:3R ^`.;b'`LĤd-$_UhqJizzOP{J VdĀ)XgB9LVmd2Ч; H2fL$"[ظ.*ޱ>iq9Ji״RgȑfZ2?M7))3v`mY2O fL 0df݂2AB6OV֚4Њ2AUؒef-m:m kd--j.I1f݀kY~vSt֬̐>E lJa0'3 6ԕh K|(Eh]D&9ʏI=yrxum2l$+]FM3VAhc*F6YvU%IxL39P2]/A}D-PiA *i܄Q%re۴笯f0{pYw[BJɍ5(R`=ccED6WE~T`]g;`i9Q՞É`$hRe`keTwnT sP"C|'Gb9k\h eTˁBot"_'ӇA6lyprtRX-F:`w)]Vj&RE> fy-rV(.) T EWsIENDB`polkit-126/docs/polkit/000077500000000000000000000000001474122443600151275ustar00rootroot00000000000000polkit-126/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.AuthenticationAgent.xml000066400000000000000000000160371474122443600322610ustar00rootroot00000000000000 org.freedesktop.PolicyKit1.AuthenticationAgent Interface org.freedesktop.PolicyKit1.AuthenticationAgent Interface Authentication Agent Interface Methods BeginAuthentication (IN String action_id, IN String message, IN String icon_name, IN Dict<String,String> details, IN String cookie, IN Array<Identity> identities) CancelAuthentication (IN String cookie) Description This D-Bus interface is used for communication between the system-wide PolicyKit daemon and one or more authentication agents each running in a user session.An authentication agent must implement this interface and register (passing the object path of the object implementing the interface) using the RegisterAuthenticationAgent() and UnregisterAuthenticationAgent() methods on the org.freedesktop.PolicyKit1.Authority interface of the PolicyKit daemon. Method Details BeginAuthentication () BeginAuthentication (IN String action_id, IN String message, IN String icon_name, IN Dict<String,String> details, IN String cookie, IN Array<Identity> identities) Called by the PolicyKit daemon when the authentication agent needs the user to authenticate as one of the identities in identities for the action with the identifier action_id.Upon succesful authentication, the authentication agent must invoke the AuthenticationAgentResponse2() method on the org.freedesktop.PolicyKit1.Authority interface of the PolicyKit daemon before returning. This is normally achieved via the PolkitAgentSession API, which invokes a private setuid helper process to verify the authentication. The authentication agent should not return until after authentication is complete. If the user dismisses the authentication dialog, the authentication agent should return the org.freedesktop.PolicyKit1.Error.Cancelled error. IN String action_id: The identifier for the action that the user is authentication for. IN String message: The message to display to the user. This is translated into the locale passed when registering the authentication agent using RegisterAuthenticationAgent(). IN String icon_name: The themed icon describing the action or the empty string if no icon is set. IN Dict<String,String> details: Details about the authentication request. This is a dictionary of key/value pairs where both key and value are strings. Known key/value-pairs include polkit.caller-pid (the process id of the mechanism making the authorization check) and polkit.subject-pid (the process id of the subject the check is for). IN String cookie: A cookie identifying the authentication request. IN Array<Identity> identities: An array of Identity structs that the user can use for authentication. CancelAuthentication () CancelAuthentication (IN String cookie) Called by the PolicyKit daemon if the authentication agent needs to cancel an authentication dialog. IN String cookie: The cookie identifying the authentication request. polkit-126/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.Authority.xml000066400000000000000000001276001474122443600303120ustar00rootroot00000000000000 org.freedesktop.PolicyKit1.Authority Interface org.freedesktop.PolicyKit1.Authority Interface Authority Interface Methods Flags CheckAuthorizationFlags Enumeration ImplicitAuthorization ErrorDomain org.freedesktop.PolicyKit1.Error.* Flags AuthorityFeatures Structure Subject Structure Identity Structure ActionDescription Structure AuthorizationResult Structure TemporaryAuthorization EnumerateActions (IN String locale, OUT Array<ActionDescription> action_descriptions) CheckAuthorization (IN Subject subject, IN String action_id, IN Dict<String,String> details, IN CheckAuthorizationFlags flags, IN String cancellation_id, OUT AuthorizationResult result) CancelCheckAuthorization (IN String cancellation_id) RegisterAuthenticationAgent (IN Subject subject, IN String locale, IN String object_path) RegisterAuthenticationAgentWithOptions (IN Subject subject, IN String locale, IN String object_path, IN Dict<String,Variant> options) UnregisterAuthenticationAgent (IN Subject subject, IN String object_path) AuthenticationAgentResponse (IN String cookie, IN Identity identity) AuthenticationAgentResponse2 (IN uint32 uid, IN String cookie, IN Identity identity) EnumerateTemporaryAuthorizations (IN Subject subject, OUT Array<TemporaryAuthorization> temporary_authorizations) RevokeTemporaryAuthorizations (IN Subject subject) RevokeTemporaryAuthorizationById (IN String id) Signals Changed () Properties BackendName readable String BackendVersion readable String BackendFeatures readable AuthorityFeatures Description This D-Bus interface is implemented by the /org/freedesktop/PolicyKit1/Authority object on the well-known name org.freedesktop.PolicyKit1 on the system message bus. Enumerations The CheckAuthorizationFlags Flags { None = 0x00000000, AllowUserInteraction = 0x00000001, AlwaysCheck = 0x00000002 } Flags used in the CheckAuthorization() method. None No flags set. AllowUserInteraction If the Subject can obtain the authorization through authentication, and an authentication agent is available, then attempt to do so. Note, this means that the CheckAuthorization() method will block while the user is being asked to authenticate. AlwaysCheck Check access against policy even if the Subject is the root user. The ImplicitAuthorization Enumeration { NotAuthorized = 0, AuthenticationRequired = 1, AdministratorAuthenticationRequired = 2, AuthenticationRequiredRetained = 3, AdministratorAuthenticationRequiredRetained = 4, Authorized = 5 } An enumeration for granting implicit authorizations. NotAuthorized The Subject is not authorized. AuthenticationRequired Authentication is required. AdministratorAuthenticationRequired Authentication as an administrator is required. AuthenticationRequiredRetained Authentication is required. If the authorization is obtained, it is retained. AdministratorAuthenticationRequiredRetained Authentication as an administrator is required. If the authorization is obtained, it is retained. Authorized The subject is authorized. The org.freedesktop.PolicyKit1.Error.* Error Domain { org.freedesktop.PolicyKit1.Error.Failed, org.freedesktop.PolicyKit1.Error.Cancelled, org.freedesktop.PolicyKit1.Error.NotSupported, org.freedesktop.PolicyKit1.Error.NotAuthorized, org.freedesktop.PolicyKit1.Error.CancellationIdNotUnique } Errors that can be returned by various method calls. org.freedesktop.PolicyKit1.Error.Failed The operation failed. org.freedesktop.PolicyKit1.Error.Cancelled The operation was cancelled. org.freedesktop.PolicyKit1.Error.NotSupported The operation is not supported. org.freedesktop.PolicyKit1.Error.NotAuthorized You are not authorized to perform the requested operation. org.freedesktop.PolicyKit1.Error.CancellationIdNotUnique The passed cancellation_id is already in use. The AuthorityFeatures Flags { None = 0x00000000, TemporaryAuthorization = 0x00000001 } Flags describing features supported by the Authority implementation. None No flags set. TemporaryAuthorization The authority supports temporary authorizations that can be obtained through authentication. Structures The Subject Structure { String subject_kind, Dict<String,Variant> subject_details } This struct describes subjects such as UNIX processes. It is typically used to check if a given process is authorized for an action.The following kinds of subjects are known: Unix Processsubject_kind should be set to unix-process with keys pidfd (of type int32) and uid (of type int32) (if the operating system supports ProcessID File Descriptors), or alternatively with keys pid (of type uint32), uid (of type int32) and start-time (of type uint64). Unix Sessionsubject_kind should be set to unix-session with the key session-id (of type string). System Bus Namesubject_kind should be set to system-bus-name with the key name (of type string). String subject_kind The type of the subject. Dict<String,Variant> subject_details Details about the subject. Depending of the value of subject_kind, a set of well-defined key/value pairs are guaranteed to be available. The Identity Structure { String identity_kind, Dict<String,Variant> identity_details } This struct describes identities such as UNIX users and UNIX groups. It is typically used to check if a given process is authorized for an action.The following kinds of identities are known: Unix Useridentity_kind should be set to unix-user with key uid (of type uint32). Unix Groupidentity_kind should be set to unix-group with key gid (of type uint32). String identity_kind Type of identity. Dict<String,Variant> identity_details Details about the identity. Depending of the value of identity_kind, a set of well-defined key/value pairs are guaranteed to be available. The ActionDescription Structure { String action_id, String description, String message, String vendor_name, String vendor_url, String icon_name, ImplicitAuthorization implicit_any, ImplicitAuthorization implicit_inactive, ImplicitAuthorization implicit_active, Dict<String,String> annotations } This struct describes actions registered with the PolicyKit daemon. String action_id Action Identifier. String description Localized description of the action. String message Localized message to be displayed when making the user authenticate for an action. String vendor_name Name of the provider of the action or the empty string. String vendor_url A URL pointing to a place with more information about the action or the empty string. String icon_name The themed icon describing the action or the empty string if no icon is set. ImplicitAuthorization implicit_any A value from the ImplicitAuthorization. enumeration for implicit authorizations that apply to any Subject. ImplicitAuthorization implicit_inactive A value from the ImplicitAuthorization. enumeration for implicit authorizations that apply any Subject in an inactive user session on the local console. ImplicitAuthorization implicit_active A value from the ImplicitAuthorization. enumeration for implicit authorizations that apply any Subject in an active user session on the local console. Dict<String,String> annotations Annotations for the action. The AuthorizationResult Structure { Boolean is_authorized, Boolean is_challenge, Dict<String,String> details } Describes the result of calling CheckAuthorization(). Boolean is_authorized TRUE if the given Subject is authorized for the given action. Boolean is_challenge TRUE if the given Subject could be authorized if more information was provided, and CheckAuthorizationFlags.AllowUserInteraction wasn't passed or no suitable authentication agent was available. Dict<String,String> details Details for the result. Known key/value-pairs include polkit.temporary_authorization_id (if the authorization is temporary, this is set to the opaque temporary authorization id), polkit.retains_authorization_after_challenge (Set to a non-empty string if the authorization will be retained after authentication (if is_challenge is TRUE)), polkit.dismissed (Set to a non-empty string if the authentication dialog was dismissed by the user). The TemporaryAuthorization Structure { String id, String action_id, Subject subject, UInt64 time_obtained, UInt64 time_expires } This struct describes a temporary authorization. String id An opaque identifier for the temporary authorization. String action_id The action the temporary authorization is for. Subject subject The subject the temporary authorization is for. UInt64 time_obtained When the temporary authorization was obtained, in seconds since the Epoch Jan 1, 1970 0:00 UTC. Note that the PolicyKit daemon is using monotonic time internally so the returned value may change if system time changes. UInt64 time_expires When the temporary authorization is set to expire, in seconds since the Epoch Jan 1, 1970 0:00 UTC. Note that the PolicyKit daemon is using monotonic time internally so the returned value may change if system time changes. Method Details EnumerateActions () EnumerateActions (IN String locale, OUT Array<ActionDescription> action_descriptions) Enumerates all registered PolicyKit actions. IN String locale: The locale to get descriptions in or the blank string to use the system locale. OUT Array<ActionDescription> action_descriptions: An array of ActionDescription structs. CheckAuthorization () CheckAuthorization (IN Subject subject, IN String action_id, IN Dict<String,String> details, IN CheckAuthorizationFlags flags, IN String cancellation_id, OUT AuthorizationResult result) Checks if subject is authorized to perform the action with identifier action_id If cancellation_id is non-empty and already in use for the caller, the org.freedesktop.PolicyKit1.Error.CancellationIdNotUnique error is returned. Note that CheckAuthorizationFlags.AllowUserInteraction SHOULD be passed ONLY if the event that triggered the authorization check is stemming from an user action, e.g. the user pressing a button or attaching a device. IN Subject subject: A Subject struct. IN String action_id: Identifier for the action that subject is attempting to do. IN Dict<String,String> details: Details describing the action. Keys starting with polkit. are can only be set if defined in this document. Known keys include polkit.message and polkit.gettext_domain that can be used to override the message shown to the user. This latter is needed because the user could be running an authentication agent in another locale than the calling process. The (translated version of) polkit.message may include references to other keys that are expanded with their respective values. For example if the key device_file has the value /dev/sda then the message "Authenticate to format $(device_file)" is expanded to "Authenticate to format /dev/sda". The key polkit.icon_name is used to override the icon shown in the authentication dialog. If non-empty, then the request will fail with org.freedesktop.PolicyKit1.Error.Failed unless the process doing the check itsef is sufficiently authorized (e.g. running as uid 0). IN CheckAuthorizationFlags flags: A set of CheckAuthorizationFlags. IN String cancellation_id: A unique id used to cancel the the authentication check via CancelCheckAuthorization() or the empty string if cancellation is not needed. OUT AuthorizationResult result: An AuthorizationResult structure. CancelCheckAuthorization () CancelCheckAuthorization (IN String cancellation_id) Cancels an authorization check. IN String cancellation_id: The cancellation_id passed to CheckAuthorization(). RegisterAuthenticationAgent () RegisterAuthenticationAgent (IN Subject subject, IN String locale, IN String object_path) Register an authentication agent.Note that this should be called by same effective UID which will be passed to AuthenticationAgentResponse2(). IN Subject subject: The subject to register the authentication agent for, typically a session subject. IN String locale: The locale of the authentication agent. IN String object_path: The object path of authentication agent object on the unique name of the caller. RegisterAuthenticationAgentWithOptions () RegisterAuthenticationAgentWithOptions (IN Subject subject, IN String locale, IN String object_path, IN Dict<String,Variant> options) Like RegisterAuthenticationAgent but takes additional options. If the option fallback (of type Boolean) is TRUE, then the authentcation agent will only be used as a fallback, e.g. if another agent (without the fallback option set TRUE) is available, it will be used instead. UnregisterAuthenticationAgent () UnregisterAuthenticationAgent (IN Subject subject, IN String object_path) Unregister an authentication agent. IN Subject subject: The subject passed to RegisterAuthenticationAgent(). IN String object_path: The object_path passed to RegisterAuthenticationAgent(). AuthenticationAgentResponse () AuthenticationAgentResponse (IN String cookie, IN Identity identity) Method for authentication agents to invoke on successful authentication, intended only for use by a privileged helper process internal to polkit. This method will fail unless a sufficiently privileged +caller invokes it. Deprecated in favor of AuthenticationAgentResponse2(). IN String cookie: The cookie identifying the authentication request that was passed to the authentication agent. IN Identity identity: A Identity struct describing what identity was authenticated. AuthenticationAgentResponse2 () AuthenticationAgentResponse2 (IN uint32 uid, IN String cookie, IN Identity identity) Method for authentication agents to invoke on successful authentication, intended only for use by a privileged helper process internal to polkit. This method will fail unless a sufficiently privileged caller invokes it. Note this method was introduced in 0.114 and should be preferred over AuthenticationAgentResponse() as it fixes a security issue. IN uint32 uid: The user id of the agent; normally this is the owner of the parent pid of the process that invoked the internal setuid helper. IN String cookie: The cookie identifying the authentication request that was passed to the authentication agent. IN Identity identity: A Identity struct describing what identity was authenticated. EnumerateTemporaryAuthorizations () EnumerateTemporaryAuthorizations (IN Subject subject, OUT Array<TemporaryAuthorization> temporary_authorizations) Retrieves all temporary authorizations that applies to subject. IN Subject subject: The subject to get temporary authorizations for. OUT Array<TemporaryAuthorization> temporary_authorizations: An array of TemporaryAuthorization structs. RevokeTemporaryAuthorizations () RevokeTemporaryAuthorizations (IN Subject subject) Revokes all temporary authorizations that applies to subject. IN Subject subject: The subject to revoke temporary authorizations from. RevokeTemporaryAuthorizationById () RevokeTemporaryAuthorizationById (IN String id) Revokes all temporary authorizations that applies to subject. IN String id: The opaque identifier of the temporary authorization. Signal Details The "Changed" signal Changed () This signal is emitted when actions and/or authorizations change Property Details The "BackendName" property BackendName readable String The name of the currently used Authority backend. The "BackendVersion" property BackendVersion readable String The version of the currently used Authority backend. The "BackendFeatures" property BackendFeatures readable AuthorityFeatures The features supported by the currently used Authority backend. polkit-126/docs/polkit/meson.build000066400000000000000000000015421474122443600172730ustar00rootroot00000000000000doc_module = pk_api_name src_dirs = [ source_root / 'src/polkit', source_root / 'src/polkitagent', ] private_headers = [ 'polkitagenthelperprivate.h', 'polkitprivate.h', ] content_files += files( 'docbook-interface-org.freedesktop.PolicyKit1.AuthenticationAgent.xml', 'docbook-interface-org.freedesktop.PolicyKit1.Authority.xml', ) html_images = [ '../polkit-architecture.png', '../polkit-authentication-agent-example.png', '../polkit-authentication-agent-example-wheel.png', ] gnome.gtkdoc( doc_module, main_xml: doc_module + '-docs.xml', src_dir: src_dirs, ignore_headers: private_headers, dependencies: libpolkit_agent_dep, namespace: meson.project_name(), html_assets: html_images, fixxref_args: '--html-dir=' + pk_api_docpath, gobject_typesfile: doc_module + '.types', content_files: content_files, install: true, ) polkit-126/docs/polkit/overview.xml000066400000000000000000000410731474122443600175240ustar00rootroot00000000000000 ]> polkit Overview Introduction polkit provides an authorization API intended to be used by privileged programs (MECHANISMS) offering service to unprivileged programs (CLIENTS). See the polkit manual page for the system architecture and big picture. Writing polkit applications polkit applications are applications using the polkit authority as a decider component. They do this by installing a .policy file into the /usr/share/polkit-1/actions directory and communicating with the polkit authority at runtime (either via the D-Bus API or indirectly through the libpolkit-gobject-1 library or the pkcheck command). Best practices DO use polkit if you are writing a privileged mechanism (that is, running as root or otherwise has special permissions) that is intended to be used by unprivileged programs. DO carefully consider what actions to define. In many cases there isn't a 1:1 mapping between operations and polkit actions. Often a polkit action has more to do with the object the operation is acting on than the operation itself. It is important to strike the right balance between too fine-grained and too coarse-grained. DO try to pick actions and implicit authorizations so applications using your mechanism will work out-of-the box for users logged in at the console. Not interrupting console users with authentication dialogs should be considered a priority. For example, it is not wise to require console users to authenticate for such mundane tasks as adding a printer queue (if the administrator really wants the OS to act this way, he can always deploy suitable authorization rules). DO consider the impact of the chosen implicit authorizations on multi-user systems. Generally, ordinary users should be able to neither modify important system's behavior for other users, nor view other users' private data. If your application needs an authorization framework at all, it is fairly likely that the default configuration should deny authorization in at least some cases. Default to using auth_admin* instead of auth_self*. (On single-user desktops, the single user is typically configured as a polkit administrator, so the two variants behave equally. On multi-user systems, non-administrator users will be restricted by the default configuration.) DO pass polkit variables along with CheckAuthorization() requests so it's possible to write authorization rules matching on these. Also document these variables in your documentation (for example, see the udisks2 actions and variables). DO pass a customized authentication message (using the polkit.message and polkit.gettext_domain variables) that include more detailed information about the request than whatever is declared in the .policy file's message element. For example, it's better to show Authentication is needed to format INTEL SSDSA2MH080G1GC (/dev/sda) than just Authentication is needed to format the device. DO make sure your application works even when the org.freedesktop.PolicyKit1 D-Bus service is not available (this can happen if polkitd8 is not installed or if the polkit.service systemd unit/service has been masked). If you are using the libpolkit-gobject-1 library this means handling polkit_authority_get_sync() or polkit_authority_get_finish() returning NULL or polkit_authority_check_authorization() / polkit_authority_check_authorization_sync() failing with an error not in the POLKIT_ERROR domain. An appropriate way of dealing with the polkit authority not being available, could be to allow only uid 0 to perform operations, forbid all operations or something else. DON'T use polkit if your program isn't intended to be used by unprivileged programs. For example, if you are writing developer tools or low-level core OS command-line tools it's fine to just require the user to be root. Users can always run your tool through e.g. sudo8, pkexec1 or write a simple polkit-using mechanism that allows access to a (safe) subset of your tool. DON'T use polkit unless you actually have to. In other words, not every single privileged program providing service to unprivileged programs has to use polkit. For example, if you have a small well-written setuid helper to help deal with some implementation-detail of the OS (such as elevating the priority of the sound server process to real-time for console users) it's not really helpful to define a polkit action for this since, realistically, no-one is going to choose to not grant the privilege. Remember, a secure program is often one with little amount of code and few dependencies. DON'T call CheckAuthorization() for all your actions every time the authority emits the Changed signal. Not only is this a waste of resources, the result may also be inaccurate as authorization rules can return whatever they want, whenever they want. DON'T block the main thread in your mechanism (e.g. the one used to service IPC requests from unprivileged programs) while waiting for the authority to reply - calls to CheckAuthorization() may take a very long time (seconds, even minutes) to complete as user interaction may be involved. Instead, use either the asynchronous API or a dedicated thread with the synchronous API. DON'T include any authorization rules with your application as this is only intended for administrators and special-purpose operating systems / environments. See for more information. Usage in unprivileged programs An unprivileged program normally does not use polkit directly - it simply calls into a privileged mechanism and the mechanism either renders service (or refuses the request) after checking with polkit (which may include presenting an authentication dialog). In this setup, the unprivileged program is oblivious to the fact that polkit is being used - it simply just waits for the privileged mechanism to carry out the request (which, if authentication dialogs are involved may take many seconds). This is a good thing because not worrying about implementation details like polkit, helps simplify the unprivileged program. Occasionally unprivileged programs need to disable, modify or remove UI elements to convey to the user that a certain action cannot be carried out (because e.g. the user is not authorized) or authentication is needed (by e.g. displaying a padlock icon in the UI). In this case, the best approach is usually to have the unprivileged program get this information from the privileged mechanism instead of polkit. This is especially true because often there is no reliable way that the unprivileged program can know what polkit action is going to be used. In general, there is no guarantee that operations (such as D-Bus methods) map 1:1: to polkit action - for example, a disk manager service's Format() method may check for the action net.company.diskmanager.format-removable if the disk is removable and net.company.diskmanager.format-fixed otherwise. However, in certain cases, for example when using the org.freedesktop.policykit.imply annotation (see the polkit8 man page), it is meaningful for an unprivileged program to query the polkit authority (to e.g. update UI elements) and it is in fact allowed to do so as long as the unprivileged program doesn't pass any variables along with the CheckAuthorization() call (otherwise it would be easy to spoof authentication dialogs and bypass authorization rules). In fact, since this use-case is so common, libpolkit-gobject-1 provides the PolkitPermission type (which is derived from GPermission) that can be used together with GtkLockButton. Note that for GtkLockButton to work well, the polkit action backing it should use auth_admin_keep for its implicit authorizations (or more rarely auth_self_keep for services which don't affect other users). This is often used to implement an instant apply paradigm whereby the user unlocks (by authenticating) e.g. a preference pane window and is then free to change settings until the authorization expires or is revoked. No authentication agent If a polkit application wants to handle the case where no authentication agent exists (for example if the app is launched via a ssh1 login), the application can use the PolkitAgentTextListener type to spawn its own authentication agent as needed. Alternatively, the helper can be used to do this. Writing polkit Authentication Agents Authentication agents are provided by desktop environments. When an user session starts, the agent registers with the polkit Authority using the RegisterAuthenticationAgent() method. When services are needed, the authority will invoke methods on the org.freedesktop.PolicyKit1.AuthenticationAgent D-Bus interface. Once the user is authenticated, (a privileged part of) the agent invokes the AuthenticationAgentResponse2() method. This method should be treated as an internal implementation detail, and callers should use the PolkitAgentSession API to invoke it, which currently uses a setuid helper program. The libpolkit-agent-1 library provides helpers to make it easy to build authentication agents that use the native authentication system e.g. pam(8). If the environment variable POLKIT_DEBUG is set, the libpolkit-agent-1 library prints out diagnostic information on standard output. polkit-126/docs/polkit/polkit-1-docs.xml000066400000000000000000000060671474122443600202500ustar00rootroot00000000000000 ]> polkit Reference Manual For version &version; — the latest version of this documentation can be found at https://polkit.pages.freedesktop.org/polkit. D-Bus API Reference Library API Reference Subjects Identities Authentication Agent API Reference Manual Pages Object Hierarchy Index License FIXME: MISSING XINCLUDE CONTENT polkit-126/docs/polkit/polkit-1-sections.txt000066400000000000000000000235661474122443600211710ustar00rootroot00000000000000

polkitunixuser PolkitUnixUser polkit_unix_user_new polkit_unix_user_new_for_name polkit_unix_user_get_uid polkit_unix_user_set_uid polkit_unix_user_get_name PolkitUnixUserClass POLKIT_UNIX_USER POLKIT_IS_UNIX_USER POLKIT_TYPE_UNIX_USER polkit_unix_user_get_type POLKIT_UNIX_USER_CLASS POLKIT_IS_UNIX_USER_CLASS POLKIT_UNIX_USER_GET_CLASS
polkitauthority PolkitAuthority PolkitAuthorityFeatures PolkitCheckAuthorizationFlags polkit_authority_get_async polkit_authority_get_finish polkit_authority_get_sync polkit_authority_get polkit_authority_get_owner polkit_authority_get_backend_name polkit_authority_get_backend_version polkit_authority_get_backend_features polkit_authority_check_authorization polkit_authority_check_authorization_finish polkit_authority_check_authorization_sync polkit_authority_enumerate_actions polkit_authority_enumerate_actions_finish polkit_authority_enumerate_actions_sync polkit_authority_register_authentication_agent polkit_authority_register_authentication_agent_finish polkit_authority_register_authentication_agent_sync polkit_authority_register_authentication_agent_with_options polkit_authority_register_authentication_agent_with_options_finish polkit_authority_register_authentication_agent_with_options_sync polkit_authority_unregister_authentication_agent polkit_authority_unregister_authentication_agent_finish polkit_authority_unregister_authentication_agent_sync polkit_authority_authentication_agent_response polkit_authority_authentication_agent_response_finish polkit_authority_authentication_agent_response_sync polkit_authority_enumerate_temporary_authorizations polkit_authority_enumerate_temporary_authorizations_finish polkit_authority_enumerate_temporary_authorizations_sync polkit_authority_revoke_temporary_authorizations polkit_authority_revoke_temporary_authorizations_finish polkit_authority_revoke_temporary_authorizations_sync polkit_authority_revoke_temporary_authorization_by_id polkit_authority_revoke_temporary_authorization_by_id_finish polkit_authority_revoke_temporary_authorization_by_id_sync PolkitAuthorityClass POLKIT_AUTHORITY POLKIT_IS_AUTHORITY POLKIT_TYPE_AUTHORITY polkit_authority_get_type POLKIT_AUTHORITY_CLASS POLKIT_IS_AUTHORITY_CLASS POLKIT_AUTHORITY_GET_CLASS polkit_check_authorization_flags_get_type
polkitauthorizationresult PolkitAuthorizationResult polkit_authorization_result_new polkit_authorization_result_get_is_authorized polkit_authorization_result_get_is_challenge polkit_authorization_result_get_retains_authorization polkit_authorization_result_get_temporary_authorization_id polkit_authorization_result_get_dismissed polkit_authorization_result_get_details PolkitAuthorizationResultClass POLKIT_AUTHORIZATION_RESULT POLKIT_IS_AUTHORIZATION_RESULT POLKIT_TYPE_AUTHORIZATION_RESULT polkit_authorization_result_get_type POLKIT_AUTHORIZATION_RESULT_CLASS POLKIT_IS_AUTHORIZATION_RESULT_CLASS POLKIT_AUTHORIZATION_RESULT_GET_CLASS
polkitsystembusname PolkitSystemBusName polkit_system_bus_name_new polkit_system_bus_name_get_name polkit_system_bus_name_set_name polkit_system_bus_name_get_process_sync PolkitSystemBusNameClass POLKIT_SYSTEM_BUS_NAME POLKIT_IS_SYSTEM_BUS_NAME POLKIT_TYPE_SYSTEM_BUS_NAME polkit_system_bus_name_get_type POLKIT_SYSTEM_BUS_NAME_CLASS POLKIT_IS_SYSTEM_BUS_NAME_CLASS POLKIT_SYSTEM_BUS_NAME_GET_CLASS
polkitunixgroup PolkitUnixGroup polkit_unix_group_new polkit_unix_group_new_for_name polkit_unix_group_get_gid polkit_unix_group_set_gid PolkitUnixGroupClass POLKIT_UNIX_GROUP POLKIT_IS_UNIX_GROUP POLKIT_TYPE_UNIX_GROUP polkit_unix_group_get_type POLKIT_UNIX_GROUP_CLASS POLKIT_IS_UNIX_GROUP_CLASS POLKIT_UNIX_GROUP_GET_CLASS
polkitunixnetgroup PolkitUnixNetgroup polkit_unix_netgroup_new polkit_unix_netgroup_get_name polkit_unix_netgroup_set_name PolkitUnixNetgroupClass POLKIT_UNIX_NETGROUP POLKIT_IS_UNIX_NETGROUP POLKIT_TYPE_UNIX_NETGROUP polkit_unix_netgroup_get_type POLKIT_UNIX_NETGROUP_CLASS POLKIT_IS_UNIX_NETGROUP_CLASS POLKIT_UNIX_NETGROUP_GET_CLASS
polkitunixsession PolkitUnixSession polkit_unix_session_new polkit_unix_session_new_for_process polkit_unix_session_new_for_process_finish polkit_unix_session_new_for_process_sync polkit_unix_session_get_session_id polkit_unix_session_set_session_id PolkitUnixSessionClass POLKIT_UNIX_SESSION POLKIT_IS_UNIX_SESSION POLKIT_TYPE_UNIX_SESSION polkit_unix_session_get_type POLKIT_UNIX_SESSION_CLASS POLKIT_IS_UNIX_SESSION_CLASS POLKIT_UNIX_SESSION_GET_CLASS
polkitunixprocess PolkitUnixProcess polkit_unix_process_new polkit_unix_process_new_full polkit_unix_process_new_for_owner polkit_unix_process_set_pid polkit_unix_process_get_pid polkit_unix_process_set_start_time polkit_unix_process_get_start_time polkit_unix_process_set_uid polkit_unix_process_get_uid polkit_unix_process_get_owner PolkitUnixProcessClass POLKIT_UNIX_PROCESS POLKIT_IS_UNIX_PROCESS POLKIT_TYPE_UNIX_PROCESS polkit_unix_process_get_type POLKIT_UNIX_PROCESS_CLASS POLKIT_IS_UNIX_PROCESS_CLASS POLKIT_UNIX_PROCESS_GET_CLASS
polkitidentity PolkitIdentity PolkitIdentityIface polkit_identity_hash polkit_identity_equal polkit_identity_to_string polkit_identity_from_string POLKIT_IDENTITY POLKIT_IS_IDENTITY POLKIT_TYPE_IDENTITY polkit_identity_get_type POLKIT_IDENTITY_GET_IFACE
polkitsubject PolkitSubject PolkitSubjectIface polkit_subject_hash polkit_subject_equal polkit_subject_exists polkit_subject_exists_finish polkit_subject_exists_sync polkit_subject_to_string polkit_subject_from_string POLKIT_SUBJECT POLKIT_IS_SUBJECT POLKIT_TYPE_SUBJECT polkit_subject_get_type POLKIT_SUBJECT_GET_IFACE
polkitactiondescription PolkitActionDescription PolkitImplicitAuthorization polkit_action_description_get_action_id polkit_action_description_get_description polkit_action_description_get_message polkit_action_description_get_vendor_name polkit_action_description_get_vendor_url polkit_action_description_get_icon_name polkit_action_description_get_implicit_any polkit_action_description_get_implicit_inactive polkit_action_description_get_implicit_active polkit_action_description_get_annotation polkit_action_description_get_annotation_keys polkit_implicit_authorization_to_string polkit_implicit_authorization_from_string PolkitActionDescriptionClass POLKIT_ACTION_DESCRIPTION POLKIT_IS_ACTION_DESCRIPTION POLKIT_TYPE_ACTION_DESCRIPTION polkit_action_description_get_type POLKIT_ACTION_DESCRIPTION_CLASS POLKIT_IS_ACTION_DESCRIPTION_CLASS POLKIT_ACTION_DESCRIPTION_GET_CLASS polkit_implicit_authorization_get_type
polkiterror POLKIT_ERROR PolkitError polkit_error_quark POLKIT_TYPE_ERROR polkit_error_get_type
polkitdetails PolkitDetails polkit_details_new polkit_details_lookup polkit_details_insert polkit_details_get_keys PolkitDetailsClass POLKIT_DETAILS POLKIT_IS_DETAILS POLKIT_TYPE_DETAILS polkit_details_get_type POLKIT_DETAILS_CLASS POLKIT_IS_DETAILS_CLASS POLKIT_DETAILS_GET_CLASS
polkitagentsession PolkitAgentSession PolkitAgentSession polkit_agent_session_new polkit_agent_session_initiate polkit_agent_session_response polkit_agent_session_cancel POLKIT_AGENT_SESSION POLKIT_AGENT_IS_SESSION POLKIT_AGENT_TYPE_SESSION polkit_agent_session_get_type POLKIT_AGENT_SESSION_CLASS POLKIT_AGENT_IS_SESSION_CLASS POLKIT_AGENT_SESSION_GET_CLASS
polkitagentlistener PolkitAgentListener PolkitAgentListener PolkitAgentListenerClass polkit_agent_listener_initiate_authentication polkit_agent_listener_initiate_authentication_finish PolkitAgentRegisterFlags polkit_agent_listener_register polkit_agent_listener_register_with_options polkit_agent_listener_unregister polkit_agent_register_listener POLKIT_AGENT_LISTENER POLKIT_AGENT_IS_LISTENER POLKIT_AGENT_TYPE_LISTENER polkit_agent_listener_get_type POLKIT_AGENT_LISTENER_CLASS POLKIT_AGENT_IS_LISTENER_CLASS POLKIT_AGENT_LISTENER_GET_CLASS
polkitagenttextlistener PolkitAgentTextListener PolkitAgentTextListener polkit_agent_text_listener_new POLKIT_AGENT_TEXT_LISTENER POLKIT_AGENT_IS_TEXT_LISTENER POLKIT_AGENT_TYPE_TEXT_LISTENER polkit_agent_text_listener_get_type POLKIT_AGENT_TEXT_LISTENER_CLASS POLKIT_AGENT_IS_TEXT_LISTENER_CLASS POLKIT_AGENT_TEXT_LISTENER_GET_CLASS
polkittemporaryauthorization PolkitTemporaryAuthorization PolkitTemporaryAuthorization polkit_temporary_authorization_get_id polkit_temporary_authorization_get_action_id polkit_temporary_authorization_get_subject polkit_temporary_authorization_get_time_obtained polkit_temporary_authorization_get_time_expires POLKIT_TEMPORARY_AUTHORIZATION POLKIT_IS_TEMPORARY_AUTHORIZATION POLKIT_TYPE_TEMPORARY_AUTHORIZATION polkit_temporary_authorization_get_type POLKIT_TEMPORARY_AUTHORIZATION_CLASS POLKIT_IS_TEMPORARY_AUTHORIZATION_CLASS POLKIT_TEMPORARY_AUTHORIZATION_GET_CLASS
polkitpermission PolkitPermission polkit_permission_new polkit_permission_new_finish polkit_permission_new_sync polkit_permission_get_action_id polkit_permission_get_subject PolkitPermissionClass POLKIT_PERMISSION POLKIT_IS_PERMISSION POLKIT_TYPE_PERMISSION polkit_permission_get_type POLKIT_PERMISSION_CLASS POLKIT_IS_PERMISSION_CLASS POLKIT_PERMISSION_GET_CLASS
polkit-126/docs/polkit/polkit-1.types000066400000000000000000000011441474122443600176550ustar00rootroot00000000000000polkit_authority_get_type polkit_action_description_get_type polkit_details_get_type polkit_check_authorization_flags_get_type polkit_implicit_authorization_get_type polkit_identity_get_type polkit_unix_user_get_type polkit_unix_group_get_type polkit_unix_netgroup_get_type polkit_subject_get_type polkit_unix_process_get_type polkit_unix_session_get_type polkit_system_bus_name_get_type polkit_error_get_type polkit_authorization_result_get_type polkit_temporary_authorization_get_type polkit_permission_get_type polkit_agent_session_get_type polkit_agent_listener_get_type polkit_agent_text_listener_get_type polkit-126/docs/version.xml.in000066400000000000000000000000121474122443600164320ustar00rootroot00000000000000@VERSION@ polkit-126/gettext/000077500000000000000000000000001474122443600143615ustar00rootroot00000000000000polkit-126/gettext/its/000077500000000000000000000000001474122443600151605ustar00rootroot00000000000000polkit-126/gettext/its/polkit.its000066400000000000000000000004651474122443600172100ustar00rootroot00000000000000 polkit-126/gettext/its/polkit.loc000066400000000000000000000003031474122443600171550ustar00rootroot00000000000000 polkit-126/meson.build000066400000000000000000000337351474122443600150520ustar00rootroot00000000000000project( 'polkit', ['c'], version: '126', license: 'LGPL2+', default_options: [ 'buildtype=debugoptimized', 'prefix=/usr', 'c_std=c99', 'cpp_std=c++17', ], meson_version: '>= 0.63.0', ) pk_version = meson.project_version() pk_api_version = '1' pk_api_name = '@0@-@1@'.format(meson.project_name(), pk_api_version) pk_gir_ns = 'Polkit' pk_gir_version = '1.0' pk_prefix = get_option('prefix') pk_datadir = get_option('datadir') pk_includedir = get_option('includedir') pk_libdir = get_option('libdir') pk_mandir = get_option('mandir') pk_sysconfdir = get_option('sysconfdir') pk_pkgdatadir = pk_datadir / pk_api_name pk_pkgincludedir = pk_includedir / pk_api_name # note that this is always 'lib', not lib64 or lib/x86_64-linux-gnu pk_libprivdir = 'lib' / pk_api_name pk_pkgsysconfdir = pk_sysconfdir / pk_api_name pk_actiondir = pk_api_name / 'actions' pk_pkgactiondir = pk_datadir / pk_actiondir soversion = 0 current = 0 revision = 0 libversion = '@0@.@1@.@2@'.format(soversion, current, revision) gnome = import('gnome') i18n = import('i18n') pkg = import('pkgconfig') source_root = meson.current_source_dir() build_root = meson.current_build_dir() data_dir = source_root / 'data' its_dir = source_root / 'gettext' po_dir = source_root / 'po' its_data = files( 'gettext/its/polkit.its', 'gettext/its/polkit.loc', ) install_data( its_data, install_dir: pk_datadir / 'gettext/its', ) top_inc = include_directories('.') cc = meson.get_compiler('c') config_data = configuration_data() # defines set_defines = [ # package ['PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/polkit/polkit/-/issues/'], ['PACKAGE_NAME', meson.project_name()], ['PACKAGE_URL', 'http://www.freedesktop.org/wiki/Software/polkit'], ['PACKAGE_VERSION', pk_version], ['VERSION', pk_version], # i18n ['GETTEXT_PACKAGE', pk_api_name], ] foreach define: set_defines config_data.set_quoted(define[0], define[1]) endforeach # functions check_functions = [ 'clearenv', 'fdatasync', 'setnetgrent', ] foreach func: check_functions config_data.set('HAVE_' + func.to_upper(), cc.has_function(func)) endforeach compiler_common_flags = [ '-D_GNU_SOURCE', ] compiler_c_flags = [] compiler_cpp_flags = [] if get_option('buildtype').contains('debug') compiler_c_flags += cc.get_supported_arguments([ '-Waggregate-return', '-Wdeclaration-after-statement', '-Wformat=2', '-Wimplicit-function-declaration', '-Winit-self', '-Wmissing-declarations', '-Wmissing-include-dirs', '-Wmissing-prototypes', '-Wstrict-prototypes', ]) endif compiler_c_flags += cc.get_supported_arguments([ '-Wno-format-y2k', '-Wno-declaration-after-statement', ]) if cc.get_id() == 'gcc' compiler_c_flags += cc.get_supported_arguments([ # -Wunused-result can't be suppressed by using (void) in gcc, so let's disable it for now # See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 '-Wno-unused-result', ]) endif glib_req_version = '>= 2.30.0' gio_dep = dependency('gio-2.0', version: glib_req_version) gio_unix_dep = dependency('gio-unix-2.0', version: glib_req_version) glib_dep = dependency('glib-2.0', version: glib_req_version) gobject_dep = dependency('gobject-2.0', version: glib_req_version) duktape_req_version = '>= 2.2.0' libs_only = get_option('libs-only') if not libs_only expat_dep = dependency('expat') assert(cc.has_header('expat.h', dependencies: expat_dep), 'Can\'t find expat.h. Please install expat.') assert(cc.has_function('XML_ParserCreate', dependencies: expat_dep), 'Can\'t find expat library. Please install expat.') js_dep = dependency('duktape', version: duktape_req_version, required: false) if not js_dep.found() message('Falling back to looking for library and header...') js_dep = cc.find_library('duktape', has_headers: ['duktape.h'], required: true) endif libm_dep = cc.find_library('m') thread_dep = dependency('threads') func = 'pthread_condattr_setclock' config_data.set('HAVE_' + func.to_upper(), cc.has_function(func, prefix : '#include ')) endif dbus_dep = dependency('dbus-1', required: false) dbus_policydir = pk_prefix / pk_datadir / 'dbus-1/system.d' if dbus_dep.found() dbus_system_bus_services_dir = dbus_dep.get_variable(pkgconfig: 'system_bus_services_dir', pkgconfig_define: ['datadir', pk_prefix / pk_datadir]) else # libdbus development files not installed, assume a standard layout dbus_system_bus_services_dir = pk_prefix / pk_datadir / 'dbus-1' / 'system-services' endif # check OS host_system = host_machine.system() config_data.set('HAVE_' + host_system.to_upper(), true) # Check whether setnetgrent has a return value have_netgroup_h = cc.has_header('netgroup.h') config_data.set('HAVE_NETGROUP_H', have_netgroup_h) if config_data.get('HAVE_SETNETGRENT', false) setnetgrent_return_src = ''' #include #ifdef HAVE_NETGROUP_H #include #else #include #endif int main() { int r = setnetgrent (NULL); }; ''' args = ['-D_GNU_SOURCE'] if have_netgroup_h args += '-DHAVE_NETGROUP_H' endif config_data.set('HAVE_SETNETGRENT_RETURN', cc.compiles(setnetgrent_return_src, args: args, name: 'setnetgrent return support')) endif # Select wether to use logind, elogind or ConsoleKit for session tracking session_tracking = get_option('session_tracking') enable_logind = (session_tracking != 'ConsoleKit') if enable_logind if session_tracking == 'logind' logind_dep = dependency('libsystemd', not_found_message: 'logind support requested but libsystemd library not found') else logind_dep = dependency('libelogind', not_found_message: 'elogind support requested but libelogind library not found') endif func = 'sd_uid_get_display' config_data.set10('HAVE_' + func.to_upper(), cc.has_function(func, dependencies: logind_dep)) func = 'sd_pidfd_get_session' config_data.set10('HAVE_' + func.to_upper(), cc.has_function(func, dependencies: logind_dep)) endif config_data.set('HAVE_LIBSYSTEMD', enable_logind) config_data.set('HAVE_PIDFD_OPEN', cc.get_define('SYS_pidfd_open', prefix: '#include ') != '') # systemd unit / sysuser / tmpfiles.d file installation directories systemdsystemunitdir = get_option('systemdsystemunitdir') systemd_dep = dependency('systemd', required : false) if systemd_dep.found() if systemdsystemunitdir == '' # FIXME: systemd.pc file does not use variables with relative paths, so `define_variable` cannot be used systemdsystemunitdir = systemd_dep.get_variable(pkgconfig : 'systemdsystemunitdir') endif sysusers_dir = systemd_dep.get_variable(pkgconfig : 'sysusers_dir') tmpfiles_dir = systemd_dep.get_variable(pkgconfig : 'tmpfiles_dir') else if systemdsystemunitdir == '' systemdsystemunitdir = '/usr/lib/systemd/system' endif sysusers_dir = '/usr/lib/sysusers.d' tmpfiles_dir = '/usr/lib/tmpfiles.d' endif # User for running polkitd polkitd_user = get_option('polkitd_user') config_data.set_quoted('POLKITD_USER', polkitd_user) polkitd_uid = get_option('polkitd_uid') config_data.set('POLKITD_UID', polkitd_uid) # Select which authentication framework to use auth_deps = [] auth_fw = get_option('authfw') enable_pam = (auth_fw == 'pam') if enable_pam # Check for PAM pam_dep = cc.find_library('pam') assert(pam_dep.found() and cc.has_function('pam_start', dependencies: pam_dep), 'Could not find pam/pam-devel, please install the needed packages.') # how to call pam_strerror pam_strerror_src = ''' #include #include #include #endif int main() { @0@ }; ''' # FIXME: Not necessary anymore? if cc.compiles(pam_strerror_src.format('pam_handle_t *pamh = 0; char *s = pam_strerror(pamh, PAM_SUCCESS);')) # FIXME: unused? config_data.set('PAM_STRERROR_TWO_ARGS', true) else message('how to call pam_strerror: ' + cc.compiles(pam_strerror_src.format('char *s = pam_strerror(PAM_SUCCESS);')).to_string('1', 'unknown')) endif pam_prefix = get_option('pam_prefix') if pam_prefix == '' pam_prefix = pk_prefix / 'lib/pam.d' else message('PAM files will be installed in prefix ' + pam_prefix) endif pam_module_dir = get_option('pam_module_dir') if pam_module_dir == '' pam_module_dir = pk_libdir / 'security' endif auth_deps += pam_dep elif auth_fw == 'shadow' auth_deps += cc.find_library('crypt') endif config_data.set('POLKIT_AUTHFW_' + auth_fw.to_upper(), true) # FIXME: sigtimedwait is not used anywhere? ''' if host_system == 'solaris' rt_dep = cc.find_library('rt') cc.has_function('sigtimedwait', dependencies: rt_dep) else cc.has_function('sigtimedwait') endif ''' os_type = get_option('os_type') if os_type == '' os_paths = [ ['redhat', '/etc/sysconfig/network-scripts'], ['suse', '/etc/SuSE-release'], ['debian', '/etc/debian_version'], ['gentoo', '/etc/gentoo-release'], ['pardus', '/etc/pardus-release'], ['lfs', '/etc/lfs-release'], ] foreach os_path: os_paths if run_command('test', '-e', os_path[1], check: false).returncode() == 0 os_type = os_path[0] break endif endforeach if os_type == '' message('Linux distribution autodetection failed, specify the distribution to target using -Dos_type=') endif endif # The default privileged group differs between distributions, set it accordingly if not specified privileged_group = get_option('privileged_group') if privileged_group == '' if os_type == 'debian' privileged_group = 'sudo' else privileged_group = 'wheel' endif endif pam_include = get_option('pam_include') if pam_include == '' if ['suse', 'solaris', 'openembedded'].contains(os_type) pam_conf = { 'PAM_FILE_INCLUDE_AUTH': 'common-auth', 'PAM_FILE_INCLUDE_ACCOUNT': 'common-account', 'PAM_FILE_INCLUDE_PASSWORD': 'common-password', 'PAM_FILE_INCLUDE_SESSION': 'common-session', } elif os_type.contains('bsd') pam_conf = { 'PAM_FILE_INCLUDE_AUTH': 'system', 'PAM_FILE_INCLUDE_ACCOUNT': 'system', 'PAM_FILE_INCLUDE_PASSWORD': 'system', 'PAM_FILE_INCLUDE_SESSION': 'system', } elif os_type == 'lfs' pam_conf = { 'PAM_FILE_INCLUDE_AUTH': 'system-auth', 'PAM_FILE_INCLUDE_ACCOUNT': 'system-account', 'PAM_FILE_INCLUDE_PASSWORD': 'system-password', 'PAM_FILE_INCLUDE_SESSION': 'system-session', } #if ['redhat', 'gentoo', 'pardus'].contains(os_type) else pam_conf = { 'PAM_FILE_INCLUDE_AUTH': 'system-auth', 'PAM_FILE_INCLUDE_ACCOUNT': 'system-auth', 'PAM_FILE_INCLUDE_PASSWORD': 'system-auth', 'PAM_FILE_INCLUDE_SESSION': 'system-auth', } endif else pam_conf = { 'PAM_FILE_INCLUDE_AUTH': pam_include, 'PAM_FILE_INCLUDE_ACCOUNT': pam_include, 'PAM_FILE_INCLUDE_PASSWORD': pam_include, 'PAM_FILE_INCLUDE_SESSION': pam_include, } endif enable_introspection = get_option('introspection') if enable_introspection dependency('gobject-introspection-1.0', version: '>= 0.6.2') endif gettext = get_option('gettext') if gettext config_data.set('ENABLE_GETTEXT', 1) endif configure_file( output: 'config.h', configuration: config_data, ) compiler_common_flags += ['-include', 'config.h'] add_project_arguments(compiler_common_flags + compiler_c_flags, language: 'c') add_project_arguments(compiler_common_flags + compiler_cpp_flags, language: 'cpp') content_files = files('COPYING') subdir('actions') subdir('data') subdir('src') subdir('docs') subdir('po') enable_tests = get_option('tests') if enable_tests subdir('test') endif if not libs_only meson.add_install_script( 'meson_post_install.py', get_option('bindir'), pk_libprivdir, pk_pkgsysconfdir, polkitd_user, ) endif output = '\n ' + meson.project_name() + ' ' + meson.project_version() + '\n' output += ' ============\n\n' output += ' prefix: ' + pk_prefix + '\n' output += ' datadir: ' + pk_datadir + '\n\n' output += ' includedir: ' + pk_includedir + '\n' output += ' libdir: ' + pk_libdir + '\n' output += ' sysconfdir: ' + pk_sysconfdir + '\n' output += ' source code location: ' + source_root + '\n' output += ' compiler: ' + cc.get_id() + '\n' output += ' c_flags: ' + ' '.join(compiler_c_flags) + '\n\n' if enable_man output += ' xsltproc: ' + xsltproc.full_path() + '\n' endif output += ' introspection: ' + enable_introspection.to_string() + '\n' output += ' Distribution/OS: ' + os_type + '\n' output += ' Authentication framework: ' + auth_fw + '\n' output += ' Session tracking: ' + session_tracking + '\n' output += ' system unit directory: ' + systemdsystemunitdir + '\n' output += ' sysusers.d directory: ' + sysusers_dir + '\n' output += ' tmpfiles.d directory: ' + tmpfiles_dir + '\n' output += ' polkitd user: ' + polkitd_user + ' \n' if polkitd_uid != '-' output += ' polkitd UID: ' + polkitd_uid + ' \n' endif output += ' PAM support: ' + enable_pam.to_string() + '\n\n' if libs_only output += ' !!! Only building polkit libraries, not polkitd !!!\n\n' endif if enable_pam output += ' PAM file auth: ' + pam_conf['PAM_FILE_INCLUDE_AUTH'] + '\n' output += ' PAM file acount: ' + pam_conf['PAM_FILE_INCLUDE_ACCOUNT'] + '\n' output += ' PAM file password: ' + pam_conf['PAM_FILE_INCLUDE_PASSWORD'] + '\n' output += ' PAM file session: ' + pam_conf['PAM_FILE_INCLUDE_SESSION'] + '\n' output += ' PAM config location: ' + pam_prefix + '\n\n' endif output += ' Building api docs: ' + enable_gtk_doc.to_string() + '\n' output += ' Building man pages: ' + enable_man.to_string() + '\n' output += ' Building examples: ' + enable_examples.to_string() + '\n' output += ' Building tests: ' + enable_tests.to_string() message(output) polkit-126/meson_options.txt000066400000000000000000000035571474122443600163440ustar00rootroot00000000000000option('session_tracking', type: 'combo', choices: ['logind', 'elogind', 'ConsoleKit'], value: 'logind', description: 'session tracking (logind/elogind/ConsoleKit)') option('systemdsystemunitdir', type: 'string', value: '', description: 'custom directory for systemd system units') option('libs-only', type: 'boolean', value: false, description: 'Only build libraries (skips building polkitd)') option('polkitd_user', type: 'string', value: 'polkitd', description: 'User for running polkitd (polkitd)') option('polkitd_uid', type: 'string', value: '-', description: 'Fixed UID for user running polkitd (polkitd)') option('privileged_group', type: 'string', value: '', description: 'Group to use for default privileged access (default: wheel)') option('authfw', type: 'combo', choices: ['pam', 'shadow', 'bsdauth'], value: 'pam', description: 'Authentication framework (pam/shadow)') option('os_type', type: 'combo', choices: ['redhat', 'suse', 'gentoo', 'pardus', 'solaris', 'netbsd', 'lfs', 'openembedded', 'debian', ''], value: '', description: 'distribution or OS') option('pam_include', type: 'string', value: '', description: 'pam file to include') option('pam_module_dir', type: 'string', value: '', description: 'directory to install PAM security module') option('pam_prefix', type: 'string', value: '', description: 'specify where pam files go') option('examples', type: 'boolean', value: false, description: 'Build example programs') option('tests', type: 'boolean', value: false, description: 'Build tests') option('introspection', type: 'boolean', value: true, description: 'Enable introspection for this build') option('gtk_doc', type: 'boolean', value: false, description: 'use gtk-doc to build documentation') option('man', type: 'boolean', value: false, description: 'build manual pages') option('gettext', type: 'boolean', value: false, description: 'use gettext for translations') polkit-126/meson_post_install.py000066400000000000000000000032261474122443600171660ustar00rootroot00000000000000#!/usr/bin/env python3 import os import pwd import sys destdir = os.environ.get('DESTDIR') prefix = os.environ['MESON_INSTALL_DESTDIR_PREFIX'] def destdir_path(p): if os.path.isabs(p): if destdir is None: return p else: return os.path.join(destdir, os.path.relpath(p, '/')) else: return os.path.join(prefix, p) bindir = destdir_path(sys.argv[1]) pkglibdir = destdir_path(sys.argv[2]) pkgsysconfdir = destdir_path(sys.argv[3]) polkitd_user = sys.argv[4] try: polkitd_gid = pwd.getpwnam(polkitd_user).pw_gid except KeyError: polkitd_gid = None dst = os.path.join(bindir, 'pkexec') if os.geteuid() == 0: os.chown(dst, 0, -1) os.chmod(dst, 0o4755) else: print( 'Owner and mode of {} need to be setuid root (04755) after ' 'installation'.format( dst, ) ) dst = os.path.join(pkgsysconfdir, 'rules.d') if not os.path.exists(dst): os.makedirs(dst, mode=0o750) if os.geteuid() == 0 and polkitd_gid is not None: os.chown(dst, 0, polkitd_gid) else: print( 'Owner of {} needs to be set to root and group to {} after installation'.format( dst, polkitd_user, ) ) # polkit-agent-helper-1 need to be setuid root because it's used to # authenticate not only the invoking user, but possibly also root # and/or other users. dst = os.path.join(pkglibdir, 'polkit-agent-helper-1') if os.geteuid() == 0: os.chown(dst, 0, -1) os.chmod(dst, 0o4755) else: print( 'Owner and mode of {} need to be setuid root (04755) after ' 'installation'.format( dst, ) ) polkit-126/po/000077500000000000000000000000001474122443600133135ustar00rootroot00000000000000polkit-126/po/ChangeLog000066400000000000000000000000001474122443600150530ustar00rootroot00000000000000polkit-126/po/LINGUAS000066400000000000000000000001761474122443600143440ustar00rootroot00000000000000# please keep this list sorted alphabetically # cs da de hi hr hu id it ka nl nn pl pt pt_BR ro ru sk sl sv tr uk zh_CN zh_TW polkit-126/po/Makevars000066400000000000000000000066671474122443600150260ustar00rootroot00000000000000# Makefile variables for PO directory in any package using GNU gettext. # Usually the message domain is the same as the package name. DOMAIN = polkit-1 # These two variables depend on the location of this directory. subdir = po top_builddir = .. # These options get passed to xgettext. XGETTEXT_OPTIONS = --from-code=UTF-8 --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=g_dngettext:2,3 --add-comments # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding # package. (Note that the msgstr strings, extracted from the package's # sources, belong to the copyright holder of the package.) Translators are # expected to transfer the copyright for their translations to this person # or entity, or to disclaim their copyright. The empty string stands for # the public domain; in this case the translators are expected to disclaim # their copyright. COPYRIGHT_HOLDER = polkit Authors # This tells whether or not to prepend "GNU " prefix to the package # name that gets inserted into the header of the $(DOMAIN).pot file. # Possible values are "yes", "no", or empty. If it is empty, try to # detect it automatically by scanning the files in $(top_srcdir) for # "GNU packagename" string. PACKAGE_GNU = no # This is the email address or URL to which the translators shall report # bugs in the untranslated strings: # - Strings which are not entire sentences, see the maintainer guidelines # in the GNU gettext documentation, section 'Preparing Strings'. # - Strings which use unclear terms or require additional context to be # understood. # - Strings which make invalid assumptions about notation of date, time or # money. # - Pluralisation problems. # - Incorrect English spelling. # - Incorrect formatting. # It can be your email address, or a mailing list address where translators # can write to without being subscribed, or the URL of a web page through # which the translators can contact you. MSGID_BUGS_ADDRESS = https://gitlab.freedesktop.org/polkit/polkit/-/issues/ # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. EXTRA_LOCALE_CATEGORIES = # This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' # context. Possible values are "yes" and "no". Set this to yes if the # package uses functions taking also a message context, like pgettext(), or # if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. USE_MSGCTXT = yes # These options get passed to msgmerge. # Useful options are in particular: # --previous to keep previous msgids of translated messages, # --quiet to reduce the verbosity. MSGMERGE_OPTIONS = # These options get passed to msginit. # If you want to disable line wrapping when writing PO files, add # --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and # MSGINIT_OPTIONS. MSGINIT_OPTIONS = # This tells whether or not to regenerate a PO file when $(DOMAIN).pot # has changed. Possible values are "yes" and "no". Set this to no if # the POT file is checked in the repository and the version control # program ignores timestamps. PO_DEPENDS_ON_POT = no # This tells whether or not to forcibly update $(DOMAIN).pot and # regenerate PO files on "make dist". Possible values are "yes" and # "no". Set this to no if the POT file and PO files are maintained # externally. DIST_DEPENDS_ON_UPDATE_PO = no polkit-126/po/POTFILES.in000066400000000000000000000004631474122443600150730ustar00rootroot00000000000000# List of source files containing translatable strings. # Please keep this file sorted alphabetically. actions/org.freedesktop.policykit.policy.in src/examples/org.freedesktop.policykit.examples.pkexec.policy.in src/programs/pkaction.c src/programs/pkcheck.c src/programs/pkexec.c src/programs/pkttyagent.c polkit-126/po/POTFILES.skip000066400000000000000000000000041474122443600154220ustar00rootroot00000000000000.pc polkit-126/po/cs.po000066400000000000000000000165361474122443600142730ustar00rootroot00000000000000# Danish translations for PolicyKit. # Copyright (C) 2013 SUSE Linux GmbH # This file is distributed under the same license as the PolicyKit package. # # Tomáš Chvátal , 2013. # Marek Černocký , 2018. # msgid "" msgstr "" "Project-Id-Version: polkit\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2023-06-20 15:27+0000\n" "PO-Revision-Date: 2023-08-26 00:16+0200\n" "Last-Translator: Daniel Rusek \n" "Language-Team: čeština \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Poedit 3.3.2\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Spustit program jako jiný uživatel" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "Pro spuštění programu pod jiným uživatelem je vyžadováno ověření" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "Spustit ukázkový program polkit Frobnicate" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Pro spuštění ukázkového programu polkit Frobnicate je vyžadováno ověření " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Pouze vypsat informace o ČINNOSTI" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "ČINNOST" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Vypsat podrobné informace o činnosti" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "Vypsat verzi" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id ČINNOST]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Chyby hlaste na: %s\n" "%s domovská stránka: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: neočekávaný argument „%s“\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Použití:\n" " pkcheck [VOLBY…]\n" "\n" "Volby nápovědy:\n" " -h, --help Zobrazit nápovědu k volbám\n" "\n" "Volby aplikace:\n" " -a, --action-id=ČINNOST Zkontrolovat oprávnění pro vykonání " "ČINNOSTI\n" " -u, --allow-user-interaction Povolit interakci s uživatelem, pokud " "je potřeba\n" " -d, --details=KLÍČ HODNOTA Přidat (KLÍČ, HODNOTA) do informací o " "činnosti\n" " --enable-internal-agent Použít interního ověřovacího agenta, " "pokud je potřeba\n" " --list-temp Vypsat dočasná oprávnění pro aktuální " "sezení\n" " -p, --process=PID[,ČAS_SPUŠTĚNÍ] Zkontrolovat oprávnění vybraného " "procesu\n" " --revoke-temp Zrušit všechna dočasná oprávnění pro " "aktuální sezení\n" " -s, --system-bus-name=NÁZEV_SBĚRNICE\n" " Zkontrolovat oprávnění vlastníka " "sběrnice NÁZEV_SBĚRNICE\n" " --version Vypsat verzi\n" "\n" "Chyby hlaste na: %s\n" "%s domovská stránka: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: po „%s“ je očekáván argument\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: neplatná hodnota „%s“ pro „--process“\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: po „--detail, -d“ jsou očekávány dva argumenty\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: není určen předmět\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:821 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Pro spuštění „$(cmdline_short)“ pod účtem správce je vyžadováno ověření" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:831 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Pro spuštění „$(cmdline_short)“ pod uživatelem $(user.display) je vyžadováno " "ověření" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "Nenahrazovat již bežícího agenta" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "Uzavřít FD při registraci agenta" #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "Registrovat agenta pro určený proces" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,ČAS_SPUŠTĚNÍ]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "Registrovat agenta pro vlastníka sběrnice NÁZEV_SBĚRNICE" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "NÁZEV_SBĚRNICE" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "%s: Volby --process a --system-bus-name se vzájemně vylučují\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: neplatný identifikátor procesu „%s“\n" polkit-126/po/da.po000066400000000000000000000156171474122443600142510ustar00rootroot00000000000000# Danish translations for PolicyKit. # Copyright (C) 2009 Red Hat, Inc. # This file is distributed under the same license as the polkit package. # David Zeuthen , 2009. # scootergrisen, 2019. # msgid "" msgstr "" "Project-Id-Version: polkit\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2011-03-03 13:03-0500\n" "PO-Revision-Date: 2019-02-04 21:06+0200\n" "Last-Translator: scootergrisen\n" "Language-Team: Danish\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Kør et program som en anden bruger" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "Der kræves autentifikation for at køre et program som en anden bruger" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Kør polkits Frobnicate-eksempelprogram" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Der kræves autentifikation for at køre polkits Frobnicate-eksempelprogram " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Output kun information om HANDLING" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "HANDLING" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Output detaljeret handlingsinformation" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:62 msgid "Show version" msgstr "Vis version" #: ../src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id HANDLING]" #: ../src/programs/pkaction.c:133 ../src/programs/pkttyagent.c:83 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Rapportér fejl til: %s\n" "%s hjemmeside: <%s>" #: ../src/programs/pkaction.c:147 ../src/programs/pkcheck.c:493 #: ../src/programs/pkttyagent.c:97 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Uventet argument `%s'\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Anvendelse:\n" " pkcheck [TILVALG...]\n" "\n" "Tilvalg for hjælp:\n" " -h, --help Vis tilvalg for hjælp\n" "\n" "Tilvalg for program:\n" " -a, --action-id=HANDLING Tjek autorisation til at udføre " "HANDLING\n" " -u, --allow-user-interaction Interager med brugeren, hvis det er " "nødvendigt\n" " -d, --details=NØGLE VÆRDI Tilføj (NØGLE, VÆRDI) til information " "om handlingen\n" " --enable-internal-agent Brug en intern autentifikationsagent, " "hvis det er nødvendigt\n" " --list-temp Oplist midlertidige autorisationer for " "den nuværende session\n" " -p, --process=PID[,STARTTID,UID] Tjek autorisation af den angive proces\n" " --revoke-temp Tilbagekald alle midlertidige " "autorisationer for den nuværende session\n" " -s, --system-bus-name=BUSNAVN Tjek autorisation for ejeren af BUSNAVN\n" " --version Vis version\n" "\n" "Rapportér fejl til: %s\n" "%s hjemmeside: <%s>\n" #: ../src/programs/pkcheck.c:393 ../src/programs/pkcheck.c:426 #: ../src/programs/pkcheck.c:438 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Argument ventet efter `%s'\n" #: ../src/programs/pkcheck.c:416 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Ugyldig --process-værdi `%s'\n" #: ../src/programs/pkcheck.c:453 ../src/programs/pkcheck.c:462 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: To argumenter ventet after `--detail'\n" #: ../src/programs/pkcheck.c:523 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Emne ikke angivet\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:790 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "Der kræves autentifikation for at køre `$(cmdline_short)' som superbrugeren" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:800 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Der kræves autentifikation for at køre `$(cmdline_short)' som brugeren " "$(user.display)" #: ../src/programs/pkttyagent.c:45 msgid "Don't replace existing agent if any" msgstr "Erstat ikke den eksisterende agent hvis der er en" #: ../src/programs/pkttyagent.c:49 msgid "Close FD when the agent is registered" msgstr "Luk FD når agenten registreres" #: ../src/programs/pkttyagent.c:49 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:53 msgid "Register the agent for the specified process" msgstr "Registrer agenten for den angivne proces" #: ../src/programs/pkttyagent.c:54 msgid "PID[,START_TIME]" msgstr "PID[,STARTTID]" #: ../src/programs/pkttyagent.c:58 msgid "Register the agent for the owner of BUS_NAME" msgstr "Registrer agenten for ejeren af BUSNAVN" #: ../src/programs/pkttyagent.c:58 msgid "BUS_NAME" msgstr "BUSNAVN" #: ../src/programs/pkttyagent.c:129 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Ugyldig procesangiver `%s'\n" polkit-126/po/de.po000066400000000000000000000166241474122443600142540ustar00rootroot00000000000000# German translation for polkit. # Copyright (C) 2015 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Christian Kirbach , 2015. # Wolfgang Stöggl , 2015. # Jürgen Benvenuti , 2023. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2023-09-12 15:27+0000\n" "PO-Revision-Date: 2023-09-12 19:52+0200\n" "Last-Translator: Jürgen Benvenuti \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.3.1\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Ein Programm als ein anderer Benutzer ausführen" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "" "Legitimierung ist erforderlich, um ein Programm als ein anderer Benutzer " "auszuführen" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "Das polkit-Beispielprogramm Frobnicate ausführen" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Legitimierung ist erforderlich, um das polkit-Beispielprogramm Frobnicate " "auszuführen (user=$(user), user.gecos=$(user.gecos), user.display=$(user." "display), program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Nur Informationen zu AKTION ausgeben" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "AKTION" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Detaillierte Aktions-Informationen ausgeben" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "Version anzeigen" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id AKTION]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Fehler melden an: %s\n" "%s Homepage: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Unerwartetes Argument »%s«\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Aufruf:\n" " pkcheck [OPTION …]\n" "\n" "Hilfeoptionen:\n" " -h, --help Hilfeoptionen anzeigen\n" "\n" "Programmoptionen:\n" " -a, --action-id=ACTION Legitimierung prüfen, um AKTION " "durchzuführen\n" " -u, --allow-user-interaction Interaktion mit Benutzer, falls " "erforderlich\n" " -d, --details=KEY VALUE (KEY, VALUE) zur Information über die " "Aktion hinzufügen\n" " --enable-internal-agent Internen Legitimierungsagenten " "verwenden, falls erforderlich\n" " --list-temp Temporäre Legitimierungen für die " "aktuelle Sitzung auflisten\n" " -p, --process=PID[,START_TIME,UID] Legitimierung des angegebenen Prozesses " "prüfen\n" " --revoke-temp Alle temporären Legitimierungen der " "aktuellen Sitzung aufheben\n" " -s, --system-bus-name=BUS_NAME Legitimierung des Eigentümers von " "BUS_NAME prüfen\n" " --version Version anzeigen\n" "\n" "Fehler melden an: %s\n" "%s Homepage: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Argument erwartet nach »%s«\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Ungültiger Wert »%s« für »--process«\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: Es werden zwei Argumente nach »--detail, -d« erwartet\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Betreff nicht angegeben\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:822 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Legitimierung ist erforderlich, um »$(cmdline_short)« als Superuser " "auszuführen" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:832 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Legitimierung ist erforderlich, um »$(cmdline_short)« als Benutzer $(user." "display) auszuführen" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "Vorhandenen Agenten nicht ersetzen" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "Dateideskriptor (FD) schließen, sobald der Agent registriert ist" # file descriptor #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "Agenten des angegebenen Prozesses registrieren" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,STARTZEIT]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "Den Agenten für den Besitzer von BUS_NAME registrieren" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "BUS_NAME" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "" "%s: Die Optionen »--process« und »--system-bus-name« schließen sich " "gegenseitig aus\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Ungültige Prozessangabe »%s«\n" polkit-126/po/hi.po000066400000000000000000000226631474122443600142640ustar00rootroot00000000000000# Hindi translation for polkit. # Copyright (C) 2024 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Scrambled 777 , 2024. # Scrambled777 , 2024. # msgid "" msgstr "" "Project-Id-Version: polkit main\n" "Report-Msgid-Bugs-To: https://github.com/polkit-org/polkit/issues\n" "POT-Creation-Date: 2024-05-19 03:27+0000\n" "PO-Revision-Date: 2024-05-19 15:12+0530\n" "Last-Translator: Scrambled777 \n" "Language-Team: Hindi \n" "Language: hi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Gtranslator 46.1\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "किसी प्रोग्राम को दूसरे उपयोक्ता के रूप में चलाएं" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "" "किसी प्रोग्राम को किसी अन्य उपयोक्ता के रूप में चलाने के लिए प्रमाणीकरण की आवश्यकता होती " "है" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "polkit उदाहरण प्रोग्राम Frobnicate चलाएं" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "polkit उदाहरण प्रोग्राम Frobnicate को चलाने के लिए प्रमाणीकरण आवश्यक है " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "केवल कार्रवाई के बारे में जानकारी आउटपुट करें" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "कार्रवाई" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "विस्तृत कार्रवाई की जानकारी आउटपुट करें" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "संस्करण दिखाएं" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id कार्रवाई]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "बग यहां रिपोर्ट करें: %s\n" "%s मुखपृष्ठ: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: अप्रत्याशित तर्क `%s'\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "उपयोग:\n" " pkcheck [विकल्प...]\n" "\n" "सहायता विकल्प:\n" " -h, --help सहायता विकल्प दिखाएं\n" "\n" "अनुप्रयोग विकल्प:\n" " -a, --action-id=कार्रवाई कार्रवाई करने के " "लिए प्राधिकरण की जांच करें\n" " -u, --allow-user-interaction यदि आवश्यक हो तो उपयोक्ता के साथ बातचीत " "करें\n" " -d, --details=मौलिक मान कार्रवाई के बारे में " "जानकारी में (मौलिक, मान) जोड़ें\n" " --enable-internal-agent यदि आवश्यक हो तो आंतरिक प्रमाणीकरण एजेंट का " "उपयोग करें\n" " --list-temp वर्तमान सत्र के लिए अस्थायी प्राधिकरणों की " "सूची बनाएं\n" " -p, --process=PID[,शुरू_समय,UID] निर्दिष्ट प्रक्रिया के प्राधिकरण की जांच करें\n" " --revoke-temp वर्तमान सत्र के लिए सभी अस्थायी प्राधिकरण " "रद्द करें\n" " -s, --system-bus-name=बस_नाम बस_नाम के मालिक का प्राधिकरण " "जाँचें\n" " --version संस्करण दिखाएं\n" "\n" "बग यहां रिपोर्ट करें: %s\n" "%s मुखपृष्ठ: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: `%s' के बाद तर्क अपेक्षित\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: अमान्य --process मान `%s'\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: `--detail, -d' के बाद दो तर्क अपेक्षित हैं\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: विषय निर्दिष्ट नहीं\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:822 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "`$(cmdline_short)' को सुपर उपयोक्ता के रूप में चलाने के लिए प्रमाणीकरण की आवश्यकता है" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:832 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "`$(cmdline_short)' को उपयोक्ता $(user.display) के रूप में चलाने के लिए प्रमाणीकरण की " "आवश्यकता है" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "यदि कोई मौजूदा एजेंट है तो उसे न बदलें" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "एजेंट पंजीकृत होने पर FD बंद कर दें" #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "निर्दिष्ट प्रक्रिया के लिए एजेंट को पंजीकृत करें" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,शुरू_समय]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "बस_नाम के मालिक के लिए एजेंट पंजीकृत करें" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "बस_नाम" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "%s: विकल्प --process और --system-bus-name परस्पर अनन्य हैं\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: अमान्य प्रक्रिया विनिर्देशक `%s'\n" polkit-126/po/hr.po000066400000000000000000000160041474122443600142650ustar00rootroot00000000000000# Croatian translation for polkit. # Copyright (C) 2017 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?" "product=PolicyKit&keywords=I18N+L10N&component=libpolkit\n" "POT-Creation-Date: 2017-04-23 15:27+0000\n" "PO-Revision-Date: 2017-04-23 19:27+0200\n" "Language-Team: Croatian \n" "Language: hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "Last-Translator: gogo \n" "X-Generator: Poedit 1.8.7.1\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Pokreni programe kao drugi korisnik" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "Potrebna je ovjera za pokretanje programa kao drugog korisnika" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Pokreni primjer PolicyKit programa Frobnicate" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Potrebna je ovjera za pokretanje PolicyKit programa Frobnicate (user=" "$(korisnik), program=$(program), command_line=$(naredbeni_redak))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Samo izlazne informacije o RADNJAMA" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "RADNJA" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Opširnije izlazne informacije radnje" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:62 msgid "Show version" msgstr "Prikaži inačicu" #: ../src/programs/pkaction.c:134 msgid "[--action-id ACTION]" msgstr "[--action-id RADNJA]" #: ../src/programs/pkaction.c:135 ../src/programs/pkttyagent.c:85 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Prijavite greške na: %s\n" "%s početna stranica: <%s>" #: ../src/programs/pkaction.c:149 ../src/programs/pkcheck.c:495 #: ../src/programs/pkttyagent.c:99 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Neočekivani argument '%s'\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Upotreba:\n" " pkcheck [MOGUĆNOST...]\n" "\n" "Mogućnosti pomoći:\n" " -h, --help Prikaži mogućnosti pomoći\n" "\n" "Mogućnosti aplikacije:\n" " -a, --action-id=RADNJA Provjeri ovjeru za pokretanje RADNJE\n" " -u, --allow-user-interaction Djeluj s korisnikom ako je potrebno\n" " -d, --details=KLJUČ VRIJEDNOST Dodaj (KLJUČ, VRIJEDNOST) u " "informaciju o radnji\n" " --enable-internal-agent Koristi unutrašnjeg agenta ovjere ako " "je potrebno\n" " --list-temp Prikaži privremena ovlaštenja za " "trenutnu sesiju\n" " -p, --process=PID[,POČETNO_VRIJEME,UID] Provjeri ovlaštenja za određeni " "proces\n" " --revoke-temp Opozovi sva privremena ovlaštenja za " "trenutnu sesiju\n" " -s, --system-bus-name=BUS_NAZIV Provjeri ovlaštenja vlasnika od " "BUS_NAZIVA\n" " --version Prikaži inačicu\n" "\n" "Prijavi greške na: %s\n" "%s početna stranica: <%s>\n" #: ../src/programs/pkcheck.c:395 ../src/programs/pkcheck.c:428 #: ../src/programs/pkcheck.c:440 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Očekivani argument nakon `%s'\n" #: ../src/programs/pkcheck.c:418 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Neispravna --process vrijednost `%s'\n" #: ../src/programs/pkcheck.c:455 ../src/programs/pkcheck.c:464 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: Dva argumenta su očekivana nakon `--detail'\n" #: ../src/programs/pkcheck.c:525 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Subjekt nije određen\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:797 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "Potrebna je ovjera za pokretanje `$(cmdline_short)' kao super korisnika" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:807 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Potrebna je ovjera za pokretanje `$(cmdline_short)' kao korisnika $(user.display)" #: ../src/programs/pkttyagent.c:45 msgid "Don't replace existing agent if any" msgstr "Ne zamjenjuj postojećeg agenta ako postoji" #: ../src/programs/pkttyagent.c:49 msgid "Close FD when the agent is registered" msgstr "Zatvori FD kada je agent registriran" #: ../src/programs/pkttyagent.c:49 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:53 msgid "Register the agent for the specified process" msgstr "Registriraj agenta za određeni proces" #: ../src/programs/pkttyagent.c:54 msgid "PID[,START_TIME]" msgstr "PID[,POČETNO_VRIJEME]" #: ../src/programs/pkttyagent.c:58 msgid "Register the agent owner of BUS_NAME" msgstr "Registriraj vlasnika agenta BUS_NAZIVA" #: ../src/programs/pkttyagent.c:58 msgid "BUS_NAME" msgstr "BUS_NAZIV" #: ../src/programs/pkttyagent.c:131 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Neispravan razvrstač procesa `%s'\n" polkit-126/po/hu.po000066400000000000000000000173021474122443600142720ustar00rootroot00000000000000# Hungarian translation for polkit. # Copyright (C) 2016, 2021. Free Software Foundation, Inc. # This file is distributed under the same license as the polkit package. # # Gabor Kelemen , 2016. # Balázs Úr , 2021. msgid "" msgstr "" "Project-Id-Version: polkit\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2020-05-26 15:36+0000\n" "PO-Revision-Date: 2021-03-18 01:19+0100\n" "Last-Translator: Balázs Úr \n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 19.12.3\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Program futtatása másik felhasználóként" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "Hitelesítés szükséges a program futtatásához másik felhasználóként" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Frobnicate polkit példaprogram futtatása" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Hitelesítés szükséges a Frobnicate polkit példaprogram futtatásához (user=" "$(user), user.gecos=$(user.gecos), user.display=$(user.display), program=" "$(program), command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Csak a MŰVELETRŐL írjon ki információkat" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "MŰVELET" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Részletes műveletinformációk megjelenítése" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:95 msgid "Show version" msgstr "Verziószám megjelenítése" #: ../src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id MŰVELET]" #: ../src/programs/pkaction.c:133 ../src/programs/pkttyagent.c:118 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "A hibák itt jelenthetők: %s\n" "A %s honlapja: <%s>" #: ../src/programs/pkaction.c:147 ../src/programs/pkcheck.c:493 #: ../src/programs/pkttyagent.c:132 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: váratlan argumentum: „%s”\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Használat:\n" " pkcheck [KAPCSOLÓ…]\n" "\n" "Súgó kapcsolói:\n" " -h, --help Súgókapcsolók megjelenítése\n" "\n" "Alkalmazás kapcsolói:\n" " -a, --action-id=MŰVELET A MŰVELET elvégzésére való " "felhatalmazás\n" " ellenőrzése\n" " -u, --allow-user-interaction Felhasználóval való párbeszéd\n" " engedélyezése, ha szükséges\n" " -d, --details=KULCS ÉRTÉK A (KULCS, ÉRTÉK) hozzáadása a " "művelettel\n" " kapcsolatos információkhoz\n" " --enable-internal-agent Belső hitelesítési ügynök használata, " "ha\n" " szükséges\n" " --list-temp Ideiglenes felhatalmazások felsorolása " "az\n" " aktuális munkamenethez\n" " -p, --process=PID[,INDÍTÁSI_IDŐ,UID] A megadott folyamat " "felhatalmazásának\n" " ellenőrzése\n" " --revoke-temp Minden ideiglenes felhatalmazás " "visszavonása\n" " az aktuális munkamenetből\n" " -s, --system-bus-name=BUSZNÉV A BUSZNÉV tulajdonosának " "felhatalmazásának\n" " ellenőrzése\n" " --version Verziószám kiírása\n" "\n" "A hibák itt jelenthetők: %s\n" "A %s honlapja: <%s>\n" #: ../src/programs/pkcheck.c:393 ../src/programs/pkcheck.c:426 #: ../src/programs/pkcheck.c:438 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: argumentum szükséges a következő után: „%s”\n" #: ../src/programs/pkcheck.c:416 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Érvénytelen --process érték: „%s”\n" #: ../src/programs/pkcheck.c:453 ../src/programs/pkcheck.c:462 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: Két argumentum szükséges a „--detail” után\n" #: ../src/programs/pkcheck.c:523 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Nincs megadva az alany\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:790 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "Hitelesítés szükséges a(z) „$(cmdline_short)” futtatásához rendszergazdaként" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:800 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Hitelesítés szükséges a(z) „$(cmdline_short)” futtatásához $(user.display) " "felhasználóként" #: ../src/programs/pkttyagent.c:78 msgid "Don't replace existing agent if any" msgstr "Ne cserélje a meglévő ügynököt, ha van" #: ../src/programs/pkttyagent.c:82 msgid "Close FD when the agent is registered" msgstr "Fájlleíró lezárása az ügynök regisztrálásakor" #: ../src/programs/pkttyagent.c:82 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:86 msgid "Register the agent for the specified process" msgstr "Az ügynök regisztrálása a megadott folyamathoz" #: ../src/programs/pkttyagent.c:87 msgid "PID[,START_TIME]" msgstr "PID[,INDÍTÁSI_IDŐ]" #: ../src/programs/pkttyagent.c:91 msgid "Register the agent for the owner of BUS_NAME" msgstr "Az ügynök regisztrálása a BUSZNÉV tulajdonosánál" #: ../src/programs/pkttyagent.c:91 msgid "BUS_NAME" msgstr "BUSZNÉV" #: ../src/programs/pkttyagent.c:164 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Érvénytelen folyamatmegadás: „%s”\n" polkit-126/po/id.po000066400000000000000000000156451474122443600142620ustar00rootroot00000000000000# Indonesian translation for polkit. # Copyright (C) 2016 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Lorenz Adam Damara , 2016. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2020-03-22 03:56+0000\n" "PO-Revision-Date: 2020-03-31 20:21+0700\n" "Last-Translator: Andika Triwidada \n" "Language-Team: Indonesian \n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.3\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Jalankan program sebagai pengguna lain" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "Otentikasi diperlukan untuk menjalankan program sebagai pengguna lain" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Jalankan contoh program polkit Frobnicate" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Otentikasi dibutuhkan untuk menjalankan contoh program Frobnicate (user=" "$(user), user.gecos=$(user.gecos), user.display=$(user.display), program=" "$(program), command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Hanya informasi keluaran tentang AKSI" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "ACTION" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Informasi detil aksi keluaran" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:95 msgid "Show version" msgstr "Tampilkan versi" #: ../src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id ACTION]" #: ../src/programs/pkaction.c:133 ../src/programs/pkttyagent.c:118 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Laporkan kutu ke: %s\n" "halaman beranda %s: <%s>" #: ../src/programs/pkaction.c:147 ../src/programs/pkcheck.c:493 #: ../src/programs/pkttyagent.c:132 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: argumen yang tak terduga `%s'\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Penggunaan:\n" " pkcheck [PILIHAN...]\n" "\n" "Bantuan Pilihan:\n" " -h, --help Show help options\n" "Pilihan Aplikasi:\n" "-a, --action-id=ACTION Periksa otorisasi untuk melakukan ACTION\n" " -u, --allow-user-interaction Berkomunikasi dengan pengguna jika " "dibutuhkan\n" " -d, --details=KEY VALUE Tambahkan (KEY, VALUE) untuk informasi " "mengenai aksi\n" " --enable-internal-agent Gunakan otentikasi agen internal jika " "dibutuhkan\n" " --list-temp Daftar otorisasi sementara untuk sesi " "ini\n" " -p, --process=PID[,START_TIME,UID] Periksa otorisasi untuk proses yang " "spesifik\n" " --revoke-temp Cabut semua otorisasi sementara untuk " "sesi saat ini\n" " -s, --system-bus-name=BUS_NAME Periksa otorisasi bagi pemilik " "BUS_NAME\n" " --version Tampilkan versi\n" "\n" "Laporkan kutu ke: %s\n" "halaman beranda %s: <%s>\n" #: ../src/programs/pkcheck.c:393 ../src/programs/pkcheck.c:426 #: ../src/programs/pkcheck.c:438 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Argumen diharapkan setelah `%s'\n" #: ../src/programs/pkcheck.c:416 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: kesalahan nilai --process `%s'\n" #: ../src/programs/pkcheck.c:453 ../src/programs/pkcheck.c:462 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: Dua argumen diharapkan setelah `--detail'\n" #: ../src/programs/pkcheck.c:523 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Subyek tidak dispesifikasikan\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:790 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Otentikasi dibutuhkan untuk menjalankan `$(cmdline_short)' sebagai super user" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:800 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Otentikasi dibutuhkan untuk menjalankan `$(cmdline_short)' sebagai pengguna $(user." "display)" #: ../src/programs/pkttyagent.c:78 msgid "Don't replace existing agent if any" msgstr "Jangan ganti agen yang sudah ada" #: ../src/programs/pkttyagent.c:82 msgid "Close FD when the agent is registered" msgstr "Tutup FD ketika agen sudah terdaftar" #: ../src/programs/pkttyagent.c:82 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:86 msgid "Register the agent for the specified process" msgstr "Daftarkan agen untuk proses yang spesifik" #: ../src/programs/pkttyagent.c:87 msgid "PID[,START_TIME]" msgstr "PID,[,START_TIME]" #: ../src/programs/pkttyagent.c:91 msgid "Register the agent for the owner of BUS_NAME" msgstr "Daftarkan agen bagi pemilik BUS_NAME" #: ../src/programs/pkttyagent.c:91 msgid "BUS_NAME" msgstr "BUS_NAME" #: ../src/programs/pkttyagent.c:164 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Kesalahan spesifikasi proses `%s'\n" polkit-126/po/it.po000066400000000000000000000165431474122443600143000ustar00rootroot00000000000000# Italian translation for polkit. # Copyright (C) 2018 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Milo Casagrande , 2018. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2018-09-11 03:25+0000\n" "PO-Revision-Date: 2018-09-11 11:51+0200\n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Last-Translator: Milo Casagrande \n" "X-Generator: Poedit 2.1.1\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Lancia un programma come un altro utente" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "" "È richiesto autenticarsi per lanciare un programma come un altro utente" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Lancia il programma di esempio Frobnicate" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "È richiesto autenticarsi per lanciare il programma di esempio Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Stampa informazioni solo riguardo all'AZIONE" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "AZIONE" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Stampa informazioni dettagliate dell'azione" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:62 msgid "Show version" msgstr "Mostra la versione" #: ../src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id AZIONE]" #: ../src/programs/pkaction.c:133 ../src/programs/pkttyagent.c:83 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Segnalare problemi a: %s\n" "Sito web di %s: <%s>" #: ../src/programs/pkaction.c:147 ../src/programs/pkcheck.c:493 #: ../src/programs/pkttyagent.c:97 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: argomento «%s» non atteso\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Uso:\n" " pkcheck [OPZIONE...]\n" "\n" "Opzioni d'aiuto:\n" " -h, --help Mostra opzioni di aiuto\n" "\n" "Opzioni dell'applicazione:\n" " -a, --action-id=AZIONE Controlla l'autorizzazione per " "eseguire\n" " AZIONE\n" " -u, --allow-user-interaction Interagisce con l'utente, se " "necessario\n" " -d, --details=CHIAVE VALORE Aggiunge (CHIAVE, VALORE) alle " "informazioni\n" " riguardo all'azione\n" " --enable-internal-agent Usa un agente di autenticazione " "interna,\n" " se necessario\n" " --list-temp Elenca autorizzazione temporane per la\n" " sessione corrente\n" " -p, --process=PID[,START_TIME,UID] Controlla l'autorizzazione del " "processo\n" " specificato\n" " --revoke-temp Revoca tutte le autorizzazione " "temporane\n" " per la sessione corrente\n" " -s, --system-bus-name=NOME_BUS Controlla l'autorizzazione per il\n" " proprietario di NOME_BUS\n" " --version Mostra la versione\n" "\n" "Segnalare problemi a: %s\n" "Sito web di %s: <%s>\n" #: ../src/programs/pkcheck.c:393 ../src/programs/pkcheck.c:426 #: ../src/programs/pkcheck.c:438 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: atteso un argomento dopo «%s»\n" #: ../src/programs/pkcheck.c:416 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: valore «%s» di --process non valido\n" #: ../src/programs/pkcheck.c:453 ../src/programs/pkcheck.c:462 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: attesi due argomenti dopo «--detail»\n" #: ../src/programs/pkcheck.c:523 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: oggetto non specificato\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:790 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "È richiesto autenticarsi per lanciare «$(cmdline_short)» come super utente" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:800 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "È richiesto autenticarsi per lanciare «$(cmdline_short)» come l'utente $(user." "display)" #: ../src/programs/pkttyagent.c:45 msgid "Don't replace existing agent if any" msgstr "Non sostituisce l'agente esistente" #: ../src/programs/pkttyagent.c:49 msgid "Close FD when the agent is registered" msgstr "Chiude il FD quando l'agente viene registrato" #: ../src/programs/pkttyagent.c:49 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:53 msgid "Register the agent for the specified process" msgstr "Registra l'agente per il processo specificato" #: ../src/programs/pkttyagent.c:54 msgid "PID[,START_TIME]" msgstr "PID[,TEMPO_INIZIO]" #: ../src/programs/pkttyagent.c:58 msgid "Register the agent for the owner of BUS_NAME" msgstr "Registra l'agente per il proprietario di NOME_BUS" #: ../src/programs/pkttyagent.c:58 msgid "BUS_NAME" msgstr "NOME_BUS" #: ../src/programs/pkttyagent.c:129 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: indicatore di processo «%s» non valido\n" polkit-126/po/ka.po000066400000000000000000000222621474122443600142520ustar00rootroot00000000000000# Georgian translation of polkit # Copyright (C) 2023 Polkit's authors # This file is distributed under the same license as the polkit package. # Temuri Doghonadze , 2023. # msgid "" msgstr "" "Project-Id-Version: polkit\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-08-07 21:51+0200\n" "PO-Revision-Date: 2023-08-07 21:55+0200\n" "Last-Translator: Temuri Doghonadze \n" "Language-Team: Georgian <(nothing)>\n" "Language: ka\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.3.2\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "პროგრამის სხვა სახელით გაშვება" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "პროგრამის სხვა სახელით გაშვებას ავთენტიკაცია სჭირდება" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "გაუშვით polkit-ის მაგალითი Frobnicate" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Polkit მაგალითის პროგრამის Frobnicate-ის გასაშვებად საჭიროა ავტორიზაცია " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "ინფორმაციის მხოლოდ ACTION-ის შესახებ გამოტანა" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "[ქმედება]" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "ქმედების დეტალური ინფორმაციის გამოტანა" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "ვერსიის ჩვენება" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id ACTION]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "შეცდომებზე მისწერეთ: %s\n" "%s ვებგვერდი: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: მოულოდნელი არგუმენტი `%s'\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "გამოყენება:\n" " pkcheck [OPTION...]\n" "\n" "დახმარების პარამეტრები:\n" " -h, --help დახმარების პარამეტრების ჩვენება\n" "\n" "განაცხადის პარამეტრები:\n" " -a, --action-id=ACTION ACTION-ის შესასრულებლად ავტორიზაციის შემოწმება \n" " -u, --allow-user-interaction საჭიროების შემთხვევაში მომხმარებელთან " "ურთერთობა\n" " -d, --details=KEY VALUE ქმედების შესახებ ინფორმაციისთვის (KEY, VALUE) " "დამატება\n" " --enable-internal-agent საჭიროების შემთხვევაში შიდა ავთენტიფიკაციის " "აგენტის გამოყენება\n" " --list-temp მიმდინარე სესიისთვის დროებითი ავტორიზაციების სია \n" " -p, --process=PID[,START_TIME,UID] მითითებული პროცესის ავტორიზაციის " "შემოწმება\n" " --revoke-temp მიმდინარე სესიისთვის ყველა დროებითი ავტორიზაციის გაუქმება \n" " -s, --system-bus-name=BUS_NAME BUS_NAME-ის მფლობელის ავტორიზაციის " "შემოწმება\n" " --version ვერსიის ჩვენება\n" "\n" "შეატყობინეთ შეცდომების შესახებ: %s\n" "%s მთავარი გვერდი: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: `%s'-ის შემდეგ მოველოდი არგუმენტს\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: --process -ის არასწორი მნიშვნელობა `%s'\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: `--detail'-ის და '-d-ის შემდეგ საჭიროა ორი არგუმენტის მითითება\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: თემა მითითებული არაა\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:821 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "'$(cmdlne_short)'-ის ზემომხმარებლით გაშვებას ავთენტიკაცია სჭირდება" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:831 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "'$(cmdlne_short)'-ის მომხმარებლით '$(user.display)' გაშვებას ავთენტიკაცია " "სჭირდება" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "თუ აგენტი არსებობს, ის არ ჩანაცვლდება" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "FD-ის დახურვა აგენტის რეგისტრაციის შემდეგ" #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "აგენტის რეგისტრაცია მითითებული პროცესისთვის" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,START_TIME]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "აგენტის რეგისტრაცია BUS_NAME-ის მფლობელისთვის" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "BUS_NAME" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "%s: პარამეტრები --process და --system-bus-name ურთიერთგამომრიცხავია\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: პროცესის არასწორი მიმთითებელი: %s\n" polkit-126/po/meson.build000066400000000000000000000001101474122443600154450ustar00rootroot00000000000000i18n.gettext( pk_api_name, data_dirs: data_dir, preset: 'glib', ) polkit-126/po/nl.po000066400000000000000000000157261474122443600142770ustar00rootroot00000000000000# Dutch translation for polkit. # Copyright (C) 2022 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Nathan Follens , 2022. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2022-03-27 15:28+0000\n" "PO-Revision-Date: 2022-03-27 20:29+0200\n" "Last-Translator: Nathan Follens \n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.0.1\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Voer een programma uit als een andere gebruiker" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "" "Authenticatie is vereist om een programma als een andere gebruiker uit te " "voeren" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "Polkit-voorbeeldprogramma Frobnicate uitvoeren" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Authenticatie is vereist om het Polkit-voorbeeldprogramma Frobnicate uit te " "voeren (user=$(user), user.gecos=$(user.gecos), user.display=$(user." "display), program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Enkel informatie over ACTIE uitvoeren" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "ACTIE" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Gedetailleerde actie-informatie tonen" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:95 msgid "Show version" msgstr "Versie tonen" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id ACTIE]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:118 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Gelieve problemen te melden aan: %s\n" "%s projectwebsite: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:132 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Onverwachte parameter `%s'\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Gebruik:\n" " pkcheck [OPTIE...]\n" "\n" "Hulpopties:\n" " -h, --help Hulpopties tonen\n" "\n" "Toepassingsopties:\n" " -a, --action-id=ACTIE Controleer vereiste autorisatie voor " "ACTIE\n" " -u, --allow-user-interaction Interageer indien nodig met de " "gebruiker\n" " -d, --details=SLEUTEL WAARDE Voeg (SLEUTEL, WAARDE) toe aan " "informatie over de actie\n" " --enable-internal-agent Gebruik indien nodig een interne " "authenticatieagent\n" " --list-temp Lijst tijdelijke autorisaties voor " "huidige sessies op\n" " -p, --process=PID[,BEGINTIJD,UID] Controleer autorisatie van opgegeven " "proces\n" " --revoke-temp Trek alle tijdelijke autorisaties voor " "huidige sessie in\n" " -s, --system-bus-name=BUSNAAM Controleer autorisatie van eigenaar van " "BUSNAAM\n" " --version Versie tonen\n" "\n" "Gelieve problemen te melden aan: %s\n" "%s projectwebsite: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Parameter verwacht na `%s'\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Ongeldige --process-waarde `%s'\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: Twee parameters verwacht na `--detail'\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Onderwerp niet opgegeven\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:807 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Authenticatie is vereist om `$(cmdline_short)' uit te voeren als de rootgebruiker" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:817 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Authenticatie is vereist om `$(cmdline_short)' uit te voeren als gebruiker $(user." "display)" #: src/programs/pkttyagent.c:78 msgid "Don't replace existing agent if any" msgstr "Indien er reeds een bestaande agent is, deze niet vervangen" #: src/programs/pkttyagent.c:82 msgid "Close FD when the agent is registered" msgstr "FD sluiten wanneer de agent geregistreerd is" #: src/programs/pkttyagent.c:82 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:86 msgid "Register the agent for the specified process" msgstr "Registreer de agent voor het opgegeven proces" #: src/programs/pkttyagent.c:87 msgid "PID[,START_TIME]" msgstr "PID[,BEGINTIJD]" #: src/programs/pkttyagent.c:91 msgid "Register the agent for the owner of BUS_NAME" msgstr "Registreer de agent voor de eigenaar van BUSNAAM" #: src/programs/pkttyagent.c:91 msgid "BUS_NAME" msgstr "BUSNAAM" #: src/programs/pkttyagent.c:164 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Ongeldige processpecificeerder `%s'\n" polkit-126/po/nn.po000066400000000000000000000157501474122443600142760ustar00rootroot00000000000000# Norwegian Nynorsk translation for polkit. # Copyright (C) 2020 Karl Ove Hufthammer # This file is distributed under the same license as the polkit package. # Karl Ove Hufthammer , 2020. # msgid "" msgstr "" "Project-Id-Version: polkit\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-05-09 18:09+0200\n" "PO-Revision-Date: 2020-05-09 18:42+0200\n" "Last-Translator: Karl Ove Hufthammer \n" "Language-Team: Norwegian Nynorsk \n" "Language: nn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 20.04.0\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Køyr program som ein annan brukar" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "Krev autentisering for å køyra program som ein annan brukar" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Køyr polkit sitt eksempel­program Frobnicate" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Krev autentisering for å køyra polkit sitt eksempel­program Frobnicate (user=" "$(user), user.gecos=$(user.gecos), user.display=$(user.display), program=" "$(program), command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Vis berre informasjon om HANDLING" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "HANDLING" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Vis detaljert handlingsinformasjon" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:95 msgid "Show version" msgstr "Vis programversjon" #: ../src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id HANDLING]" #: ../src/programs/pkaction.c:133 ../src/programs/pkttyagent.c:118 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Meld frå om feil til: %s\n" "Heimesida til %s: <%s>" #: ../src/programs/pkaction.c:147 ../src/programs/pkcheck.c:493 #: ../src/programs/pkttyagent.c:132 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Uventa argument «%s»\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "BRUK:\n" " pkcheck [VAL …]\n" "\n" "Hjelpeval:\n" " -h, --help Vis argumentoversikt\n" "\n" "Programval:\n" " -a, --action-id=HANDLING Kontroller godkjenning for utføring av " "HANDLING\n" " -u, --allow-user-interaction Samhandla om nødvendig med brukaren\n" " -d, --details=NØKKEL VERDI Legg (NØKKEL, VERDI) til informasjon om " "handlinga\n" " --enable-internal-agent Bruk om nødvendig intern autentiserings­" "agent\n" " --list-temp Vis oversikt over mellombelse " "godkjenningar for gjeldande økt\n" " -p, --process=PID[,STARTTID,UID] Kontroller godkjenning til vald " "prosess\n" " --revoke-temp Kall tilbake alle mellombelse " "godkjenningar for gjeldande økt\n" " -s, --system-bus-name=BUSSNAMN Kontroller godkjenning til eigaren av " "BUSSNAMN\n" " --version Vis programversjon\n" "\n" "Meld frå om feil til: %s\n" "Heimesida til %s: <%s>\n" #: ../src/programs/pkcheck.c:393 ../src/programs/pkcheck.c:426 #: ../src/programs/pkcheck.c:438 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Venta argument etter «%s»\n" #: ../src/programs/pkcheck.c:416 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Ugyldig verdi til «--process»: «%s»\n" #: ../src/programs/pkcheck.c:453 ../src/programs/pkcheck.c:462 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: Venta to argument etter «--detail»\n" # subject = prosess (program) #: ../src/programs/pkcheck.c:523 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Prosess må oppgjevast\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:790 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "Krev autentisering for å køyra «$(cmdline_short)» som rotbrukar" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:800 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Krev autentisering for å køyra «$(cmdline_short)» som brukaren $(user.display)" # skip-rule: eksistera #: ../src/programs/pkttyagent.c:78 msgid "Don't replace existing agent if any" msgstr "Ikkje erstatt eventuell eksisterande agent" # FD = fildeskriptor #: ../src/programs/pkttyagent.c:82 msgid "Close FD when the agent is registered" msgstr "Lukk FD når agenten er registrert" #: ../src/programs/pkttyagent.c:82 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:86 msgid "Register the agent for the specified process" msgstr "Registrer agenten for den valde prosessen" #: ../src/programs/pkttyagent.c:87 msgid "PID[,START_TIME]" msgstr "PID[,STARTTID]" #: ../src/programs/pkttyagent.c:91 msgid "Register the agent for the owner of BUS_NAME" msgstr "Registrer agenten for eigaren til BUSSNAMN" #: ../src/programs/pkttyagent.c:91 msgid "BUS_NAME" msgstr "BUSSNAMN" #: ../src/programs/pkttyagent.c:164 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Ugyldig prosess-spesifikasjon «%s»\n" polkit-126/po/pl.po000066400000000000000000000175331474122443600142770ustar00rootroot00000000000000# Polish translation for polkit. # Copyright © 2010-2018, 2023 the polkit authors. # This file is distributed under the same license as the polkit package. # Piotr Drąg , 2010-2018, 2023. # Aviary.pl , 2018, 2023. # msgid "" msgstr "" "Project-Id-Version: polkit\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2023-06-20 15:27+0000\n" "PO-Revision-Date: 2023-07-16 12:44+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Uruchomienie programu jako inny użytkownik" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "" "Wymagane jest uwierzytelnienie, aby uruchomić program jako inny użytkownik" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "Uruchomienie przykładowego programu polkit „Frobnicate”" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Wymagane jest uwierzytelnienie, aby uruchomić przykładowy program polkit " "„Frobnicate” (user=$(user), user.gecos=$(user.gecos), user.display=$(user." "display), program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Wyświetla tylko informacje o DZIAŁANIU" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "DZIAŁANIE" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Wyświetla szczegółowe informacje o działaniu" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "Wyświetla wersję" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id DZIAŁANIE]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Prosimy zgłaszać błędy na (w języku angielskim): %s\n" "Strona domowa programu %s: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: nieoczekiwany parametr „%s”\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Użycie:\n" " pkcheck [OPCJA…]\n" "\n" "Opcje pomocy:\n" " -h, --help Wyświetla opcje pomocy\n" "\n" "Opcje programu:\n" " -a, --action-id=DZIAŁANIE Sprawdza upoważnienie do wykonania\n" " DZIAŁANIA\n" " -u, --allow-user-interaction Pyta użytkownika, jeśli trzeba\n" " -d, --details=KLUCZ WARTOŚĆ Dodaje (KLUCZ, WARTOŚĆ) do\n" " informacji o działaniu\n" " --enable-internal-agent Używa wewnętrznego agenta\n" " uwierzytelniania, jeśli trzeba\n" " --list-temp Wyświetla listę tymczasowych\n" " upoważnień dla bieżącej sesji\n" " -p, --process=PID[,CZAS_POCZĄTKOWY,UID] Sprawdza upoważnienie podanego\n" " procesu\n" " --revoke-temp Unieważnia wszystkie tymczasowe\n" " upoważnienia dla bieżącej sesji\n" " -s, --system-bus-name=NAZWA_MAGISTRALI Sprawdza upoważnienie właściciela\n" " NAZWY_MAGISTRALI\n" " --version Wyświetla wersję\n" "\n" "Prosimy zgłaszać błędy na (w języku angielskim): %s\n" "Strona domowa programu %s: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: oczekiwano parametru po opcji „%s”\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: nieprawidłowa wartość „%s” opcji --process\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: oczekiwano dwóch parametrów po opcji „--detail, -d”\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: nie podano tematu\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:821 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Wymagane jest uwierzytelnienie, aby uruchomić program „$(cmdline_short)” " "jako superużytkownik" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:831 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Wymagane jest uwierzytelnienie, aby uruchomić program „$(cmdline_short)” " "jako użytkownik $(user.display)" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "Bez zastępowania istniejących agentów, jeśli jakieś są" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "Zamyka deskryptor pliku po zarejestrowaniu agenta" #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "DP" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "Rejestruje agenta dla podanego procesu" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,CZAS_POCZĄTKOWY]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "Rejestruje agenta dla właściciela NAZWY_MAGISTRALI" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "NAZWA_MAGISTRALI" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "%s: opcje --process i --system-bus-name wzajemnie się wykluczają\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: nieprawidłowe określenie procesu „%s”\n" polkit-126/po/pt.po000066400000000000000000000166311474122443600143050ustar00rootroot00000000000000# Portuguese translation for polkit. # Copyright © 2010-2018 the polkit authors. # This file is distributed under the same license as the polkit package. # Hugo Carvalho , 2021. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2018-08-20 21:47+0000\n" "PO-Revision-Date: 2021-06-12 16:37+0100\n" "Last-Translator: Hugo Carvalho \n" "Language-Team: Portuguese (https://l10n.gnome.org/teams/pt/)\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Poedit 3.0\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Executa um programa como outro utilizador" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "" "A autenticação é necessária para executar um programa como outro utilizador" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Executa Frobnicate, o programa exemplo do polkit" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "A autenticação é necessária para executar o exemplo de programa do polkit " "Frobnicate (user=$(user), user.gecos=$(user.gecos), user.display=$(user." "display), program=$(program), command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Apresenta apenas informação sobre AÇÃO" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "AÇÃO" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Apresenta informação detalhada da ação" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:62 msgid "Show version" msgstr "Mostra a versão" #: ../src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id AÇÃO]" #: ../src/programs/pkaction.c:133 ../src/programs/pkttyagent.c:83 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Relate erros para: %s\n" "Página web do %s: <%s>" #: ../src/programs/pkaction.c:147 ../src/programs/pkcheck.c:493 #: ../src/programs/pkttyagent.c:97 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Argumento inesperado '%s'\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Utilização:\n" " pkcheck [OPÇÃO...]\n" "\n" "Opções de ajuda:\n" " -h, --help Mostra as opções de ajuda\n" "\n" "Opções da aplicação:\n" " -a, --action-id=AÇÃO Verifica autorização para realizar " "AÇÃO\n" " -u, --allow-user-interaction Interage com o utilizador, se " "necessário\n" " -d, --details=CHAVE VALOR Adiciona (CHAVE, VALOR) à informação\n" " sobre a ação\n" " --enable-internal-agent Usa um agente de autenticação interno,\n" " se necessário\n" " --list-temp Lista autorizações temporárias para a\n" " sessão atual\n" " -p, --process=PID[,START_TIME,UID] Verifica autorização do processo\n" " especificado\n" " --revoke-temp Revoga todas as autorizações\n" " temporárias para sessão atual\n" " -s, --system-bus-name=BARRAMENTO Verifica a autorização do proprietário " "do\n" " BARRAMENTO\n" " --version Mostra a versão\n" "\n" "Relate erros para: %s\n" "Página web do %s: <%s>\n" #: ../src/programs/pkcheck.c:393 ../src/programs/pkcheck.c:426 #: ../src/programs/pkcheck.c:438 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Esperava argumento após '%s'\n" #: ../src/programs/pkcheck.c:416 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Valor '%s' inválido de --process\n" #: ../src/programs/pkcheck.c:453 ../src/programs/pkcheck.c:462 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: Dois argumentos esperados após '--detail'\n" #: ../src/programs/pkcheck.c:523 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Sujeito não especificado\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:790 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "A autenticação é necessária para executar `$(cmdline_short)' como o superutilizador" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:800 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "A autenticação é necessária para executar `$(cmdline_short)' como o utilizador " "$(user.display)" #: ../src/programs/pkttyagent.c:45 msgid "Don't replace existing agent if any" msgstr "Não substitui o agente existente, seu houver" #: ../src/programs/pkttyagent.c:49 msgid "Close FD when the agent is registered" msgstr "Fecha o descritor de ficheiro FD quando o agente é registado" #: ../src/programs/pkttyagent.c:49 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:53 msgid "Register the agent for the specified process" msgstr "Regista o agente para o processo especificado" #: ../src/programs/pkttyagent.c:54 msgid "PID[,START_TIME]" msgstr "PID[,HORÁRIO_INÍCIO]" #: ../src/programs/pkttyagent.c:58 msgid "Register the agent for the owner of BUS_NAME" msgstr "Regista o agente para o proprietário do BARRAMENTO" #: ../src/programs/pkttyagent.c:58 msgid "BUS_NAME" msgstr "BARRAMENTO" #: ../src/programs/pkttyagent.c:129 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Especificador do processo '%s' inválido\n" polkit-126/po/pt_BR.po000066400000000000000000000170031474122443600146620ustar00rootroot00000000000000# Brazilian Portuguese translation for polkit. # Copyright (C) 2024 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Rafael Fontenelle , 2015-2024. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://github.com/polkit-org/polkit/issues\n" "POT-Creation-Date: 2024-03-19 15:27+0000\n" "PO-Revision-Date: 2024-10-09 22:44-0300\n" "Last-Translator: Rafael Fontenelle \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "X-Generator: Gtranslator 47.0\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Executar um programa como outro usuário" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "" "A autenticação é necessária para executar um programa como outro usuário" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "Executar Frobnicate, o programa exemplo do polkit" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "A autenticação é necessária para executar o exemplo de programa do polkit " "Frobnicate (user=$(user), user.gecos=$(user.gecos), user.display=$(user." "display), program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Exibe apenas informação sobre AÇÃO" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "AÇÃO" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Exibe informação detalhada da ação" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "Mostra a versão" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id AÇÃO]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Relate erros para: %s\n" "Página web do %s: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Argumento inesperado \"%s\"\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Uso:\n" " pkcheck [OPÇÃO...]\n" "\n" "Opções de ajuda:\n" " -h, --help Mostra as opções de ajuda\n" "\n" "Opções do aplicativo:\n" " -a, --action-id=AÇÃO Verifica autorização para realizar " "AÇÃO\n" " -u, --allow-user-interaction Interage com o usuário, se necessário\n" " -d, --details=CHAVE VALOR Adiciona (CHAVE, VALOR) à informação\n" " sobre a ação\n" " --enable-internal-agent Usa um agente de autenticação interno,\n" " se necessário\n" " --list-temp Lista autorizações temporárias para a\n" " sessão atual\n" " -p, --process=PID[,START_TIME,UID] Verifica autorização do processo\n" " especificado\n" " --revoke-temp Revoga todas as autorizações\n" " temporárias para sessão atual\n" " -s, --system-bus-name=BARRAMENTO Verifica a autorização do dono do\n" " BARRAMENTO\n" " --version Mostra a versão\n" "\n" "Relate erros para: %s\n" "Página web do %s: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Esperava argumento após \"%s\"\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Valor \"%s\" inválido de --process\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: Dois argumentos esperados após \"--detail, -d\"\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Sujeito não especificado\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:822 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "A autenticação é necessária para executar `$(cmdline_short)' como o " "superusuário" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:832 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "A autenticação é necessária para executar `$(cmdline_short)' como o usuário " "$(user.display)" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "Não substitui o agente existente, seu houver" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "Fecha o descritor de arquivo FD quando o agente é registrado" #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "Registra o agente para o processo especificado" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,HORÁRIO_INÍCIO]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "Registra o agente para o dono do BARRAMENTO" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "BARRAMENTO" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "%s: As opções --process e --system-bus-name são mutuamente exclusivas\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Especificador do processo \"%s\" inválido\n" polkit-126/po/ro.po000066400000000000000000000163111474122443600142750ustar00rootroot00000000000000# Romanian translation for polkit # Copyright (C) 2021 polkit authors # This file is distributed under the same license as the polkit package. # # Sergiu Bivol , 2021. msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-06-03 22:41+0100\n" "PO-Revision-Date: 2021-06-04 00:15+0100\n" "Last-Translator: Sergiu Bivol \n" "Language-Team: Romanian\n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 <" " 20)) ? 1 : 2;\n" "X-Generator: Lokalize 19.12.3\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Rulează un program ca alt utilizator" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "" "Este necesară autentificarea pentru a rula un program ca alt utilizator" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Rulează programul polkit exemplificator Frobnicate" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Este necesară autentificarea pentru a rula programul polkit exemplificator" " Frobnicate (user=$(user), user.gecos=$(user.gecos)," " user.display=$(user.display), program=$(program)," " command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Afișează numai informații despre ACȚIUNE" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "ACȚIUNE" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Afișează informații detaliate despre acțiune" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:95 msgid "Show version" msgstr "Arată versiunea" #: ../src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id ACȚIUNE]" #: ../src/programs/pkaction.c:133 ../src/programs/pkttyagent.c:118 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Raportați defecte la: %s\n" "Pagina %s: <%s>" #: ../src/programs/pkaction.c:147 ../src/programs/pkcheck.c:493 #: ../src/programs/pkttyagent.c:132 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Argument neașteptat „%s”\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Utilizare:\n" " pkcheck [OPȚIUNE...]\n" "\n" "Opțiuni pentru ajutor:\n" " -h, --help Arată opțiunile pentru ajutor\n" "\n" "Opțiuni pentru aplicație:\n" " -a, --action-id=ACȚIUNE Verifică autorizarea pentru efectuarea" " ACȚIUNII\n" " -u, --allow-user-interaction Interacționează cu utilizatorul dacă e" " necesar\n" " -d, --details=CHEIE VALOARE Adaugă (CHEIE, VALOARE) la informațiile" " despre acțiune\n" " --enable-internal-agent Folosește un agent de autentificare" " intern dacă e necesar\n" " --list-temp Enumeră autorizările temporare pentru" " sesiunea actuală\n" " -p, --process=PID[,TIMP_PORNIRE,UID] Verifică autorizarea procesului" " specificat\n" " --revoke-temp Revocă toate autorizările temporare din" " sesiunea actuală\n" " -s, --system-bus-name=DENUMIRE_MAGISTRALĂ " " Verifică autorizarea proprietarului" " DENUMIRE_MAGISTRALĂ\n" " --version Arată versiunea\n" "\n" "Raportați defecte la: %s\n" "Pagina %s: <%s>\n" #: ../src/programs/pkcheck.c:393 ../src/programs/pkcheck.c:426 #: ../src/programs/pkcheck.c:438 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Argument așteptat după „%s”\n" #: ../src/programs/pkcheck.c:416 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Valoare nevalidă pentru --process „%s”\n" #: ../src/programs/pkcheck.c:453 ../src/programs/pkcheck.c:462 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: Două argumente așteptate după „--detail”\n" #: ../src/programs/pkcheck.c:523 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Subiect nespecificat\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:790 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Este necesară autentificarea pentru a rula „$(cmdline_short)” ca administrator" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:800 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Este necesară autentificarea pentru a rula „$(cmdline_short)” ca utilizatorul" " $(user.display)" #: ../src/programs/pkttyagent.c:78 msgid "Don't replace existing agent if any" msgstr "Nu înlocui agentul existent dacă există" #: ../src/programs/pkttyagent.c:82 msgid "Close FD when the agent is registered" msgstr "Închide descriptorul de fișier la înregistrarea agentului" #: ../src/programs/pkttyagent.c:82 msgid "FD" msgstr "DF" #: ../src/programs/pkttyagent.c:86 msgid "Register the agent for the specified process" msgstr "Înregistrează agentul pentru procesul specificat" #: ../src/programs/pkttyagent.c:87 msgid "PID[,START_TIME]" msgstr "PID[,TIMP_PORNIRE]" #: ../src/programs/pkttyagent.c:91 msgid "Register the agent for the owner of BUS_NAME" msgstr "Înregistrează agentul pentru proprietarul DENUMIRE_MAGISTRALĂ" #: ../src/programs/pkttyagent.c:91 msgid "BUS_NAME" msgstr "DENUMIRE_MAGISTRALĂ" #: ../src/programs/pkttyagent.c:164 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Specificator de proces nevalid „%s”\n" polkit-126/po/ru.po000066400000000000000000000210061474122443600143000ustar00rootroot00000000000000# Russian translation for polkit. # Copyright (C) 2023 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Aleksandr Melman , 2023. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2023-11-28 03:27+0000\n" "PO-Revision-Date: 2023-11-28 14:40+0300\n" "Last-Translator: Aleksandr Melman \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.4.1\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Запуск программы от имени другого пользователя" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "" "Требуется аутентификация для запуска программы от имени другого пользователя" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "Запуск программы Frobnicate для примера polkit" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Требуется аутентификация для запуска программы Frobnicate для примера polkit " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Выводить информацию только о действии" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "ДЕЙСТВИЕ" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Вывод подробной информации о действии" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "Показать версию" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id ДЕЙСТВИЕ]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Сообщить об ошибках: %s\n" "%s домашняя страница: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Неожиданный аргумент `%s'\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Использование:\n" " pkcheck [ПАРАМЕТР...]\n" "\n" "Параметры справки:\n" " -h, --help Показать параметры справки\n" "\n" "Параметры приложения:\n" " -a, --action-id=ACTION Проверить авторизацию для выполнения " "действия\n" " -u, --allow-user-interaction Взаимодействовать с пользователем при " "необходимости\n" " -d, --details=KEY VALUE Добавить (ключ, значение) к информации " "о действии\n" " --enable-internal-agent Использовать внутренний агент " "аутентификации при необходимости\n" " --list-temp Список временных авторизаций для " "текущего сеанса\n" " -p, --process=PID[,START_TIME,UID] Проверить авторизацию указанного " "процесса\n" " --revoke-temp Отменить все временные авторизации для " "текущего сеанса\n" " -s, --system-bus-name=BUS_NAME Проверить авторизацию владельца " "имени_шины\n" " --version Показать версию\n" "\n" "Сообщить об ошибках: %s\n" "%s домашняя страница: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Аргумент ожидается после `%s'\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Недопустимое значение --process `%s'\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: Ожидается два аргумента после `--detail, -d'\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Субъект не указан\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:822 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Необходима аутентификация для запуска `$(cmdline_short)' от имени " "суперпользователя" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:832 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Необходима аутентификация для запуска `$(cmdline_short)' от имени " "пользователя $(user.display)" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "Не заменять существующего агента, если таковой имеется" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "Закрыть FD, когда агент зарегистрирован" #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "Зарегистрировать агента для указанного процесса" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,ВРЕМЯ_НАЧАЛА]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "Зарегистрировать агента для владельца имени_шины" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "ИМЯ_ШИНЫ" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "" "%s: Параметры --process и --system-bus-name являются взаимоисключающими\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Недопустимый спецификатор процесса `%s'\n" polkit-126/po/sk.po000066400000000000000000000166461474122443600143050ustar00rootroot00000000000000# Slovak translation for polkit. # Copyright (C) 2016 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Dušan Kazik , 2016. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?" "product=PolicyKit&keywords=I18N+L10N&component=libpolkit\n" "POT-Creation-Date: 2016-08-08 02:28+0000\n" "PO-Revision-Date: 2016-08-08 10:49+0200\n" "Language-Team: Slovak \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" "Last-Translator: Dušan Kazik \n" "X-Generator: Poedit 1.8.8\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Spustenie programu ako iný používateľ" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "" "Na spustenie programu ako iný používateľ sa vyžaduje overenie totožnosti" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Spustenie vzorového programu Frobnicate nástroja polkit" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Na spustenie vzorového programu Frobnicate nástroja polkit sa vyžaduje " "overenie totožnosti (user=$(user), user.gecos=$(user.gecos), user.display=" "$(user.display), program=$(program), command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Iba výstup informácií o AKCII" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "AKCIA" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Podrobný výstup informácií o AKCII" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:62 msgid "Show version" msgstr "Zobrazí verziu" #: ../src/programs/pkaction.c:134 msgid "[--action-id ACTION]" msgstr "[--action-id AKCIA]" #: ../src/programs/pkaction.c:135 ../src/programs/pkttyagent.c:85 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Chyby nahláste na: %s\n" "Domovská stránka balíka %s: <%s>" #: ../src/programs/pkaction.c:149 ../src/programs/pkcheck.c:495 #: ../src/programs/pkttyagent.c:99 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Neočakávaný parameter „%s“\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Použitie:\n" " pkcheck [VOĽBA...]\n" "\n" "Voľby pomocníka:\n" " -h, --help Zobrazí voľby pomocníka\n" "\n" "Voľby aplikácie:\n" " -a, --action-id=AKCIA Skontroluje overenie totožnosti na " "vykonanie AKCIE\n" " -u, --allow-user-interaction Umožní interakciu s používateľom, ak je " "to potrebné\n" " -d, --details=KĽÚČ HODNOTA Pridá parametre (KĽÚČ, HODNOTA) do " "informácií o akcii\n" " --enable-internal-agent Použije vnútorného agenta overenia " "totožnosti, ak je to potrebné\n" " --list-temp Vypíše dočasné overenia totožností pre " "aktuálnu reláciu\n" " -p, --process=PID[,ČAS_SPUSTENIA,UID] Skontroluje overenie totožnosti " "určeného procesu\n" " --revoke-temp Odvolá všetky dočasné overenia " "totožností pre aktuálnu reláciu\n" " -s, --system-bus-name=NÁZOV_ZBERNICE Skontroluje overenie totožnosti " "vlastníka definovaného premennou NÁZOV_ZBERNICE\n" " --version Zobrazí verziu\n" "\n" "Chyby nahláste na: %s\n" "Domovská stránka balíka %s: <%s>\n" #: ../src/programs/pkcheck.c:395 ../src/programs/pkcheck.c:428 #: ../src/programs/pkcheck.c:440 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Očakával sa parameter po voľbe „%s“\n" #: ../src/programs/pkcheck.c:418 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Neplatný parameter voľby --process value „%s“\n" #: ../src/programs/pkcheck.c:455 ../src/programs/pkcheck.c:464 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: Očakávali sa dva parametre po voľbe „--detail“\n" #: ../src/programs/pkcheck.c:525 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Predmet nebol určený\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:797 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Na spustenie programu „$(cmdline_short)“ ako superpoužívateľ sa vyžaduje overenie " "totožnosti" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:807 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Na spustenie programu „$(cmdline_short)“ ako používateľ $(user.display) sa " "vyžaduje overenie totožnosti" #: ../src/programs/pkttyagent.c:45 msgid "Don't replace existing agent if any" msgstr "Nenahradí existujúceho agenta, ak nejaký existuje" #: ../src/programs/pkttyagent.c:49 msgid "Close FD when the agent is registered" msgstr "Zavrie FD, keď je agent zaregistrovaný" #: ../src/programs/pkttyagent.c:49 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:53 msgid "Register the agent for the specified process" msgstr "Zaregistruje agenta pre určený proces" #: ../src/programs/pkttyagent.c:54 msgid "PID[,START_TIME]" msgstr "PID[,ČAS_SPUSTENIA]" #: ../src/programs/pkttyagent.c:58 msgid "Register the agent owner of BUS_NAME" msgstr "" "Zaregistruje agenta pre vlastníka definovaného premennou NÁZOV_ZBERNICE" #: ../src/programs/pkttyagent.c:58 msgid "BUS_NAME" msgstr "NÁZOV_ZBERNICE" #: ../src/programs/pkttyagent.c:131 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Neplatný určovateľ procesu „%s“\n" polkit-126/po/sl.po000066400000000000000000000163611474122443600143000ustar00rootroot00000000000000# Slovenian translation for polkit. # Copyright (C) 2024 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # # Martin , 2024. # msgid "" msgstr "" "Project-Id-Version: polkit main\n" "Report-Msgid-Bugs-To: https://github.com/polkit-org/polkit/issues\n" "POT-Creation-Date: 2024-07-26 15:27+0000\n" "PO-Revision-Date: 2024-07-26 18:40+0200\n" "Language-Team: Slovenian GNOME Translation Team \n" "Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n" "%100==4 ? 3 : 0);\n" "Last-Translator: Martin Srebotnjak \n" "X-Generator: Poedit 2.2.1\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Zaženite program kot drug uporabnik" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "" "Preverjanje pristnosti je potrebno, da bi lahko zagnali program kot drug " "uporabnik" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "Zaženite primer programa polkit, Frobnicate" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Preverjanje pristnosti je potrebno za zagon primera programa polkit " "Frobnicate (user=$(user), user.gecos=$(user.gecos), user.display=$(user." "display), program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:97 msgid "Only output information about ACTION" msgstr "Samo izhodne informacije o funkciji DEJANJE" #: src/programs/pkaction.c:97 msgid "ACTION" msgstr "DEJANJE" #: src/programs/pkaction.c:101 msgid "Output detailed action information" msgstr "Izpiši podrobne informacije o dejanjih" #: src/programs/pkaction.c:105 src/programs/pkttyagent.c:100 msgid "Show version" msgstr "Prikaži različico" #: src/programs/pkaction.c:128 msgid "[--action-id ACTION]" msgstr "[--action-id DEJANJE]" #: src/programs/pkaction.c:129 src/programs/pkttyagent.c:123 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Prijavite napake na: %s\n" "domačo stran %s: <%s>" #: src/programs/pkaction.c:143 src/programs/pkcheck.c:494 #: src/programs/pkttyagent.c:137 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: nepričakovan argument »%s«\n" #: src/programs/pkcheck.c:32 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Navada:\n" " pkcheck [MOŽNOST ...]\n" "\n" "Možnosti pomoči:\n" " -h, --help Pokaži možnosti pomoči\n" "\n" "Možnosti programa:\n" " -a, --action-id=DEJANJE Preveri pooblastilo za izvedbo " "DEJANJA\n" " -u, --allow-user-interaction Po potrebi komuniciraj z uporabnikom\n" " -d, --details=KLJUČ VREDNOST Dodaj (KLJUČ, VREDNOST) " "informacijam o dejanju\n" " --enable-internal-agent Po potrebi uporabite notranjega agenta " "za preverjanje pristnosti\n" " --list-temp Izpiši seznam začasnih pooblastil za " "trenutno sejo\n" " -p, --process=PID[,ČAS_ZAČETKA,UID] Preveri avtorizacijo določenega " "procesa\n" " --revoke-temp Prekliči vsa začasna pooblastila za " "trenutno sejo\n" " -s, --system-bus-name=IME_VODILA Preveri pooblastilo lastnika " "IME_VODILA\n" " --version Pokaži različico\n" "\n" "Prijavite napake na: %s\n" "domačo stran %s: <%s>\n" #: src/programs/pkcheck.c:394 src/programs/pkcheck.c:427 #: src/programs/pkcheck.c:439 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: pričakovan je argument po »%s«\n" #: src/programs/pkcheck.c:417 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: neveljavna vrednost --process »%s«\n" #: src/programs/pkcheck.c:454 src/programs/pkcheck.c:463 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: dva argumenta sta pričakovana po »--detail, -d«\n" #: src/programs/pkcheck.c:524 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: zadeva ni navedena\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:830 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Preverjanje pristnosti je potrebno za zagon `$(cmdline_short)` kot super " "uporabnik" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:840 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Preverjanje pristnosti je potrebno za zagon `$(cmdline_short)` kot uporabnik " "$(user.display)" #: src/programs/pkttyagent.c:83 msgid "Don't replace existing agent if any" msgstr "Ne zamenjaj obstoječega agenta, če obstaja" #: src/programs/pkttyagent.c:87 msgid "Close FD when the agent is registered" msgstr "Zapri FD, ko je agent registriran" #: src/programs/pkttyagent.c:87 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:91 msgid "Register the agent for the specified process" msgstr "Registriraj agenta za določen postopek" #: src/programs/pkttyagent.c:92 msgid "PID[,START_TIME]" msgstr "PID[,ČAS_ZAČETKA]" #: src/programs/pkttyagent.c:96 msgid "Register the agent for the owner of BUS_NAME" msgstr "Registriraj agenta za lastnika vodila IME_VODILA" #: src/programs/pkttyagent.c:96 msgid "BUS_NAME" msgstr "IME_VODILA" #: src/programs/pkttyagent.c:151 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "" "%s: možnosti --process in --system-bus-name se medsebojno izključujeta\n" #: src/programs/pkttyagent.c:175 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: neveljaven določitelj procesa »%s«\n" polkit-126/po/sv.po000066400000000000000000000163111474122443600143050ustar00rootroot00000000000000# Swedish translation for polkit. # Copyright © 2015-2023 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Anders Jonsson , 2015, 2019, 2022, 2023. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2023-06-20 15:27+0000\n" "PO-Revision-Date: 2023-06-20 21:05+0200\n" "Last-Translator: Anders Jonsson \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.2.2\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Kör ett program som en annan användare" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "Autentisering krävs för att köra ett program som en annan användare" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "Kör polkit-exempelprogrammet Frobnicate" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Autentisering krävs för att köra polkit-exempelprogrammet Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Visa endast information om ÅTGÄRD" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "ÅTGÄRD" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Visa detaljerad åtgärdsinformation" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "Visa version" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id ÅTGÄRD]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Rapportera fel till: %s\n" "Webbplats för %s: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Oväntat argument ”%s”\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Användning:\n" " pkcheck [FLAGGA…]\n" "\n" "Hjälpflaggor:\n" " -h, --help Visa hjälpflaggor\n" "\n" "Programflaggor:\n" " -a, --action-id=ÅTGÄRD Kontrollera auktorisering för att " "utföra ÅTGÄRD\n" " -u, --allow-user-interaction Interagera med användaren om " "nödvändigt\n" " -d, --details=NYCKEL VÄRDE Lägg till (NYCKEL, VÄRDE) till " "information om åtgärden\n" " --enable-internal-agent Använd en intern autentiseringsagent om " "nödvändigt\n" " --list-temp Lista tillfälliga auktoriseringar för " "aktuell session\n" " -p, --process=PID[,STARTTID,UID] Kontrollera auktorisering för angiven " "process\n" " --revoke-temp Återkalla alla tillfälliga " "auktoriseringar för aktuell session\n" " -s, --system-bus-name=BUSSNAMN Kontrollera auktorisering för ägare av " "BUSSNAMN\n" " --version Visa version\n" "\n" "Rapportera fel till: %s\n" "Webbplats för %s: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: Argument förväntades efter ”%s”\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Ogiltigt värde ”%s” för --process\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: Två argument förväntades efter ”--detail, -d”\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Subjekt ej angivet\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:821 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Autentisering krävs för att köra ”$(cmdline_short)” som superanvändaren" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:831 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Autentisering krävs för att köra ”$(cmdline_short)” som användaren $(user." "display)" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "Ersätt inte befintlig agent om sådan finns" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "Stäng FD då agenten registrerats" #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "Registrera agenten för den angivna processen" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,STARTTID]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "Registrera agenten för ägaren av BUSSNAMN" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "BUSSNAMN" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "" "%s: Flaggorna --process och --system-bus-name är ömsesidigt uteslutande\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Ogiltig processbeskrivare ”%s”\n" polkit-126/po/tr.po000066400000000000000000000166141474122443600143100ustar00rootroot00000000000000# Turkish translation for polkit. # Copyright (C) 2016-2023 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # # Muhammet Kara , 2016. # Sabri Ünal , 2019, 2023. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2023-08-06 15:27+0000\n" "PO-Revision-Date: 2023-08-07 03:24+0300\n" "Last-Translator: Sabri Ünal \n" "Language-Team: Türkçe \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.2.2\n" #: actions/org.freedesktop.policykit.policy.in:12 msgid "Run a program as another user" msgstr "Bir programı başka bir kullanıcı olarak çalıştırın" #: actions/org.freedesktop.policykit.policy.in:13 msgid "Authentication is required to run a program as another user" msgstr "" "Bir programı başka bir kullanıcı olarak çalıştırmak için kimlik doğrulama " "gerekir" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:10 msgid "Run the polkit example program Frobnicate" msgstr "polkit örnek programı Frobnicate'i çalıştırın" #: src/examples/org.freedesktop.policykit.examples.pkexec.policy.in:11 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "polkit örnek programı Frobnicate'i çalıştırmak için kimlik doğrulama gerekir " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" #: src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Sadece EYLEM hakkındaki bilgileri çıktı verin" #: src/programs/pkaction.c:101 msgid "ACTION" msgstr "EYLEM" #: src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Ayrıntılı eylem bilgilerini çıktı verin" #: src/programs/pkaction.c:109 src/programs/pkttyagent.c:104 msgid "Show version" msgstr "Sürümü göster" #: src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id EYLEM]" #: src/programs/pkaction.c:133 src/programs/pkttyagent.c:127 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Hataları şuraya bildirin: %s\n" "%s ev sayfası: <%s>" #: src/programs/pkaction.c:147 src/programs/pkcheck.c:498 #: src/programs/pkttyagent.c:141 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: Beklenmeyen değişken `%s'\n" #: src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Kullanımı:\n" " pkcheck [SEÇENEK...]\n" "\n" "Yardım Seçenekleri:\n" " -h, --help Yardım seçeneklerini göster\n" "\n" "Uygulama seçenekleri:\n" " -a, --action-id=EYLEM EYLEM'i gerçekleştirmek için " "yetkilendirmeyi kontrol et\n" " -u, --allow-user-interaction Eğer gerekliyse kullanıcı ile " "etkileşime gir\n" " -d, --details=ANAHTAR DEĞER (ANAHTAR, DEĞER)'i eylem hakkındaki " "bilgilere ekle\n" " --enable-internal-agent Eğer gerekirse dahili bir kimlik " "doğrulama vekilini kullan\n" " --list-temp Geçerli oturum için geçici " "yetkilendirmeleri listele\n" " -p, --process=PID[,START_TIME,UID] Belirtilen sürecin yetkilendirmesini " "kontrol et\n" " --revoke-temp Geçerli oturum için tüm geçici " "yetkilendirmeleri iptal et\n" " -s, --system-bus-name=VERİYOLU_ADI VERİYOLU_ADI'nın sahibinin " "yetkilendirmesini kontrol et\n" " --version Sürümü göster\n" "\n" "Hataları şuraya bildirin: %s\n" "%s ev sayfası: <%s>\n" #: src/programs/pkcheck.c:398 src/programs/pkcheck.c:431 #: src/programs/pkcheck.c:443 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: `%s'den sonra değişken bekleniyor\n" #: src/programs/pkcheck.c:421 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: Geçersiz --process değeri `%s'\n" #: src/programs/pkcheck.c:458 src/programs/pkcheck.c:467 #, c-format msgid "%s: Two arguments expected after `--detail, -d'\n" msgstr "%s: `--detail, -d'den sonra iki değişken bekleniyor\n" #: src/programs/pkcheck.c:528 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: Konu belirtilmedi\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: src/programs/pkexec.c:821 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "`$(cmdline_short)' programını süper kullanıcı olarak çalıştırmak için kimlik " "doğrulama gerekir" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: src/programs/pkexec.c:831 msgid "" "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "`$(cmdline_short)' programını $(user.display) kullanıcısı olarak çalıştırmak " "için kimlik doğrulama gerekir" #: src/programs/pkttyagent.c:87 msgid "Don't replace existing agent if any" msgstr "Eğer varsa, mevcut vekili değiştirme" #: src/programs/pkttyagent.c:91 msgid "Close FD when the agent is registered" msgstr "Vekil kaydedildiğinde FD'yi kapat" #: src/programs/pkttyagent.c:91 msgid "FD" msgstr "FD" #: src/programs/pkttyagent.c:95 msgid "Register the agent for the specified process" msgstr "Belirtilen süreç için vekili kaydet" #: src/programs/pkttyagent.c:96 msgid "PID[,START_TIME]" msgstr "PID[,BAŞLANGIÇ_ZAMANI]" #: src/programs/pkttyagent.c:100 msgid "Register the agent for the owner of BUS_NAME" msgstr "Vekili, VERİYOLU_ADI sahibi için kaydet" #: src/programs/pkttyagent.c:100 msgid "BUS_NAME" msgstr "VERİYOLU_ADI" #: src/programs/pkttyagent.c:155 #, c-format msgid "%s: Options --process and --system-bus-name are mutually exclusive\n" msgstr "%s: --process ve --system-bus-name seçenekleri birbirini dışlar\n" #: src/programs/pkttyagent.c:179 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: Geçersiz süreç belirteci `%s'\n" polkit-126/po/uk.po000066400000000000000000000211061474122443600142720ustar00rootroot00000000000000# Ukrainian translation for polkit. # Copyright (C) 2015 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # # Yuri Chornoivan , 2015, 2020. msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/polkit/polkit/issues\n" "POT-Creation-Date: 2020-03-22 03:56+0000\n" "PO-Revision-Date: 2020-04-02 13:05+0300\n" "Last-Translator: Yuri Chornoivan \n" "Language-Team: Ukrainian \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" "%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Lokalize 20.07.70\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "Виконання програми від імені іншого користувача" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "" "Для виконання програми від імені іншого користувача слід пройти розпізнавання" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "Виконання прикладу програми polkit Frobnicate" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "Для запуску прикладу програми polkit Frobnicate (user=$(user), user.gecos=" "$(user.gecos), user.display=$(user.display), program=$(program), " "command_line=$(command_line)) слід пройти розпізнавання" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "Вивести дані лише щодо дії ДІЯ" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "ДІЯ" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "Вивести докладні дані щодо дії" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:95 msgid "Show version" msgstr "Показати дані щодо версії" #: ../src/programs/pkaction.c:132 msgid "[--action-id ACTION]" msgstr "[--action-id ДІЯ]" #: ../src/programs/pkaction.c:133 ../src/programs/pkttyagent.c:118 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "Про вади повідомляйте за такою адресою: %s\n" "Домашня сторінка %s: <%s>" #: ../src/programs/pkaction.c:147 ../src/programs/pkcheck.c:493 #: ../src/programs/pkttyagent.c:132 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: неочікуваний аргумент «%s»\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "Користування:\n" " pkcheck [ПАРАМЕТР...]\n" "\n" "Параметри довідки:\n" " -h, --help Вивести довідку щодо параметрів\n" "\n" "Параметри програми:\n" " -a, --action-id=ДІЯ Перевірити уповноваження щодо виконання " "дії ДІЯ\n" " -u, --allow-user-interaction Взаємодіяти із користувачем, якщо " "потрібно\n" " -d, --details=КЛЮЧ ЗНАЧЕННЯ Додати пару (КЛЮЧ, ЗНАЧЕННЯ) до " "інформації щодо дії\n" " --enable-internal-agent Використати вбудований агент " "розпізнавання, якщо потрібно\n" " --list-temp Вивести список тимчасових уповноважень " "для поточного сеансу\n" " -p, --process=PID[,ЧАС_ЗАПУСКУ,UID] Перевірити уповноваження для вказаного " "процесу\n" " --revoke-temp Відкликати усі тимчасові уповноваження " "для поточного сеансу\n" " -s, --system-bus-name=НАЗВА_КАНАЛУ Перевірити уповноваження власника " "НАЗВА_КАНАЛУ\n" " --version Вивести дані щодо версії\n" "\n" "Про вади повідомляйте за такою адресою: %s\n" "Домашня сторінка %s: <%s>\n" #: ../src/programs/pkcheck.c:393 ../src/programs/pkcheck.c:426 #: ../src/programs/pkcheck.c:438 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: після «%s» мало бути вказано аргумент'\n" #: ../src/programs/pkcheck.c:416 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: некоректне значення --process, «%s»\n" #: ../src/programs/pkcheck.c:453 ../src/programs/pkcheck.c:462 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: після «--detail» мало бути вказано два аргументи\n" #: ../src/programs/pkcheck.c:523 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: не вказано суб’єкт\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:790 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "" "Для запуску «$(cmdline_short)» від імені суперкористувача слід пройти розпізнавання" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:800 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "" "Для запуску «$(cmdline_short)» від імені користувача $(user.display) слід пройти " "розпізнавання" #: ../src/programs/pkttyagent.c:78 msgid "Don't replace existing agent if any" msgstr "Не змінювати наявного агента, якщо такий є" #: ../src/programs/pkttyagent.c:82 msgid "Close FD when the agent is registered" msgstr "Закрити дескриптор файла, якщо агент не зареєстровано" #: ../src/programs/pkttyagent.c:82 msgid "FD" msgstr "ДФ" #: ../src/programs/pkttyagent.c:86 msgid "Register the agent for the specified process" msgstr "Зареєструвати агент для вказаного процесу" #: ../src/programs/pkttyagent.c:87 msgid "PID[,START_TIME]" msgstr "PID[,ЧАС_ЗАПУСКУ]" #: ../src/programs/pkttyagent.c:91 #| msgid "Register the agent owner of BUS_NAME" msgid "Register the agent for the owner of BUS_NAME" msgstr "Зареєструвати агент для власника НАЗВА_КАНАЛУ" #: ../src/programs/pkttyagent.c:91 msgid "BUS_NAME" msgstr "НАЗВА_КАНАЛУ" #: ../src/programs/pkttyagent.c:164 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: некоректний специфікатор процесу, «%s»\n" polkit-126/po/zh_CN.po000066400000000000000000000153451474122443600146640ustar00rootroot00000000000000# Chinese (China) translation for polkit. # Copyright (C) 2015 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Mingye Wang , 2015. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?" "product=PolicyKit&keywords=I18N+L10N&component=libpolkit\n" "POT-Creation-Date: 2015-11-13 02:11+0000\n" "PO-Revision-Date: 2015-11-13 01:59-0500\n" "Last-Translator: Mingye Wang (Arthur2e5) \n" "Language-Team: Chinese (China) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.8.6\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "作为另一个用户运行程序" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "需要验证:作为另一个用户运行程序" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "运行 polkit 示例程序“Frobnicate”" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "需要验证:运行 polkit 示例程序 Frobnicate (user=$(user), user.gecos=$(user." "gecos), user.display=$(user.display), program=$(program), command_line=" "$(command_line))" #: ../src/programs/pkaction.c:100 msgid "Only output information about ACTION" msgstr "只输出与操作有关的信息" #: ../src/programs/pkaction.c:100 msgid "ACTION" msgstr "操作" #: ../src/programs/pkaction.c:104 msgid "Output detailed action information" msgstr "输出详细的操作信息" #: ../src/programs/pkaction.c:108 ../src/programs/pkttyagent.c:61 msgid "Show version" msgstr "显示版本" #: ../src/programs/pkaction.c:130 msgid "[--action-id ACTION]" msgstr "[--action-id 操作]" #: ../src/programs/pkaction.c:131 ../src/programs/pkttyagent.c:81 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "报告错误到:%s\n" "%s 项目主页:<%s>" #: ../src/programs/pkaction.c:145 ../src/programs/pkcheck.c:491 #: ../src/programs/pkttyagent.c:95 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s: 意外的参数 \"%s\"\n" #: ../src/programs/pkcheck.c:35 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "用法\n" " pkcheck [选项...]\n" "\n" "帮助选项\n" " -h, --help 显示可选的帮助\n" "\n" "应用选项\n" " -a, --action-id=操作 检查 <操作> 的授权情况\n" " -u, --allow-user-interaction 在必要时进行用户交互\n" " -d, --details=键 值 将 (键, 值) 加入有关操作的信息\n" " --enable-internal-agent 在必要时使用内置授权助理程序\n" " --list-temp 列出当前会话的临时授权\n" " -p, --process=PID[,开始时间,UID] 检查指定进程的授权\n" " --revoke-temp 吊销所有当前会话的临时授权\n" " -s, --system-bus-name=BUS_NAME 检查 BUS_NAME 所有者的授权\n" " --version 显示版本\n" "\n" "报告错误到:%s\n" "%s 项目主页:<%s>\n" #: ../src/programs/pkcheck.c:391 ../src/programs/pkcheck.c:424 #: ../src/programs/pkcheck.c:436 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s: %s 后预期参数\n" #: ../src/programs/pkcheck.c:414 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s: 无效 --process 值 \"%s\"\n" #: ../src/programs/pkcheck.c:451 ../src/programs/pkcheck.c:460 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s: --detail 后预期两个参数\n" #: ../src/programs/pkcheck.c:521 #, c-format msgid "%s: Subject not specified\n" msgstr "%s: 主题未指定\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:794 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "需要授权:作为超级用户身份运行 \"$(cmdline_short)\" " #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:804 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "需要授权:作为用户 \"$(user.display)\" 运行 \"$(cmdline_short)\" " #: ../src/programs/pkttyagent.c:44 msgid "Don't replace existing agent if any" msgstr "不替换现有助理程序,若有的话" #: ../src/programs/pkttyagent.c:48 msgid "Close FD when the agent is registered" msgstr "注册助理程序时关闭文件描述符" #: ../src/programs/pkttyagent.c:48 msgid "FD" msgstr "文件描述符" #: ../src/programs/pkttyagent.c:52 msgid "Register the agent for the specified process" msgstr "对指定进程注册助理程序" #: ../src/programs/pkttyagent.c:53 msgid "PID[,START_TIME]" msgstr "PID[,开始时间]" #: ../src/programs/pkttyagent.c:57 msgid "Register the agent owner of BUS_NAME" msgstr "注册 BUS_NAME 的助理所有者" #: ../src/programs/pkttyagent.c:57 msgid "BUS_NAME" msgstr "" #: ../src/programs/pkttyagent.c:127 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s: 无效进程定义 \"%s\" \n" polkit-126/po/zh_TW.po000066400000000000000000000154171474122443600147160ustar00rootroot00000000000000# Chinese (Taiwan) translation for polkit. # Copyright (C) 2017 polkit's COPYRIGHT HOLDER # This file is distributed under the same license as the polkit package. # Cheng-Chia Tseng , 2017. # msgid "" msgstr "" "Project-Id-Version: polkit master\n" "Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?" "product=PolicyKit&keywords=I18N+L10N&component=libpolkit\n" "POT-Creation-Date: 2017-08-31 03:26+0000\n" "PO-Revision-Date: 2017-08-31 21:24+0800\n" "Language-Team: Chinese (Taiwan) \n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Last-Translator: Cheng-Chia Tseng \n" "X-Generator: Poedit 2.0.3\n" #: ../actions/org.freedesktop.policykit.policy.in.h:1 msgid "Run a program as another user" msgstr "以其他使用者身份執行程式" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "Authentication is required to run a program as another user" msgstr "必須先核對身份才能以其他使用者執行程式" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 msgid "Run the polkit example program Frobnicate" msgstr "執行 polkit 範例程式 Frobnicate" #: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 msgid "" "Authentication is required to run the polkit example program Frobnicate " "(user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), " "program=$(program), command_line=$(command_line))" msgstr "" "必須先核對身份才能執行 polkit 範例程式 Frobnicate (user=$(user), user.gecos=" "$(user.gecos), user.display=$(user.display), program=$(program), " "command_line=$(command_line))" #: ../src/programs/pkaction.c:101 msgid "Only output information about ACTION" msgstr "僅輸出 ACTION 相關資訊" #: ../src/programs/pkaction.c:101 msgid "ACTION" msgstr "ACTION" #: ../src/programs/pkaction.c:105 msgid "Output detailed action information" msgstr "輸出詳細動作資訊" #: ../src/programs/pkaction.c:109 ../src/programs/pkttyagent.c:62 msgid "Show version" msgstr "顯示版本" #: ../src/programs/pkaction.c:134 msgid "[--action-id ACTION]" msgstr "[--action-id ACTION]" #: ../src/programs/pkaction.c:135 ../src/programs/pkttyagent.c:85 #, c-format msgid "" "Report bugs to: %s\n" "%s home page: <%s>" msgstr "" "回報臭蟲處:%s\n" "%s 網頁:<%s>" #: ../src/programs/pkaction.c:149 ../src/programs/pkcheck.c:495 #: ../src/programs/pkttyagent.c:99 #, c-format msgid "%s: Unexpected argument `%s'\n" msgstr "%s:未預期引數「%s」\n" #: ../src/programs/pkcheck.c:36 #, c-format msgid "" "Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about " "the action\n" " --enable-internal-agent Use an internal authentication agent if " "necessary\n" " --list-temp List temporary authorizations for " "current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified " "process\n" " --revoke-temp Revoke all temporary authorizations for " "current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of " "BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n" msgstr "" "用法:\n" " pkcheck [OPTION...]\n" "\n" "幫助選項:\n" " -h, --help 顯示幫助選項\n" "\n" "應用選項:\n" " -a, --action-id=ACTION 檢查授權以執行 ACTION\n" " -u, --allow-user-interaction 若有必要,則和使用者互動\n" " -d, --details=KEY VALUE 加入 (KEY, VALUE) 到動作的相關資訊中\n" " --enable-internal-agent 若有必要,使用內部身份核對代理\n" " --list-temp 列出目前工作階段的暫時授權\n" " -p, --process=PID[,START_TIME,UID] 檢查指定程序的授權\n" " --revoke-temp 撤銷目前工作階段的所有暫時授權\n" " -s, --system-bus-name=BUS_NAME 檢查 BUS_NAME 的使用者授權\n" " --version 顯示版號\n" "\n" "請回報臭蟲到:%s\n" "%s 網頁:<%s>\n" #: ../src/programs/pkcheck.c:395 ../src/programs/pkcheck.c:428 #: ../src/programs/pkcheck.c:440 #, c-format msgid "%s: Argument expected after `%s'\n" msgstr "%s:預期「%s」後要有引數\n" #: ../src/programs/pkcheck.c:418 #, c-format msgid "%s: Invalid --process value `%s'\n" msgstr "%s:無效 --process 值「%s」\n" #: ../src/programs/pkcheck.c:455 ../src/programs/pkcheck.c:464 #, c-format msgid "%s: Two arguments expected after `--detail'\n" msgstr "%s:預期「--detail」後有兩個引數\n" #: ../src/programs/pkcheck.c:525 #, c-format msgid "%s: Subject not specified\n" msgstr "%s:未指定目標\n" #. Translators: message shown when trying to run a program as root. Do not #. * translate the $(program) fragment - it will be expanded to the path #. * of the program e.g. /bin/bash. #. #: ../src/programs/pkexec.c:797 msgid "Authentication is needed to run `$(cmdline_short)' as the super user" msgstr "必須先核對身份才能以超級使用者執行「$(cmdline_short)」" #. Translators: message shown when trying to run a program as another user. #. * Do not translate the $(program) or $(user) fragments - the former will #. * be expanded to the path of the program e.g. "/bin/bash" and the latter #. * to the user e.g. "John Doe (johndoe)" or "johndoe". #. #: ../src/programs/pkexec.c:807 msgid "Authentication is needed to run `$(cmdline_short)' as user $(user.display)" msgstr "必須先核對身份才能以 $(user.display) 使用者執行「$(cmdline_short)」" #: ../src/programs/pkttyagent.c:45 msgid "Don't replace existing agent if any" msgstr "不要替換既有代理,若有的話" #: ../src/programs/pkttyagent.c:49 msgid "Close FD when the agent is registered" msgstr "當代裡已註冊時關閉 FD" #: ../src/programs/pkttyagent.c:49 msgid "FD" msgstr "FD" #: ../src/programs/pkttyagent.c:53 msgid "Register the agent for the specified process" msgstr "為指定程序註冊代理" #: ../src/programs/pkttyagent.c:54 msgid "PID[,START_TIME]" msgstr "PID[,START_TIME]" #: ../src/programs/pkttyagent.c:58 msgid "Register the agent owner of BUS_NAME" msgstr "註冊 BUS_NAME 的代理擁有者" #: ../src/programs/pkttyagent.c:58 msgid "BUS_NAME" msgstr "BUS_NAME" #: ../src/programs/pkttyagent.c:131 #, c-format msgid "%s: Invalid process specifier `%s'\n" msgstr "%s:無效程序指定碼「%s」\n" polkit-126/src/000077500000000000000000000000001474122443600134645ustar00rootroot00000000000000polkit-126/src/examples/000077500000000000000000000000001474122443600153025ustar00rootroot00000000000000polkit-126/src/examples/cancel.c000066400000000000000000000112161474122443600166740ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ /* Simple example that shows how to check for an authorization * including cancelling the check. * * Cancelling an authorization check is desirable in situations where * the object/action to check for vanishes. * * One concrete example of this is a disks service in which the user * needs to authenticate to modify a disk. If the disk is removed * while the authentication dialog is shown, the disks service should * cancel the authorization check. A side effect of this, is that the * authentication dialog is removed. */ #include static gboolean on_tensec_timeout (gpointer user_data) { GMainLoop *loop = user_data; g_print ("Ten seconds has passed. Now exiting.\n"); g_main_loop_quit (loop); return FALSE; } static void check_authorization_cb (PolkitAuthority *authority, GAsyncResult *res, gpointer user_data) { GMainLoop *loop = user_data; PolkitAuthorizationResult *result; GError *error; error = NULL; result = polkit_authority_check_authorization_finish (authority, res, &error); if (error != NULL) { g_print ("Error checking authorization: %s\n", error->message); g_error_free (error); } else { const gchar *result_str; if (polkit_authorization_result_get_is_authorized (result)) { result_str = "authorized"; } else if (polkit_authorization_result_get_is_challenge (result)) { result_str = "challenge"; } else { result_str = "not authorized"; } g_print ("Authorization result: %s\n", result_str); } g_print ("Authorization check has been cancelled and the dialog should now be hidden.\n" "This process will exit in ten seconds.\n"); g_timeout_add (10000, on_tensec_timeout, loop); } static gboolean do_cancel (GCancellable *cancellable) { g_print ("Timer has expired; cancelling authorization check\n"); g_cancellable_cancel (cancellable); return FALSE; } int main (int argc, char *argv[]) { pid_t parent_pid; const gchar *action_id; GMainLoop *loop; PolkitSubject *subject; PolkitAuthority *authority; GCancellable *cancellable; if (argc != 2) { g_printerr ("usage: %s \n", argv[0]); return 1; } action_id = argv[1]; loop = g_main_loop_new (NULL, FALSE); authority = polkit_authority_get_sync (NULL, NULL); /* Typically mechanisms will use a PolkitSystemBusName since most * clients communicate with the mechanism via D-Bus. However for * this simple example we use the process id of the calling process. * * Note that if the parent was reaped we have to be careful not to * check if init(1) is authorized (it always is). */ parent_pid = getppid (); if (parent_pid == 1) { g_printerr ("Parent process was reaped by init(1)\n"); return 1; } subject = polkit_unix_process_new_for_owner (parent_pid, 0, getuid ()); cancellable = g_cancellable_new (); g_print ("Will cancel authorization check in 10 seconds\n"); /* Set up a 10 second timer to cancel the check */ g_timeout_add (10 * 1000, (GSourceFunc) do_cancel, cancellable); polkit_authority_check_authorization (authority, subject, action_id, NULL, /* PolkitDetails */ POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, cancellable, (GAsyncReadyCallback) check_authorization_cb, loop); g_main_loop_run (loop); g_object_unref (authority); g_object_unref (subject); g_object_unref (cancellable); g_main_loop_unref (loop); return 0; } polkit-126/src/examples/frobnicate.c000066400000000000000000000035141474122443600175650ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #include int main (int argc, char *argv[]) { gchar *args; gchar **env; guint n; int ret; #ifdef __GLIBC__ gchar *cwd = NULL; #else gchar cwd[PATH_MAX]; #endif ret = 1; args = NULL; env = NULL; #ifdef __GLIBC__ if ((cwd = get_current_dir_name ()) == NULL) #else if (getcwd (cwd, sizeof cwd) == NULL) #endif { g_printerr ("Error getting cwd: %s\n", g_strerror (errno)); goto out; } args = g_strjoinv (" ", argv); g_print ("In pk-example-frobnicate\n"); g_print ("uid: %d\n", getuid ()); g_print ("euid: %d\n", geteuid ()); g_print ("args: `%s'\n", args); g_print ("cwd: %s\n", cwd); g_print ("environment:\n"); env = g_listenv (); for (n = 0; env[n] != NULL; n++) { g_print (" %s=%s\n", env[n], g_getenv (env[n])); } ret = 0; out: #ifdef __GLIBC__ free (cwd); #endif g_free (args); g_strfreev (env); return ret; } polkit-126/src/examples/meson.build000066400000000000000000000007501474122443600174460ustar00rootroot00000000000000policy = 'org.freedesktop.policykit.examples.pkexec.policy' i18n.merge_file( input: policy + '.in', output: '@BASENAME@', po_dir: po_dir, data_dirs: its_dir, install: true, install_dir: pk_pkgactiondir, ) program = 'cancel' executable( program, program + '.c', include_directories: top_inc, dependencies: libpolkit_gobject_dep, ) executable( 'pk-example-frobnicate', 'frobnicate.c', include_directories: top_inc, dependencies: glib_dep, install: true, ) polkit-126/src/examples/org.freedesktop.policykit.examples.pkexec.policy.in000066400000000000000000000020301474122443600273250ustar00rootroot00000000000000 Examples for the polkit project http://www.freedesktop.org/wiki/Software/polkit/ Run the polkit example program Frobnicate Authentication is required to run the polkit example program Frobnicate (user=$(user), user.gecos=$(user.gecos), user.display=$(user.display), program=$(program), command_line=$(command_line)) audio-x-generic no no auth_admin_keep /usr/bin/pk-example-frobnicate polkit-126/src/examples/polkit-raw-dbus.py000077500000000000000000000027041474122443600207060ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (C) 2009 Red Hat, Inc. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General # Public License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place, Suite 330, # Boston, MA 02111-1307, USA. # # Author: David Zeuthen # Simple example showing how to access the Authority via D-Bus calls # import dbus bus = dbus.SystemBus() proxy = bus.get_object('org.freedesktop.PolicyKit1', '/org/freedesktop/PolicyKit1/Authority') authority = dbus.Interface(proxy, dbus_interface='org.freedesktop.PolicyKit1.Authority') system_bus_name = bus.get_unique_name() subject = ('system-bus-name', {'name' : system_bus_name}) action_id = 'org.freedesktop.policykit.exec' details = {} flags = 1 # AllowUserInteraction flag cancellation_id = '' # No cancellation id result = authority.CheckAuthorization(subject, action_id, details, flags, cancellation_id) print result polkit-126/src/meson.build000066400000000000000000000006101474122443600156230ustar00rootroot00000000000000src_inc = include_directories('.') symbol_map = meson.current_source_dir() / 'symbol.map' ldflags = cc.get_supported_link_arguments('-Wl,--version-script,@0@'.format(symbol_map)) subdir('polkit') subdir('polkitagent') if not get_option('libs-only') subdir('polkitbackend') subdir('programs') endif enable_examples = get_option('examples') if enable_examples subdir('examples') endif polkit-126/src/polkit/000077500000000000000000000000001474122443600147665ustar00rootroot00000000000000polkit-126/src/polkit/meson.build000066400000000000000000000053631474122443600171370ustar00rootroot00000000000000name = '@0@-gobject-@1@'.format(meson.project_name(), pk_api_version) enum_headers = files( 'polkitauthorityfeatures.h', 'polkitcheckauthorizationflags.h', 'polkiterror.h', 'polkitimplicitauthorization.h', ) headers = enum_headers + files( 'polkitactiondescription.h', 'polkitauthority.h', 'polkitauthorizationresult.h', 'polkitdetails.h', 'polkit.h', 'polkitidentity.h', 'polkitpermission.h', 'polkitsubject.h', 'polkitsystembusname.h', 'polkittemporaryauthorization.h', 'polkitunixgroup.h', 'polkitunixnetgroup.h', 'polkitunixprocess.h', 'polkitunixsession.h', 'polkitunixuser.h', ) private_headers = files( 'polkitprivate.h', 'polkittypes.h', ) install_headers( headers + private_headers, install_dir: pk_pkgincludedir / 'polkit', ) common_deps = [ gio_dep, gio_unix_dep, glib_dep, ] enum_sources = gnome.mkenums_simple( 'polkitenumtypes', sources: enum_headers, install_header: true, install_dir: pk_pkgincludedir / 'polkit', ) sources = enum_sources + files( 'polkitactiondescription.c', 'polkitauthority.c', 'polkitauthorityfeatures.c', 'polkitauthorizationresult.c', 'polkitcheckauthorizationflags.c', 'polkitdetails.c', 'polkiterror.c', 'polkitidentity.c', 'polkitimplicitauthorization.c', 'polkitpermission.c', 'polkitsubject.c', 'polkitsystembusname.c', 'polkittemporaryauthorization.c', 'polkitunixgroup.c', 'polkitunixnetgroup.c', 'polkitunixprocess.c', 'polkitunixuser.c', ) incs = [ top_inc, src_inc, ] deps = common_deps c_flags = '-D_POLKIT_COMPILATION' if enable_logind sources += 'polkitunixsession-systemd.c' deps += logind_dep else sources += 'polkitunixsession.c' endif libpolkit_gobject = shared_library( name, sources: sources, version: libversion, include_directories: incs, dependencies: deps, c_args: c_flags, link_args: ldflags, link_depends: symbol_map, install: true, ) libpolkit_gobject_dep = declare_dependency( sources: enum_sources[1], include_directories: src_inc, dependencies: common_deps, link_with: libpolkit_gobject, ) pkg.generate( libraries: libpolkit_gobject, version: pk_version, name: name, description: 'PolicyKit Authorization API', filebase: name, subdirs: pk_api_name, requires: common_deps, variables: [ 'exec_prefix=${prefix}', 'policydir=' + ('${datadir}' / pk_actiondir), 'actiondir=' + ('${datadir}' / pk_actiondir), 'pkcheck_supports_uid=true', ], ) if enable_introspection libpolkit_gobject_gir = gnome.generate_gir( libpolkit_gobject, sources: sources + headers, extra_args: c_flags, nsversion: pk_gir_version, namespace: pk_gir_ns, export_packages: name, includes: 'Gio-2.0', header: 'polkit/polkit.h', install: true, ) endif polkit-126/src/polkit/polkit.h000066400000000000000000000033221474122443600164410ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #ifndef __POLKIT_H #define __POLKIT_H #define _POLKIT_INSIDE_POLKIT_H 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef _POLKIT_INSIDE_POLKIT_H #endif /* __POLKIT_H */ polkit-126/src/polkit/polkitactiondescription.c000066400000000000000000000312631474122443600221030ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include "polkitimplicitauthorization.h" #include "polkitactiondescription.h" #include "polkitprivate.h" /** * SECTION:polkitactiondescription * @title: PolkitActionDescription * @short_description: Description of Actions * * Object used to encapsulate a registered action. */ /** * PolkitActionDescription: * * The #PolkitActionDescription struct should not be accessed directly. */ struct _PolkitActionDescription { GObject parent_instance; gchar *action_id; gchar *description; gchar *message; gchar *vendor_name; gchar *vendor_url; gchar *icon_name; PolkitImplicitAuthorization implicit_any; PolkitImplicitAuthorization implicit_inactive; PolkitImplicitAuthorization implicit_active; GHashTable *annotations; gchar **annotation_keys; }; struct _PolkitActionDescriptionClass { GObjectClass parent_class; }; G_DEFINE_TYPE (PolkitActionDescription, polkit_action_description, G_TYPE_OBJECT); static void polkit_action_description_init (PolkitActionDescription *action_description) { action_description->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } static void polkit_action_description_finalize (GObject *object) { PolkitActionDescription *action_description; action_description = POLKIT_ACTION_DESCRIPTION (object); g_free (action_description->action_id); g_free (action_description->description); g_free (action_description->message); g_free (action_description->vendor_name); g_free (action_description->vendor_url); g_free (action_description->icon_name); g_hash_table_unref (action_description->annotations); g_strfreev (action_description->annotation_keys); if (G_OBJECT_CLASS (polkit_action_description_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_action_description_parent_class)->finalize (object); } static void polkit_action_description_class_init (PolkitActionDescriptionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_action_description_finalize; } /** * polkit_action_description_get_action_id: * @action_description: A #PolkitActionDescription. * * Gets the action id for @action_description. * * Returns: A string owned by @action_description. Do not free. */ const gchar * polkit_action_description_get_action_id (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), NULL); return action_description->action_id; } /** * polkit_action_description_get_description: * @action_description: A #PolkitActionDescription. * * Gets the description used for @action_description. * * Returns: A string owned by @action_description. Do not free. */ const gchar * polkit_action_description_get_description (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), NULL); return action_description->description; } /** * polkit_action_description_get_message: * @action_description: A #PolkitActionDescription. * * Gets the message used for @action_description. * * Returns: A string owned by @action_description. Do not free. */ const gchar * polkit_action_description_get_message (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), NULL); return action_description->message; } /** * polkit_action_description_get_vendor_name: * @action_description: A #PolkitActionDescription. * * Gets the vendor name for @action_description, if any. * * Returns: A string owned by @action_description. Do not free. */ const gchar * polkit_action_description_get_vendor_name (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), NULL); return action_description->vendor_name; } /** * polkit_action_description_get_vendor_url: * @action_description: A #PolkitActionDescription. * * Gets the vendor URL for @action_description, if any. * * Returns: A string owned by @action_description. Do not free. */ const gchar * polkit_action_description_get_vendor_url (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), NULL); return action_description->vendor_url; } /** * polkit_action_description_get_implicit_any: * @action_description: A #PolkitActionDescription. * * Gets the implicit authorization for @action_description used for * any subject. * * Returns: A value from the #PolkitImplicitAuthorization enumeration. */ PolkitImplicitAuthorization polkit_action_description_get_implicit_any (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), 0); return action_description->implicit_any; } /** * polkit_action_description_get_implicit_inactive: * @action_description: A #PolkitActionDescription. * * Gets the implicit authorization for @action_description used for * subjects in inactive sessions on a local console. * * Returns: A value from the #PolkitImplicitAuthorization enumeration. */ PolkitImplicitAuthorization polkit_action_description_get_implicit_inactive (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), 0); return action_description->implicit_inactive; } /** * polkit_action_description_get_implicit_active: * @action_description: A #PolkitActionDescription. * * Gets the implicit authorization for @action_description used for * subjects in active sessions on a local console. * * Returns: A value from the #PolkitImplicitAuthorization enumeration. */ PolkitImplicitAuthorization polkit_action_description_get_implicit_active (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), 0); return action_description->implicit_active; } /** * polkit_action_description_get_icon_name: * @action_description: A #PolkitActionDescription. * * Gets the icon name for @action_description, if any. * * Returns: A string owned by @action_description. Do not free. */ const gchar * polkit_action_description_get_icon_name (PolkitActionDescription *action_description) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), NULL); return action_description->icon_name; } /** * polkit_action_description_get_annotation: * @action_description: A #PolkitActionDescription. * @key: An annotation key. * * Get the value of the annotation with @key. * * Returns: (allow-none): %NULL if there is no annoation with @key, * otherwise the annotation value owned by @action_description. Do not * free. */ const gchar * polkit_action_description_get_annotation (PolkitActionDescription *action_description, const gchar *key) { g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), NULL); return g_hash_table_lookup (action_description->annotations, key); } /** * polkit_action_description_get_annotation_keys: * @action_description: A #PolkitActionDescription. * * Gets the keys of annotations defined in @action_description. * * Returns: (transfer none): The annotation keys owned by @action_description. Do not free. */ const gchar * const * polkit_action_description_get_annotation_keys (PolkitActionDescription *action_description) { GPtrArray *p; GHashTableIter iter; const gchar *key; g_return_val_if_fail (POLKIT_IS_ACTION_DESCRIPTION (action_description), NULL); if (action_description->annotation_keys != NULL) goto out; p = g_ptr_array_new (); g_hash_table_iter_init (&iter, action_description->annotations); while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) g_ptr_array_add (p, g_strdup (key)); g_ptr_array_add (p, NULL); action_description->annotation_keys = (gchar **) g_ptr_array_free (p, FALSE); out: return (const gchar * const *) action_description->annotation_keys; } PolkitActionDescription * polkit_action_description_new (const gchar *action_id, const gchar *description, const gchar *message, const gchar *vendor_name, const gchar *vendor_url, const gchar *icon_name, PolkitImplicitAuthorization implicit_any, PolkitImplicitAuthorization implicit_inactive, PolkitImplicitAuthorization implicit_active, GHashTable *annotations) { PolkitActionDescription *ret; g_return_val_if_fail (annotations != NULL, NULL); ret = POLKIT_ACTION_DESCRIPTION (g_object_new (POLKIT_TYPE_ACTION_DESCRIPTION, NULL)); ret->action_id = g_strdup (action_id); ret->description = g_strdup (description); ret->message = g_strdup (message); ret->vendor_name = g_strdup (vendor_name); ret->vendor_url = g_strdup (vendor_url); ret->icon_name = g_strdup (icon_name); ret->implicit_any = implicit_any; ret->implicit_inactive = implicit_inactive; ret->implicit_active = implicit_active; if (ret->annotations != NULL) g_hash_table_unref (ret->annotations); ret->annotations = g_hash_table_ref (annotations); return ret; } PolkitActionDescription * polkit_action_description_new_for_gvariant (GVariant *value) { PolkitActionDescription *action_description; GVariantIter iter; GVariant *annotations_dict; gchar *a_key; gchar *a_value; action_description = POLKIT_ACTION_DESCRIPTION (g_object_new (POLKIT_TYPE_ACTION_DESCRIPTION, NULL)); g_variant_get (value, "(ssssssuuu@a{ss})", &action_description->action_id, &action_description->description, &action_description->message, &action_description->vendor_name, &action_description->vendor_url, &action_description->icon_name, &action_description->implicit_any, &action_description->implicit_inactive, &action_description->implicit_active, &annotations_dict); g_variant_iter_init (&iter, annotations_dict); while (g_variant_iter_next (&iter, "{ss}", &a_key, &a_value)) g_hash_table_insert (action_description->annotations, a_key, a_value); /* adopts a_key and a_value */ g_variant_unref (annotations_dict); return action_description; } /* Note that this returns a floating value. */ GVariant * polkit_action_description_to_gvariant (PolkitActionDescription *action_description) { GVariantBuilder builder; GHashTableIter iter; const gchar *a_key; const gchar *a_value; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); g_hash_table_iter_init (&iter, action_description->annotations); while (g_hash_table_iter_next (&iter, (gpointer) &a_key, (gpointer) &a_value)) g_variant_builder_add (&builder, "{ss}", a_key, a_value); /* TODO: note 'foo ? : ""' is a gcc specific extension (it's a short-hand for 'foo ? foo : ""') */ return g_variant_new ("(ssssssuuua{ss})", action_description->action_id ? : "", action_description->description ? : "", action_description->message ? : "", action_description->vendor_name ? : "", action_description->vendor_url ? : "", action_description->icon_name ? : "", action_description->implicit_any, action_description->implicit_inactive, action_description->implicit_active, &builder); } polkit-126/src/polkit/polkitactiondescription.h000066400000000000000000000070421474122443600221060ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_ACTION_DESCRIPTION_H #define __POLKIT_ACTION_DESCRIPTION_H #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_ACTION_DESCRIPTION (polkit_action_description_get_type()) #define POLKIT_ACTION_DESCRIPTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_ACTION_DESCRIPTION, PolkitActionDescription)) #define POLKIT_ACTION_DESCRIPTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_ACTION_DESCRIPTION, PolkitActionDescriptionClass)) #define POLKIT_ACTION_DESCRIPTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_ACTION_DESCRIPTION, PolkitActionDescriptionClass)) #define POLKIT_IS_ACTION_DESCRIPTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_ACTION_DESCRIPTION)) #define POLKIT_IS_ACTION_DESCRIPTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_ACTION_DESCRIPTION)) #if 0 typedef struct _PolkitActionDescription PolkitActionDescription; #endif typedef struct _PolkitActionDescriptionClass PolkitActionDescriptionClass; GType polkit_action_description_get_type (void) G_GNUC_CONST; const gchar *polkit_action_description_get_action_id (PolkitActionDescription *action_description); const gchar *polkit_action_description_get_description (PolkitActionDescription *action_description); const gchar *polkit_action_description_get_message (PolkitActionDescription *action_description); const gchar *polkit_action_description_get_vendor_name (PolkitActionDescription *action_description); const gchar *polkit_action_description_get_vendor_url (PolkitActionDescription *action_description); const gchar *polkit_action_description_get_icon_name (PolkitActionDescription *action_description); PolkitImplicitAuthorization polkit_action_description_get_implicit_any (PolkitActionDescription *action_description); PolkitImplicitAuthorization polkit_action_description_get_implicit_inactive (PolkitActionDescription *action_description); PolkitImplicitAuthorization polkit_action_description_get_implicit_active (PolkitActionDescription *action_description); const gchar *polkit_action_description_get_annotation (PolkitActionDescription *action_description, const gchar *key); const gchar * const *polkit_action_description_get_annotation_keys (PolkitActionDescription *action_description); G_END_DECLS #endif /* __POLKIT_ACTION_DESCRIPTION_H */ polkit-126/src/polkit/polkitauthority.c000066400000000000000000002370161474122443600204160ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include "polkitauthorizationresult.h" #include "polkitcheckauthorizationflags.h" #include "polkitauthority.h" #include "polkiterror.h" #include "polkitenumtypes.h" #include "polkitsubject.h" #include "polkitidentity.h" #include "polkitdetails.h" #include "polkitprivate.h" /** * SECTION:polkitauthority * @title: PolkitAuthority * @short_description: Authority * @stability: Stable * * #PolkitAuthority is used for checking whether a given subject is * authorized to perform a given action. Typically privileged system * daemons or suid helpers will use this when handling requests from * untrusted clients. * * User sessions can register an authentication agent with the * authority. This is used for requests from untrusted clients where * system policy requires that the user needs to acknowledge (through * proving he is the user or the administrator) a given action. See * #PolkitAgentListener and #PolkitAgentSession for details. */ /** * PolkitAuthority: * * The #PolkitAuthority struct should not be accessed directly. */ struct _PolkitAuthority { /*< private >*/ GObject parent_instance; gchar *name; gchar *version; GDBusProxy *proxy; guint cancellation_id_counter; gboolean initialized; GError *initialization_error; }; struct _PolkitAuthorityClass { GObjectClass parent_class; }; G_LOCK_DEFINE_STATIC (the_lock); static PolkitAuthority *the_authority = NULL; enum { CHANGED_SIGNAL, SESSIONS_CHANGED_SIGNAL, LAST_SIGNAL, }; enum { PROP_0, PROP_OWNER, PROP_BACKEND_NAME, PROP_BACKEND_VERSION, PROP_BACKEND_FEATURES }; static guint signals[LAST_SIGNAL] = {0}; static void initable_iface_init (GInitableIface *initable_iface); static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); G_DEFINE_TYPE_WITH_CODE (PolkitAuthority, polkit_authority, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)) static void on_proxy_signal (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { PolkitAuthority *authority = POLKIT_AUTHORITY (user_data); guint16 msg_mask; if (g_strcmp0 (signal_name, "Changed") == 0) { if ((parameters != NULL) && g_variant_check_format_string(parameters, "(q)", FALSE)) { g_variant_get(parameters, "(q)", &msg_mask); if (msg_mask >= LAST_SIGNAL) { msg_mask = CHANGED_SIGNAL; /* If signal not valid, we send generic "changed". */ } g_signal_emit (authority, signals[msg_mask], 0); } else { g_signal_emit_by_name (authority, "changed"); } } } static void on_notify_g_name_owner (GObject *object, GParamSpec *ppsec, gpointer user_data) { PolkitAuthority *authority = POLKIT_AUTHORITY (user_data); g_object_notify (G_OBJECT (authority), "owner"); } static void polkit_authority_init (PolkitAuthority *authority) { } static void polkit_authority_dispose (GObject *object) { PolkitAuthority *authority = POLKIT_AUTHORITY (object); G_LOCK (the_lock); if (authority == the_authority) the_authority = NULL; G_UNLOCK (the_lock); if (G_OBJECT_CLASS (polkit_authority_parent_class)->dispose != NULL) G_OBJECT_CLASS (polkit_authority_parent_class)->dispose (object); } static void polkit_authority_finalize (GObject *object) { PolkitAuthority *authority = POLKIT_AUTHORITY (object); if (authority->initialization_error != NULL) g_error_free (authority->initialization_error); g_free (authority->name); g_free (authority->version); if (authority->proxy != NULL) g_object_unref (authority->proxy); if (G_OBJECT_CLASS (polkit_authority_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_authority_parent_class)->finalize (object); } static void polkit_authority_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitAuthority *authority = POLKIT_AUTHORITY (object); switch (prop_id) { case PROP_OWNER: g_value_take_string (value, polkit_authority_get_owner (authority)); break; case PROP_BACKEND_NAME: g_value_set_string (value, polkit_authority_get_backend_name (authority)); break; case PROP_BACKEND_VERSION: g_value_set_string (value, polkit_authority_get_backend_version (authority)); break; case PROP_BACKEND_FEATURES: g_value_set_flags (value, polkit_authority_get_backend_features (authority)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_authority_class_init (PolkitAuthorityClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->dispose = polkit_authority_dispose; gobject_class->finalize = polkit_authority_finalize; gobject_class->get_property = polkit_authority_get_property; /** * PolkitAuthority:owner: * * The unique name of the owner of the org.freedesktop.PolicyKit1 * D-Bus service or %NULL if there is no owner. Connect to the * #GObject::notify signal to track changes to this property. */ g_object_class_install_property (gobject_class, PROP_OWNER, g_param_spec_string ("owner", "Owner", "Owner.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * PolkitAuthority:backend-name: * * The name of the currently used Authority backend. */ g_object_class_install_property (gobject_class, PROP_BACKEND_NAME, g_param_spec_string ("backend-name", "Backend name", "The name of the currently used Authority backend.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * PolkitAuthority:version: * * The version of the currently used Authority backend. */ g_object_class_install_property (gobject_class, PROP_BACKEND_VERSION, g_param_spec_string ("backend-version", "Backend version", "The version of the currently used Authority backend.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * PolkitAuthority:backend-features: * * The features of the currently used Authority backend. */ g_object_class_install_property (gobject_class, PROP_BACKEND_FEATURES, g_param_spec_flags ("backend-features", "Backend features", "The features of the currently used Authority backend.", POLKIT_TYPE_AUTHORITY_FEATURES, POLKIT_AUTHORITY_FEATURES_NONE, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * PolkitAuthority::changed: * @authority: A #PolkitAuthority. * * Emitted when actions and/or authorizations change */ signals[CHANGED_SIGNAL] = g_signal_new ("changed", POLKIT_TYPE_AUTHORITY, G_SIGNAL_RUN_LAST, 0, /* class offset */ NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * PolkitAuthority::sessions-changed: * @authority: A #PolkitAuthority. * * Emitted when sessions change */ signals[SESSIONS_CHANGED_SIGNAL] = g_signal_new ("sessions-changed", POLKIT_TYPE_AUTHORITY, G_SIGNAL_RUN_LAST, 0, /* class offset */ NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /* ---------------------------------------------------------------------------------------------------- */ static gboolean polkit_authority_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { PolkitAuthority *authority = POLKIT_AUTHORITY (initable); gboolean ret; /* This method needs to be idempotent to work with the singleton * pattern. See the docs for g_initable_init(). We implement this by * locking. */ ret = FALSE; G_LOCK (the_lock); if (authority->initialized) { if (authority->initialization_error == NULL) ret = TRUE; goto out; } authority->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, /* TODO: pass GDBusInterfaceInfo* */ "org.freedesktop.PolicyKit1", /* name */ "/org/freedesktop/PolicyKit1/Authority", /* path */ "org.freedesktop.PolicyKit1.Authority", /* interface */ cancellable, &authority->initialization_error); if (authority->proxy == NULL) { g_prefix_error (&authority->initialization_error, "Error initializing authority: "); goto out; } g_signal_connect (authority->proxy, "g-signal", G_CALLBACK (on_proxy_signal), authority); g_signal_connect (authority->proxy, "notify::g-name-owner", G_CALLBACK (on_notify_g_name_owner), authority); ret = TRUE; out: authority->initialized = TRUE; if (!ret) { g_assert (authority->initialization_error != NULL); g_propagate_error (error, g_error_copy (authority->initialization_error)); } G_UNLOCK (the_lock); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = polkit_authority_initable_init; } static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface) { /* for now, we use default implementation to run GInitable code in a * thread - would probably be nice to have real async version to * avoid the thread-overhead */ } /* ---------------------------------------------------------------------------------------------------- */ /* deprecated, see polkitauthority.h */ /** * polkit_authority_get: * * (deprecated) * * Returns: (transfer full): value */ PolkitAuthority * polkit_authority_get (void) { GError *error; PolkitAuthority *ret; error = NULL; ret = polkit_authority_get_sync (NULL, /* GCancellable* */ &error); if (ret == NULL) { g_warning ("polkit_authority_get: Error getting authority: %s", error->message); g_error_free (error); } return ret; } /* ---------------------------------------------------------------------------------------------------- */ static PolkitAuthority * get_uninitialized_authority (GCancellable *cancellable, GError **error) { static volatile GQuark error_quark = 0; G_LOCK (the_lock); if (error_quark == 0) error_quark = POLKIT_ERROR; if (the_authority != NULL) { g_object_ref (the_authority); goto out; } the_authority = POLKIT_AUTHORITY (g_object_new (POLKIT_TYPE_AUTHORITY, NULL)); out: G_UNLOCK (the_lock); return the_authority; } static void authority_get_async_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); GError *error; error = NULL; if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source_object), res, &error)) { g_assert (error != NULL); g_simple_async_result_set_from_error (simple, error); g_error_free (error); g_object_unref (source_object); } else { g_simple_async_result_set_op_res_gpointer (simple, source_object, g_object_unref); } g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } /** * polkit_authority_get_async: * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously gets a reference to the authority. * * This is an asynchronous failable function. When the result is * ready, @callback will be invoked in the thread-default main * loop of the thread you are calling this method from and you * can use polkit_authority_get_finish() to get the result. See * polkit_authority_get_sync() for the synchronous version. */ void polkit_authority_get_async (GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PolkitAuthority *authority; GSimpleAsyncResult *simple; GError *error; g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); simple = g_simple_async_result_new (NULL, callback, user_data, polkit_authority_get_async); error = NULL; authority = get_uninitialized_authority (cancellable, &error); if (authority == NULL) { g_assert (error != NULL); g_simple_async_result_set_from_error (simple, error); g_error_free (error); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } else { g_async_initable_init_async (G_ASYNC_INITABLE (authority), G_PRIORITY_DEFAULT, cancellable, authority_get_async_cb, simple); } } /** * polkit_authority_get_finish: * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to polkit_authority_get_async(). * @error: (allow-none): Return location for error or %NULL. * * Finishes an operation started with polkit_authority_get_async(). * * Returns: (transfer full): A #PolkitAuthority. Free it with * g_object_unref() when done with it. */ PolkitAuthority * polkit_authority_get_finish (GAsyncResult *res, GError **error) { GSimpleAsyncResult *simple; GObject *object; PolkitAuthority *ret; g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); simple = G_SIMPLE_ASYNC_RESULT (res); g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_authority_get_async); ret = NULL; if (g_simple_async_result_propagate_error (simple, error)) goto out; object = g_simple_async_result_get_op_res_gpointer (simple); g_assert (object != NULL); ret = g_object_ref (POLKIT_AUTHORITY (object)); out: return ret; } /** * polkit_authority_get_sync: * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Synchronously gets a reference to the authority. * * This is a synchronous failable function - the calling thread is * blocked until a reply is received. See polkit_authority_get_async() * for the asynchronous version. * * Returns: (transfer full): A #PolkitAuthority. Free it with * g_object_unref() when done with it. */ PolkitAuthority * polkit_authority_get_sync (GCancellable *cancellable, GError **error) { PolkitAuthority *authority; g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); authority = get_uninitialized_authority (cancellable, error); if (authority == NULL) goto out; if (!g_initable_init (G_INITABLE (authority), cancellable, error)) { g_object_unref (authority); authority = NULL; } out: return authority; } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { GAsyncResult *res; GMainContext *context; GMainLoop *loop; } CallSyncData; static CallSyncData * call_sync_new (void) { CallSyncData *data; data = g_new0 (CallSyncData, 1); data->context = g_main_context_new (); data->loop = g_main_loop_new (data->context, FALSE); g_main_context_push_thread_default (data->context); return data; } static void call_sync_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { CallSyncData *data = user_data; data->res = g_object_ref (res); g_main_loop_quit (data->loop); } static void call_sync_block (CallSyncData *data) { g_main_loop_run (data->loop); } static void call_sync_free (CallSyncData *data) { g_main_context_pop_thread_default (data->context); g_main_context_unref (data->context); g_main_loop_unref (data->loop); g_object_unref (data->res); g_free (data); } /* ---------------------------------------------------------------------------------------------------- */ static void generic_async_cb (GObject *source_obj, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (res), g_object_unref); g_simple_async_result_complete (simple); g_object_unref (simple); } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_enumerate_actions: * @authority: A #PolkitAuthority. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously retrieves all registered actions. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call polkit_authority_enumerate_actions_finish() * to get the result of the operation. **/ void polkit_authority_enumerate_actions (PolkitAuthority *authority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_dbus_proxy_call (authority->proxy, "EnumerateActions", g_variant_new ("(s)", ""), /* TODO: use system locale */ G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_enumerate_actions)); } /** * polkit_authority_enumerate_actions_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes retrieving all registered actions. * * Returns: (element-type Polkit.ActionDescription) (transfer full): A list of * #PolkitActionDescription objects or %NULL if @error is set. The returned * list should be freed with g_list_free() after each element have been freed * with g_object_unref(). **/ GList * polkit_authority_enumerate_actions_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { GList *ret; GVariant *value; GVariantIter iter; GVariant *child; GVariant *array; GAsyncResult *_res; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); ret = NULL; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_authority_enumerate_actions); _res = G_ASYNC_RESULT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); value = g_dbus_proxy_call_finish (authority->proxy, _res, error); if (value == NULL) goto out; array = g_variant_get_child_value (value, 0); g_variant_iter_init (&iter, array); while ((child = g_variant_iter_next_value (&iter)) != NULL) { ret = g_list_prepend (ret, polkit_action_description_new_for_gvariant (child)); g_variant_unref (child); } ret = g_list_reverse (ret); g_variant_unref (array); g_variant_unref (value); out: return ret; } /** * polkit_authority_enumerate_actions_sync: * @authority: A #PolkitAuthority. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Synchronously retrieves all registered actions - the calling thread * is blocked until a reply is received. See * polkit_authority_enumerate_actions() for the asynchronous version. * * Returns: (element-type Polkit.ActionDescription) (transfer full): A list of * #PolkitActionDescription or %NULL if @error is set. The returned list should * be freed with g_list_free() after each element have been freed with * g_object_unref(). **/ GList * polkit_authority_enumerate_actions_sync (PolkitAuthority *authority, GCancellable *cancellable, GError **error) { GList *ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); data = call_sync_new (); polkit_authority_enumerate_actions (authority, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_enumerate_actions_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { PolkitAuthority *authority; GSimpleAsyncResult *simple; gchar *cancellation_id; } CheckAuthData; static void cancel_check_authorization_cb (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { GVariant *value; GError *error; error = NULL; value = g_dbus_proxy_call_finish (proxy, res, &error); if (value == NULL) { g_warning ("Error cancelling authorization check: %s", error->message); g_error_free (error); } else { g_variant_unref (value); } } static void check_authorization_cb (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { CheckAuthData *data = user_data; GVariant *value; GError *error; error = NULL; value = g_dbus_proxy_call_finish (proxy, res, &error); if (value == NULL) { if (data->cancellation_id != NULL && (!g_dbus_error_is_remote_error (error) && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)) { g_dbus_proxy_call (data->authority->proxy, "CancelCheckAuthorization", g_variant_new ("(s)", data->cancellation_id), G_DBUS_CALL_FLAGS_NONE, -1, NULL, /* GCancellable */ (GAsyncReadyCallback) cancel_check_authorization_cb, NULL); } g_simple_async_result_set_from_error (data->simple, error); g_error_free (error); } else { GVariant *result_value; PolkitAuthorizationResult *result; result_value = g_variant_get_child_value (value, 0); result = polkit_authorization_result_new_for_gvariant (result_value); g_variant_unref (result_value); g_variant_unref (value); g_simple_async_result_set_op_res_gpointer (data->simple, result, g_object_unref); } g_simple_async_result_complete (data->simple); g_object_unref (data->authority); g_object_unref (data->simple); g_free (data->cancellation_id); g_free (data); } /** * polkit_authority_check_authorization: * @authority: A #PolkitAuthority. * @subject: A #PolkitSubject. * @action_id: The action to check for. * @details: (allow-none): Details about the action or %NULL. * @flags: A set of #PolkitCheckAuthorizationFlags. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously checks if @subject is authorized to perform the action represented * by @action_id. * * Note that %POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION * SHOULD be passed ONLY if * the event that triggered the authorization check is stemming from * an user action, e.g. the user pressing a button or attaching a * device. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_authority_check_authorization_finish() to get the result of * the operation. * * Known keys in @details include polkit.message * and polkit.gettext_domain that can be used to * override the message shown to the user. See the documentation for * the D-Bus method for more details. * * If @details is non-empty then the request will fail with * #POLKIT_ERROR_FAILED unless the process doing the check itsef is * sufficiently authorized (e.g. running as uid 0). **/ void polkit_authority_check_authorization (PolkitAuthority *authority, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { CheckAuthData *data; g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (POLKIT_IS_SUBJECT (subject)); g_return_if_fail (action_id != NULL); g_return_if_fail (details == NULL || POLKIT_IS_DETAILS (details)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); data = g_new0 (CheckAuthData, 1); data->authority = g_object_ref (authority); data->simple = g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_check_authorization); G_LOCK (the_lock); if (cancellable != NULL) data->cancellation_id = g_strdup_printf ("cancellation-id-%d", authority->cancellation_id_counter++); G_UNLOCK (the_lock); g_dbus_proxy_call (authority->proxy, "CheckAuthorization", g_variant_new ("(@(sa{sv})s@a{ss}us)", polkit_subject_to_gvariant (subject), /* A floating value */ action_id, polkit_details_to_gvariant (details), /* A floating value */ flags, data->cancellation_id != NULL ? data->cancellation_id : ""), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, /* no timeout */ cancellable, (GAsyncReadyCallback) check_authorization_cb, data); } /** * polkit_authority_check_authorization_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes checking if a subject is authorized for an action. * * Returns: (transfer full): A #PolkitAuthorizationResult or %NULL if * @error is set. Free with g_object_unref(). **/ PolkitAuthorizationResult * polkit_authority_check_authorization_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { PolkitAuthorizationResult *ret; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); ret = NULL; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) goto out; ret = g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); out: return ret; } /** * polkit_authority_check_authorization_sync: * @authority: A #PolkitAuthority. * @subject: A #PolkitSubject. * @action_id: The action to check for. * @details: (allow-none): Details about the action or %NULL. * @flags: A set of #PolkitCheckAuthorizationFlags. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Checks if @subject is authorized to perform the action represented * by @action_id. * * Note that %POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION * SHOULD be passed ONLY if * the event that triggered the authorization check is stemming from * an user action, e.g. the user pressing a button or attaching a * device. * * Note the calling thread is blocked until a reply is received. You * should therefore NEVER do this from a GUI * thread or a daemon service thread when using the * %POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION flag. This * is because it may potentially take minutes (or even hours) for the * operation to complete because it involves waiting for the user to * authenticate. * * Known keys in @details include polkit.message * and polkit.gettext_domain that can be used to * override the message shown to the user. See the documentation for * the D-Bus method for more details. * * Returns: (transfer full): A #PolkitAuthorizationResult or %NULL if @error is set. Free with g_object_unref(). */ PolkitAuthorizationResult * polkit_authority_check_authorization_sync (PolkitAuthority *authority, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GError **error) { PolkitAuthorizationResult *ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), NULL); g_return_val_if_fail (action_id != NULL, NULL); g_return_val_if_fail (details == NULL || POLKIT_IS_DETAILS (details), NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); data = call_sync_new (); polkit_authority_check_authorization (authority, subject, action_id, details, flags, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_check_authorization_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_register_authentication_agent: * @authority: A #PolkitAuthority. * @subject: The subject the authentication agent is for, typically a #PolkitUnixSession object. * @locale: The locale of the authentication agent. * @object_path: The object path for the authentication agent. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously registers an authentication agent. * * Note that this should be called by the same effective UID which will be * the real UID using the #PolkitAgentSession API or otherwise calling * polkit_authority_authentication_agent_response(). * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_authority_register_authentication_agent_finish() to get the * result of the operation. **/ void polkit_authority_register_authentication_agent (PolkitAuthority *authority, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (POLKIT_IS_SUBJECT (subject)); g_return_if_fail (locale != NULL); g_return_if_fail (g_variant_is_object_path (object_path)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_dbus_proxy_call (authority->proxy, "RegisterAuthenticationAgent", g_variant_new ("(@(sa{sv})ss)", polkit_subject_to_gvariant (subject), /* A floating value */ locale, object_path), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_register_authentication_agent)); } /** * polkit_authority_register_authentication_agent_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes registering an authentication agent. * * Returns: %TRUE if the authentication agent was successfully registered, %FALSE if @error is set. **/ gboolean polkit_authority_register_authentication_agent_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { gboolean ret; GVariant *value; GAsyncResult *_res; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = FALSE; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_authority_register_authentication_agent); _res = G_ASYNC_RESULT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); value = g_dbus_proxy_call_finish (authority->proxy, _res, error); if (value == NULL) goto out; ret = TRUE; g_variant_unref (value); out: return ret; } /** * polkit_authority_register_authentication_agent_sync: * @authority: A #PolkitAuthority. * @subject: The subject the authentication agent is for, typically a #PolkitUnixSession object. * @locale: The locale of the authentication agent. * @object_path: The object path for the authentication agent. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Registers an authentication agent. * * Note that this should be called by the same effective UID which will be * the real UID using the #PolkitAgentSession API or otherwise calling * polkit_authority_authentication_agent_response(). * * The calling thread is blocked * until a reply is received. See * polkit_authority_register_authentication_agent() for the * asynchronous version. * * Returns: %TRUE if the authentication agent was successfully registered, %FALSE if @error is set. **/ gboolean polkit_authority_register_authentication_agent_sync (PolkitAuthority *authority, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GCancellable *cancellable, GError **error) { gboolean ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE); g_return_val_if_fail (locale != NULL, FALSE); g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); data = call_sync_new (); polkit_authority_register_authentication_agent (authority, subject, locale, object_path, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_register_authentication_agent_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_register_authentication_agent_with_options: * @authority: A #PolkitAuthority. * @subject: The subject the authentication agent is for, typically a #PolkitUnixSession object. * @locale: The locale of the authentication agent. * @object_path: The object path for the authentication agent. * @options: (allow-none): A #GVariant with options or %NULL. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously registers an authentication agent. * * Note that this should be called by the same effective UID which will be * the real UID using the #PolkitAgentSession API or otherwise calling * polkit_authority_authentication_agent_response(). * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_authority_register_authentication_agent_with_options_finish() to get the * result of the operation. **/ void polkit_authority_register_authentication_agent_with_options (PolkitAuthority *authority, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GVariant *subject_value; g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (POLKIT_IS_SUBJECT (subject)); g_return_if_fail (locale != NULL); g_return_if_fail (g_variant_is_object_path (object_path)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); subject_value = polkit_subject_to_gvariant (subject); g_variant_ref_sink (subject_value); if (options != NULL) { g_dbus_proxy_call (authority->proxy, "RegisterAuthenticationAgentWithOptions", g_variant_new ("(@(sa{sv})ss@a{sv})", subject_value, locale, object_path, options), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_register_authentication_agent_with_options)); } else { g_dbus_proxy_call (authority->proxy, "RegisterAuthenticationAgent", g_variant_new ("(@(sa{sv})ss)", subject_value, locale, object_path), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_register_authentication_agent_with_options)); } g_variant_unref (subject_value); } /** * polkit_authority_register_authentication_agent_with_options_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes registering an authentication agent. * * Returns: %TRUE if the authentication agent was successfully registered, %FALSE if @error is set. **/ gboolean polkit_authority_register_authentication_agent_with_options_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { gboolean ret; GVariant *value; GAsyncResult *_res; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = FALSE; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_authority_register_authentication_agent_with_options); _res = G_ASYNC_RESULT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); value = g_dbus_proxy_call_finish (authority->proxy, _res, error); if (value == NULL) goto out; ret = TRUE; g_variant_unref (value); out: return ret; } /** * polkit_authority_register_authentication_agent_with_options_sync: * @authority: A #PolkitAuthority. * @subject: The subject the authentication agent is for, typically a #PolkitUnixSession object. * @locale: The locale of the authentication agent. * @object_path: The object path for the authentication agent. * @options: (allow-none): A #GVariant with options or %NULL. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Registers an authentication agent. * * Note that this should be called by the same effective UID which will be * the real UID using the #PolkitAgentSession API or otherwise calling * polkit_authority_authentication_agent_response(). * * The calling thread is blocked * until a reply is received. See * polkit_authority_register_authentication_agent_with_options() for the * asynchronous version. * * Returns: %TRUE if the authentication agent was successfully registered, %FALSE if @error is set. **/ gboolean polkit_authority_register_authentication_agent_with_options_sync (PolkitAuthority *authority, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GCancellable *cancellable, GError **error) { gboolean ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE); g_return_val_if_fail (locale != NULL, FALSE); g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); data = call_sync_new (); polkit_authority_register_authentication_agent_with_options (authority, subject, locale, object_path, options, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_register_authentication_agent_with_options_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_unregister_authentication_agent: * @authority: A #PolkitAuthority. * @subject: The subject the authentication agent is for, typically a #PolkitUnixSession object. * @object_path: The object path for the authentication agent. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously unregisters an authentication agent. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_authority_unregister_authentication_agent_finish() to get * the result of the operation. **/ void polkit_authority_unregister_authentication_agent (PolkitAuthority *authority, PolkitSubject *subject, const gchar *object_path, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (POLKIT_IS_SUBJECT (subject)); g_return_if_fail (g_variant_is_object_path (object_path)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_dbus_proxy_call (authority->proxy, "UnregisterAuthenticationAgent", g_variant_new ("(@(sa{sv})s)", polkit_subject_to_gvariant (subject), /* A floating value */ object_path), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_unregister_authentication_agent)); } /** * polkit_authority_unregister_authentication_agent_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes unregistering an authentication agent. * * Returns: %TRUE if the authentication agent was successfully unregistered, %FALSE if @error is set. **/ gboolean polkit_authority_unregister_authentication_agent_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { gboolean ret; GVariant *value; GAsyncResult *_res; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = FALSE; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_authority_unregister_authentication_agent); _res = G_ASYNC_RESULT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); value = g_dbus_proxy_call_finish (authority->proxy, _res, error); if (value == NULL) goto out; ret = TRUE; g_variant_unref (value); out: return ret; } /** * polkit_authority_unregister_authentication_agent_sync: * @authority: A #PolkitAuthority. * @subject: The subject the authentication agent is for, typically a #PolkitUnixSession object. * @object_path: The object path for the authentication agent. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Unregisters an authentication agent. The calling thread is blocked * until a reply is received. See * polkit_authority_unregister_authentication_agent() for the * asynchronous version. * * Returns: %TRUE if the authentication agent was successfully unregistered, %FALSE if @error is set. **/ gboolean polkit_authority_unregister_authentication_agent_sync (PolkitAuthority *authority, PolkitSubject *subject, const gchar *object_path, GCancellable *cancellable, GError **error) { gboolean ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE); g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); data = call_sync_new (); polkit_authority_unregister_authentication_agent (authority, subject, object_path, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_unregister_authentication_agent_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_authentication_agent_response: * @authority: A #PolkitAuthority. * @cookie: The cookie passed to the authentication agent from the authority. * @identity: The identity that was authenticated. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously provide response that @identity successfully authenticated * for the authentication request identified by @cookie. * * This function is only used by the privileged bits of an authentication agent. * It will fail if the caller is not sufficiently privileged (typically uid 0). * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_authority_authentication_agent_response_finish() to get the * result of the operation. **/ void polkit_authority_authentication_agent_response (PolkitAuthority *authority, const gchar *cookie, PolkitIdentity *identity, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { /* Note that in reality, this API is only accessible to root, and * only called from the setuid helper `polkit-agent-helper-1`. * * However, because this is currently public API, we avoid * triggering warnings from ABI diff type programs by just grabbing * the real uid of the caller here. */ uid_t uid = getuid (); g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (cookie != NULL); g_return_if_fail (POLKIT_IS_IDENTITY (identity)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_dbus_proxy_call (authority->proxy, "AuthenticationAgentResponse2", g_variant_new ("(us@(sa{sv}))", (guint32)uid, cookie, polkit_identity_to_gvariant (identity)), /* A floating value */ G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_authentication_agent_response)); } /** * polkit_authority_authentication_agent_response_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes providing response from an authentication agent. * * Returns: %TRUE if @authority acknowledged the call, %FALSE if @error is set. **/ gboolean polkit_authority_authentication_agent_response_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { gboolean ret; GVariant *value; GAsyncResult *_res; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = FALSE; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_authority_authentication_agent_response); _res = G_ASYNC_RESULT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); value = g_dbus_proxy_call_finish (authority->proxy, _res, error); if (value == NULL) goto out; ret = TRUE; g_variant_unref (value); out: return ret; } /** * polkit_authority_authentication_agent_response_sync: * @authority: A #PolkitAuthority. * @cookie: The cookie passed to the authentication agent from the authority. * @identity: The identity that was authenticated. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Provide response that @identity successfully authenticated for the * authentication request identified by @cookie. See polkit_authority_authentication_agent_response() * for limitations on who is allowed is to call this method. * * The calling thread is blocked until a reply is received. See * polkit_authority_authentication_agent_response() for the * asynchronous version. * * Returns: %TRUE if @authority acknowledged the call, %FALSE if @error is set. **/ gboolean polkit_authority_authentication_agent_response_sync (PolkitAuthority *authority, const gchar *cookie, PolkitIdentity *identity, GCancellable *cancellable, GError **error) { gboolean ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (cookie != NULL, FALSE); g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); data = call_sync_new (); polkit_authority_authentication_agent_response (authority, cookie, identity, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_authentication_agent_response_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_enumerate_temporary_authorizations: * @authority: A #PolkitAuthority. * @subject: A #PolkitSubject, typically a #PolkitUnixSession. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously gets all temporary authorizations for @subject. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_authority_enumerate_temporary_authorizations_finish() to get * the result of the operation. **/ void polkit_authority_enumerate_temporary_authorizations (PolkitAuthority *authority, PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (POLKIT_IS_SUBJECT (subject)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_dbus_proxy_call (authority->proxy, "EnumerateTemporaryAuthorizations", g_variant_new ("(@(sa{sv}))", polkit_subject_to_gvariant (subject)), /* A floating value */ G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_enumerate_temporary_authorizations)); } /** * polkit_authority_enumerate_temporary_authorizations_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes retrieving all registered actions. * * Returns: (element-type Polkit.TemporaryAuthorization) (transfer full): A * list of #PolkitTemporaryAuthorization objects or %NULL if @error is set. The * returned list should be freed with g_list_free() after each element have * been freed with g_object_unref(). **/ GList * polkit_authority_enumerate_temporary_authorizations_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { GList *ret; GVariant *value; GVariantIter iter; GVariant *child; GVariant *array; GAsyncResult *_res; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); ret = NULL; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_authority_enumerate_temporary_authorizations); _res = G_ASYNC_RESULT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); value = g_dbus_proxy_call_finish (authority->proxy, _res, error); if (value == NULL) goto out; array = g_variant_get_child_value (value, 0); g_variant_iter_init (&iter, array); while ((child = g_variant_iter_next_value (&iter)) != NULL) { PolkitTemporaryAuthorization *auth; auth = polkit_temporary_authorization_new_for_gvariant (child, error); g_variant_unref (child); if (auth == NULL) { g_prefix_error (error, "Error serializing return value of EnumerateTemporaryAuthorizations: "); g_list_foreach (ret, (GFunc) g_object_unref, NULL); g_list_free (ret); ret = NULL; goto out_array; } ret = g_list_prepend (ret, auth); } ret = g_list_reverse (ret); out_array: g_variant_unref (array); g_variant_unref (value); out: return ret; } /** * polkit_authority_enumerate_temporary_authorizations_sync: * @authority: A #PolkitAuthority. * @subject: A #PolkitSubject, typically a #PolkitUnixSession. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Synchronousky gets all temporary authorizations for @subject. * * The calling thread is blocked until a reply is received. See * polkit_authority_enumerate_temporary_authorizations() for the * asynchronous version. * * Returns: (element-type Polkit.TemporaryAuthorization) (transfer full): A * list of #PolkitTemporaryAuthorization objects or %NULL if @error is set. The * returned list should be freed with g_list_free() after each element have * been freed with g_object_unref(). **/ GList * polkit_authority_enumerate_temporary_authorizations_sync (PolkitAuthority *authority, PolkitSubject *subject, GCancellable *cancellable, GError **error) { GList *ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); data = call_sync_new (); polkit_authority_enumerate_temporary_authorizations (authority, subject, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_enumerate_temporary_authorizations_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_revoke_temporary_authorizations: * @authority: A #PolkitAuthority. * @subject: The subject to revoke authorizations from, typically a #PolkitUnixSession. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously revokes all temporary authorizations for @subject. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_authority_revoke_temporary_authorizations_finish() to get * the result of the operation. **/ void polkit_authority_revoke_temporary_authorizations (PolkitAuthority *authority, PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (POLKIT_IS_SUBJECT (subject)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_dbus_proxy_call (authority->proxy, "RevokeTemporaryAuthorizations", g_variant_new ("(@(sa{sv}))", polkit_subject_to_gvariant (subject)), /* A floating value */ G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_revoke_temporary_authorizations)); } /** * polkit_authority_revoke_temporary_authorizations_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes revoking temporary authorizations. * * Returns: %TRUE if all the temporary authorizations was revoked, %FALSE if error is set. **/ gboolean polkit_authority_revoke_temporary_authorizations_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { gboolean ret; GVariant *value; GAsyncResult *_res; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = FALSE; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_authority_revoke_temporary_authorizations); _res = G_ASYNC_RESULT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); value = g_dbus_proxy_call_finish (authority->proxy, _res, error); if (value == NULL) goto out; ret = TRUE; g_variant_unref (value); out: return ret; } /** * polkit_authority_revoke_temporary_authorizations_sync: * @authority: A #PolkitAuthority. * @subject: The subject to revoke authorizations from, typically a #PolkitUnixSession. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Synchronously revokes all temporary authorization from @subject. * * The calling thread is blocked until a reply is received. See * polkit_authority_revoke_temporary_authorizations() for the * asynchronous version. * * Returns: %TRUE if the temporary authorization was revoked, %FALSE if error is set. **/ gboolean polkit_authority_revoke_temporary_authorizations_sync (PolkitAuthority *authority, PolkitSubject *subject, GCancellable *cancellable, GError **error) { gboolean ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); data = call_sync_new (); polkit_authority_revoke_temporary_authorizations (authority, subject, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_revoke_temporary_authorizations_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_revoke_temporary_authorization_by_id: * @authority: A #PolkitAuthority. * @id: The opaque identifier for the temporary authorization. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously revoke a temporary authorization. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_authority_revoke_temporary_authorization_by_id_finish() to * get the result of the operation. */ void polkit_authority_revoke_temporary_authorization_by_id (PolkitAuthority *authority, const gchar *id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (POLKIT_IS_AUTHORITY (authority)); g_return_if_fail (id != NULL); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_dbus_proxy_call (authority->proxy, "RevokeTemporaryAuthorizationById", g_variant_new ("(s)", id), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, generic_async_cb, g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_authority_revoke_temporary_authorization_by_id)); } /** * polkit_authority_revoke_temporary_authorization_by_id_finish: * @authority: A #PolkitAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: (allow-none): Return location for error or %NULL. * * Finishes revoking a temporary authorization by id. * * Returns: %TRUE if the temporary authorization was revoked, %FALSE if error is set. **/ gboolean polkit_authority_revoke_temporary_authorization_by_id_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error) { gboolean ret; GVariant *value; GAsyncResult *_res; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = FALSE; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_authority_revoke_temporary_authorization_by_id); _res = G_ASYNC_RESULT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); value = g_dbus_proxy_call_finish (authority->proxy, _res, error); if (value == NULL) goto out; ret = TRUE; g_variant_unref (value); out: return ret; } /** * polkit_authority_revoke_temporary_authorization_by_id_sync: * @authority: A #PolkitAuthority. * @id: The opaque identifier for the temporary authorization. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Synchronously revokes a temporary authorization. * * The calling thread is blocked until a reply is received. See * polkit_authority_revoke_temporary_authorization_by_id() for the * asynchronous version. * * Returns: %TRUE if the temporary authorization was revoked, %FALSE if error is set. **/ gboolean polkit_authority_revoke_temporary_authorization_by_id_sync (PolkitAuthority *authority, const gchar *id, GCancellable *cancellable, GError **error) { gboolean ret; CallSyncData *data; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE); g_return_val_if_fail (id != NULL, FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); data = call_sync_new (); polkit_authority_revoke_temporary_authorization_by_id (authority, id, cancellable, call_sync_cb, data); call_sync_block (data); ret = polkit_authority_revoke_temporary_authorization_by_id_finish (authority, data->res, error); call_sync_free (data); return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authority_get_owner: * @authority: A #PolkitAuthority. * * The unique name on the system message bus of the owner of the name * org.freedesktop.PolicyKit1 or %NULL if no-one * currently owns the name. You may connect to the #GObject::notify * signal to track changes to the #PolkitAuthority:owner property. * * Returns: (allow-none): %NULL or a string that should be freed with g_free(). **/ gchar * polkit_authority_get_owner (PolkitAuthority *authority) { g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); return g_dbus_proxy_get_name_owner (authority->proxy); } /** * polkit_authority_get_backend_name: * @authority: A #PolkitAuthority. * * Gets the name of the authority backend. * * Returns: The name of the backend. */ const gchar * polkit_authority_get_backend_name (PolkitAuthority *authority) { g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); if (authority->name == NULL) { GVariant *value; value = g_dbus_proxy_get_cached_property (authority->proxy, "BackendName"); authority->name = g_variant_dup_string (value, NULL); g_variant_unref (value); } return authority->name; } /** * polkit_authority_get_backend_version: * @authority: A #PolkitAuthority. * * Gets the version of the authority backend. * * Returns: The version string for the backend. */ const gchar * polkit_authority_get_backend_version (PolkitAuthority *authority) { g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), NULL); if (authority->version == NULL) { GVariant *value; value = g_dbus_proxy_get_cached_property (authority->proxy, "BackendVersion"); authority->version = g_variant_dup_string (value, NULL); g_variant_unref (value); } return authority->version; } /** * polkit_authority_get_backend_features: * @authority: A #PolkitAuthority. * * Gets the features supported by the authority backend. * * Returns: Flags from #PolkitAuthorityFeatures. */ PolkitAuthorityFeatures polkit_authority_get_backend_features (PolkitAuthority *authority) { PolkitAuthorityFeatures ret; GVariant *value; g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), 0); value = g_dbus_proxy_get_cached_property (authority->proxy, "BackendFeatures"); ret = (PolkitAuthorityFeatures) g_variant_get_uint32 (value); g_variant_unref (value); return ret; } polkit-126/src/polkit/polkitauthority.h000066400000000000000000000417301474122443600204170ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_AUTHORITY_H #define __POLKIT_AUTHORITY_H #include #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_AUTHORITY (polkit_authority_get_type()) #define POLKIT_AUTHORITY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_AUTHORITY, PolkitAuthority)) #define POLKIT_AUTHORITY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_AUTHORITY, PolkitAuthorityClass)) #define POLKIT_AUTHORITY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_AUTHORITY, PolkitAuthorityClass)) #define POLKIT_IS_AUTHORITY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_AUTHORITY)) #define POLKIT_IS_AUTHORITY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_AUTHORITY)) #if 0 typedef struct _PolkitAuthority PolkitAuthority; #endif typedef struct _PolkitAuthorityClass PolkitAuthorityClass; GType polkit_authority_get_type (void) G_GNUC_CONST; PolkitAuthority *polkit_authority_get (void) G_GNUC_DEPRECATED_FOR (polkit_authority_get_sync); void polkit_authority_get_async (GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); PolkitAuthority *polkit_authority_get_finish (GAsyncResult *res, GError **error); PolkitAuthority *polkit_authority_get_sync (GCancellable *cancellable, GError **error); gchar *polkit_authority_get_owner (PolkitAuthority *authority); const gchar *polkit_authority_get_backend_name (PolkitAuthority *authority); const gchar *polkit_authority_get_backend_version (PolkitAuthority *authority); PolkitAuthorityFeatures polkit_authority_get_backend_features (PolkitAuthority *authority); /* ---------------------------------------------------------------------------------------------------- */ GList *polkit_authority_enumerate_actions_sync (PolkitAuthority *authority, GCancellable *cancellable, GError **error); PolkitAuthorizationResult *polkit_authority_check_authorization_sync (PolkitAuthority *authority, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GError **error); gboolean polkit_authority_register_authentication_agent_sync (PolkitAuthority *authority, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GCancellable *cancellable, GError **error); gboolean polkit_authority_register_authentication_agent_with_options_sync (PolkitAuthority *authority, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GCancellable *cancellable, GError **error); gboolean polkit_authority_unregister_authentication_agent_sync (PolkitAuthority *authority, PolkitSubject *subject, const gchar *object_path, GCancellable *cancellable, GError **error); gboolean polkit_authority_authentication_agent_response_sync (PolkitAuthority *authority, const gchar *cookie, PolkitIdentity *identity, GCancellable *cancellable, GError **error); GList *polkit_authority_enumerate_temporary_authorizations_sync (PolkitAuthority *authority, PolkitSubject *subject, GCancellable *cancellable, GError **error); gboolean polkit_authority_revoke_temporary_authorizations_sync (PolkitAuthority *authority, PolkitSubject *subject, GCancellable *cancellable, GError **error); gboolean polkit_authority_revoke_temporary_authorization_by_id_sync (PolkitAuthority *authority, const gchar *id, GCancellable *cancellable, GError **error); /* ---------------------------------------------------------------------------------------------------- */ void polkit_authority_enumerate_actions (PolkitAuthority *authority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GList * polkit_authority_enumerate_actions_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); void polkit_authority_check_authorization (PolkitAuthority *authority, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); PolkitAuthorizationResult *polkit_authority_check_authorization_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); void polkit_authority_register_authentication_agent (PolkitAuthority *authority, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean polkit_authority_register_authentication_agent_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); gboolean polkit_authority_register_authentication_agent_with_options_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); void polkit_authority_register_authentication_agent_with_options (PolkitAuthority *authority, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); void polkit_authority_unregister_authentication_agent (PolkitAuthority *authority, PolkitSubject *subject, const gchar *object_path, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean polkit_authority_unregister_authentication_agent_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); void polkit_authority_authentication_agent_response (PolkitAuthority *authority, const gchar *cookie, PolkitIdentity *identity, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean polkit_authority_authentication_agent_response_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); void polkit_authority_enumerate_temporary_authorizations (PolkitAuthority *authority, PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GList *polkit_authority_enumerate_temporary_authorizations_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); void polkit_authority_revoke_temporary_authorizations (PolkitAuthority *authority, PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean polkit_authority_revoke_temporary_authorizations_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); void polkit_authority_revoke_temporary_authorization_by_id (PolkitAuthority *authority, const gchar *id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean polkit_authority_revoke_temporary_authorization_by_id_finish (PolkitAuthority *authority, GAsyncResult *res, GError **error); /* ---------------------------------------------------------------------------------------------------- */ G_END_DECLS #endif /* __POLKIT_AUTHORITY_H */ polkit-126/src/polkit/polkitauthorityfeatures.c000066400000000000000000000016101474122443600221420ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include "polkitcheckauthorizationflags.h" #include "polkitprivate.h" polkit-126/src/polkit/polkitauthorityfeatures.h000066400000000000000000000031761474122443600221600ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_AUTHORITY_FEATURES_H #define __POLKIT_AUTHORITY_FEATURES_H #include G_BEGIN_DECLS /** * PolkitAuthorityFeatures: * @POLKIT_AUTHORITY_FEATURES_NONE: No flags set. * @POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION: The authority supports temporary authorizations * that can be obtained through authentication. * * Flags describing features supported by the Authority implementation. */ typedef enum { POLKIT_AUTHORITY_FEATURES_NONE = 0, POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION = (1<<0), } PolkitAuthorityFeatures; G_END_DECLS #endif /* __POLKIT_AUTHORITY_FEATURES_H */ polkit-126/src/polkit/polkitauthorizationresult.c000066400000000000000000000232471474122443600225240ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include "polkitauthorizationresult.h" #include "polkitdetails.h" #include "polkitprivate.h" /** * SECTION:polkitauthorizationresult * @title: PolkitAuthorizationResult * @short_description: Result for checking an authorization * @stability: Stable * * This class represents the result you get when checking for an authorization. */ /** * PolkitAuthorizationResult: * * The #PolkitAuthorizationResult struct should not be accessed directly. */ struct _PolkitAuthorizationResult { GObject parent_instance; gboolean is_authorized; gboolean is_challenge; PolkitDetails *details; }; struct _PolkitAuthorizationResultClass { GObjectClass parent_class; }; G_DEFINE_TYPE (PolkitAuthorizationResult, polkit_authorization_result, G_TYPE_OBJECT); static void polkit_authorization_result_init (PolkitAuthorizationResult *authorization_result) { } static void polkit_authorization_result_finalize (GObject *object) { PolkitAuthorizationResult *authorization_result; authorization_result = POLKIT_AUTHORIZATION_RESULT (object); if (authorization_result->details != NULL) g_object_unref (authorization_result->details); if (G_OBJECT_CLASS (polkit_authorization_result_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_authorization_result_parent_class)->finalize (object); } static void polkit_authorization_result_class_init (PolkitAuthorizationResultClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_authorization_result_finalize; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_authorization_result_new: * @is_authorized: Whether the subject is authorized. * @is_challenge: Whether the subject is authorized if more * information is provided. Must be %FALSE unless @is_authorized is * %TRUE. * @details: (allow-none): Must be %NULL unless @is_authorized is %TRUE * * Creates a new #PolkitAuthorizationResult object. * * Returns: A #PolkitAuthorizationResult object. Free with g_object_unref(). */ PolkitAuthorizationResult * polkit_authorization_result_new (gboolean is_authorized, gboolean is_challenge, PolkitDetails *details) { PolkitAuthorizationResult *authorization_result; g_return_val_if_fail (details == NULL || POLKIT_IS_DETAILS (details), NULL); authorization_result = POLKIT_AUTHORIZATION_RESULT (g_object_new (POLKIT_TYPE_AUTHORIZATION_RESULT, NULL)); authorization_result->is_authorized = is_authorized; authorization_result->is_challenge = is_challenge; authorization_result->details = details != NULL ? g_object_ref (details) : NULL; return authorization_result; } /** * polkit_authorization_result_get_is_authorized: * @result: A #PolkitAuthorizationResult. * * Gets whether the subject is authorized. * * If the authorization is temporary, use polkit_authorization_result_get_temporary_authorization_id() * to get the opaque identifier for the temporary authorization. * * Returns: Whether the subject is authorized. */ gboolean polkit_authorization_result_get_is_authorized (PolkitAuthorizationResult *result) { g_return_val_if_fail (POLKIT_IS_AUTHORIZATION_RESULT (result), FALSE); return result->is_authorized; } /** * polkit_authorization_result_get_is_challenge: * @result: A #PolkitAuthorizationResult. * * Gets whether the subject is authorized if more information is provided. * * Returns: Whether the subject is authorized if more information is provided. */ gboolean polkit_authorization_result_get_is_challenge (PolkitAuthorizationResult *result) { g_return_val_if_fail (POLKIT_IS_AUTHORIZATION_RESULT (result), FALSE); return result->is_challenge; } /** * polkit_authorization_result_get_details: * @result: A #PolkitAuthorizationResult. * * Gets the details about the result. * * Returns: (allow-none) (transfer none): A #PolkitDetails object or * %NULL if there are no details. This object is owned by @result and * should not be freed by the caller. */ PolkitDetails * polkit_authorization_result_get_details (PolkitAuthorizationResult *result) { g_return_val_if_fail (POLKIT_IS_AUTHORIZATION_RESULT (result), NULL); return result->details; } /** * polkit_authorization_result_get_retains_authorization: * @result: A #PolkitAuthorizationResult. * * Gets whether authorization is retained if obtained via authentication. This can only be the case * if @result indicates that the subject can obtain authorization after challenge (cf. * polkit_authorization_result_get_is_challenge()), e.g. when the subject is not already authorized (cf. * polkit_authorization_result_get_is_authorized()). * * If the subject is already authorized, use polkit_authorization_result_get_temporary_authorization_id() * to check if the authorization is temporary. * * This method simply reads the value of the key/value pair in @details with the * key polkit.retains_authorization_after_challenge. * * Returns: %TRUE if the authorization is or will be temporary. */ gboolean polkit_authorization_result_get_retains_authorization (PolkitAuthorizationResult *result) { gboolean ret; PolkitDetails *details; g_return_val_if_fail (POLKIT_IS_AUTHORIZATION_RESULT (result), FALSE); ret = FALSE; details = polkit_authorization_result_get_details (result); if (details != NULL && polkit_details_lookup (details, "polkit.retains_authorization_after_challenge") != NULL) ret = TRUE; return ret; } /** * polkit_authorization_result_get_temporary_authorization_id: * @result: A #PolkitAuthorizationResult. * * Gets the opaque temporary authorization id for @result if @result indicates the * subject is authorized and the authorization is temporary rather than one-shot or * permanent. * * You can use this string together with the result from * polkit_authority_enumerate_temporary_authorizations() to get more details * about the temporary authorization or polkit_authority_revoke_temporary_authorization_by_id() * to revoke the temporary authorization. * * If the subject is not authorized, use polkit_authorization_result_get_retains_authorization() * to check if the authorization will be retained if obtained via authentication. * * This method simply reads the value of the key/value pair in @details with the * key polkit.temporary_authorization_id. * * Returns: (allow-none): The opaque temporary authorization id for * @result or %NULL if not available. Do not free this string, it * is owned by @result. */ const gchar * polkit_authorization_result_get_temporary_authorization_id (PolkitAuthorizationResult *result) { const gchar *ret; PolkitDetails *details; g_return_val_if_fail (POLKIT_IS_AUTHORIZATION_RESULT (result), NULL); ret = NULL; details = polkit_authorization_result_get_details (result); if (details != NULL) ret = polkit_details_lookup (details, "polkit.temporary_authorization_id"); return ret; } /** * polkit_authorization_result_get_dismissed: * @result: A #PolkitAuthorizationResult. * * Gets whether the authentication request was dismissed / canceled by the user. * * This method simply reads the value of the key/value pair in @details with the * key polkit.dismissed. * * Returns: %TRUE if the authentication request was dismissed, %FALSE otherwise. * * Since: 0.101 */ gboolean polkit_authorization_result_get_dismissed (PolkitAuthorizationResult *result) { gboolean ret; PolkitDetails *details; g_return_val_if_fail (POLKIT_IS_AUTHORIZATION_RESULT (result), FALSE); ret = FALSE; details = polkit_authorization_result_get_details (result); if (details != NULL && polkit_details_lookup (details, "polkit.dismissed") != NULL) ret = TRUE; return ret; } PolkitAuthorizationResult * polkit_authorization_result_new_for_gvariant (GVariant *value) { gboolean is_authorized; gboolean is_challenge; GVariant *dict; PolkitDetails *details; PolkitAuthorizationResult *ret; g_variant_get (value, "(bb@a{ss})", &is_authorized, &is_challenge, &dict); details = polkit_details_new_for_gvariant (dict); g_variant_unref (dict); ret = polkit_authorization_result_new (is_authorized, is_challenge, details); g_object_unref (details); return ret; } /* Note that this returns a floating value. */ GVariant * polkit_authorization_result_to_gvariant (PolkitAuthorizationResult *authorization_result) { PolkitDetails *details; details = polkit_authorization_result_get_details (authorization_result); return g_variant_new ("(bb@a{ss})", polkit_authorization_result_get_is_authorized (authorization_result), polkit_authorization_result_get_is_challenge (authorization_result), polkit_details_to_gvariant (details)); /* A floating value */ } polkit-126/src/polkit/polkitauthorizationresult.h000066400000000000000000000065251474122443600225310ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_AUTHORIZATION_RESULT_H #define __POLKIT_AUTHORIZATION_RESULT_H #include #include G_BEGIN_DECLS #define POLKIT_TYPE_AUTHORIZATION_RESULT (polkit_authorization_result_get_type()) #define POLKIT_AUTHORIZATION_RESULT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_AUTHORIZATION_RESULT, PolkitAuthorizationResult)) #define POLKIT_AUTHORIZATION_RESULT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_AUTHORIZATION_RESULT, PolkitAuthorizationResultClass)) #define POLKIT_AUTHORIZATION_RESULT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_AUTHORIZATION_RESULT, PolkitAuthorizationResultClass)) #define POLKIT_IS_AUTHORIZATION_RESULT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_AUTHORIZATION_RESULT)) #define POLKIT_IS_AUTHORIZATION_RESULT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_AUTHORIZATION_RESULT)) #if 0 typedef struct _PolkitAuthorizationResult PolkitAuthorizationResult; #endif typedef struct _PolkitAuthorizationResultClass PolkitAuthorizationResultClass; GType polkit_authorization_result_get_type (void) G_GNUC_CONST; PolkitAuthorizationResult *polkit_authorization_result_new (gboolean is_authorized, gboolean is_challenge, PolkitDetails *details); PolkitDetails *polkit_authorization_result_get_details (PolkitAuthorizationResult *result); gboolean polkit_authorization_result_get_is_authorized (PolkitAuthorizationResult *result); gboolean polkit_authorization_result_get_is_challenge (PolkitAuthorizationResult *result); gboolean polkit_authorization_result_get_retains_authorization (PolkitAuthorizationResult *result); const gchar *polkit_authorization_result_get_temporary_authorization_id (PolkitAuthorizationResult *result); gboolean polkit_authorization_result_get_dismissed (PolkitAuthorizationResult *result); /* ---------------------------------------------------------------------------------------------------- */ G_END_DECLS #endif /* __POLKIT_AUTHORIZATION_RESULT_H */ polkit-126/src/polkit/polkitcheckauthorizationflags.c000066400000000000000000000016101474122443600232660ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include "polkitcheckauthorizationflags.h" #include "polkitprivate.h" polkit-126/src/polkit/polkitcheckauthorizationflags.h000066400000000000000000000036731474122443600233060ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_CHECK_AUTHORIZATION_FLAGS_H #define __POLKIT_CHECK_AUTHORIZATION_FLAGS_H #include G_BEGIN_DECLS /** * PolkitCheckAuthorizationFlags: * @POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE: No flags set. * @POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION: If the subject can obtain the authorization * through authentication, and an authentication agent is available, then attempt to do so. Note, this * means that the method used for checking authorization is likely to block for a long time. * @POLKIT_CHECK_AUTHORIZATION_FLAGS_ALWAYS_CHECK: Check access against policy even for root user. * * Possible flags when checking authorizations. */ typedef enum { POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE = 0, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1<<0), POLKIT_CHECK_AUTHORIZATION_FLAGS_ALWAYS_CHECK = (1<<1), } PolkitCheckAuthorizationFlags; G_END_DECLS #endif /* __POLKIT_CHECK_AUTHORIZATION_FLAGS_H */ polkit-126/src/polkit/polkitdetails.c000066400000000000000000000132001474122443600177760ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include "polkitimplicitauthorization.h" #include "polkitdetails.h" #include "polkitprivate.h" /** * SECTION:polkitdetails * @title: PolkitDetails * @short_description: Object used for passing details * @stability: Stable * * An object used for passing details around. */ /** * PolkitDetails: * * The #PolkitDetails struct should not be accessed directly. */ struct _PolkitDetails { GObject parent_instance; GHashTable *hash; }; struct _PolkitDetailsClass { GObjectClass parent_class; }; G_DEFINE_TYPE (PolkitDetails, polkit_details, G_TYPE_OBJECT); static void polkit_details_init (PolkitDetails *details) { } static void polkit_details_finalize (GObject *object) { PolkitDetails *details; details = POLKIT_DETAILS (object); if (details->hash != NULL) g_hash_table_unref (details->hash); if (G_OBJECT_CLASS (polkit_details_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_details_parent_class)->finalize (object); } static void polkit_details_class_init (PolkitDetailsClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_details_finalize; } /** * polkit_details_new: * * Creates a new #PolkitDetails object. * * Returns: A #PolkitDetails object. Free with g_object_unref(). */ PolkitDetails * polkit_details_new (void) { PolkitDetails *details; details = POLKIT_DETAILS (g_object_new (POLKIT_TYPE_DETAILS, NULL)); return details; } /* private */ static PolkitDetails * polkit_details_new_for_hash (GHashTable *hash) { PolkitDetails *details; details = POLKIT_DETAILS (g_object_new (POLKIT_TYPE_DETAILS, NULL)); if (hash != NULL) details->hash = g_hash_table_ref (hash); return details; } /** * polkit_details_lookup: * @details: A #PolkitDetails. * @key: A key. * * Gets the value for @key on @details. * * Returns: (allow-none): %NULL if there is no value for @key, otherwise a string owned by @details. */ const gchar * polkit_details_lookup (PolkitDetails *details, const gchar *key) { g_return_val_if_fail (POLKIT_IS_DETAILS (details), NULL); g_return_val_if_fail (key != NULL, NULL); if (details->hash == NULL) return NULL; else return g_hash_table_lookup (details->hash, key); } /** * polkit_details_insert: * @details: A #PolkitDetails. * @key: A key. * @value: (allow-none): A value. * * Inserts a copy of @key and @value on @details. * * If @value is %NULL, the key will be removed. */ void polkit_details_insert (PolkitDetails *details, const gchar *key, const gchar *value) { g_return_if_fail (POLKIT_IS_DETAILS (details)); g_return_if_fail (key != NULL); if (details->hash == NULL) details->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); if (value != NULL) g_hash_table_insert (details->hash, g_strdup (key), g_strdup (value)); else g_hash_table_remove (details->hash, key); } /** * polkit_details_get_keys: * @details: A #PolkitDetails. * * Gets a list of all keys on @details. * * Returns: (transfer full) (allow-none): %NULL if there are no keys * otherwise an array of strings that should be freed with * g_strfreev(). */ gchar ** polkit_details_get_keys (PolkitDetails *details) { GList *keys, *l; gchar **ret; guint n; g_return_val_if_fail (POLKIT_IS_DETAILS (details), NULL); if (details->hash == NULL) return NULL; keys = g_hash_table_get_keys (details->hash); ret = g_new0 (gchar*, g_list_length (keys) + 1); for (l = keys, n = 0; l != NULL; l = l->next, n++) ret[n] = g_strdup (l->data); g_list_free (keys); return ret; } /* Note that this returns a floating value. */ GVariant * polkit_details_to_gvariant (PolkitDetails *details) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); if (details != NULL && details->hash != NULL) { GHashTableIter hash_iter; const gchar *key; const gchar *value; g_hash_table_iter_init (&hash_iter, details->hash); while (g_hash_table_iter_next (&hash_iter, (gpointer) &key, (gpointer) &value)) g_variant_builder_add (&builder, "{ss}", key, value); } return g_variant_builder_end (&builder); } PolkitDetails * polkit_details_new_for_gvariant (GVariant *value) { PolkitDetails *ret; GHashTable *hash; GVariantIter iter; gchar *hash_key; gchar *hash_value; hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); g_variant_iter_init (&iter, value); while (g_variant_iter_next (&iter, "{ss}", &hash_key, &hash_value)) g_hash_table_insert (hash, hash_key, hash_value); ret = polkit_details_new_for_hash (hash); g_hash_table_unref (hash); return ret; } polkit-126/src/polkit/polkitdetails.h000066400000000000000000000046011474122443600200100ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_DETAILS_H #define __POLKIT_DETAILS_H #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_DETAILS (polkit_details_get_type()) #define POLKIT_DETAILS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_DETAILS, PolkitDetails)) #define POLKIT_DETAILS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_DETAILS, PolkitDetailsClass)) #define POLKIT_DETAILS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_DETAILS, PolkitDetailsClass)) #define POLKIT_IS_DETAILS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_DETAILS)) #define POLKIT_IS_DETAILS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_DETAILS)) #if 0 typedef struct _PolkitDetails PolkitDetails; #endif typedef struct _PolkitDetailsClass PolkitDetailsClass; GType polkit_details_get_type (void) G_GNUC_CONST; PolkitDetails *polkit_details_new (void); const gchar *polkit_details_lookup (PolkitDetails *details, const gchar *key); void polkit_details_insert (PolkitDetails *details, const gchar *key, const gchar *value); gchar **polkit_details_get_keys (PolkitDetails *details); G_END_DECLS #endif /* __POLKIT_DETAILS_H */ polkit-126/src/polkit/polkitenumtypes.c.template000066400000000000000000000016531474122443600222250ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; if (g_once_init_enter ((gsize*) &g_define_type_id__volatile)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType g_define_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); } return g_define_type_id__volatile; } /*** END value-tail ***/ /*** BEGIN file-tail ***/ /*** END file-tail ***/ polkit-126/src/polkit/polkitenumtypes.h.template000066400000000000000000000010221474122443600222200ustar00rootroot00000000000000/*** BEGIN file-header ***/ #ifndef __POLKIT_ENUM_TYPES_H__ #define __POLKIT_ENUM_TYPES_H__ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) /*** END value-header ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* __POLKIT_ENUM_TYPES_H__ */ /*** END file-tail ***/ polkit-126/src/polkit/polkiterror.c000066400000000000000000000034501474122443600175100ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include "polkiterror.h" #include "polkitprivate.h" /** * SECTION:polkiterror * @title: PolkitError * @short_description: Error codes * * Error codes. */ static const GDBusErrorEntry polkit_error_entries[] = { {POLKIT_ERROR_FAILED, "org.freedesktop.PolicyKit1.Error.Failed"}, {POLKIT_ERROR_CANCELLED, "org.freedesktop.PolicyKit1.Error.Cancelled"}, {POLKIT_ERROR_NOT_SUPPORTED, "org.freedesktop.PolicyKit1.Error.NotSupported"}, {POLKIT_ERROR_NOT_AUTHORIZED, "org.freedesktop.PolicyKit1.Error.NotAuthorized"}, }; GQuark polkit_error_quark (void) { static volatile gsize quark_volatile = 0; g_dbus_error_register_error_domain ("polkit-error-quark", &quark_volatile, polkit_error_entries, G_N_ELEMENTS (polkit_error_entries)); G_STATIC_ASSERT (G_N_ELEMENTS (polkit_error_entries) - 1 == POLKIT_ERROR_NOT_AUTHORIZED); return (GQuark) quark_volatile; } polkit-126/src/polkit/polkiterror.h000066400000000000000000000035251474122443600175200ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_ERROR_H #define __POLKIT_ERROR_H #include G_BEGIN_DECLS /** * POLKIT_ERROR: * * Error domain for errors when using PolicyKit. Errors in this domain will be from the #PolkitError * enumeration. See #GError for information on error domains */ #define POLKIT_ERROR (polkit_error_quark()) GQuark polkit_error_quark (void); /** * PolkitError: * @POLKIT_ERROR_FAILED: The operation failed. * @POLKIT_ERROR_CANCELLED: The operation was cancelled. * @POLKIT_ERROR_NOT_SUPPORTED: Operation is not supported. * @POLKIT_ERROR_NOT_AUTHORIZED: Not authorized to perform operation. * * Possible error when using PolicyKit. */ typedef enum { POLKIT_ERROR_FAILED = 0, POLKIT_ERROR_CANCELLED = 1, POLKIT_ERROR_NOT_SUPPORTED = 2, POLKIT_ERROR_NOT_AUTHORIZED = 3, } PolkitError; G_END_DECLS #endif /* __POLKIT_ERROR_H */ polkit-126/src/polkit/polkitidentity.c000066400000000000000000000251261474122443600202140ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include "polkitidentity.h" #include "polkitunixuser.h" #include "polkitunixgroup.h" #include "polkitunixnetgroup.h" #include "polkiterror.h" #include "polkitprivate.h" /** * SECTION:polkitidentity * @title: PolkitIdentity * @short_description: Type for representing identities * * #PolkitIdentity is an abstract type for representing one or more * identities. */ static void base_init (gpointer g_iface) { } GType polkit_identity_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; if (g_once_init_enter ((gsize*) &g_define_type_id__volatile)) { static const GTypeInfo info = { sizeof (PolkitIdentityIface), base_init, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, /* instance_size */ 0, /* n_preallocs */ NULL, /* instance_init */ NULL /* value_table */ }; GType iface_type = g_type_register_static (G_TYPE_INTERFACE, "PolkitIdentity", &info, 0); g_type_interface_add_prerequisite (iface_type, G_TYPE_OBJECT); g_once_init_leave (&g_define_type_id__volatile, iface_type); } return g_define_type_id__volatile; } /** * polkit_identity_hash: * @identity: A #PolkitIdentity. * * Gets a hash code for @identity that can be used with e.g. g_hash_table_new(). * * Returns: A hash code. */ guint polkit_identity_hash (PolkitIdentity *identity) { g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), 0); return POLKIT_IDENTITY_GET_IFACE (identity)->hash (identity); } /** * polkit_identity_equal: * @a: A #PolkitIdentity. * @b: A #PolkitIdentity. * * Checks if @a and @b are equal, ie. represent the same identity. * * This function can be used in e.g. g_hash_table_new(). * * Returns: %TRUE if @a and @b are equal, %FALSE otherwise. */ gboolean polkit_identity_equal (PolkitIdentity *a, PolkitIdentity *b) { g_return_val_if_fail (POLKIT_IS_IDENTITY (a), FALSE); g_return_val_if_fail (POLKIT_IS_IDENTITY (b), FALSE); if (!g_type_is_a (G_TYPE_FROM_INSTANCE (a), G_TYPE_FROM_INSTANCE (b))) return FALSE; return POLKIT_IDENTITY_GET_IFACE (a)->equal (a, b); } /** * polkit_identity_to_string: * @identity: A #PolkitIdentity. * * Serializes @identity to a string that can be used in * polkit_identity_from_string(). * * Returns: A string representing @identity. Free with g_free(). */ gchar * polkit_identity_to_string (PolkitIdentity *identity) { g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), NULL); return POLKIT_IDENTITY_GET_IFACE (identity)->to_string (identity); } /** * polkit_identity_from_string: * @str: A string obtained from polkit_identity_to_string(). * @error: Return location for error. * * Creates an object from @str that implements the #PolkitIdentity * interface. * * Returns: (allow-none) (transfer full): A #PolkitIdentity or %NULL * if @error is set. Free with g_object_unref(). */ PolkitIdentity * polkit_identity_from_string (const gchar *str, GError **error) { PolkitIdentity *identity; guint64 val; gchar *endptr; g_return_val_if_fail (str != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); /* TODO: we could do something with VFuncs like in g_icon_from_string() */ identity = NULL; if (g_str_has_prefix (str, "unix-user:")) { val = g_ascii_strtoull (str + sizeof "unix-user:" - 1, &endptr, 10); if (*endptr == '\0') identity = polkit_unix_user_new ((gint) val); else identity = polkit_unix_user_new_for_name (str + sizeof "unix-user:" - 1, error); } else if (g_str_has_prefix (str, "unix-group:")) { val = g_ascii_strtoull (str + sizeof "unix-group:" - 1, &endptr, 10); if (*endptr == '\0') identity = polkit_unix_group_new ((gint) val); else identity = polkit_unix_group_new_for_name (str + sizeof "unix-group:" - 1, error); } else if (g_str_has_prefix (str, "unix-netgroup:")) { #ifndef HAVE_SETNETGRENT g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Netgroups are not available on this machine ('%s')", str); #else identity = polkit_unix_netgroup_new (str + sizeof "unix-netgroup:" - 1); #endif } if (identity == NULL && (error != NULL && *error == NULL)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Malformed identity string '%s'", str); } return identity; } /* Note that this returns a floating value. */ GVariant * polkit_identity_to_gvariant (PolkitIdentity *identity) { GVariantBuilder builder; GVariant *dict; const gchar *kind; kind = ""; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (POLKIT_IS_UNIX_USER (identity)) { kind = "unix-user"; g_variant_builder_add (&builder, "{sv}", "uid", g_variant_new_uint32 (polkit_unix_user_get_uid (POLKIT_UNIX_USER (identity)))); } else if (POLKIT_IS_UNIX_GROUP (identity)) { kind = "unix-group"; g_variant_builder_add (&builder, "{sv}", "gid", g_variant_new_uint32 (polkit_unix_group_get_gid (POLKIT_UNIX_GROUP (identity)))); } else if (POLKIT_IS_UNIX_NETGROUP (identity)) { kind = "unix-netgroup"; g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string (polkit_unix_netgroup_get_name (POLKIT_UNIX_NETGROUP (identity)))); } else { g_warning ("Unknown class %s implementing PolkitIdentity", g_type_name (G_TYPE_FROM_INSTANCE (identity))); } dict = g_variant_builder_end (&builder); return g_variant_new ("(s@a{sv})", kind, dict); } static GVariant * lookup_asv (GVariant *dict, const gchar *given_key, const GVariantType *given_type, GError **error) { GVariantIter iter; const gchar *key; GVariant *value; GVariant *ret; ret = NULL; g_variant_iter_init (&iter, dict); while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) { if (g_strcmp0 (key, given_key) == 0) { if (!g_variant_is_of_type (value, given_type)) { gchar *type_string; type_string = g_variant_type_dup_string (given_type); g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Value for key `%s' found but is of type %s and type %s was expected", given_key, g_variant_get_type_string (value), type_string); g_free (type_string); g_variant_unref (value); goto out; } ret = value; goto out; } g_variant_unref (value); } out: if (ret == NULL) { gchar *type_string; type_string = g_variant_type_dup_string (given_type); g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Didn't find value for key `%s' of type %s", given_key, type_string); g_free (type_string); } return ret; } PolkitIdentity * polkit_identity_new_for_gvariant (GVariant *variant, GError **error) { PolkitIdentity *ret; const gchar *kind; GVariant *details_gvariant; ret = NULL; g_variant_get (variant, "(&s@a{sv})", &kind, &details_gvariant); if (g_strcmp0 (kind, "unix-user") == 0) { GVariant *v; guint32 uid; v = lookup_asv (details_gvariant, "uid", G_VARIANT_TYPE_UINT32, error); if (v == NULL) { g_prefix_error (error, "Error parsing unix-user identity: "); goto out; } uid = g_variant_get_uint32 (v); g_variant_unref (v); ret = polkit_unix_user_new (uid); } else if (g_strcmp0 (kind, "unix-group") == 0) { GVariant *v; guint32 gid; v = lookup_asv (details_gvariant, "gid", G_VARIANT_TYPE_UINT32, error); if (v == NULL) { g_prefix_error (error, "Error parsing unix-user identity: "); goto out; } gid = g_variant_get_uint32 (v); g_variant_unref (v); ret = polkit_unix_group_new (gid); } else if (g_strcmp0 (kind, "unix-netgroup") == 0) { GVariant *v; const char *name; #ifndef HAVE_SETNETGRENT g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Netgroups are not available on this machine"); goto out; #else v = lookup_asv (details_gvariant, "name", G_VARIANT_TYPE_STRING, error); if (v == NULL) { g_prefix_error (error, "Error parsing net identity: "); goto out; } name = g_variant_get_string (v, NULL); ret = polkit_unix_netgroup_new (name); g_variant_unref (v); #endif } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Unknown identity of kind `%s'", kind); } out: g_variant_unref (details_gvariant); return ret; } polkit-126/src/polkit/polkitidentity.h000066400000000000000000000055241474122443600202210ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_IDENTITY_H #define __POLKIT_IDENTITY_H #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_IDENTITY (polkit_identity_get_type()) #define POLKIT_IDENTITY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_IDENTITY, PolkitIdentity)) #define POLKIT_IS_IDENTITY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_IDENTITY)) #define POLKIT_IDENTITY_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), POLKIT_TYPE_IDENTITY, PolkitIdentityIface)) #if 0 /** * PolkitIdentity: * * Generic type for all objects that can be used as identities. */ typedef struct _PolkitIdentity PolkitIdentity; /* Dummy typedef */ #endif typedef struct _PolkitIdentityIface PolkitIdentityIface; /** * PolkitIdentityIface: * @parent_iface: The parent interface. * @hash: Gets a hash value for a #PolkitIdentity. * @equal: Checks if two #PolkitIdentitys are equal. * @to_string: Serializes a #PolkitIdentity to a string that can be * used in polkit_identity_from_string(). * * An interface for identities. */ struct _PolkitIdentityIface { GTypeInterface parent_iface; guint (*hash) (PolkitIdentity *identity); gboolean (*equal) (PolkitIdentity *a, PolkitIdentity *b); gchar * (*to_string) (PolkitIdentity *identity); }; GType polkit_identity_get_type (void) G_GNUC_CONST; guint polkit_identity_hash (PolkitIdentity *identity); gboolean polkit_identity_equal (PolkitIdentity *a, PolkitIdentity *b); gchar *polkit_identity_to_string (PolkitIdentity *identity); PolkitIdentity *polkit_identity_from_string (const gchar *str, GError **error); G_END_DECLS #endif /* __POLKIT_IDENTITY_H */ polkit-126/src/polkit/polkitimplicitauthorization.c000066400000000000000000000062021474122443600230100ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include "polkitimplicitauthorization.h" #include "polkitprivate.h" gboolean polkit_implicit_authorization_from_string (const gchar *string, PolkitImplicitAuthorization *out_implicit_authorization) { PolkitImplicitAuthorization result; gboolean ret; ret = TRUE; result = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; if (strcmp (string, "no") == 0) { result = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; } else if (strcmp (string, "auth_self") == 0) { result = POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED; } else if (strcmp (string, "auth_admin") == 0) { result = POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED; } else if (strcmp (string, "auth_self_keep") == 0) { result = POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED_RETAINED; } else if (strcmp (string, "auth_admin_keep") == 0) { result = POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED; } else if (strcmp (string, "yes") == 0) { result = POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED; } else { g_warning ("Unknown PolkitImplicitAuthorization string '%s'", string); ret = FALSE; result = POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN; } if (out_implicit_authorization != NULL) *out_implicit_authorization = result; return ret; } const gchar * polkit_implicit_authorization_to_string (PolkitImplicitAuthorization implicit_authorization) { const gchar *s; s = "(unknown)"; switch (implicit_authorization) { case POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN: s = "unknown"; break; case POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED: s = "no"; break; case POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED: s = "auth_self"; break; case POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED: s = "auth_admin"; break; case POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED_RETAINED: s = "auth_self_keep"; break; case POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED: s = "auth_admin_keep"; break; case POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED: s = "yes"; break; } return s; } polkit-126/src/polkit/polkitimplicitauthorization.h000066400000000000000000000060261474122443600230210ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_IMPLICIT_AUTHORIZATION_H #define __POLKIT_IMPLICIT_AUTHORIZATION_H #include G_BEGIN_DECLS /** * PolkitImplicitAuthorization: * @POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN: Unknown whether the subject is authorized, never returned in any public API. * @POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED: Subject is not authorized. * @POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED: Authentication is required. * @POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED: Authentication as an administrator is required. * @POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED_RETAINED: Authentication is required. If the authorization is obtained, it is retained. * @POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED: Authentication as an administrator is required. If the authorization is obtained, it is retained. * @POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED: The subject is authorized * * Possible implicit authorizations. */ typedef enum { POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN = -1, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED = 0, POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED = 1, POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED = 2, POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED_RETAINED = 3, POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED = 4, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED = 5, } PolkitImplicitAuthorization; const gchar *polkit_implicit_authorization_to_string (PolkitImplicitAuthorization implicit_authorization); /** * polkit_implicit_authorization_from_string: * @string: A string * @out_implicit_authorization: (out caller-allocates): The location of the resulting deserialization */ gboolean polkit_implicit_authorization_from_string (const gchar *string, PolkitImplicitAuthorization *out_implicit_authorization); G_END_DECLS #endif /* __POLKIT_IMPLICIT_AUTHORIZATION_H */ polkit-126/src/polkit/polkitpermission.c000066400000000000000000000774211474122443600205600ustar00rootroot00000000000000/* * Copyright (C) 2008-2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Matthias Clasen * David Zeuthen */ #ifdef HAVE_LIBSYSTEMD # include #endif #include #include #include #include "polkitpermission.h" #include #include "polkitpermission.h" /** * SECTION:polkitpermission * @title: PolkitPermission * @short_description: PolicyKit #GPermission implementation * @stability: Stable * * #PolkitPermission is a #GPermission implementation. It can be used * with e.g. #GtkLockButton. See the #GPermission documentation for * more information. */ typedef GPermissionClass PolkitPermissionClass; /** * PolkitPermission: * * The #PolkitPermission struct should not be accessed directly. */ struct _PolkitPermission { GPermission parent_instance; PolkitAuthority *authority; PolkitSubject *subject; gchar *action_id; gchar *session_state; /* non-NULL exactly when authorized with a temporary authorization */ gchar *tmp_authz_id; }; enum { PROP_0, PROP_ACTION_ID, PROP_SUBJECT }; static void process_result (PolkitPermission *permission, PolkitAuthorizationResult *result); static char *get_session_state(void); static void on_authority_changed (PolkitAuthority *authority, gpointer user_data); static void on_sessions_changed (PolkitAuthority *authority, gpointer user_data); static gboolean acquire (GPermission *permission, GCancellable *cancellable, GError **error); static void acquire_async (GPermission *permission, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static gboolean acquire_finish (GPermission *permission, GAsyncResult *result, GError **error); static gboolean release (GPermission *permission, GCancellable *cancellable, GError **error); static void release_async (GPermission *permission, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static gboolean release_finish (GPermission *permission, GAsyncResult *result, GError **error); static void initable_iface_init (GInitableIface *initable_iface); static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); static gboolean polkit_permission_initable_init (GInitable *initable, GCancellable *cancellable, GError **error); G_DEFINE_TYPE_WITH_CODE (PolkitPermission, polkit_permission, G_TYPE_PERMISSION, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)) static void polkit_permission_init (PolkitPermission *simple) { } static void polkit_permission_constructed (GObject *object) { PolkitPermission *permission = POLKIT_PERMISSION (object); if (permission->subject == NULL) permission->subject = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); if (G_OBJECT_CLASS (polkit_permission_parent_class)->constructed != NULL) G_OBJECT_CLASS (polkit_permission_parent_class)->constructed (object); permission->session_state = get_session_state(); } static void polkit_permission_finalize (GObject *object) { PolkitPermission *permission = POLKIT_PERMISSION (object); g_free (permission->action_id); g_free (permission->tmp_authz_id); g_free (permission->session_state); g_object_unref (permission->subject); if (permission->authority != NULL) { g_signal_handlers_disconnect_by_func (permission->authority, on_authority_changed, permission); g_signal_handlers_disconnect_by_func (permission->authority, on_sessions_changed, permission); g_object_unref (permission->authority); } if (G_OBJECT_CLASS (polkit_permission_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_permission_parent_class)->finalize (object); } static void polkit_permission_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PolkitPermission *permission = POLKIT_PERMISSION (object); switch (property_id) { case PROP_ACTION_ID: g_value_set_string (value, permission->action_id); break; case PROP_SUBJECT: g_value_set_object (value, permission->subject); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void polkit_permission_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PolkitPermission *permission = POLKIT_PERMISSION (object); switch (property_id) { case PROP_ACTION_ID: permission->action_id = g_value_dup_string (value); break; case PROP_SUBJECT: permission->subject = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void polkit_permission_class_init (PolkitPermissionClass *class) { GObjectClass *object_class; GPermissionClass *permission_class; permission_class = G_PERMISSION_CLASS (class); permission_class->acquire = acquire; permission_class->acquire_async = acquire_async; permission_class->acquire_finish = acquire_finish; permission_class->release = release; permission_class->release_async = release_async; permission_class->release_finish = release_finish; object_class = G_OBJECT_CLASS (class); object_class->finalize = polkit_permission_finalize; object_class->constructed = polkit_permission_constructed; object_class->get_property = polkit_permission_get_property; object_class->set_property = polkit_permission_set_property; /** * PolkitPermission:action-id: * * The action identifier to use for the permission. */ g_object_class_install_property (object_class, PROP_ACTION_ID, g_param_spec_string ("action-id", "Action Identifier", "The action identifier to use for the permission", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * PolkitPermission:subject: * * The #PolkitSubject to use for the permission. If not set during * construction, it will be set to match the current process. */ g_object_class_install_property (object_class, PROP_SUBJECT, g_param_spec_object ("subject", "Subject", "The subject to use for the permission", POLKIT_TYPE_SUBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } /** * polkit_permission_new: * @action_id: The PolicyKit action identifier. * @subject: (allow-none): A #PolkitSubject or %NULL for the current process. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Creates a #GPermission instance for the PolicyKit action * @action_id. * * When the operation is finished, @callback will be invoked. You can * then call polkit_permission_new_finish() to get the result of the * operation. * * This is a asynchronous failable constructor. See * polkit_permission_new_sync() for the synchronous version. */ void polkit_permission_new (const gchar *action_id, PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (action_id != NULL); g_return_if_fail (subject == NULL || POLKIT_IS_SUBJECT (subject)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_async_initable_new_async (POLKIT_TYPE_PERMISSION, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "action-id", action_id, "subject", subject, NULL); } /** * polkit_permission_new_finish: * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to polkit_permission_new(). * @error: (allow-none): Return location for error or %NULL. * * Finishes an operation started with polkit_permission_new(). * * Returns: A #GPermission or %NULL if @error is set. */ GPermission * polkit_permission_new_finish (GAsyncResult *res, GError **error) { GObject *object; GObject *source_object; g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); source_object = g_async_result_get_source_object (res); g_assert (source_object != NULL); object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); g_object_unref (source_object); if (object != NULL) return G_PERMISSION (object); else return NULL; } /** * polkit_permission_new_sync: * @action_id: The PolicyKit action identifier. * @subject: (allow-none): A #PolkitSubject or %NULL for the current process. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Creates a #GPermission instance for the PolicyKit action * @action_id. * * This is a synchronous failable constructor. See * polkit_permission_new() for the asynchronous version. * * Returns: A #GPermission or %NULL if @error is set. */ GPermission * polkit_permission_new_sync (const gchar *action_id, PolkitSubject *subject, GCancellable *cancellable, GError **error) { g_return_val_if_fail (action_id != NULL, NULL); g_return_val_if_fail (subject == NULL || POLKIT_IS_SUBJECT (subject), NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); return g_initable_new (POLKIT_TYPE_PERMISSION, cancellable, error, "action-id", action_id, "subject", subject, NULL); } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = polkit_permission_initable_init; } static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface) { /* for now, we use default implementation to run GInitable code in a * thread - would probably be nice to have real async version to * avoid the thread-overhead */ } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_permission_get_action_id: * @permission: A #PolkitPermission. * * Gets the PolicyKit action identifier used for @permission. * * Returns: A string owned by @permission. Do not free. */ const gchar * polkit_permission_get_action_id (PolkitPermission *permission) { g_return_val_if_fail (POLKIT_IS_PERMISSION (permission), NULL); return permission->action_id; } /** * polkit_permission_get_subject: * @permission: A #PolkitPermission. * * Gets the subject used for @permission. * * Returns: (transfer none): An object owned by @permission. Do not free. */ PolkitSubject * polkit_permission_get_subject (PolkitPermission *permission) { g_return_val_if_fail (POLKIT_IS_PERMISSION (permission), NULL); return permission->subject; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean polkit_permission_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { PolkitPermission *permission = POLKIT_PERMISSION (initable); PolkitAuthorizationResult *result; gboolean ret; ret = FALSE; permission->authority = polkit_authority_get_sync (cancellable, error); if (permission->authority == NULL) goto out; g_signal_connect (permission->authority, "changed", G_CALLBACK (on_authority_changed), permission); g_signal_connect (permission->authority, "sessions-changed", G_CALLBACK (on_sessions_changed), permission); result = polkit_authority_check_authorization_sync (permission->authority, permission->subject, permission->action_id, NULL, /* PolkitDetails */ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, cancellable, error); if (result == NULL) goto out; process_result (permission, result); g_object_unref (result); ret = TRUE; out: return ret; } /* ---------------------------------------------------------------------------------------------------- */ static void changed_check_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { PolkitPermission *permission = POLKIT_PERMISSION (user_data); PolkitAuthorizationResult *result; GError *error; error = NULL; result = polkit_authority_check_authorization_finish (permission->authority, res, &error); if (result != NULL) { process_result (permission, result); g_object_unref (result); } else { /* this really should never fail (since we are not passing any * details) so log to stderr if it happens */ g_assert (error != NULL); g_warning ("Error checking authorization for action id %s: %s", permission->action_id, error->message); g_error_free (error); } g_object_unref (permission); } static char *get_session_state(void) { #ifdef HAVE_LIBSYSTEMD char *session = NULL; char *state = NULL; uid_t uid; if ( sd_pid_get_session(getpid(), &session) < 0 ) { if ( sd_pid_get_owner_uid(getpid(), &uid) < 0) { goto out; } if (sd_uid_get_display(uid, &session) < 0) { goto out; } } if (session != NULL) { sd_session_get_state(session, &state); } out: g_free(session); return state; #else return NULL; #endif } static void on_authority_changed (PolkitAuthority *authority, gpointer user_data) { PolkitPermission *permission = POLKIT_PERMISSION (user_data); polkit_authority_check_authorization (permission->authority, permission->subject, permission->action_id, NULL, /* PolkitDetails */ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, NULL /* cancellable */, changed_check_cb, g_object_ref (permission)); } static void on_sessions_changed (PolkitAuthority *authority, gpointer user_data) { #ifdef HAVE_LIBSYSTEMD char *new_session_state = NULL; char *last_state = NULL; PolkitPermission *permission = POLKIT_PERMISSION (user_data); new_session_state = get_session_state(); /* if we cannot tell the session state, we should do CheckAuthorization anyway */ if ((new_session_state == NULL) || ( g_strcmp0(new_session_state, permission->session_state) != 0 )) { last_state = permission->session_state; permission->session_state = new_session_state; g_free(last_state); polkit_authority_check_authorization (permission->authority, permission->subject, permission->action_id, NULL, /* PolkitDetails */ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, NULL /* cancellable */, changed_check_cb, g_object_ref (permission)); } #else on_authority_changed(authority, user_data); /* TODO: resolve the "too many session signals" issue for non-systemd systems later */ #endif } static void process_result (PolkitPermission *permission, PolkitAuthorizationResult *result) { gboolean can_acquire; gboolean can_release; gboolean allowed; /* save the temporary authorization id */ g_free (permission->tmp_authz_id); permission->tmp_authz_id = g_strdup (polkit_authorization_result_get_temporary_authorization_id (result)); allowed = polkit_authorization_result_get_is_authorized (result); if (permission->tmp_authz_id != NULL) { can_acquire = FALSE; can_release = TRUE; } else { if (allowed) can_acquire = FALSE; else can_acquire = polkit_authorization_result_get_retains_authorization (result); can_release = FALSE; } g_permission_impl_update (G_PERMISSION (permission), allowed, can_acquire, can_release); } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { PolkitPermission *permission; GSimpleAsyncResult *simple; } AcquireData; static void acquire_data_free (AcquireData *data) { g_object_unref (data->simple); g_free (data); } static void acquire_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { AcquireData *data = user_data; PolkitAuthorizationResult *result; GError *error; error = NULL; result = polkit_authority_check_authorization_finish (data->permission->authority, res, &error); if (result != NULL) { /* Process the result such that allowed, can_acquire and * can_release are updated before returning to the user - see * also release_cb for where we do this as well */ process_result (data->permission, result); if (!polkit_authorization_result_get_is_authorized (result)) { if (polkit_authorization_result_get_dismissed (result)) { g_simple_async_result_set_error (data->simple, G_IO_ERROR, G_IO_ERROR_CANCELLED, "User dismissed authentication dialog while trying to acquire permission for action-id %s", data->permission->action_id); } else { g_simple_async_result_set_error (data->simple, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Failed to acquire permission for action-id %s", data->permission->action_id); } } g_object_unref (result); } else { g_simple_async_result_set_from_error (data->simple, error); g_error_free (error); } /* don't complete in idle since we're already completing in idle * due to how PolkitAuthority works */ g_simple_async_result_complete (data->simple); acquire_data_free (data); } static void acquire_async (GPermission *gpermission, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PolkitPermission *permission = POLKIT_PERMISSION (gpermission); AcquireData *data; data = g_new0 (AcquireData, 1); data->permission = permission; data->simple = g_simple_async_result_new (G_OBJECT (permission), callback, user_data, acquire_async); polkit_authority_check_authorization (permission->authority, permission->subject, permission->action_id, NULL, /* PolkitDetails */ POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, cancellable, acquire_cb, data); } static gboolean acquire_finish (GPermission *gpermission, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; simple = G_SIMPLE_ASYNC_RESULT (result); g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == acquire_async); if (g_simple_async_result_propagate_error (simple, error)) return FALSE; return TRUE; } static gboolean acquire (GPermission *gpermission, GCancellable *cancellable, GError **error) { PolkitPermission *permission = POLKIT_PERMISSION (gpermission); PolkitAuthorizationResult *result; gboolean ret; ret = FALSE; result = polkit_authority_check_authorization_sync (permission->authority, permission->subject, permission->action_id, NULL, /* PolkitDetails */ POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, cancellable, error); if (result != NULL) { /* need to update allowed, can_acquire, can_release before returning to the user */ process_result (permission, result); if (polkit_authorization_result_get_is_authorized (result)) { ret = TRUE; } else if (polkit_authorization_result_get_dismissed (result)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "User dismissed authentication dialog while trying to acquire permission for action-id %s", permission->action_id); } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Failed to acquire permission for action-id %s", permission->action_id); } g_object_unref (result); } return ret; } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { PolkitPermission *permission; GSimpleAsyncResult *simple; } ReleaseData; static void release_data_free (ReleaseData *data) { g_object_unref (data->simple); g_free (data); } static void release_check_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { ReleaseData *data = user_data; PolkitAuthorizationResult *result; GError *error; error = NULL; result = polkit_authority_check_authorization_finish (data->permission->authority, res, &error); if (result == NULL) { g_prefix_error (&error, "Error checking authorization for action id %s after releasing the permission: ", data->permission->action_id); g_simple_async_result_set_from_error (data->simple, error); g_error_free (error); } else { process_result (data->permission, result); g_object_unref (result); } /* don't complete in idle since we're already completing in idle * due to how PolkitAuthority works */ g_simple_async_result_complete (data->simple); release_data_free (data); } static void release_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { ReleaseData *data = user_data; GError *error; gboolean ret; ret = FALSE; error = NULL; ret = polkit_authority_revoke_temporary_authorization_by_id_finish (data->permission->authority, res, &error); if (!ret) { g_simple_async_result_set_from_error (data->simple, error); g_error_free (error); /* don't complete in idle since we're already completing in idle * due to how PolkitAuthority works */ g_simple_async_result_complete (data->simple); release_data_free (data); } else { /* need to update allowed, can_acquire and can_release before * returning to the user - see also acquire_cb where we do this * as well */ polkit_authority_check_authorization (data->permission->authority, data->permission->subject, data->permission->action_id, NULL, /* PolkitDetails */ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, NULL /* cancellable */, release_check_cb, data); } } static void release_async (GPermission *gpermission, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PolkitPermission *permission = POLKIT_PERMISSION (gpermission); ReleaseData *data; data = g_new0 (ReleaseData, 1); data->permission = permission; data->simple = g_simple_async_result_new (G_OBJECT (permission), callback, user_data, release_async); if (permission->tmp_authz_id == NULL) { g_simple_async_result_set_error (data->simple, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot release permission: no temporary authorization for action-id %s exist", permission->action_id); g_simple_async_result_complete_in_idle (data->simple); release_data_free (data); goto out; } polkit_authority_revoke_temporary_authorization_by_id (permission->authority, permission->tmp_authz_id, cancellable, release_cb, data); out: ; } static gboolean release_finish (GPermission *gpermission, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; simple = G_SIMPLE_ASYNC_RESULT (result); g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == release_async); if (g_simple_async_result_propagate_error (simple, error)) return FALSE; return TRUE; } static gboolean release (GPermission *gpermission, GCancellable *cancellable, GError **error) { PolkitPermission *permission = POLKIT_PERMISSION (gpermission); PolkitAuthorizationResult *result; gboolean ret; ret = FALSE; if (permission->tmp_authz_id == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot release permission: no temporary authorization for action-id %s exist", permission->action_id); goto out; } ret = polkit_authority_revoke_temporary_authorization_by_id_sync (permission->authority, permission->tmp_authz_id, cancellable, error); if (!ret) goto out; /* need to update allowed, can_acquire, can_release before returning to the user */ result = polkit_authority_check_authorization_sync (permission->authority, permission->subject, permission->action_id, NULL, /* PolkitDetails */ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, cancellable, error); if (result == NULL) goto out; process_result (permission, result); g_object_unref (result); out: return ret; } /* ---------------------------------------------------------------------------------------------------- */ polkit-126/src/polkit/polkitpermission.h000066400000000000000000000050771474122443600205630ustar00rootroot00000000000000/* * Copyright (C) 2008-2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Matthias Clasen * David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_PERMISSION_H #define __POLKIT_PERMISSION_H #include #include G_BEGIN_DECLS #define POLKIT_TYPE_PERMISSION (polkit_permission_get_type ()) #define POLKIT_PERMISSION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_PERMISSION, PolkitPermission)) #define POLKIT_IS_PERMISSION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_PERMISSION)) GType polkit_permission_get_type (void) G_GNUC_CONST; void polkit_permission_new (const gchar *action_id, PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GPermission *polkit_permission_new_finish (GAsyncResult *res, GError **error); GPermission *polkit_permission_new_sync (const gchar *action_id, PolkitSubject *subject, GCancellable *cancellable, GError **error); const gchar *polkit_permission_get_action_id (PolkitPermission *permission); PolkitSubject *polkit_permission_get_subject (PolkitPermission *permission); G_END_DECLS #endif /* __POLKIT_PERMISSION_H */ polkit-126/src/polkit/polkitprivate.h000066400000000000000000000103671474122443600200430ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #ifndef __POLKIT_PRIVATE_H #define __POLKIT_PRIVATE_H #include "polkitimplicitauthorization.h" #include "polkitactiondescription.h" #include "polkitsubject.h" #include "polkitauthorizationresult.h" #include "polkittemporaryauthorization.h" /* FIXME: This header file is currently installed among other public header files, and the symbols are exported in the shared library. For application writers: relying on any function here is strongly discouraged. For polkit maintainers: This should be made private if a large ABI break were necessary in the future. In the meantime, consider that there is non-zero risk that changing these functions might break some applications. */ PolkitActionDescription *polkit_action_description_new_for_gvariant (GVariant *value); GVariant *polkit_action_description_to_gvariant (PolkitActionDescription *action_description); GVariant *polkit_subject_to_gvariant (PolkitSubject *subject); GVariant *polkit_identity_to_gvariant (PolkitIdentity *identity); gint polkit_unix_process_get_racy_uid__ (PolkitUnixProcess *process, GError **error); PolkitSubject *polkit_subject_new_for_gvariant (GVariant *variant, GError **error); PolkitSubject *polkit_subject_new_for_gvariant_invocation (GVariant *variant, GDBusMethodInvocation *invocation, GError **error); PolkitIdentity *polkit_identity_new_for_gvariant (GVariant *variant, GError **error); PolkitAuthorizationResult *polkit_authorization_result_new_for_gvariant (GVariant *value); GVariant *polkit_authorization_result_to_gvariant (PolkitAuthorizationResult *authorization_result); PolkitTemporaryAuthorization *polkit_temporary_authorization_new (const gchar *id, const gchar *action_id, PolkitSubject *subject, guint64 time_obtained, guint64 time_expires); PolkitTemporaryAuthorization *polkit_temporary_authorization_new_for_gvariant (GVariant *value, GError **error); GVariant *polkit_temporary_authorization_to_gvariant (PolkitTemporaryAuthorization *authorization); GVariant *polkit_details_to_gvariant (PolkitDetails *details); PolkitDetails *polkit_details_new_for_gvariant (GVariant *value); PolkitActionDescription * polkit_action_description_new (const gchar *action_id, const gchar *description, const gchar *message, const gchar *vendor_name, const gchar *vendor_url, const gchar *icon_name, PolkitImplicitAuthorization implicit_any, PolkitImplicitAuthorization implicit_inactive, PolkitImplicitAuthorization implicit_active, GHashTable *annotations); #endif /* __POLKIT_PRIVATE_H */ polkit-126/src/polkit/polkitsubject.c000066400000000000000000000422301474122443600200150ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include "polkitsubject.h" #include "polkitunixprocess.h" #include "polkitunixsession.h" #include "polkitsystembusname.h" #include "polkiterror.h" #include "polkitprivate.h" /** * SECTION:polkitsubject * @title: PolkitSubject * @short_description: Type for representing subjects * * #PolkitSubject is an abstract type for representing one or more * processes. */ static void base_init (gpointer g_iface) { } GType polkit_subject_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; if (g_once_init_enter ((gsize*) &g_define_type_id__volatile)) { static const GTypeInfo info = { sizeof (PolkitSubjectIface), base_init, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, /* instance_size */ 0, /* n_preallocs */ NULL, /* instance_init */ NULL /* value_table */ }; GType iface_type = g_type_register_static (G_TYPE_INTERFACE, "PolkitSubject", &info, 0); g_type_interface_add_prerequisite (iface_type, G_TYPE_OBJECT); g_once_init_leave (&g_define_type_id__volatile, iface_type); } return g_define_type_id__volatile; } /** * polkit_subject_hash: * @subject: A #PolkitSubject. * * Gets a hash code for @subject that can be used with e.g. g_hash_table_new(). * * Returns: A hash code. */ guint polkit_subject_hash (PolkitSubject *subject) { g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), 0); return POLKIT_SUBJECT_GET_IFACE (subject)->hash (subject); } /** * polkit_subject_equal: * @a: A #PolkitSubject. * @b: A #PolkitSubject. * * Checks if @a and @b are equal, ie. represent the same subject. * However, avoid calling polkit_subject_equal() to compare two processes; * for more information see the `PolkitUnixProcess` documentation. * * This function can be used in e.g. g_hash_table_new(). * * Returns: %TRUE if @a and @b are equal, %FALSE otherwise. */ gboolean polkit_subject_equal (PolkitSubject *a, PolkitSubject *b) { g_return_val_if_fail (POLKIT_IS_SUBJECT (a), FALSE); g_return_val_if_fail (POLKIT_IS_SUBJECT (b), FALSE); if (!g_type_is_a (G_TYPE_FROM_INSTANCE (a), G_TYPE_FROM_INSTANCE (b))) return FALSE; return POLKIT_SUBJECT_GET_IFACE (a)->equal (a, b); } /** * polkit_subject_to_string: * @subject: A #PolkitSubject. * * Serializes @subject to a string that can be used in * polkit_subject_from_string(). * * Returns: A string representing @subject. Free with g_free(). */ gchar * polkit_subject_to_string (PolkitSubject *subject) { g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), NULL); return POLKIT_SUBJECT_GET_IFACE (subject)->to_string (subject); } /** * polkit_subject_exists: * @subject: A #PolkitSubject. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied * @user_data: The data to pass to @callback. * * Asynchronously checks if @subject exists. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call polkit_subject_exists_finish() to get the * result of the operation. **/ void polkit_subject_exists (PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (POLKIT_IS_SUBJECT (subject)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); POLKIT_SUBJECT_GET_IFACE (subject)->exists (subject, cancellable, callback, user_data); } /** * polkit_subject_exists_finish: * @subject: A #PolkitSubject. * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to polkit_subject_exists(). * @error: (allow-none): Return location for error or %NULL. * * Finishes checking whether a subject exists. * * Returns: %TRUE if the subject exists, %FALSE if not or @error is set. */ gboolean polkit_subject_exists_finish (PolkitSubject *subject, GAsyncResult *res, GError **error) { g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return POLKIT_SUBJECT_GET_IFACE (subject)->exists_finish (subject, res, error); } /** * polkit_subject_exists_sync: * @subject: A #PolkitSubject. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Checks if @subject exists. * * This is a synchronous blocking call - the calling thread is blocked * until a reply is received. See polkit_subject_exists() for the * asynchronous version. * * Returns: %TRUE if the subject exists, %FALSE if not or @error is set. */ gboolean polkit_subject_exists_sync (PolkitSubject *subject, GCancellable *cancellable, GError **error) { g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return POLKIT_SUBJECT_GET_IFACE (subject)->exists_sync (subject, cancellable, error); } /** * polkit_subject_from_string: * @str: A string obtained from polkit_subject_to_string(). * @error: (allow-none): Return location for error or %NULL. * * Creates an object from @str that implements the #PolkitSubject * interface. * * Returns: (transfer full): A #PolkitSubject or %NULL if @error is * set. Free with g_object_unref(). */ PolkitSubject * polkit_subject_from_string (const gchar *str, GError **error) { PolkitSubject *subject; g_return_val_if_fail (str != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); /* TODO: we could do something with VFuncs like in g_icon_from_string() */ subject = NULL; if (g_str_has_prefix (str, "unix-process:")) { gint scanned_pid; guint64 scanned_starttime; gint scanned_uid; gint scanned_pidfd; if (sscanf (str, "unix-process:%d:%" G_GUINT64_FORMAT ":%d:%d", &scanned_pid, &scanned_starttime, &scanned_uid, &scanned_pidfd) == 4) { subject = polkit_unix_process_new_pidfd (scanned_pidfd, scanned_uid, NULL); if (subject) polkit_unix_process_set_start_time (POLKIT_UNIX_PROCESS (subject), scanned_starttime); } else if (sscanf (str, "unix-process:%d:%" G_GUINT64_FORMAT ":%d", &scanned_pid, &scanned_starttime, &scanned_uid) == 3) { subject = polkit_unix_process_new_for_owner (scanned_pid, scanned_starttime, scanned_uid); } else if (sscanf (str, "unix-process:%d:%" G_GUINT64_FORMAT, &scanned_pid, &scanned_starttime) == 2) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS subject = polkit_unix_process_new_full (scanned_pid, scanned_starttime); G_GNUC_END_IGNORE_DEPRECATIONS } else if (sscanf (str, "unix-process:%d", &scanned_pid) == 1) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS subject = polkit_unix_process_new (scanned_pid); G_GNUC_END_IGNORE_DEPRECATIONS if (polkit_unix_process_get_start_time (POLKIT_UNIX_PROCESS (subject)) == 0) { g_object_unref (subject); subject = NULL; g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Unable to determine start time for process with pid %d", scanned_pid); } } } else if (g_str_has_prefix (str, "unix-session:")) { subject = polkit_unix_session_new (str + sizeof "unix-session:" - 1); } else if (g_str_has_prefix (str, "system-bus-name:")) { subject = polkit_system_bus_name_new (str + sizeof "system-bus-name:" - 1); } if (subject == NULL && (error != NULL && *error == NULL)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Malformed subject string `%s'", str); } return subject; } /* Note that this returns a floating value. */ GVariant * polkit_subject_to_gvariant (PolkitSubject *subject) { GVariantBuilder builder; GVariant *dict; const gchar *kind; kind = ""; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (POLKIT_IS_UNIX_PROCESS (subject)) { kind = "unix-process"; g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_uint32 (polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject)))); g_variant_builder_add (&builder, "{sv}", "start-time", g_variant_new_uint64 (polkit_unix_process_get_start_time (POLKIT_UNIX_PROCESS (subject)))); g_variant_builder_add (&builder, "{sv}", "uid", g_variant_new_int32 (polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)))); if (polkit_unix_process_get_pidfd_is_safe(POLKIT_UNIX_PROCESS (subject))) g_variant_builder_add (&builder, "{sv}", "pidfd", g_variant_new_handle (polkit_unix_process_get_pidfd (POLKIT_UNIX_PROCESS (subject)))); } else if (POLKIT_IS_UNIX_SESSION (subject)) { kind = "unix-session"; g_variant_builder_add (&builder, "{sv}", "session-id", g_variant_new_string (polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (subject)))); } else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { kind = "system-bus-name"; g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string (polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)))); } else { g_warning ("Unknown class %s implementing PolkitSubject", g_type_name (G_TYPE_FROM_INSTANCE (subject))); } dict = g_variant_builder_end (&builder); return g_variant_new ("(s@a{sv})", kind, dict); } static GVariant * lookup_asv (GVariant *dict, const gchar *given_key, const GVariantType *given_type, GError **error) { GVariantIter iter; const gchar *key; GVariant *value; GVariant *ret; ret = NULL; g_variant_iter_init (&iter, dict); while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) { if (g_strcmp0 (key, given_key) == 0) { if (!g_variant_is_of_type (value, given_type)) { gchar *type_string; type_string = g_variant_type_dup_string (given_type); g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Value for key `%s' found but is of type %s and type %s was expected", given_key, g_variant_get_type_string (value), type_string); g_free (type_string); g_variant_unref (value); goto out; } ret = value; goto out; } g_variant_unref (value); } out: if (ret == NULL) { gchar *type_string; type_string = g_variant_type_dup_string (given_type); g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Didn't find value for key `%s' of type %s", given_key, type_string); g_free (type_string); } return ret; } PolkitSubject * polkit_subject_new_for_gvariant_invocation (GVariant *variant, GDBusMethodInvocation *invocation, GError **error) { PolkitSubject *ret; const gchar *kind; GVariant *details_gvariant; ret = NULL; g_variant_get (variant, "(&s@a{sv})", &kind, &details_gvariant); if (g_strcmp0 (kind, "unix-process") == 0) { GUnixFDList *fd_list; GVariant *v; gint index, pidfd; guint32 pid; guint64 start_time; gint32 uid; v = lookup_asv (details_gvariant, "uid", G_VARIANT_TYPE_INT32, NULL); if (v != NULL) { uid = g_variant_get_int32 (v); g_variant_unref (v); } else { uid = -1; } fd_list = g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation)); if (fd_list) { v = lookup_asv (details_gvariant, "pidfd", G_VARIANT_TYPE_HANDLE, NULL); if (v != NULL) { if (uid == -1) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error parsing unix-process subject: 'pidfd' specified withtout 'uid'"); goto out; } index = g_variant_get_handle (v); pidfd = g_unix_fd_list_get (fd_list, index, NULL); g_variant_unref (v); ret = polkit_unix_process_new_pidfd (pidfd, uid, NULL); } } if (!ret) { v = lookup_asv (details_gvariant, "pid", G_VARIANT_TYPE_UINT32, error); if (v == NULL) { g_prefix_error (error, "Error parsing unix-process subject: "); goto out; } pid = g_variant_get_uint32 (v); g_variant_unref (v); v = lookup_asv (details_gvariant, "start-time", G_VARIANT_TYPE_UINT64, error); if (v == NULL) { g_prefix_error (error, "Error parsing unix-process subject: "); goto out; } start_time = g_variant_get_uint64 (v); g_variant_unref (v); ret = polkit_unix_process_new_for_owner (pid, start_time, uid); } } else if (g_strcmp0 (kind, "unix-session") == 0) { GVariant *v; const gchar *session_id; v = lookup_asv (details_gvariant, "session-id", G_VARIANT_TYPE_STRING, error); if (v == NULL) { g_prefix_error (error, "Error parsing unix-session subject: "); goto out; } session_id = g_variant_get_string (v, NULL); ret = polkit_unix_session_new (session_id); g_variant_unref (v); } else if (g_strcmp0 (kind, "system-bus-name") == 0) { GVariant *v; const gchar *name; v = lookup_asv (details_gvariant, "name", G_VARIANT_TYPE_STRING, error); if (v == NULL) { g_prefix_error (error, "Error parsing system-bus-name subject: "); goto out; } name = g_variant_get_string (v, NULL); if (!g_dbus_is_unique_name (name)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error parsing system-bus-name subject: `%s' is not a valid unique name", name); goto out; } ret = polkit_system_bus_name_new (name); g_variant_unref (v); } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Unknown subject of kind `%s'", kind); } out: g_variant_unref (details_gvariant); return ret; } PolkitSubject * polkit_subject_new_for_gvariant (GVariant *variant, GError **error) { return polkit_subject_new_for_gvariant_invocation (variant, NULL, error); } polkit-126/src/polkit/polkitsubject.h000066400000000000000000000105731474122443600200270ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_SUBJECT_H #define __POLKIT_SUBJECT_H #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_SUBJECT (polkit_subject_get_type()) #define POLKIT_SUBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_SUBJECT, PolkitSubject)) #define POLKIT_IS_SUBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_SUBJECT)) #define POLKIT_SUBJECT_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), POLKIT_TYPE_SUBJECT, PolkitSubjectIface)) #if 0 /** * PolkitSubject: * * Generic type for all objects that can be used as subjects. */ typedef struct _PolkitSubject PolkitSubject; /* Dummy typedef */ #endif typedef struct _PolkitSubjectIface PolkitSubjectIface; /** * PolkitSubjectIface: * @parent_iface: The parent interface. * @hash: Gets a hash value for a #PolkitSubject. * @equal: Checks if two #PolkitSubjects are equal. * @to_string: Serializes a #PolkitSubject to a string that can be * used in polkit_subject_from_string(). * @exists: Asynchronously check if a #PolkitSubject exists. * @exists_finish: Finishes checking if a #PolkitSubject exists. * @exists_sync: Synchronously check if a #PolkitSubject exists. * * An interface for subjects. */ struct _PolkitSubjectIface { GTypeInterface parent_iface; guint (*hash) (PolkitSubject *subject); gboolean (*equal) (PolkitSubject *a, PolkitSubject *b); gchar * (*to_string) (PolkitSubject *subject); void (*exists) (PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (*exists_finish) (PolkitSubject *subject, GAsyncResult *res, GError **error); gboolean (*exists_sync) (PolkitSubject *subject, GCancellable *cancellable, GError **error); }; GType polkit_subject_get_type (void) G_GNUC_CONST; guint polkit_subject_hash (PolkitSubject *subject); gboolean polkit_subject_equal (PolkitSubject *a, PolkitSubject *b); gchar *polkit_subject_to_string (PolkitSubject *subject); PolkitSubject *polkit_subject_from_string (const gchar *str, GError **error); void polkit_subject_exists (PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean polkit_subject_exists_finish (PolkitSubject *subject, GAsyncResult *res, GError **error); gboolean polkit_subject_exists_sync (PolkitSubject *subject, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* __POLKIT_SUBJECT_H */ polkit-126/src/polkit/polkitsystembusname.c000066400000000000000000000477421474122443600212720ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include "polkitsystembusname.h" #include "polkitunixuser.h" #include "polkitsubject.h" #include "polkitprivate.h" #include "polkitunixprocess.h" /** * SECTION:polkitsystembusname * @title: PolkitSystemBusName * @short_description: Unique system bus names * * An object that represents a process owning a unique name on the system bus. */ /** * PolkitUnixSystemBusName: * * The #PolkitSystemBusName struct should not be accessed directly. */ struct _PolkitSystemBusName { GObject parent_instance; gchar *name; }; struct _PolkitSystemBusNameClass { GObjectClass parent_class; }; enum { PROP_0, PROP_NAME, }; guint8 dbus_call_respond_fails; // has to be global because of callback static void subject_iface_init (PolkitSubjectIface *subject_iface); G_DEFINE_TYPE_WITH_CODE (PolkitSystemBusName, polkit_system_bus_name, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (POLKIT_TYPE_SUBJECT, subject_iface_init) ); static void polkit_system_bus_name_init (PolkitSystemBusName *system_bus_name) { } static void polkit_system_bus_name_finalize (GObject *object) { PolkitSystemBusName *system_bus_name = POLKIT_SYSTEM_BUS_NAME (object); g_free (system_bus_name->name); if (G_OBJECT_CLASS (polkit_system_bus_name_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_system_bus_name_parent_class)->finalize (object); } static void polkit_system_bus_name_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitSystemBusName *system_bus_name = POLKIT_SYSTEM_BUS_NAME (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, system_bus_name->name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_system_bus_name_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitSystemBusName *system_bus_name = POLKIT_SYSTEM_BUS_NAME (object); switch (prop_id) { case PROP_NAME: polkit_system_bus_name_set_name (system_bus_name, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_system_bus_name_class_init (PolkitSystemBusNameClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = polkit_system_bus_name_get_property; gobject_class->set_property = polkit_system_bus_name_set_property; gobject_class->finalize = polkit_system_bus_name_finalize; /** * PolkitSystemBusName:name: * * The unique name on the system message bus. */ g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", "Name", "The unique name on the system message bus", NULL, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); } /** * polkit_system_bus_name_get_name: * @system_bus_name: A #PolkitSystemBusName. * * Gets the unique system bus name for @system_bus_name. * * Returns: The unique system bus name for @system_bus_name. Do not * free, this string is owned by @system_bus_name. */ const gchar * polkit_system_bus_name_get_name (PolkitSystemBusName *system_bus_name) { g_return_val_if_fail (POLKIT_IS_SYSTEM_BUS_NAME (system_bus_name), NULL); return system_bus_name->name; } /** * polkit_system_bus_name_set_name: * @system_bus_name: A #PolkitSystemBusName. * @name: A unique system bus name. * * Sets the unique system bus name for @system_bus_name. */ void polkit_system_bus_name_set_name (PolkitSystemBusName *system_bus_name, const gchar *name) { g_return_if_fail (POLKIT_IS_SYSTEM_BUS_NAME (system_bus_name)); g_return_if_fail (g_dbus_is_unique_name (name)); g_free (system_bus_name->name); system_bus_name->name = g_strdup (name); } /** * polkit_system_bus_name_new: * @name: A unique system bus name. * * Creates a new #PolkitSystemBusName for @name. * * Returns: (transfer full): A #PolkitSystemBusName. Free with g_object_unref(). */ PolkitSubject * polkit_system_bus_name_new (const gchar *name) { g_return_val_if_fail (g_dbus_is_unique_name (name), NULL); return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_SYSTEM_BUS_NAME, "name", name, NULL)); } static guint polkit_system_bus_name_hash (PolkitSubject *subject) { PolkitSystemBusName *system_bus_name = POLKIT_SYSTEM_BUS_NAME (subject); return g_str_hash (system_bus_name->name); } static gboolean polkit_system_bus_name_equal (PolkitSubject *a, PolkitSubject *b) { PolkitSystemBusName *name_a; PolkitSystemBusName *name_b; name_a = POLKIT_SYSTEM_BUS_NAME (a); name_b = POLKIT_SYSTEM_BUS_NAME (b); return strcmp (name_a->name, name_b->name) == 0; } static gchar * polkit_system_bus_name_to_string (PolkitSubject *subject) { PolkitSystemBusName *system_bus_name = POLKIT_SYSTEM_BUS_NAME (subject); return g_strdup_printf ("system-bus-name:%s", system_bus_name->name); } static gboolean polkit_system_bus_name_exists_sync (PolkitSubject *subject, GCancellable *cancellable, GError **error) { PolkitSystemBusName *name = POLKIT_SYSTEM_BUS_NAME (subject); GDBusConnection *connection; GVariant *result; gboolean ret; ret = FALSE; connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error); if (connection == NULL) goto out; result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", /* name */ "/org/freedesktop/DBus", /* object path */ "org.freedesktop.DBus", /* interface name */ "NameHasOwner", /* method */ g_variant_new ("(s)", name->name), G_VARIANT_TYPE ("(b)"), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error); if (result == NULL) goto out; g_variant_get (result, "(b)", &ret); g_variant_unref (result); out: if (connection != NULL) g_object_unref (connection); return ret; } static void exists_in_thread_func (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { GError *error; error = NULL; if (!polkit_system_bus_name_exists_sync (POLKIT_SUBJECT (object), cancellable, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } } static void polkit_system_bus_name_exists (PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; g_return_if_fail (POLKIT_IS_SYSTEM_BUS_NAME (subject)); simple = g_simple_async_result_new (G_OBJECT (subject), callback, user_data, polkit_system_bus_name_exists); g_simple_async_result_run_in_thread (simple, exists_in_thread_func, G_PRIORITY_DEFAULT, cancellable); g_object_unref (simple); } static gboolean polkit_system_bus_name_exists_finish (PolkitSubject *subject, GAsyncResult *res, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); gboolean ret; g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_system_bus_name_exists); ret = FALSE; if (g_simple_async_result_propagate_error (simple, error)) goto out; ret = g_simple_async_result_get_op_res_gboolean (simple); out: return ret; } static void subject_iface_init (PolkitSubjectIface *subject_iface) { subject_iface->hash = polkit_system_bus_name_hash; subject_iface->equal = polkit_system_bus_name_equal; subject_iface->to_string = polkit_system_bus_name_to_string; subject_iface->exists = polkit_system_bus_name_exists; subject_iface->exists_finish = polkit_system_bus_name_exists_finish; subject_iface->exists_sync = polkit_system_bus_name_exists_sync; } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { GError **error; guint retrieved_uid : 1; guint retrieved_pid : 1; guint caught_error : 1; guint32 uid; guint32 pid; } AsyncGetBusNameCredsData; static void on_retrieved_unix_uid_pid (GObject *src, GAsyncResult *res, gpointer user_data) { AsyncGetBusNameCredsData *data = user_data; GVariant *v; v = g_dbus_connection_call_finish ((GDBusConnection*)src, res, data->caught_error ? NULL : data->error); if (!v) { data->caught_error = TRUE; dbus_call_respond_fails += 1; } else { guint32 value; g_variant_get (v, "(u)", &value); g_variant_unref (v); if (!data->retrieved_uid) { data->retrieved_uid = TRUE; data->uid = value; } else { g_assert (!data->retrieved_pid); data->retrieved_pid = TRUE; data->pid = value; } } } static gboolean polkit_system_bus_name_get_creds_fallback (PolkitSystemBusName *system_bus_name, guint32 *out_uid, guint32 *out_pid, GCancellable *cancellable, GDBusConnection *connection, GMainContext *tmp_context, GError **error) { gboolean ret = FALSE; AsyncGetBusNameCredsData data = { }; data.error = error; dbus_call_respond_fails = 0; /* Do two async calls as it's basically as fast as one sync call. */ g_dbus_connection_call (connection, "org.freedesktop.DBus", /* name */ "/org/freedesktop/DBus", /* object path */ "org.freedesktop.DBus", /* interface name */ "GetConnectionUnixUser", /* method */ g_variant_new ("(s)", system_bus_name->name), G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, on_retrieved_unix_uid_pid, &data); g_dbus_connection_call (connection, "org.freedesktop.DBus", /* name */ "/org/freedesktop/DBus", /* object path */ "org.freedesktop.DBus", /* interface name */ "GetConnectionUnixProcessID", /* method */ g_variant_new ("(s)", system_bus_name->name), G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, on_retrieved_unix_uid_pid, &data); while (TRUE) { /* If one dbus call returns error, we must wait until the other call * calls _call_finish(), otherwise fd leak is possible. * Resolves: GHSL-2021-077 */ if ( (dbus_call_respond_fails > 1) ) { // we got two faults, we can leave goto out; } if ((data.caught_error && (data.retrieved_pid || data.retrieved_uid))) { // we got one fault and the other call finally finished, we can leave goto out; } if ( !(data.retrieved_uid && data.retrieved_pid) ) { g_main_context_iteration (tmp_context, TRUE); } else { break; } } if (out_uid) *out_uid = data.uid; if (out_pid) *out_pid = data.pid; ret = TRUE; out: if (tmp_context) { g_main_context_pop_thread_default (tmp_context); g_main_context_unref (tmp_context); } if (connection != NULL) g_object_unref (connection); return ret; } static gboolean polkit_system_bus_name_get_creds_sync (PolkitSystemBusName *system_bus_name, guint32 *out_uid, GArray **out_gids, guint32 *out_pid, gint *out_pidfd, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GDBusConnection *connection = NULL; GMainContext *tmp_context = NULL; GVariantIter *iter; GVariant *result, *value; GUnixFDList *fd_list = NULL; GError *dbus_error = NULL; const gchar *key; guint32 uid = G_MAXUINT32, pid = 0; gint pidfd = -1; GArray *gids = NULL; connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error); if (connection == NULL) goto out; tmp_context = g_main_context_new (); g_main_context_push_thread_default (tmp_context); /* If the new unified API is available (since dbus-daemon 1.10.4) use it, * or fallback to the old separate calls. * Since dbus-daemon 1.15.7 and dbus-broker 34, the new API will return * a ProcessFD that we can use to pin the caller against PID reuse. */ result = g_dbus_connection_call_with_unix_fd_list_sync (connection, "org.freedesktop.DBus", /* name */ "/org/freedesktop/DBus", /* object path */ "org.freedesktop.DBus", /* interface name */ "GetConnectionCredentials", /* method */ g_variant_new ("(s)", system_bus_name->name), G_VARIANT_TYPE ("(a{sv})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &fd_list, cancellable, &dbus_error); if (result == NULL) { if (g_error_matches (dbus_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { g_error_free (dbus_error); return polkit_system_bus_name_get_creds_fallback(system_bus_name, out_uid, out_pid, cancellable, connection, tmp_context, error); } else goto out; } g_variant_get (result, "(a{sv})", &iter); while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) { if (g_strcmp0 (key, "ProcessID") == 0) pid = g_variant_get_uint32 (value); else if (g_strcmp0 (key, "UnixUserID") == 0) uid = g_variant_get_uint32 (value); else if (g_strcmp0 (key, "UnixGroupIDs") == 0) { GVariantIter *group_iter; gid_t gid; gids = g_array_new (FALSE, FALSE, sizeof (gid_t)); g_variant_get (value, "au", &group_iter); while (g_variant_iter_loop (group_iter, "u", &gid)) g_array_append_val (gids, gid); g_variant_iter_free (group_iter); } else if (g_strcmp0 (key, "ProcessFD") == 0) { gint32 index = g_variant_get_handle (value); pidfd = g_unix_fd_list_get (fd_list, index, error); } } g_variant_unref (result); g_variant_iter_free (iter); if (out_uid) *out_uid = uid; if (out_gids && gids) *out_gids = g_array_ref(gids); if (out_pid) *out_pid = pid; if (out_pidfd) *out_pidfd = pidfd; else if (pidfd >= 0) close (pidfd); ret = TRUE; out: if (tmp_context) { g_main_context_pop_thread_default (tmp_context); g_main_context_unref (tmp_context); } if (connection != NULL) g_object_unref (connection); if (!ret && pidfd >= 0) close (pidfd); if (dbus_error && error) g_propagate_error (error, dbus_error); else if (dbus_error) g_error_free (dbus_error); if (gids) g_array_unref (gids); if (fd_list != NULL) g_object_unref (fd_list); return ret; } /** * polkit_system_bus_name_get_process_sync: * @system_bus_name: A #PolkitSystemBusName. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Synchronously gets a #PolkitUnixProcess object for @system_bus_name * - the calling thread is blocked until a reply is received. * * Returns: (allow-none) (transfer full): A #PolkitUnixProcess object or %NULL if @error is set. **/ PolkitSubject * polkit_system_bus_name_get_process_sync (PolkitSystemBusName *system_bus_name, GCancellable *cancellable, GError **error) { PolkitSubject *ret = NULL; gint pidfd = -1; guint32 pid; guint32 uid; GArray *gids = NULL; g_return_val_if_fail (POLKIT_IS_SYSTEM_BUS_NAME (system_bus_name), NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (!polkit_system_bus_name_get_creds_sync (system_bus_name, &uid, &gids, &pid, &pidfd, cancellable, error)) goto out; if (pidfd >= 0) ret = polkit_unix_process_new_pidfd (pidfd, uid, gids); else ret = polkit_unix_process_new_for_owner (pid, 0, uid); polkit_unix_process_set_gids (POLKIT_UNIX_PROCESS (ret), gids); out: if (gids) g_array_unref (gids); return ret; } /** * polkit_system_bus_name_get_user_sync: * @system_bus_name: A #PolkitSystemBusName. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error or %NULL. * * Synchronously gets a #PolkitUnixUser object for @system_bus_name; * the calling thread is blocked until a reply is received. * * Returns: (allow-none) (transfer full): A #PolkitUnixUser object or %NULL if @error is set. **/ PolkitUnixUser * polkit_system_bus_name_get_user_sync (PolkitSystemBusName *system_bus_name, GCancellable *cancellable, GError **error) { PolkitUnixUser *ret = NULL; guint32 uid; g_return_val_if_fail (POLKIT_IS_SYSTEM_BUS_NAME (system_bus_name), NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (!polkit_system_bus_name_get_creds_sync (system_bus_name, &uid, NULL, NULL, NULL, cancellable, error)) goto out; ret = (PolkitUnixUser*)polkit_unix_user_new (uid); out: return ret; } polkit-126/src/polkit/polkitsystembusname.h000066400000000000000000000060051474122443600212620ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_SYSTEM_BUS_NAME_H #define __POLKIT_SYSTEM_BUS_NAME_H #include #include #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_SYSTEM_BUS_NAME (polkit_system_bus_name_get_type()) #define POLKIT_SYSTEM_BUS_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_SYSTEM_BUS_NAME, PolkitSystemBusName)) #define POLKIT_SYSTEM_BUS_NAME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_SYSTEM_BUS_NAME, PolkitSystemBusNameClass)) #define POLKIT_SYSTEM_BUS_NAME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_SYSTEM_BUS_NAME, PolkitSystemBusNameClass)) #define POLKIT_IS_SYSTEM_BUS_NAME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_SYSTEM_BUS_NAME)) #define POLKIT_IS_SYSTEM_BUS_NAME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_SYSTEM_BUS_NAME)) #if 0 typedef struct _PolkitSystemBusName PolkitSystemBusName; #endif typedef struct _PolkitSystemBusNameClass PolkitSystemBusNameClass; GType polkit_system_bus_name_get_type (void) G_GNUC_CONST; PolkitSubject *polkit_system_bus_name_new (const gchar *name); const gchar *polkit_system_bus_name_get_name (PolkitSystemBusName *system_bus_name); void polkit_system_bus_name_set_name (PolkitSystemBusName *system_bus_name, const gchar *name); /* TODO: add async version of get_process() method */ PolkitSubject *polkit_system_bus_name_get_process_sync (PolkitSystemBusName *system_bus_name, GCancellable *cancellable, GError **error); PolkitUnixUser * polkit_system_bus_name_get_user_sync (PolkitSystemBusName *system_bus_name, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* __POLKIT_SYSTEM_BUS_NAME_H */ polkit-126/src/polkit/polkittemporaryauthorization.c000066400000000000000000000160141474122443600232220ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include "polkitimplicitauthorization.h" #include "polkittemporaryauthorization.h" #include "polkitprivate.h" /** * SECTION:polkittemporaryauthorization * @title: PolkitTemporaryAuthorization * @short_description: Temporary Authorizations * * Object used to describe a temporary authorization. */ /** * PolkitTemporaryAuthorization: * * The #PolkitTemporaryAuthorization struct should not be accessed directly. */ struct _PolkitTemporaryAuthorization { GObject parent_instance; gchar *id; gchar *action_id; PolkitSubject *subject; guint64 time_obtained; guint64 time_expires; }; struct _PolkitTemporaryAuthorizationClass { GObjectClass parent_class; }; G_DEFINE_TYPE (PolkitTemporaryAuthorization, polkit_temporary_authorization, G_TYPE_OBJECT); static void polkit_temporary_authorization_init (PolkitTemporaryAuthorization *authorization) { } static void polkit_temporary_authorization_finalize (GObject *object) { PolkitTemporaryAuthorization *authorization = POLKIT_TEMPORARY_AUTHORIZATION (object); g_free (authorization->id); g_free (authorization->action_id); g_object_unref (authorization->subject); if (G_OBJECT_CLASS (polkit_temporary_authorization_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_temporary_authorization_parent_class)->finalize (object); } static void polkit_temporary_authorization_class_init (PolkitTemporaryAuthorizationClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_temporary_authorization_finalize; } PolkitTemporaryAuthorization * polkit_temporary_authorization_new (const gchar *id, const gchar *action_id, PolkitSubject *subject, guint64 time_obtained, guint64 time_expires) { PolkitTemporaryAuthorization *authorization; authorization = POLKIT_TEMPORARY_AUTHORIZATION (g_object_new (POLKIT_TYPE_TEMPORARY_AUTHORIZATION, NULL)); authorization->id = g_strdup (id); authorization->action_id = g_strdup (action_id); authorization->subject = g_object_ref (subject); authorization->time_obtained = time_obtained; authorization->time_expires = time_expires; return authorization; } /** * polkit_temporary_authorization_get_id: * @authorization: A #PolkitTemporaryAuthorization. * * Gets the opaque identifier for @authorization. * * Returns: A string owned by @authorization. Do not free. */ const gchar * polkit_temporary_authorization_get_id (PolkitTemporaryAuthorization *authorization) { g_return_val_if_fail (POLKIT_IS_TEMPORARY_AUTHORIZATION (authorization), NULL); return authorization->id; } /** * polkit_temporary_authorization_get_action_id: * @authorization: A #PolkitTemporaryAuthorization. * * Gets the action that @authorization is for. * * Returns: A string owned by @authorization. Do not free. **/ const gchar * polkit_temporary_authorization_get_action_id (PolkitTemporaryAuthorization *authorization) { g_return_val_if_fail (POLKIT_IS_TEMPORARY_AUTHORIZATION (authorization), NULL); return authorization->action_id; } /** * polkit_temporary_authorization_get_subject: * @authorization: A #PolkitTemporaryAuthorization. * * Gets the subject that @authorization is for. * * Returns: (transfer full): A #PolkitSubject, free with g_object_unref(). **/ PolkitSubject * polkit_temporary_authorization_get_subject (PolkitTemporaryAuthorization *authorization) { g_return_val_if_fail (POLKIT_IS_TEMPORARY_AUTHORIZATION (authorization), NULL); return g_object_ref (authorization->subject); } /** * polkit_temporary_authorization_get_time_obtained: * @authorization: A #PolkitTemporaryAuthorization. * * Gets the time when @authorization was obtained. * * (Note that the PolicyKit daemon is using monotonic time internally * so the returned value may change if system time changes.) * * Returns: Seconds since the Epoch Jan 1. 1970, 0:00 UTC. **/ guint64 polkit_temporary_authorization_get_time_obtained (PolkitTemporaryAuthorization *authorization) { g_return_val_if_fail (POLKIT_IS_TEMPORARY_AUTHORIZATION (authorization), 0); return authorization->time_obtained; } /** * polkit_temporary_authorization_get_time_expires: * @authorization: A #PolkitTemporaryAuthorization. * * Gets the time when @authorization will expire. * * (Note that the PolicyKit daemon is using monotonic time internally * so the returned value may change if system time changes.) * * Returns: Seconds since the Epoch Jan 1. 1970, 0:00 UTC. **/ guint64 polkit_temporary_authorization_get_time_expires (PolkitTemporaryAuthorization *authorization) { g_return_val_if_fail (POLKIT_IS_TEMPORARY_AUTHORIZATION (authorization), 0); return authorization->time_expires; } PolkitTemporaryAuthorization * polkit_temporary_authorization_new_for_gvariant (GVariant *value, GError **error) { PolkitTemporaryAuthorization *authorization; GVariant *subject_gvariant; authorization = POLKIT_TEMPORARY_AUTHORIZATION (g_object_new (POLKIT_TYPE_TEMPORARY_AUTHORIZATION, NULL)); g_variant_get (value, "(ss@(sa{sv})tt)", &authorization->id, &authorization->action_id, &subject_gvariant, &authorization->time_obtained, &authorization->time_expires); authorization->subject = polkit_subject_new_for_gvariant (subject_gvariant, error); if (authorization->subject == NULL) { g_object_unref (authorization); authorization = NULL; goto out; } out: g_variant_unref (subject_gvariant); return authorization; } /* Note that this returns a floating value. */ GVariant * polkit_temporary_authorization_to_gvariant (PolkitTemporaryAuthorization *authorization) { return g_variant_new ("(ss@(sa{sv})tt)", authorization->id, authorization->action_id, polkit_subject_to_gvariant (authorization->subject), /* A floating value */ authorization->time_obtained, authorization->time_expires); } polkit-126/src/polkit/polkittemporaryauthorization.h000066400000000000000000000055361474122443600232360ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_TEMPORARY_AUTHORIZATION_H #define __POLKIT_TEMPORARY_AUTHORIZATION_H #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_TEMPORARY_AUTHORIZATION (polkit_temporary_authorization_get_type()) #define POLKIT_TEMPORARY_AUTHORIZATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_TEMPORARY_AUTHORIZATION, PolkitTemporaryAuthorization)) #define POLKIT_TEMPORARY_AUTHORIZATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_TEMPORARY_AUTHORIZATION, PolkitTemporaryAuthorizationClass)) #define POLKIT_TEMPORARY_AUTHORIZATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_TEMPORARY_AUTHORIZATION, PolkitTemporaryAuthorizationClass)) #define POLKIT_IS_TEMPORARY_AUTHORIZATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_TEMPORARY_AUTHORIZATION)) #define POLKIT_IS_TEMPORARY_AUTHORIZATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_TEMPORARY_AUTHORIZATION)) #if 0 typedef struct _PolkitTemporaryAuthorization PolkitTemporaryAuthorization; #endif typedef struct _PolkitTemporaryAuthorizationClass PolkitTemporaryAuthorizationClass; GType polkit_temporary_authorization_get_type (void) G_GNUC_CONST; const gchar *polkit_temporary_authorization_get_id (PolkitTemporaryAuthorization *authorization); const gchar *polkit_temporary_authorization_get_action_id (PolkitTemporaryAuthorization *authorization); PolkitSubject *polkit_temporary_authorization_get_subject (PolkitTemporaryAuthorization *authorization); guint64 polkit_temporary_authorization_get_time_obtained (PolkitTemporaryAuthorization *authorization); guint64 polkit_temporary_authorization_get_time_expires (PolkitTemporaryAuthorization *authorization); G_END_DECLS #endif /* __POLKIT_TEMPORARY_AUTHORIZATION_H */ polkit-126/src/polkit/polkittypes.h000066400000000000000000000057731474122443600175420ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #ifndef __POLKIT_TYPES_H #define __POLKIT_TYPES_H #include struct _PolkitAuthority; typedef struct _PolkitAuthority PolkitAuthority; struct _PolkitActionDescription; typedef struct _PolkitActionDescription PolkitActionDescription; typedef struct _PolkitSubject PolkitSubject; /* Dummy typedef */ struct _PolkitUnixProcess; typedef struct _PolkitUnixProcess PolkitUnixProcess; struct _PolkitUnixSession; typedef struct _PolkitUnixSession PolkitUnixSession; struct _PolkitSystemBusName; typedef struct _PolkitSystemBusName PolkitSystemBusName; typedef struct _PolkitIdentity PolkitIdentity; /* Dummy typedef */ struct _PolkitUnixUser; typedef struct _PolkitUnixUser PolkitUnixUser; struct _PolkitUnixGroup; typedef struct _PolkitUnixGroup PolkitUnixGroup; struct _PolkitUnixNetgroup; typedef struct _PolkitUnixNetgroup PolkitUnixNetgroup; struct _PolkitAuthorizationResult; typedef struct _PolkitAuthorizationResult PolkitAuthorizationResult; struct _PolkitDetails; typedef struct _PolkitDetails PolkitDetails; struct _PolkitTemporaryAuthorization; typedef struct _PolkitTemporaryAuthorization PolkitTemporaryAuthorization; struct _PolkitPermission; typedef struct _PolkitPermission PolkitPermission; #if GLIB_CHECK_VERSION(2, 44, 0) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthority, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitActionDescription, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSubject, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitUnixProcess, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitUnixSession, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSystemBusName, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitIdentity, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitUnixUser, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitUnixGroup, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitUnixNetgroup, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitDetails, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitTemporaryAuthorization, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitPermission, g_object_unref) #endif #endif /* __POLKIT_TYPES_H */ polkit-126/src/polkit/polkitunixgroup.c000066400000000000000000000157741474122443600204330ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include "polkitunixgroup.h" #include "polkitidentity.h" #include "polkiterror.h" #include "polkitprivate.h" /** * SECTION:polkitunixgroup * @title: PolkitUnixGroup * @short_description: Unix groups * * An object representing a group identity on a UNIX system. */ /** * PolkitUnixGroup: * * The #PolkitUnixGroup struct should not be accessed directly. */ struct _PolkitUnixGroup { GObject parent_instance; gint gid; }; struct _PolkitUnixGroupClass { GObjectClass parent_class; }; enum { PROP_0, PROP_GID, }; static void identity_iface_init (PolkitIdentityIface *identity_iface); G_DEFINE_TYPE_WITH_CODE (PolkitUnixGroup, polkit_unix_group, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (POLKIT_TYPE_IDENTITY, identity_iface_init) ); static void polkit_unix_group_init (PolkitUnixGroup *unix_group) { unix_group->gid = -1; /* (gid_t) -1 is not a valid GID under Linux */ } static void polkit_unix_group_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitUnixGroup *unix_group = POLKIT_UNIX_GROUP (object); switch (prop_id) { case PROP_GID: g_value_set_int (value, unix_group->gid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_group_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitUnixGroup *unix_group = POLKIT_UNIX_GROUP (object); gint val; switch (prop_id) { case PROP_GID: val = g_value_get_int (value); g_return_if_fail (val != -1); unix_group->gid = val; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_group_class_init (PolkitUnixGroupClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = polkit_unix_group_get_property; gobject_class->set_property = polkit_unix_group_set_property; /** * PolkitUnixGroup:gid: * * The UNIX group id. */ g_object_class_install_property (gobject_class, PROP_GID, g_param_spec_int ("gid", "Group ID", "The UNIX group ID", G_MININT, G_MAXINT, -1, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); } /** * polkit_unix_group_get_gid: * @group: A #PolkitUnixGroup. * * Gets the UNIX group id for @group. * * Returns: A UNIX group id. */ gint polkit_unix_group_get_gid (PolkitUnixGroup *group) { g_return_val_if_fail (POLKIT_IS_UNIX_GROUP (group), -1); return group->gid; } /** * polkit_unix_group_set_gid: * @group: A #PolkitUnixGroup. * @gid: A UNIX group id. * * Sets @gid for @group. */ void polkit_unix_group_set_gid (PolkitUnixGroup *group, gint gid) { g_return_if_fail (POLKIT_IS_UNIX_GROUP (group)); g_return_if_fail (gid != -1); group->gid = gid; } /** * polkit_unix_group_new: * @gid: A UNIX group id. * * Creates a new #PolkitUnixGroup object for @gid. * * Returns: (transfer full): A #PolkitUnixGroup object. Free with g_object_unref(). */ PolkitIdentity * polkit_unix_group_new (gint gid) { g_return_val_if_fail (gid != -1, NULL); return POLKIT_IDENTITY (g_object_new (POLKIT_TYPE_UNIX_GROUP, "gid", gid, NULL)); } /** * polkit_unix_group_new_for_name: * @name: A UNIX group name. * @error: Return location for error. * * Creates a new #PolkitUnixGroup object for a group with the group name * @name. * * Returns: (transfer full) (allow-none): A #PolkitUnixGroup object or %NULL if @error * is set. */ PolkitIdentity * polkit_unix_group_new_for_name (const gchar *name, GError **error) { struct group *group; PolkitIdentity *identity; g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); identity = NULL; group = getgrnam (name); if (group == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "No UNIX group with name %s: %s", name, g_strerror (errno)); goto out; } identity = polkit_unix_group_new (group->gr_gid); out: return identity; } static guint polkit_unix_group_hash (PolkitIdentity *identity) { PolkitUnixGroup *group; group = POLKIT_UNIX_GROUP (identity); return g_direct_hash (GINT_TO_POINTER (((gint) (group->gid)) * 2 + 1)); } static gboolean polkit_unix_group_equal (PolkitIdentity *a, PolkitIdentity *b) { PolkitUnixGroup *group_a; PolkitUnixGroup *group_b; group_a = POLKIT_UNIX_GROUP (a); group_b = POLKIT_UNIX_GROUP (b); return group_a->gid == group_b->gid; } static gchar * polkit_unix_group_to_string (PolkitIdentity *identity) { PolkitUnixGroup *group = POLKIT_UNIX_GROUP (identity); struct group *gr; gr = getgrgid (group->gid); if (gr == NULL) return g_strdup_printf ("unix-group:%d", group->gid); else return g_strdup_printf ("unix-group:%s", gr->gr_name); } static void identity_iface_init (PolkitIdentityIface *identity_iface) { identity_iface->hash = polkit_unix_group_hash; identity_iface->equal = polkit_unix_group_equal; identity_iface->to_string = polkit_unix_group_to_string; } polkit-126/src/polkit/polkitunixgroup.h000066400000000000000000000047061474122443600204310ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_UNIX_GROUP_H #define __POLKIT_UNIX_GROUP_H #include #include #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_UNIX_GROUP (polkit_unix_group_get_type()) #define POLKIT_UNIX_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_UNIX_GROUP, PolkitUnixGroup)) #define POLKIT_UNIX_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_UNIX_GROUP, PolkitUnixGroupClass)) #define POLKIT_UNIX_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_UNIX_GROUP, PolkitUnixGroupClass)) #define POLKIT_IS_UNIX_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_UNIX_GROUP)) #define POLKIT_IS_UNIX_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_UNIX_GROUP)) #if 0 typedef struct _PolkitUnixGroup PolkitUnixGroup; #endif typedef struct _PolkitUnixGroupClass PolkitUnixGroupClass; GType polkit_unix_group_get_type (void) G_GNUC_CONST; PolkitIdentity *polkit_unix_group_new (gint gid); PolkitIdentity *polkit_unix_group_new_for_name (const gchar *name, GError **error); gint polkit_unix_group_get_gid (PolkitUnixGroup *group); void polkit_unix_group_set_gid (PolkitUnixGroup *group, gint gid); G_END_DECLS #endif /* __POLKIT_UNIX_GROUP_H */ polkit-126/src/polkit/polkitunixnetgroup.c000066400000000000000000000145751474122443600211400ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen * Author: Nikki VonHollen */ #include #include #include "polkitunixnetgroup.h" #include "polkitidentity.h" #include "polkiterror.h" #include "polkitprivate.h" /** * SECTION:polkitunixnetgroup * @title: PolkitUnixNetgroup * @short_description: Unix netgroups * * An object representing a netgroup identity on a UNIX system. */ /** * PolkitUnixNetgroup: * * The #PolkitUnixNetgroup struct should not be accessed directly. */ struct _PolkitUnixNetgroup { GObject parent_instance; gchar *name; }; struct _PolkitUnixNetgroupClass { GObjectClass parent_class; }; enum { PROP_0, PROP_NAME, }; static void identity_iface_init (PolkitIdentityIface *identity_iface); G_DEFINE_TYPE_WITH_CODE (PolkitUnixNetgroup, polkit_unix_netgroup, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (POLKIT_TYPE_IDENTITY, identity_iface_init) ); static void polkit_unix_netgroup_init (PolkitUnixNetgroup *net_group) { net_group->name = NULL; } static void polkit_unix_netgroup_finalize (GObject *object) { PolkitUnixNetgroup *net_group = POLKIT_UNIX_NETGROUP (object); g_free(net_group->name); G_OBJECT_CLASS (polkit_unix_netgroup_parent_class)->finalize (object); } static void polkit_unix_netgroup_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitUnixNetgroup *net_group = POLKIT_UNIX_NETGROUP (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, polkit_unix_netgroup_get_name (net_group)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_netgroup_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitUnixNetgroup *net_group = POLKIT_UNIX_NETGROUP (object); switch (prop_id) { case PROP_NAME: polkit_unix_netgroup_set_name (net_group, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_netgroup_class_init (PolkitUnixNetgroupClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_unix_netgroup_finalize; gobject_class->get_property = polkit_unix_netgroup_get_property; gobject_class->set_property = polkit_unix_netgroup_set_property; /** * PolkitUnixNetgroup:name: * * The NIS netgroup name. */ g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", "Group Name", "The NIS netgroup name", NULL, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); } /** * polkit_unix_netgroup_get_name: * @group: A #PolkitUnixNetgroup. * * Gets the netgroup name for @group. * * Returns: A netgroup name string. */ const gchar * polkit_unix_netgroup_get_name (PolkitUnixNetgroup *group) { g_return_val_if_fail (POLKIT_IS_UNIX_NETGROUP (group), NULL); return group->name; } /** * polkit_unix_netgroup_set_name: * @group: A #PolkitUnixNetgroup. * @name: A netgroup name. * * Sets @name for @group. */ void polkit_unix_netgroup_set_name (PolkitUnixNetgroup *group, const gchar * name) { g_return_if_fail (POLKIT_IS_UNIX_NETGROUP (group)); g_free(group->name); group->name = g_strdup(name); } /** * polkit_unix_netgroup_new: * @name: A netgroup name. * * Creates a new #PolkitUnixNetgroup object for @name. * * Returns: (transfer full): A #PolkitUnixNetgroup object. Free with g_object_unref(). */ PolkitIdentity * polkit_unix_netgroup_new (const gchar *name) { #ifndef HAVE_SETNETGRENT g_assert_not_reached(); #endif g_return_val_if_fail (name != NULL, NULL); return POLKIT_IDENTITY (g_object_new (POLKIT_TYPE_UNIX_NETGROUP, "name", name, NULL)); } static guint polkit_unix_netgroup_hash (PolkitIdentity *identity) { PolkitUnixNetgroup *group; group = POLKIT_UNIX_NETGROUP (identity); return g_str_hash(group->name); } static gboolean polkit_unix_netgroup_equal (PolkitIdentity *a, PolkitIdentity *b) { PolkitUnixNetgroup *group_a; PolkitUnixNetgroup *group_b; group_a = POLKIT_UNIX_NETGROUP (a); group_b = POLKIT_UNIX_NETGROUP (b); if (g_strcmp0(group_a->name, group_b->name) == 0) return TRUE; else return FALSE; } static gchar * polkit_unix_netgroup_to_string (PolkitIdentity *identity) { PolkitUnixNetgroup *group = POLKIT_UNIX_NETGROUP (identity); return g_strconcat("unix-netgroup:", group->name, NULL); } static void identity_iface_init (PolkitIdentityIface *identity_iface) { identity_iface->hash = polkit_unix_netgroup_hash; identity_iface->equal = polkit_unix_netgroup_equal; identity_iface->to_string = polkit_unix_netgroup_to_string; } polkit-126/src/polkit/polkitunixnetgroup.h000066400000000000000000000046771474122443600211470ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen * Author: Nikki VonHollen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_UNIX_NETGROUP_H #define __POLKIT_UNIX_NETGROUP_H #include #include #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_UNIX_NETGROUP (polkit_unix_netgroup_get_type()) #define POLKIT_UNIX_NETGROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_UNIX_NETGROUP, PolkitUnixNetgroup)) #define POLKIT_UNIX_NETGROUP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_UNIX_NETGROUP, PolkitUnixNetgroupClass)) #define POLKIT_UNIX_NETGROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_UNIX_NETGROUP, PolkitUnixNetgroupClass)) #define POLKIT_IS_UNIX_NETGROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_UNIX_NETGROUP)) #define POLKIT_IS_UNIX_NETGROUP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_UNIX_NETGROUP)) #if 0 typedef struct _PolkitUnixNetgroup PolkitUnixNetgroup; #endif typedef struct _PolkitUnixNetgroupClass PolkitUnixNetgroupClass; GType polkit_unix_netgroup_get_type (void) G_GNUC_CONST; PolkitIdentity *polkit_unix_netgroup_new (const gchar *name); const gchar *polkit_unix_netgroup_get_name (PolkitUnixNetgroup *group); void polkit_unix_netgroup_set_name (PolkitUnixNetgroup *group, const gchar *name); G_END_DECLS #endif /* __POLKIT_UNIX_NETGROUP_H */ polkit-126/src/polkit/polkitunixprocess.c000066400000000000000000001127401474122443600207440ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #ifdef HAVE_FREEBSD #include #include #include #endif #ifdef HAVE_NETBSD #include #include #endif #ifdef HAVE_OPENBSD #include #endif #ifdef HAVE_PIDFD_OPEN #include #endif /* HAVE_PIDFD_OPEN */ #include #include #include #include #include "polkitunixprocess.h" #include "polkitsubject.h" #include "polkitprivate.h" #include "polkiterror.h" /** * SECTION:polkitunixprocess * @title: PolkitUnixProcess * @short_description: Unix processs * * An object for representing a UNIX process. In order to be reliable and * race-free, this requires support for PID File Descriptors in the kernel, * dbus-daemon/broker and systemd. With this functionality, we can reliably * track processes without risking PID reuse and race conditions, and compare * them. * * NOTE: If PID FDs are not available, this object will fall back to using * PIDs, and this designed is now known broken; a mechanism to exploit a delay * in start time in the Linux kernel was identified. Avoid * calling polkit_subject_equal() to compare two processes. * * To uniquely identify processes, both the process id and the start * time of the process (a monotonic increasing value representing the * time since the kernel was started) is used. * * NOTE: This object stores, and provides access to, the real UID of the * process. That value can change over time (with set*uid*(2) and exec*(2)). * Checks whether an operation is allowed need to take care to use the UID * value as of the time when the operation was made (or, following the open() * privilege check model, when the connection making the operation possible * was initiated). That is usually done by initializing this with * polkit_unix_process_new_for_owner() with trusted data. */ /* See https://gitlab.freedesktop.org/polkit/polkit/issues/75 But quoting the original email in full here to ensure it's preserved: From: Jann Horn Subject: [SECURITY] polkit: temporary auth hijacking via PID reuse and non-atomic fork Date: Wednesday, October 10, 2018 5:34 PM When a (non-root) user attempts to e.g. control systemd units in the system instance from an active session over DBus, the access is gated by a polkit policy that requires "auth_admin_keep" auth. This results in an auth prompt being shown to the user, asking the user to confirm the action by entering the password of an administrator account. After the action has been confirmed, the auth decision for "auth_admin_keep" is cached for up to five minutes. Subject to some restrictions, similar actions can then be performed in this timespan without requiring re-auth: - The PID of the DBus client requesting the new action must match the PID of the DBus client requesting the old action (based on SO_PEERCRED information forwarded by the DBus daemon). - The "start time" of the client's PID (as seen in /proc/$pid/stat, field 22) must not have changed. The granularity of this timestamp is in the millisecond range. - polkit polls every two seconds whether a process with the expected start time still exists. If not, the temporary auth entry is purged. Without the start time check, this would obviously be buggy because an attacker could simply wait for the legitimate client to disappear, then create a new client with the same PID. Unfortunately, the start time check is bypassable because fork() is not atomic. Looking at the source code of copy_process() in the kernel: p->start_time = ktime_get_ns(); p->real_start_time = ktime_get_boot_ns(); [...] retval = copy_thread_tls(clone_flags, stack_start, stack_size, p, tls); if (retval) goto bad_fork_cleanup_io; if (pid != &init_struct_pid) { pid = alloc_pid(p->nsproxy->pid_ns_for_children); if (IS_ERR(pid)) { retval = PTR_ERR(pid); goto bad_fork_cleanup_thread; } } The ktime_get_boot_ns() call is where the "start time" of the process is recorded. The alloc_pid() call is where a free PID is allocated. In between these, some time passes; and because the copy_thread_tls() call between them can access userspace memory when sys_clone() is invoked through the 32-bit syscall entry point, an attacker can even stall the kernel arbitrarily long at this point (by supplying a pointer into userspace memory that is associated with a userfaultfd or is backed by a custom FUSE filesystem). This means that an attacker can immediately call sys_clone() when the victim process is created, often resulting in a process that has the exact same start time reported in procfs; and then the attacker can delay the alloc_pid() call until after the victim process has died and the PID assignment has cycled around. This results in an attacker process that polkit can't distinguish from the victim process. */ /** * PolkitUnixProcess: * * The #PolkitUnixProcess struct should not be accessed directly. */ struct _PolkitUnixProcess { GObject parent_instance; gint pid; guint64 start_time; gint uid; gint pidfd; gboolean pidfd_is_safe; GArray *gids; }; struct _PolkitUnixProcessClass { GObjectClass parent_class; }; enum { PROP_0, PROP_PID, PROP_START_TIME, PROP_UID, PROP_PIDFD, PROP_PIDFD_IS_SAFE, PROP_GIDS, }; static void subject_iface_init (PolkitSubjectIface *subject_iface); static guint64 get_start_time_for_pid (gint pid, GError **error); #if defined(HAVE_FREEBSD) || defined(HAVE_NETBSD) || defined(HAVE_OPENBSD) static gboolean get_kinfo_proc (gint pid, #if defined(HAVE_NETBSD) struct kinfo_proc2 *p); #else struct kinfo_proc *p); #endif #endif G_DEFINE_TYPE_WITH_CODE (PolkitUnixProcess, polkit_unix_process, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (POLKIT_TYPE_SUBJECT, subject_iface_init) ); static void polkit_unix_process_init (PolkitUnixProcess *unix_process) { unix_process->uid = -1; unix_process->pidfd = -1; } static void polkit_unix_process_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitUnixProcess *unix_process = POLKIT_UNIX_PROCESS (object); switch (prop_id) { case PROP_PID: g_value_set_int (value, polkit_unix_process_get_pid (unix_process)); break; case PROP_UID: g_value_set_int (value, unix_process->uid); break; case PROP_GIDS: g_value_set_boxed (value, unix_process->gids); break; case PROP_PIDFD: g_value_set_int (value, unix_process->pidfd); break; case PROP_PIDFD_IS_SAFE: g_value_set_boolean (value, unix_process->pidfd_is_safe); break; case PROP_START_TIME: g_value_set_uint64 (value, unix_process->start_time); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_process_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitUnixProcess *unix_process = POLKIT_UNIX_PROCESS (object); switch (prop_id) { case PROP_PID: polkit_unix_process_set_pid (unix_process, g_value_get_int (value)); break; case PROP_UID: polkit_unix_process_set_uid (unix_process, g_value_get_int (value)); break; case PROP_GIDS: polkit_unix_process_set_gids (unix_process, g_value_get_boxed (value)); break; case PROP_PIDFD: polkit_unix_process_set_pidfd (unix_process, g_value_get_int (value)); break; case PROP_START_TIME: polkit_unix_process_set_start_time (unix_process, g_value_get_uint64 (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gint polkit_unix_process_get_pid_from_pidfd (PolkitUnixProcess *process, GError **error) { gint result; gchar *contents; gchar **lines; gchar filename[64]; guint n; g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), -1); g_return_val_if_fail (error == NULL || *error == NULL, -1); g_return_val_if_fail (process->pidfd >= 0, -1); result = -1; lines = NULL; contents = NULL; g_snprintf (filename, sizeof filename, "/proc/self/fdinfo/%d", process->pidfd); if (!g_file_get_contents (filename, &contents, NULL, error)) goto out; lines = g_strsplit (contents, "\n", -1); for (n = 0; lines != NULL && lines[n] != NULL; n++) { gint pid; if (!g_str_has_prefix (lines[n], "Pid:")) continue; if (sscanf (lines[n] + 4, "%d", &pid) != 1) g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Unexpected line `%s' in file %s", lines[n], filename); else result = pid; goto out; } g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Didn't find any line starting with `Pid:' in file %s", filename); out: g_strfreev (lines); g_free (contents); return result; } static void polkit_unix_process_constructed (GObject *object) { PolkitUnixProcess *process = POLKIT_UNIX_PROCESS (object); /* sets pidfd, start_time and uid in case they are unset */ /* We didn't open it ourselves here, so we must have got it * from D-Bus, mark it as safe to use */ if (process->pidfd >= 0) process->pidfd_is_safe = TRUE; #ifdef HAVE_PIDFD_OPEN if (process->pid > 0 && process->pidfd < 0) { gint pidfd = (int) syscall (SYS_pidfd_open, process->pid, 0); if (pidfd >= 0) { process->pidfd = pidfd; process->pid = 0; } } #endif /* HAVE_PIDFD_OPEN */ if (process->start_time == 0) process->start_time = get_start_time_for_pid (polkit_unix_process_get_pid (process), NULL); if (process->uid == -1) { GError *error; error = NULL; process->uid = polkit_unix_process_get_racy_uid__ (process, &error); if (error != NULL) { process->uid = -1; g_error_free (error); } } if (G_OBJECT_CLASS (polkit_unix_process_parent_class)->constructed != NULL) G_OBJECT_CLASS (polkit_unix_process_parent_class)->constructed (object); } static void polkit_unix_process_finalize (GObject *object) { PolkitUnixProcess *process = POLKIT_UNIX_PROCESS (object); if (process->pidfd >= 0) { close (process->pidfd); process->pidfd = -1; } if (process->gids) g_array_unref (process->gids); if (G_OBJECT_CLASS (polkit_unix_process_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_unix_process_parent_class)->finalize (object); } static void polkit_unix_process_class_init (PolkitUnixProcessClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = polkit_unix_process_get_property; gobject_class->set_property = polkit_unix_process_set_property; gobject_class->constructed = polkit_unix_process_constructed; gobject_class->finalize = polkit_unix_process_finalize; /** * PolkitUnixProcess:pid: * * The UNIX process id. */ g_object_class_install_property (gobject_class, PROP_PID, g_param_spec_int ("pid", "Process ID", "The UNIX process ID", 0, G_MAXINT, 0, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitUnixProcess:uid: * * The UNIX user id of the process or -1 if unknown. * * Note that this is the real user-id, not the effective user-id. */ g_object_class_install_property (gobject_class, PROP_UID, g_param_spec_int ("uid", "User ID", "The UNIX user ID", G_MININT, G_MAXINT, -1, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitUnixProcess:start-time: * * The start time of the process. */ g_object_class_install_property (gobject_class, PROP_START_TIME, g_param_spec_uint64 ("start-time", "Start Time", "The start time of the process, since the machine booted", 0, G_MAXUINT64, 0, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitUnixProcess:pidfd: * * The UNIX process id file descriptor. */ g_object_class_install_property (gobject_class, PROP_PIDFD, g_param_spec_int ("pidfd", "Process ID FD", "The UNIX process ID file descriptor", -1, G_MAXINT, -1, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitUnixProcess:pidfd_is_safe: * * Whether the UNIX process id file descriptor is safe end-to-end * or it was opened locally. */ g_object_class_install_property (gobject_class, PROP_PIDFD_IS_SAFE, g_param_spec_boolean ("pidfd-is-safe", "Process ID FD", "Whether the UNIX process ID file descriptor is safe", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitUnixProcess:gids: * * The UNIX group ids of the process. */ g_object_class_install_property (gobject_class, PROP_GIDS, g_param_spec_boxed ("gids", "Group IDs", "The UNIX group IDs", G_TYPE_ARRAY, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); } /** * polkit_unix_process_get_uid: * @process: A #PolkitUnixProcess. * * Gets the user id for @process. Note that this is the real user-id, * not the effective user-id. * * NOTE: The UID may change over time, so the returned value may not match the * current state of the underlying process; or the UID may have been set by * polkit_unix_process_new_for_owner() or polkit_unix_process_set_uid(), * in which case it may not correspond to the actual UID of the referenced * process at all (at any point in time). * * Returns: The user id for @process or -1 if unknown. */ gint polkit_unix_process_get_uid (PolkitUnixProcess *process) { g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), -1); return process->uid; } /** * polkit_unix_process_set_uid: * @process: A #PolkitUnixProcess. * @uid: The user id to set for @process or -1 to unset it. * * Sets the (real, not effective) user id for @process. */ void polkit_unix_process_set_uid (PolkitUnixProcess *process, gint uid) { g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process)); process->uid = uid; } /** * polkit_unix_process_get_gids: * @process: A #PolkitUnixProcess. * * Gets the group ids for @process. Note that this is the real group-ids, * not the effective group-ids. * * Returns: (element-type GArray) (transfer full) (allow-none): a #GArray * of #gid_t containing the group ids for @process or NULL if unknown, * as a new reference to the array, caller must deref it when done. */ GArray * polkit_unix_process_get_gids (PolkitUnixProcess *process) { g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), NULL); return process->gids ? g_array_ref (process->gids) : NULL; } /** * polkit_unix_process_set_gids: * @process: A #PolkitUnixProcess. * @gids: (element-type GArray): A #GList of #gid_t containing the group * ids to set for @process or NULL to unset them. * A reference to @gids is taken. * * Sets the (real, not effective) group ids for @process. */ void polkit_unix_process_set_gids (PolkitUnixProcess *process, GArray *gids) { g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process)); if (process->gids) g_array_unref (g_steal_pointer (&process->gids)); if (gids) process->gids = g_array_ref (gids); } /** * polkit_unix_process_get_pid: * @process: A #PolkitUnixProcess. * * Gets the process id for @process. * * Returns: The process id for @process. */ gint polkit_unix_process_get_pid (PolkitUnixProcess *process) { g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), 0); if (process->pidfd >= 0) { GError *error = NULL; gint pid = polkit_unix_process_get_pid_from_pidfd(process, &error); if (pid > 0) return pid; g_error_free (error); return 0; } return process->pid; } /** * polkit_unix_process_get_start_time: * @process: A #PolkitUnixProcess. * * Gets the start time of @process. * * Returns: The start time of @process. */ guint64 polkit_unix_process_get_start_time (PolkitUnixProcess *process) { g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), 0); return process->start_time; } /** * polkit_unix_process_set_start_time: * @process: A #PolkitUnixProcess. * @start_time: The start time for @pid. * * Set the start time of @process. */ void polkit_unix_process_set_start_time (PolkitUnixProcess *process, guint64 start_time) { g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process)); process->start_time = start_time; } /** * polkit_unix_process_set_pid: * @process: A #PolkitUnixProcess. * @pid: A process id. * * Sets @pid for @process. */ void polkit_unix_process_set_pid (PolkitUnixProcess *process, gint pid) { g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process)); #ifdef HAVE_PIDFD_OPEN if (process->pidfd >= 0) { close (process->pidfd); process->pidfd = -1; process->pidfd_is_safe = FALSE; } if (pid > 0) { gint pidfd = (int) syscall (SYS_pidfd_open, process->pid, 0); if (pidfd >= 0) { process->pidfd_is_safe = FALSE; process->pidfd = pidfd; process->pid = 0; return; } } #endif /* HAVE_PIDFD_OPEN */ process->pid = pid; } /** * polkit_unix_process_get_pidfd: * @process: A #PolkitUnixProcess. * * Gets the process id file descriptor for @process. * * Returns: The process id file descriptor for @process. */ gint polkit_unix_process_get_pidfd (PolkitUnixProcess *process) { g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), -1); return process->pidfd; } /** * polkit_unix_process_get_pidfd_is_safe: * @process: A #PolkitUnixProcess. * * Checks if the process id file descriptor for @process is safe * or if it was opened locally and thus vulnerable to reuse. * * Returns: TRUE or FALSE. */ gboolean polkit_unix_process_get_pidfd_is_safe (PolkitUnixProcess *process) { g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), FALSE); return process->pidfd_is_safe; } /** * polkit_unix_process_set_pidfd: * @process: A #PolkitUnixProcess. * @pidfd: A process id file descriptor. * * Sets @pidfd for @process. */ void polkit_unix_process_set_pidfd (PolkitUnixProcess *process, gint pidfd) { g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process)); if (process->pidfd >= 0) { close (process->pidfd); process->pidfd_is_safe = FALSE; } process->pidfd = pidfd; } /** * polkit_unix_process_new: * @pid: The process id. * * Creates a new #PolkitUnixProcess for @pid. * * The uid and start time of the process will be looked up in using * e.g. the /proc filesystem depending on the * platform in use. * * Returns: (transfer full): A #PolkitSubject. Free with g_object_unref(). */ PolkitSubject * polkit_unix_process_new (gint pid) { return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_PROCESS, "pid", pid, NULL)); } /** * polkit_unix_process_new_full: * @pid: The process id. * @start_time: The start time for @pid. * * Creates a new #PolkitUnixProcess object for @pid and @start_time. * * The uid of the process will be looked up in using e.g. the * /proc filesystem depending on the platform in * use. * * Returns: (transfer full): A #PolkitSubject. Free with g_object_unref(). */ PolkitSubject * polkit_unix_process_new_full (gint pid, guint64 start_time) { return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_PROCESS, "pid", pid, "start_time", start_time, NULL)); } /** * polkit_unix_process_new_for_owner: * @pid: The process id. * @start_time: The start time for @pid or 0 to look it up in e.g. /proc. * @uid: The (real, not effective) uid of the owner of @pid or -1 to look it up in e.g. /proc. * * Creates a new #PolkitUnixProcess object for @pid, @start_time and @uid. * * Returns: (transfer full): A #PolkitSubject. Free with g_object_unref(). */ PolkitSubject * polkit_unix_process_new_for_owner (gint pid, guint64 start_time, gint uid) { return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_PROCESS, "pid", pid, "start_time", start_time, "uid", uid, NULL)); } /** * polkit_unix_process_new_pidfd: * @pidfd: The process id file descriptor. * @uid: The (real, not effective) uid of the owner of @pid or -1 to look it up in e.g. /proc. * @gids: (element-type gint) (allow-none): The (real, not effective) gids of the owner of @pid or %NULL. * * Creates a new #PolkitUnixProcess object for @pidfd and @uid. * * Returns: (transfer full): A #PolkitSubject. Free with g_object_unref(). */ PolkitSubject * polkit_unix_process_new_pidfd (gint pidfd, gint uid, GArray *gids) { return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_PROCESS, "pidfd", pidfd, "uid", uid, "gids", gids, NULL)); } static guint polkit_unix_process_hash (PolkitSubject *subject) { PolkitUnixProcess *process = POLKIT_UNIX_PROCESS (subject); return g_direct_hash (GSIZE_TO_POINTER ((polkit_unix_process_get_pid(process) + process->start_time))) ; } static gboolean polkit_unix_process_equal (PolkitSubject *a, PolkitSubject *b) { PolkitUnixProcess *process_a; PolkitUnixProcess *process_b; gint pid_a, pid_b; gint pidfd_a, pidfd_b; process_a = POLKIT_UNIX_PROCESS (a); process_b = POLKIT_UNIX_PROCESS (b); pid_a = polkit_unix_process_get_pid(process_a); pid_b = polkit_unix_process_get_pid(process_b); pidfd_a = polkit_unix_process_get_pidfd(process_a); pidfd_b = polkit_unix_process_get_pidfd(process_b); return (pid_a > 0) && (pid_b > 0) && (pid_a == pid_b) && ((pidfd_a >= 0 && pidfd_b >= 0) || (process_a->start_time == process_b->start_time)); } static gchar * polkit_unix_process_to_string (PolkitSubject *subject) { PolkitUnixProcess *process = POLKIT_UNIX_PROCESS (subject); gint pid = polkit_unix_process_get_pid(process); if (pid <= 0) return g_strdup_printf ("unix-process:unknown"); return g_strdup_printf ("unix-process:%d:%" G_GUINT64_FORMAT, pid, process->start_time); } static gboolean polkit_unix_process_exists_sync (PolkitSubject *subject, GCancellable *cancellable, GError **error) { PolkitUnixProcess *process = POLKIT_UNIX_PROCESS (subject); GError *local_error; guint64 start_time; gboolean ret; gint pid; ret = TRUE; pid = polkit_unix_process_get_pid(process); if (pid <= 0) return FALSE; /* If we have both a valid PID and a PID FD then we know the process is still the * same and it hasn't exited. */ if (polkit_unix_process_get_pidfd(process) >= 0) return TRUE; local_error = NULL; start_time = get_start_time_for_pid (pid, &local_error); if (local_error != NULL) { /* Don't propagate the error - it just means there is no process with this pid */ g_error_free (local_error); ret = FALSE; } else { if (start_time != process->start_time) { ret = FALSE; } } return ret; } static void polkit_unix_process_exists (PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new (G_OBJECT (subject), callback, user_data, polkit_unix_process_exists); g_simple_async_result_complete (simple); g_object_unref (simple); } static gboolean polkit_unix_process_exists_finish (PolkitSubject *subject, GAsyncResult *res, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_unix_process_exists); return polkit_unix_process_exists_sync (subject, NULL, error); } static void subject_iface_init (PolkitSubjectIface *subject_iface) { subject_iface->hash = polkit_unix_process_hash; subject_iface->equal = polkit_unix_process_equal; subject_iface->to_string = polkit_unix_process_to_string; subject_iface->exists = polkit_unix_process_exists; subject_iface->exists_finish = polkit_unix_process_exists_finish; subject_iface->exists_sync = polkit_unix_process_exists_sync; } #ifdef HAVE_SOLARIS static int get_pid_psinfo (pid_t pid, struct psinfo *ps) { char pname[32]; int procfd; (void) snprintf(pname, sizeof(pname), "/proc/%d/psinfo", pid); if ((procfd = open(pname, O_RDONLY)) == -1) { return -1; } if (read(procfd, ps, sizeof(struct psinfo)) < 0) { (void) close(procfd); return -1; } (void) close(procfd); return 0; } #endif #ifdef HAVE_FREEBSD static gboolean get_kinfo_proc (pid_t pid, struct kinfo_proc *p) { int mib[4]; size_t len; len = 4; sysctlnametomib ("kern.proc.pid", mib, &len); len = sizeof (struct kinfo_proc); mib[3] = pid; if (sysctl (mib, 4, p, &len, NULL, 0) == -1) return FALSE; return TRUE; } #endif #if defined(HAVE_NETBSD) || defined(HAVE_OPENBSD) static gboolean get_kinfo_proc (gint pid, #ifdef HAVE_NETBSD struct kinfo_proc2 *p) #else struct kinfo_proc *p) #endif { int name[6]; u_int namelen; size_t sz; sz = sizeof(*p); namelen = 0; name[namelen++] = CTL_KERN; #ifdef HAVE_NETBSD name[namelen++] = KERN_PROC2; #else name[namelen++] = KERN_PROC; #endif name[namelen++] = KERN_PROC_PID; name[namelen++] = pid; name[namelen++] = sz; name[namelen++] = 1; if (sysctl (name, namelen, p, &sz, NULL, 0) == -1) return FALSE; return TRUE; } #endif static guint64 get_start_time_for_pid (pid_t pid, GError **error) { guint64 start_time; #if !defined(HAVE_FREEBSD) && !defined(HAVE_NETBSD) && !defined(HAVE_OPENBSD) gchar *filename; gchar *contents; size_t length; gchar **tokens; guint num_tokens; gchar *p; gchar *endp; start_time = 0; contents = NULL; filename = g_strdup_printf ("/proc/%d/stat", pid); if (!g_file_get_contents (filename, &contents, &length, error)) goto out; /* start time is the token at index 19 after the '(process name)' entry - since only this * field can contain the ')' character, search backwards for this to avoid malicious * processes trying to fool us */ p = strrchr (contents, ')'); if (p == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error parsing file %s", filename); goto out; } p += 2; /* skip ') ' */ if (p - contents >= (int) length) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error parsing file %s", filename); goto out; } tokens = g_strsplit (p, " ", 0); num_tokens = g_strv_length (tokens); if (num_tokens < 20) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error parsing file %s", filename); goto out; } start_time = strtoull (tokens[19], &endp, 10); if (endp == tokens[19]) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error parsing file %s", filename); goto out; } g_strfreev (tokens); out: g_free (filename); g_free (contents); #else #ifdef HAVE_NETBSD struct kinfo_proc2 p; #else struct kinfo_proc p; #endif start_time = 0; if (! get_kinfo_proc (pid, &p)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error obtaining start time for %d (%s)", (gint) pid, g_strerror (errno)); goto out; } #ifdef HAVE_FREEBSD start_time = (guint64) p.ki_start.tv_sec; #else start_time = (guint64) p.p_ustart_sec; #endif out: #endif return start_time; } /* * Private: Return the "current" UID. Note that this is inherently racy, * and the value may already be obsolete by the time this function returns; * this function only guarantees that the UID was valid at some point during * its execution. */ gint polkit_unix_process_get_racy_uid__ (PolkitUnixProcess *process, GError **error) { gint result, pid; gchar *contents; gchar **lines; guint64 start_time; #if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD) struct kinfo_proc p; #elif defined(HAVE_NETBSD) struct kinfo_proc2 p; #else gchar filename[64]; guint n; GError *local_error; #endif g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); result = 0; lines = NULL; contents = NULL; pid = polkit_unix_process_get_pid(process); if (pid <= 0) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Process not found"); goto out; } #if defined(HAVE_FREEBSD) || defined(HAVE_NETBSD) || defined(HAVE_OPENBSD) if (get_kinfo_proc (pid, &p) == 0) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "get_kinfo_proc() failed for pid %d: %s", pid, g_strerror (errno)); goto out; } #if defined(HAVE_FREEBSD) result = p.ki_uid; start_time = (guint64) p.ki_start.tv_sec; #else result = p.p_uid; start_time = (guint64) p.p_ustart_sec; #endif #else /* see 'man proc' for layout of the status file * * Uid, Gid: Real, effective, saved set, and file system UIDs (GIDs). */ g_snprintf (filename, sizeof filename, "/proc/%d/status", pid); if (!g_file_get_contents (filename, &contents, NULL, error)) { goto out; } lines = g_strsplit (contents, "\n", -1); for (n = 0; lines != NULL && lines[n] != NULL; n++) { gint real_uid, effective_uid; if (!g_str_has_prefix (lines[n], "Uid:")) continue; if (sscanf (lines[n] + 4, "%d %d", &real_uid, &effective_uid) != 2) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Unexpected line `%s' in file %s", lines[n], filename); goto out; } else { result = real_uid; goto found; } } g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Didn't find any line starting with `Uid:' in file %s", filename); goto out; found: /* The UID and start time are, sadly, not available in a single file. So, * read the UID first, and then the start time; if the start time is the same * before and after reading the UID, it couldn't have changed. */ local_error = NULL; start_time = get_start_time_for_pid (pid, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); goto out; } #endif if (process->start_time != start_time) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "process with PID %d has been replaced", pid); goto out; } out: g_strfreev (lines); g_free (contents); return result; } /* deprecated public method */ /** * polkit_unix_process_get_owner: * @process: A #PolkitUnixProcess. * @error: Return location for error. * * (deprecated) */ gint polkit_unix_process_get_owner (PolkitUnixProcess *process, GError **error) { return polkit_unix_process_get_racy_uid__ (process, error); } polkit-126/src/polkit/polkitunixprocess.h000066400000000000000000000105271474122443600207510ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_UNIX_PROCESS_H #define __POLKIT_UNIX_PROCESS_H #include #include #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_UNIX_PROCESS (polkit_unix_process_get_type()) #define POLKIT_UNIX_PROCESS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_UNIX_PROCESS, PolkitUnixProcess)) #define POLKIT_UNIX_PROCESS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_UNIX_PROCESS, PolkitUnixProcessClass)) #define POLKIT_UNIX_PROCESS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_UNIX_PROCESS, PolkitUnixProcessClass)) #define POLKIT_IS_UNIX_PROCESS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_UNIX_PROCESS)) #define POLKIT_IS_UNIX_PROCESS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_UNIX_PROCESS)) #if 0 typedef struct _PolkitUnixProcess PolkitUnixProcess; #endif typedef struct _PolkitUnixProcessClass PolkitUnixProcessClass; GType polkit_unix_process_get_type (void) G_GNUC_CONST; G_GNUC_DEPRECATED_FOR(polkit_unix_process_new_for_owner) PolkitSubject *polkit_unix_process_new (gint pid); G_GNUC_DEPRECATED_FOR(polkit_unix_process_new_for_owner) PolkitSubject *polkit_unix_process_new_full (gint pid, guint64 start_time); PolkitSubject *polkit_unix_process_new_for_owner (gint pid, guint64 start_time, gint uid); PolkitSubject *polkit_unix_process_new_pidfd (gint pidfd, gint uid, GArray *gids); GArray *polkit_unix_process_get_gids (PolkitUnixProcess *process); gint polkit_unix_process_get_pid (PolkitUnixProcess *process); guint64 polkit_unix_process_get_start_time (PolkitUnixProcess *process); gint polkit_unix_process_get_uid (PolkitUnixProcess *process); void polkit_unix_process_set_gids (PolkitUnixProcess *process, GArray *gids); void polkit_unix_process_set_pid (PolkitUnixProcess *process, gint pid); void polkit_unix_process_set_uid (PolkitUnixProcess *process, gint uid); void polkit_unix_process_set_start_time (PolkitUnixProcess *process, guint64 start_time); gint polkit_unix_process_get_owner (PolkitUnixProcess *process, GError **error) G_GNUC_DEPRECATED_FOR (polkit_unix_process_get_uid); gint polkit_unix_process_get_pidfd (PolkitUnixProcess *process); void polkit_unix_process_set_pidfd (PolkitUnixProcess *process, gint pidfd); gboolean polkit_unix_process_get_pidfd_is_safe (PolkitUnixProcess *process); G_END_DECLS #endif /* __POLKIT_UNIX_PROCESS_H */ polkit-126/src/polkit/polkitunixsession-systemd.c000066400000000000000000000361651474122443600224450ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Matthias Clasen */ #include #include #include "polkitunixsession.h" #include "polkitsubject.h" #include "polkiterror.h" #include "polkitprivate.h" #include /** * SECTION:polkitunixsession * @title: PolkitUnixSession * @short_description: Unix sessions * * An object that represents an user session. * * The session id is an opaque string obtained from ConsoleKit. */ /** * PolkitUnixSession: * * The #PolkitUnixSession struct should not be accessed directly. */ struct _PolkitUnixSession { GObject parent_instance; gchar *session_id; gint pid; }; struct _PolkitUnixSessionClass { GObjectClass parent_class; }; enum { PROP_0, PROP_SESSION_ID, PROP_PID, }; static void subject_iface_init (PolkitSubjectIface *subject_iface); static void initable_iface_init (GInitableIface *initable_iface); static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); G_DEFINE_TYPE_WITH_CODE (PolkitUnixSession, polkit_unix_session, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (POLKIT_TYPE_SUBJECT, subject_iface_init) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) ); static void polkit_unix_session_init (PolkitUnixSession *session) { } static void polkit_unix_session_finalize (GObject *object) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (object); g_free (session->session_id); if (G_OBJECT_CLASS (polkit_unix_session_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_unix_session_parent_class)->finalize (object); } static void polkit_unix_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (object); switch (prop_id) { case PROP_SESSION_ID: g_value_set_string (value, session->session_id); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (object); switch (prop_id) { case PROP_SESSION_ID: polkit_unix_session_set_session_id (session, g_value_get_string (value)); break; case PROP_PID: session->pid = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_session_class_init (PolkitUnixSessionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_unix_session_finalize; gobject_class->get_property = polkit_unix_session_get_property; gobject_class->set_property = polkit_unix_session_set_property; /** * PolkitUnixSession:session-id: * * The UNIX session id. */ g_object_class_install_property (gobject_class, PROP_SESSION_ID, g_param_spec_string ("session-id", "Session ID", "The UNIX session ID", NULL, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitUnixSession:pid: * * The UNIX process id to look up the session. */ g_object_class_install_property (gobject_class, PROP_PID, g_param_spec_int ("pid", "Process ID", "Process ID to use for looking up the session", 0, G_MAXINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); } /** * polkit_unix_session_get_session_id: * @session: A #PolkitUnixSession. * * Gets the session id for @session. * * Returns: The session id for @session. Do not free this string, it * is owned by @session. **/ const gchar * polkit_unix_session_get_session_id (PolkitUnixSession *session) { g_return_val_if_fail (POLKIT_IS_UNIX_SESSION (session), NULL); return session->session_id; } /** * polkit_unix_session_set_session_id: * @session: A #PolkitUnixSession. * @session_id: The session id. * * Sets the session id for @session to @session_id. **/ void polkit_unix_session_set_session_id (PolkitUnixSession *session, const gchar *session_id) { g_return_if_fail (POLKIT_IS_UNIX_SESSION (session)); /*g_return_if_fail (session_id != NULL);*/ g_free (session->session_id); session->session_id = g_strdup (session_id); } /** * polkit_unix_session_new: * @session_id: The session id. * * Creates a new #PolkitUnixSession for @session_id. * * Returns: (transfer full): A #PolkitUnixSession. Free with g_object_unref(). **/ PolkitSubject * polkit_unix_session_new (const gchar *session_id) { return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_SESSION, "session-id", session_id, NULL)); } /** * polkit_unix_session_new_for_process: * @pid: The process id of the process to get the session for. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied * @user_data: The data to pass to @callback. * * Asynchronously creates a new #PolkitUnixSession object for the * process with process id @pid. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_unix_session_new_for_process_finish() to get the result of * the operation. * * This method constructs the object asynchronously, for the synchronous and blocking version * use polkit_unix_session_new_for_process_sync(). **/ void polkit_unix_session_new_for_process (gint pid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (POLKIT_TYPE_UNIX_SESSION, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "pid", pid, NULL); } /** * polkit_unix_session_new_for_process_finish: * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to polkit_unix_session_new_for_process(). * @error: (allow-none): Return location for error. * * Finishes constructing a #PolkitSubject for a process id. * * Returns: (transfer full) (allow-none): A #PolkitUnixSession for the @pid passed to * polkit_unix_session_new_for_process() or %NULL if @error is * set. Free with g_object_unref(). **/ PolkitSubject * polkit_unix_session_new_for_process_finish (GAsyncResult *res, GError **error) { GObject *object; GObject *source_object; source_object = g_async_result_get_source_object (res); g_assert (source_object != NULL); object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); g_object_unref (source_object); if (object != NULL) return POLKIT_SUBJECT (object); else return NULL; } /** * polkit_unix_session_new_for_process_sync: * @pid: The process id of the process to get the session for. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error. * * Creates a new #PolkitUnixSession for the process with process id @pid. * * This is a synchronous call - the calling thread is blocked until a * reply is received. For the asynchronous version, see * polkit_unix_session_new_for_process(). * * Returns: (allow-none) (transfer full): A #PolkitUnixSession for * @pid or %NULL if @error is set. Free with g_object_unref(). **/ PolkitSubject * polkit_unix_session_new_for_process_sync (gint pid, GCancellable *cancellable, GError **error) { return POLKIT_SUBJECT (g_initable_new (POLKIT_TYPE_UNIX_SESSION, cancellable, error, "pid", pid, NULL)); } static guint polkit_unix_session_hash (PolkitSubject *subject) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject); return g_str_hash (session->session_id); } static gboolean polkit_unix_session_equal (PolkitSubject *a, PolkitSubject *b) { PolkitUnixSession *session_a; PolkitUnixSession *session_b; session_a = POLKIT_UNIX_SESSION (a); session_b = POLKIT_UNIX_SESSION (b); return g_strcmp0 (session_a->session_id, session_b->session_id) == 0; } static gchar * polkit_unix_session_to_string (PolkitSubject *subject) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject); return g_strdup_printf ("unix-session:%s", session->session_id); } static gboolean polkit_unix_session_exists_sync (PolkitSubject *subject, GCancellable *cancellable, GError **error) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject); gboolean ret = FALSE; uid_t uid; if (sd_session_get_uid (session->session_id, &uid) == 0) ret = TRUE; return ret; } static void exists_in_thread_func (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { GError *error; error = NULL; if (!polkit_unix_session_exists_sync (POLKIT_SUBJECT (object), cancellable, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } } static void polkit_unix_session_exists (PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; g_return_if_fail (POLKIT_IS_UNIX_SESSION (subject)); simple = g_simple_async_result_new (G_OBJECT (subject), callback, user_data, polkit_unix_session_exists); g_simple_async_result_run_in_thread (simple, exists_in_thread_func, G_PRIORITY_DEFAULT, cancellable); g_object_unref (simple); } static gboolean polkit_unix_session_exists_finish (PolkitSubject *subject, GAsyncResult *res, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); gboolean ret; g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_unix_session_exists); ret = FALSE; if (g_simple_async_result_propagate_error (simple, error)) goto out; ret = g_simple_async_result_get_op_res_gboolean (simple); out: return ret; } static void subject_iface_init (PolkitSubjectIface *subject_iface) { subject_iface->hash = polkit_unix_session_hash; subject_iface->equal = polkit_unix_session_equal; subject_iface->to_string = polkit_unix_session_to_string; subject_iface->exists = polkit_unix_session_exists; subject_iface->exists_finish = polkit_unix_session_exists_finish; subject_iface->exists_sync = polkit_unix_session_exists_sync; } static gboolean polkit_unix_session_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (initable); gboolean ret = FALSE; char *s; uid_t uid; if (session->session_id != NULL) { /* already set, nothing to do */ ret = TRUE; goto out; } if (sd_pid_get_session (session->pid, &s) == 0) { session->session_id = g_strdup (s); free (s); ret = TRUE; goto out; } /* Now do process -> uid -> graphical session (systemd version 213)*/ if (sd_pid_get_owner_uid (session->pid, &uid) < 0) goto error; if (sd_uid_get_display (uid, &s) >= 0) { session->session_id = g_strdup (s); free (s); ret = TRUE; goto out; } error: g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "No session for pid %d", (gint) session->pid); out: return ret; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = polkit_unix_session_initable_init; } static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface) { /* use default implementation to run GInitable code in a thread */ } polkit-126/src/polkit/polkitunixsession.c000066400000000000000000000411521474122443600207470ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include "polkitunixsession.h" #include "polkitsubject.h" #include "polkiterror.h" #include "polkitprivate.h" /** * SECTION:polkitunixsession * @title: PolkitUnixSession * @short_description: Unix sessions * * An object that represents an user session. * * The session id is an opaque string obtained from ConsoleKit. */ /** * PolkitUnixSession: * * The #PolkitUnixSession struct should not be accessed directly. */ struct _PolkitUnixSession { GObject parent_instance; gchar *session_id; gint pid; }; struct _PolkitUnixSessionClass { GObjectClass parent_class; }; enum { PROP_0, PROP_SESSION_ID, PROP_PID, }; static void subject_iface_init (PolkitSubjectIface *subject_iface); static void initable_iface_init (GInitableIface *initable_iface); static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); G_DEFINE_TYPE_WITH_CODE (PolkitUnixSession, polkit_unix_session, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (POLKIT_TYPE_SUBJECT, subject_iface_init) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) ); static void polkit_unix_session_init (PolkitUnixSession *session) { } static void polkit_unix_session_finalize (GObject *object) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (object); g_free (session->session_id); if (G_OBJECT_CLASS (polkit_unix_session_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_unix_session_parent_class)->finalize (object); } static void polkit_unix_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (object); switch (prop_id) { case PROP_SESSION_ID: g_value_set_string (value, session->session_id); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (object); switch (prop_id) { case PROP_SESSION_ID: polkit_unix_session_set_session_id (session, g_value_get_string (value)); break; case PROP_PID: session->pid = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_session_class_init (PolkitUnixSessionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_unix_session_finalize; gobject_class->get_property = polkit_unix_session_get_property; gobject_class->set_property = polkit_unix_session_set_property; /** * PolkitUnixSession:session-id: * * The UNIX session id. */ g_object_class_install_property (gobject_class, PROP_SESSION_ID, g_param_spec_string ("session-id", "Session ID", "The UNIX session ID", NULL, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitUnixSession:pid: * * The UNIX process id to look up the session. */ g_object_class_install_property (gobject_class, PROP_PID, g_param_spec_int ("pid", "Process ID", "Process ID to use for looking up the session", 0, G_MAXINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); } /** * polkit_unix_session_get_session_id: * @session: A #PolkitUnixSession. * * Gets the session id for @session. * * Returns: The session id for @session. Do not free this string, it * is owned by @session. **/ const gchar * polkit_unix_session_get_session_id (PolkitUnixSession *session) { g_return_val_if_fail (POLKIT_IS_UNIX_SESSION (session), NULL); return session->session_id; } /** * polkit_unix_session_set_session_id: * @session: A #PolkitUnixSession. * @session_id: The session id. * * Sets the session id for @session to @session_id. **/ void polkit_unix_session_set_session_id (PolkitUnixSession *session, const gchar *session_id) { g_return_if_fail (POLKIT_IS_UNIX_SESSION (session)); /*g_return_if_fail (session_id != NULL);*/ g_free (session->session_id); session->session_id = g_strdup (session_id); } /** * polkit_unix_session_new: * @session_id: The session id. * * Creates a new #PolkitUnixSession for @session_id. * * Returns: (transfer full): A #PolkitUnixSession. Free with g_object_unref(). **/ PolkitSubject * polkit_unix_session_new (const gchar *session_id) { return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_SESSION, "session-id", session_id, NULL)); } /** * polkit_unix_session_new_for_process: * @pid: The process id of the process to get the session for. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied * @user_data: The data to pass to @callback. * * Asynchronously creates a new #PolkitUnixSession object for the * process with process id @pid. * * When the operation is finished, @callback will be invoked in the * thread-default * main loop of the thread you are calling this method * from. You can then call * polkit_unix_session_new_for_process_finish() to get the result of * the operation. * * This method constructs the object asynchronously, for the synchronous and blocking version * use polkit_unix_session_new_for_process_sync(). **/ void polkit_unix_session_new_for_process (gint pid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (POLKIT_TYPE_UNIX_SESSION, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "pid", pid, NULL); } /** * polkit_unix_session_new_for_process_finish: * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to polkit_unix_session_new_for_process(). * @error: (allow-none): Return location for error. * * Finishes constructing a #PolkitSubject for a process id. * * Returns: (transfer full) (allow-none): A #PolkitUnixSession for the @pid passed to * polkit_unix_session_new_for_process() or %NULL if @error is * set. Free with g_object_unref(). **/ PolkitSubject * polkit_unix_session_new_for_process_finish (GAsyncResult *res, GError **error) { GObject *object; GObject *source_object; source_object = g_async_result_get_source_object (res); g_assert (source_object != NULL); object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); g_object_unref (source_object); if (object != NULL) return POLKIT_SUBJECT (object); else return NULL; } /** * polkit_unix_session_new_for_process_sync: * @pid: The process id of the process to get the session for. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: (allow-none): Return location for error. * * Creates a new #PolkitUnixSession for the process with process id @pid. * * This is a synchronous call - the calling thread is blocked until a * reply is received. For the asynchronous version, see * polkit_unix_session_new_for_process(). * * Returns: (allow-none) (transfer full): A #PolkitUnixSession for * @pid or %NULL if @error is set. Free with g_object_unref(). **/ PolkitSubject * polkit_unix_session_new_for_process_sync (gint pid, GCancellable *cancellable, GError **error) { return POLKIT_SUBJECT (g_initable_new (POLKIT_TYPE_UNIX_SESSION, cancellable, error, "pid", pid, NULL)); } static guint polkit_unix_session_hash (PolkitSubject *subject) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject); return g_str_hash (session->session_id); } static gboolean polkit_unix_session_equal (PolkitSubject *a, PolkitSubject *b) { PolkitUnixSession *session_a; PolkitUnixSession *session_b; session_a = POLKIT_UNIX_SESSION (a); session_b = POLKIT_UNIX_SESSION (b); return g_strcmp0 (session_a->session_id, session_b->session_id) == 0; } static gchar * polkit_unix_session_to_string (PolkitSubject *subject) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject); return g_strdup_printf ("unix-session:%s", session->session_id); } static gboolean polkit_unix_session_exists_sync (PolkitSubject *subject, GCancellable *cancellable, GError **error) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject); GDBusConnection *connection; GVariant *result; gboolean ret; ret = FALSE; connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error); if (connection == NULL) goto out; result = g_dbus_connection_call_sync (connection, "org.freedesktop.ConsoleKit", /* name */ session->session_id, /* object path */ "org.freedesktop.ConsoleKit.Session", /* interface name */ "GetUser", /* method */ NULL, /* parameters */ G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error); if (result == NULL) goto out; ret = TRUE; g_variant_unref (result); out: if (connection != NULL) g_object_unref (connection); return ret; } static void exists_in_thread_func (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { GError *error; error = NULL; if (!polkit_unix_session_exists_sync (POLKIT_SUBJECT (object), cancellable, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } } static void polkit_unix_session_exists (PolkitSubject *subject, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; g_return_if_fail (POLKIT_IS_UNIX_SESSION (subject)); simple = g_simple_async_result_new (G_OBJECT (subject), callback, user_data, polkit_unix_session_exists); g_simple_async_result_run_in_thread (simple, exists_in_thread_func, G_PRIORITY_DEFAULT, cancellable); g_object_unref (simple); } static gboolean polkit_unix_session_exists_finish (PolkitSubject *subject, GAsyncResult *res, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); gboolean ret; g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_unix_session_exists); ret = FALSE; if (g_simple_async_result_propagate_error (simple, error)) goto out; ret = g_simple_async_result_get_op_res_gboolean (simple); out: return ret; } static void subject_iface_init (PolkitSubjectIface *subject_iface) { subject_iface->hash = polkit_unix_session_hash; subject_iface->equal = polkit_unix_session_equal; subject_iface->to_string = polkit_unix_session_to_string; subject_iface->exists = polkit_unix_session_exists; subject_iface->exists_finish = polkit_unix_session_exists_finish; subject_iface->exists_sync = polkit_unix_session_exists_sync; } static gboolean polkit_unix_session_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { PolkitUnixSession *session = POLKIT_UNIX_SESSION (initable); GDBusConnection *connection; GVariant *result; gboolean ret; connection = NULL; ret = FALSE; if (session->session_id != NULL) { /* already set, nothing to do */ ret = TRUE; goto out; } connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error); if (connection == NULL) goto out; result = g_dbus_connection_call_sync (connection, "org.freedesktop.ConsoleKit", /* name */ "/org/freedesktop/ConsoleKit/Manager", /* object path */ "org.freedesktop.ConsoleKit.Manager", /* interface name */ "GetSessionForUnixProcess", /* method */ g_variant_new ("(u)", session->pid), /* parameters */ G_VARIANT_TYPE ("(o)"), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error); if (result == NULL) goto out; g_variant_get (result, "(o)", &session->session_id); g_variant_unref (result); ret = TRUE; out: if (connection != NULL) g_object_unref (connection); return ret; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = polkit_unix_session_initable_init; } static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface) { /* use default implementation to run GInitable code in a thread */ } polkit-126/src/polkit/polkitunixsession.h000066400000000000000000000062601474122443600207550ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_UNIX_SESSION_H #define __POLKIT_UNIX_SESSION_H #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_UNIX_SESSION (polkit_unix_session_get_type()) #define POLKIT_UNIX_SESSION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_UNIX_SESSION, PolkitUnixSession)) #define POLKIT_UNIX_SESSION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_UNIX_SESSION, PolkitUnixSessionClass)) #define POLKIT_UNIX_SESSION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_UNIX_SESSION, PolkitUnixSessionClass)) #define POLKIT_IS_UNIX_SESSION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_UNIX_SESSION)) #define POLKIT_IS_UNIX_SESSION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_UNIX_SESSION)) #if 0 typedef struct _PolkitUnixSession PolkitUnixSession; #endif typedef struct _PolkitUnixSessionClass PolkitUnixSessionClass; GType polkit_unix_session_get_type (void) G_GNUC_CONST; PolkitSubject *polkit_unix_session_new (const gchar *session_id); void polkit_unix_session_new_for_process (gint pid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); PolkitSubject *polkit_unix_session_new_for_process_finish (GAsyncResult *res, GError **error); PolkitSubject *polkit_unix_session_new_for_process_sync (gint pid, GCancellable *cancellable, GError **error); const gchar *polkit_unix_session_get_session_id (PolkitUnixSession *session); void polkit_unix_session_set_session_id (PolkitUnixSession *session, const gchar *session_id); G_END_DECLS #endif /* __POLKIT_UNIX_SESSION_H */ polkit-126/src/polkit/polkitunixuser.c000066400000000000000000000172451474122443600202500ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include "polkitunixuser.h" #include "polkitidentity.h" #include "polkiterror.h" #include "polkitprivate.h" /** * SECTION:polkitunixuser * @title: PolkitUnixUser * @short_description: Unix users * * An object representing a user identity on a UNIX system. */ /** * PolkitUnixUser: * * The #PolkitUnixUser struct should not be accessed directly. */ struct _PolkitUnixUser { GObject parent_instance; gint uid; gchar *name; }; struct _PolkitUnixUserClass { GObjectClass parent_class; }; enum { PROP_0, PROP_UID, }; static void identity_iface_init (PolkitIdentityIface *identity_iface); G_DEFINE_TYPE_WITH_CODE (PolkitUnixUser, polkit_unix_user, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (POLKIT_TYPE_IDENTITY, identity_iface_init) ); static void polkit_unix_user_init (PolkitUnixUser *unix_user) { unix_user->uid = -1; /* (uid_t) -1 is not a valid UID under Linux */ unix_user->name = NULL; } static void polkit_unix_user_finalize (GObject *object) { PolkitUnixUser *unix_user = POLKIT_UNIX_USER (object); g_free(unix_user->name); G_OBJECT_CLASS (polkit_unix_user_parent_class)->finalize (object); } static void polkit_unix_user_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitUnixUser *unix_user = POLKIT_UNIX_USER (object); switch (prop_id) { case PROP_UID: g_value_set_int (value, unix_user->uid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_user_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitUnixUser *unix_user = POLKIT_UNIX_USER (object); gint val; switch (prop_id) { case PROP_UID: val = g_value_get_int (value); g_return_if_fail (val != -1); unix_user->uid = val; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_unix_user_class_init (PolkitUnixUserClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_unix_user_finalize; gobject_class->get_property = polkit_unix_user_get_property; gobject_class->set_property = polkit_unix_user_set_property; /** * PolkitUnixUser:uid: * * The UNIX user id. */ g_object_class_install_property (gobject_class, PROP_UID, g_param_spec_int ("uid", "User ID", "The UNIX user ID", G_MININT, G_MAXINT, -1, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); } /** * polkit_unix_user_get_uid: * @user: A #PolkitUnixUser. * * Gets the UNIX user id for @user. * * Returns: A UNIX user id. */ gint polkit_unix_user_get_uid (PolkitUnixUser *user) { g_return_val_if_fail (POLKIT_IS_UNIX_USER (user), -1); return user->uid; } /** * polkit_unix_user_set_uid: * @user: A #PolkitUnixUser. * @uid: A UNIX user id. * * Sets @uid for @user. */ void polkit_unix_user_set_uid (PolkitUnixUser *user, gint uid) { g_return_if_fail (POLKIT_IS_UNIX_USER (user)); g_return_if_fail (uid != -1); user->uid = uid; } /** * polkit_unix_user_new: * @uid: A UNIX user id. * * Creates a new #PolkitUnixUser object for @uid. * * Returns: (transfer full): A #PolkitUnixUser object. Free with g_object_unref(). */ PolkitIdentity * polkit_unix_user_new (gint uid) { g_return_val_if_fail (uid != -1, NULL); return POLKIT_IDENTITY (g_object_new (POLKIT_TYPE_UNIX_USER, "uid", uid, NULL)); } /** * polkit_unix_user_new_for_name: * @name: A UNIX user name. * @error: Return location for error. * * Creates a new #PolkitUnixUser object for a user with the user name * @name. * * Returns: (allow-none) (transfer full): A #PolkitUnixUser object or %NULL if @error is set. */ PolkitIdentity * polkit_unix_user_new_for_name (const gchar *name, GError **error) { struct passwd *passwd; PolkitIdentity *identity; g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); identity = NULL; passwd = getpwnam (name); if (passwd == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "No UNIX user with name %s: %s", name, g_strerror (errno)); goto out; } identity = polkit_unix_user_new (passwd->pw_uid); out: return identity; } /** * polkit_unix_user_get_name: * @user: A #PolkitUnixUser. * * Get the user's name. * * Returns: (allow-none) (transfer none): User name string or %NULL if user uid not found. */ const gchar * polkit_unix_user_get_name (PolkitUnixUser *user) { if (user->name == NULL) { struct passwd *passwd; passwd = getpwuid (user->uid); if (passwd != NULL) user->name = g_strdup(passwd->pw_name); } return user->name; } static gboolean polkit_unix_user_equal (PolkitIdentity *a, PolkitIdentity *b) { PolkitUnixUser *user_a; PolkitUnixUser *user_b; user_a = POLKIT_UNIX_USER (a); user_b = POLKIT_UNIX_USER (b); return user_a->uid == user_b->uid; } static guint polkit_unix_user_hash (PolkitIdentity *identity) { PolkitUnixUser *user; user = POLKIT_UNIX_USER (identity); return g_direct_hash (GINT_TO_POINTER (((gint) (user->uid)) * 2)); } static gchar * polkit_unix_user_to_string (PolkitIdentity *identity) { PolkitUnixUser *user = POLKIT_UNIX_USER (identity); const gchar *user_name = polkit_unix_user_get_name(user); if (user_name != NULL) return g_strdup_printf ("unix-user:%s", user_name); else return g_strdup_printf ("unix-user:%d", user->uid); } static void identity_iface_init (PolkitIdentityIface *identity_iface) { identity_iface->hash = polkit_unix_user_hash; identity_iface->equal = polkit_unix_user_equal; identity_iface->to_string = polkit_unix_user_to_string; } polkit-126/src/polkit/polkitunixuser.h000066400000000000000000000047471474122443600202600ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_UNIX_USER_H #define __POLKIT_UNIX_USER_H #include #include #include #include #include G_BEGIN_DECLS #define POLKIT_TYPE_UNIX_USER (polkit_unix_user_get_type()) #define POLKIT_UNIX_USER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_UNIX_USER, PolkitUnixUser)) #define POLKIT_UNIX_USER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_UNIX_USER, PolkitUnixUserClass)) #define POLKIT_UNIX_USER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_UNIX_USER, PolkitUnixUserClass)) #define POLKIT_IS_UNIX_USER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_UNIX_USER)) #define POLKIT_IS_UNIX_USER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_UNIX_USER)) #if 0 typedef struct _PolkitUnixUser PolkitUnixUser; #endif typedef struct _PolkitUnixUserClass PolkitUnixUserClass; GType polkit_unix_user_get_type (void) G_GNUC_CONST; PolkitIdentity *polkit_unix_user_new (gint uid); PolkitIdentity *polkit_unix_user_new_for_name (const gchar *name, GError **error); gint polkit_unix_user_get_uid (PolkitUnixUser *user); void polkit_unix_user_set_uid (PolkitUnixUser *user, gint uid); const gchar *polkit_unix_user_get_name (PolkitUnixUser *user); G_END_DECLS #endif /* __POLKIT_UNIX_USER_H */ polkit-126/src/polkitagent/000077500000000000000000000000001474122443600160055ustar00rootroot00000000000000polkit-126/src/polkitagent/meson.build000066400000000000000000000046651474122443600201620ustar00rootroot00000000000000name = '@0@-agent-@1@'.format(meson.project_name(), pk_api_version) enum_headers = files('polkitagentlistener.h') headers = enum_headers + files( 'polkitagent.h', 'polkitagentsession.h', 'polkitagenttextlistener.h', 'polkitagenttypes.h', ) install_headers( headers, install_dir: pk_pkgincludedir / 'polkitagent', ) enum_sources = gnome.mkenums_simple( 'polkitagentenumtypes', sources: enum_headers, install_header: true, install_dir: pk_pkgincludedir / 'polkitagent', ) marshal = 'polkitagentmarshal' marshal_sources = gnome.genmarshal( marshal, sources: marshal + '.list', prefix: '_polkit_agent_marshal', ) sources = enum_sources + marshal_sources + files( 'polkitagentlistener.c', 'polkitagentsession.c', 'polkitagenttextlistener.c', ) deps = [ gio_unix_dep, libpolkit_gobject_dep, ] c_flags = [ '-D_POLKIT_COMPILATION', '-D_POLKIT_AGENT_COMPILATION', '-DPACKAGE_PREFIX="@0@"'.format(pk_prefix), ] libpolkit_agent = shared_library( name, sources: sources, version: libversion, include_directories: top_inc, dependencies: deps, c_args: c_flags, link_args: ldflags, link_depends: symbol_map, install: true, ) libpolkit_agent_dep = declare_dependency( sources: enum_sources[1], include_directories: '.', dependencies: libpolkit_gobject_dep, link_with: libpolkit_agent, ) pkg.generate( libraries: libpolkit_agent, version: pk_version, name: name, description: 'PolicyKit Authentication Agent API', filebase: name, subdirs: pk_api_name, requires: 'polkit-gobject-1', variables: 'exec_prefix=${prefix}', ) if enable_introspection incs = [ 'Gio-2.0', libpolkit_gobject_gir[0], ] c_flags = [ '-D_POLKIT_AGENT_COMPILATION', '-D_POLKIT_COMPILATION', ] gnome.generate_gir( libpolkit_agent, sources: sources + headers, includes: incs, extra_args: c_flags, nsversion: pk_gir_version, namespace: 'PolkitAgent', export_packages: name, header: 'polkitagent/polkitagent.h', install: true, ) endif if not get_option('libs-only') sources = files( 'polkitagenthelperprivate.c', 'polkitagenthelper-@0@.c'.format(auth_fw), ) deps = auth_deps + [libpolkit_gobject_dep] executable( '@0@-agent-helper-@1@'.format(meson.project_name(), pk_api_version), sources, include_directories: top_inc, dependencies: deps, c_args: '-D_POLKIT_COMPILATION', install: true, install_dir: pk_libprivdir, ) endif polkit-126/src/polkitagent/polkitagent.h000066400000000000000000000027121474122443600205010ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #ifndef __POLKIT_AGENT_H #define __POLKIT_AGENT_H #if !defined (POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE) && !defined (_POLKIT_AGENT_COMPILATION) #error "libpolkitagent is unstable API and subject to change. You must define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE to acknowledge this." #endif #define _POLKIT_AGENT_INSIDE_POLKIT_AGENT_H 1 #include #include #include #include #include #undef _POLKIT_AGENT_INSIDE_POLKIT_AGENT_H #endif /* __POLKIT_AGENT_H */ polkit-126/src/polkitagent/polkitagentenumtypes.c.template000066400000000000000000000016651474122443600242660ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; if (g_once_init_enter ((gsize*) &g_define_type_id__volatile)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType g_define_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); } return g_define_type_id__volatile; } /*** END value-tail ***/ /*** BEGIN file-tail ***/ /*** END file-tail ***/ polkit-126/src/polkitagent/polkitagentenumtypes.h.template000066400000000000000000000010441474122443600242620ustar00rootroot00000000000000/*** BEGIN file-header ***/ #ifndef __POLKIT_AGENT_ENUM_TYPES_H__ #define __POLKIT_AGENT_ENUM_TYPES_H__ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) /*** END value-header ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* __POLKIT_AGENT_ENUM_TYPES_H__ */ /*** END file-tail ***/ polkit-126/src/polkitagent/polkitagenthelper-bsdauth.c000066400000000000000000000114671474122443600233330ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * Copyright (C) 2009-2010 Andrew Psaltis * Copyright (C) 2010 Antoine Jacoutot * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Andrew Psaltis , based on * polkitagenthelper.c which was written by * David Zeuthen */ #include "polkitagenthelperprivate.h" #include #include #include #include #include #include #include #include #include #include static gboolean bsdauth_authenticate (const char *user_to_auth); int main (int argc, char *argv[]) { struct passwd *pw; const char *user_to_auth; char *cookie = NULL; /* clear the entire environment to avoid attacks with libraries honoring environment variables */ if (_polkit_clearenv () != 0) goto error; /* set a minimal environment */ setenv ("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1); /* check that we are setuid root */ if (geteuid () != 0) { fprintf (stderr, "polkit-agent-helper-1: needs to be setuid root\n"); goto error; } openlog ("polkit-agent-helper-1", LOG_CONS | LOG_PID, LOG_AUTHPRIV); /* check for correct invocation */ if (!(argc == 2 || argc == 3)) { syslog (LOG_NOTICE, "inappropriate use of helper, wrong number of arguments [uid=%d]", getuid ()); fprintf (stderr, "polkit-agent-helper-1: wrong number of arguments. This incident has been logged.\n"); goto error; } if (getuid () != 0) { /* check we're running with a non-tty stdin */ if (isatty (STDIN_FILENO) != 0) { syslog (LOG_NOTICE, "inappropriate use of helper, stdin is a tty [uid=%d]", getuid ()); fprintf (stderr, "polkit-agent-helper-1: inappropriate use of helper, stdin is a tty. This incident has been logged.\n"); goto error; } } user_to_auth = argv[1]; cookie = read_cookie (argc, argv); if (!cookie) goto error; #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: user to auth is '%s'.\n", user_to_auth); #endif /* PAH_DEBUG */ /* Search the password database for the user requesting authentication */ if ((pw = getpwnam (user_to_auth)) == NULL) { syslog (LOG_NOTICE, "password database information request for user %s [uid=%d] failed", user_to_auth, getuid()); fprintf(stderr, "polkit-agent-helper-1: could not get user information for '%s'", user_to_auth); goto error; } /* Check the user's identity */ if (!bsdauth_authenticate (user_to_auth)) { syslog (LOG_NOTICE, "authentication failure [uid=%d] trying to authenticate '%s'", getuid (), user_to_auth); fprintf (stderr, "polkit-agent-helper-1: authentication failure. This incident has been logged.\n"); goto error; } #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: sending D-Bus message to polkit daemon\n"); #endif /* PAH_DEBUG */ /* now send a D-Bus message to the polkit daemon that * includes a) the cookie; and b) the user we authenticated */ if (!send_dbus_message (cookie, user_to_auth)) { #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: error sending D-Bus message to polkit daemon\n"); #endif /* PAH_DEBUG */ goto error; } free (cookie); #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: successfully sent D-Bus message to polkit daemon\n"); #endif /* PAH_DEBUG */ fprintf (stdout, "SUCCESS\n"); flush_and_wait (); return 0; error: free (cookie); fprintf (stdout, "FAILURE\n"); flush_and_wait (); return 1; } static gboolean bsdauth_authenticate (const char *user_to_auth) { char passwd[512]; fprintf (stdout, "PAM_PROMPT_ECHO_OFF password:\n"); fflush (stdout); usleep (10 * 1000); /* since fflush(3) seems buggy */ if (fgets (passwd, sizeof (passwd), stdin) == NULL) goto error; if (strlen (passwd) > 0 && passwd[strlen (passwd) - 1] == '\n') passwd[strlen (passwd) - 1] = '\0'; if (auth_userokay((char *)user_to_auth, NULL, "auth-polkit", passwd) == 0) goto error; return 1; error: return 0; } polkit-126/src/polkitagent/polkitagenthelper-pam.c000066400000000000000000000202101474122443600224400ustar00rootroot00000000000000/* * Copyright (C) 2008, 2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include "polkitagenthelperprivate.h" #include #include #include #include #include #include #include #include #include static int conversation_function (int n, const struct pam_message **msg, struct pam_response **resp, void *data); static void send_to_helper (const gchar *str1, const gchar *str2) { char *escaped; char *tmp2; size_t len2; tmp2 = g_strdup(str2); g_assert (tmp2 != NULL); len2 = strlen(tmp2); #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: writing `%s ' to stdout\n", str1); #endif /* PAH_DEBUG */ fprintf (stdout, "%s ", str1); if (len2 > 0 && tmp2[len2 - 1] == '\n') tmp2[len2 - 1] = '\0'; escaped = g_strescape (tmp2, NULL); #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: writing `%s' to stdout\n", escaped); #endif /* PAH_DEBUG */ fprintf (stdout, "%s", escaped); #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: writing newline to stdout\n"); #endif /* PAH_DEBUG */ fputc ('\n', stdout); #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: flushing stdout\n"); #endif /* PAH_DEBUG */ fflush (stdout); g_free (escaped); g_free (tmp2); } int main (int argc, char *argv[]) { int rc; const char *user_to_auth; char *cookie = NULL; struct pam_conv pam_conversation; pam_handle_t *pam_h; const void *authed_user; rc = 0; pam_h = NULL; /* clear the entire environment to avoid attacks using with libraries honoring environment variables */ if (_polkit_clearenv () != 0) goto error; /* set a minimal environment */ setenv ("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1); /* check that we are setuid root */ if (geteuid () != 0) { gchar *s; fprintf (stderr, "polkit-agent-helper-1: needs to be setuid root\n"); /* Special-case a very common error triggered in jhbuild setups */ s = g_strdup_printf ("Incorrect permissions on %s (needs to be setuid root)", argv[0]); send_to_helper ("PAM_ERROR_MSG", s); g_free (s); goto error; } openlog ("polkit-agent-helper-1", LOG_CONS | LOG_PID, LOG_AUTHPRIV); /* check for correct invocation */ if (!(argc == 2 || argc == 3)) { syslog (LOG_NOTICE, "inappropriate use of helper, wrong number of arguments [uid=%d]", getuid ()); fprintf (stderr, "polkit-agent-helper-1: wrong number of arguments. This incident has been logged.\n"); goto error; } user_to_auth = argv[1]; cookie = read_cookie (argc, argv); if (!cookie) goto error; if (getuid () != 0) { /* check we're running with a non-tty stdin */ if (isatty (STDIN_FILENO) != 0) { syslog (LOG_NOTICE, "inappropriate use of helper, stdin is a tty [uid=%d]", getuid ()); fprintf (stderr, "polkit-agent-helper-1: inappropriate use of helper, stdin is a tty. This incident has been logged.\n"); goto error; } } #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: user to auth is '%s'.\n", user_to_auth); #endif /* PAH_DEBUG */ pam_conversation.conv = conversation_function; pam_conversation.appdata_ptr = NULL; /* start the pam stack */ rc = pam_start ("polkit-1", user_to_auth, &pam_conversation, &pam_h); if (rc != PAM_SUCCESS) { fprintf (stderr, "polkit-agent-helper-1: pam_start failed: %s\n", pam_strerror (pam_h, rc)); goto error; } /* set the requesting user */ rc = pam_set_item (pam_h, PAM_RUSER, user_to_auth); if (rc != PAM_SUCCESS) { fprintf (stderr, "polkit-agent-helper-1: pam_set_item failed: %s\n", pam_strerror (pam_h, rc)); goto error; } /* is user really user? */ rc = pam_authenticate (pam_h, 0); if (rc != PAM_SUCCESS) { const char *err; err = pam_strerror (pam_h, rc); fprintf (stderr, "polkit-agent-helper-1: pam_authenticate failed: %s\n", err); goto error; } /* permitted access? */ rc = pam_acct_mgmt (pam_h, 0); if (rc != PAM_SUCCESS) { const char *err; err = pam_strerror (pam_h, rc); fprintf (stderr, "polkit-agent-helper-1: pam_acct_mgmt failed: %s\n", err); goto error; } /* did we auth the right user? */ rc = pam_get_item (pam_h, PAM_USER, &authed_user); if (rc != PAM_SUCCESS) { const char *err; err = pam_strerror (pam_h, rc); fprintf (stderr, "polkit-agent-helper-1: pam_get_item failed: %s\n", err); goto error; } if (strcmp (authed_user, user_to_auth) != 0) { fprintf (stderr, "polkit-agent-helper-1: Tried to auth user '%s' but we got auth for user '%s' instead", user_to_auth, (const char *) authed_user); goto error; } #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: successfully authenticated user '%s'.\n", user_to_auth); #endif /* PAH_DEBUG */ pam_end (pam_h, rc); pam_h = NULL; #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: sending D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ /* now send a D-Bus message to the PolicyKit daemon that * includes a) the cookie; and b) the user we authenticated */ if (!send_dbus_message (cookie, user_to_auth)) { #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: error sending D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ goto error; } free (cookie); #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: successfully sent D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ fprintf (stdout, "SUCCESS\n"); flush_and_wait(); return 0; error: free (cookie); if (pam_h != NULL) pam_end (pam_h, rc); fprintf (stdout, "FAILURE\n"); flush_and_wait(); return 1; } static int conversation_function (int n, const struct pam_message **msg, struct pam_response **resp, void *data) { struct pam_response *aresp; char buf[PAM_MAX_RESP_SIZE]; int i; (void)data; if (n <= 0 || n > PAM_MAX_NUM_MSG) return PAM_CONV_ERR; if ((aresp = calloc(n, sizeof *aresp)) == NULL) return PAM_BUF_ERR; for (i = 0; i < n; ++i) { aresp[i].resp_retcode = 0; aresp[i].resp = NULL; switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: send_to_helper ("PAM_PROMPT_ECHO_OFF", msg[i]->msg); goto conv1; case PAM_PROMPT_ECHO_ON: send_to_helper ("PAM_PROMPT_ECHO_ON", msg[i]->msg); conv1: if (fgets (buf, sizeof buf, stdin) == NULL) goto error; if (strlen (buf) > 0 && buf[strlen (buf) - 1] == '\n') buf[strlen (buf) - 1] = '\0'; aresp[i].resp = strdup (buf); if (aresp[i].resp == NULL) goto error; break; case PAM_ERROR_MSG: send_to_helper ("PAM_ERROR_MSG", msg[i]->msg); break; case PAM_TEXT_INFO: send_to_helper ("PAM_TEXT_INFO", msg[i]->msg); break; default: goto error; } } *resp = aresp; return PAM_SUCCESS; error: for (i = 0; i < n; ++i) { if (aresp[i].resp != NULL) { memset (aresp[i].resp, 0, strlen(aresp[i].resp)); free (aresp[i].resp); } } memset (aresp, 0, n * sizeof *aresp); free (aresp); *resp = NULL; return PAM_CONV_ERR; } polkit-126/src/polkitagent/polkitagenthelper-shadow.c000066400000000000000000000144311474122443600231600ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * Copyright (C) 2009-2010 Andrew Psaltis * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Andrew Psaltis , based on * polkitagenthelper.c which was written by * David Zeuthen */ #include "polkitagenthelperprivate.h" #include #include #include #include #include #include #include #include #include #include #include #include static gboolean shadow_authenticate (struct spwd *shadow); int main (int argc, char *argv[]) { struct spwd *shadow; const char *user_to_auth; char *cookie = NULL; time_t now; /* clear the entire environment to avoid attacks with libraries honoring environment variables */ if (_polkit_clearenv () != 0) goto error; /* set a minimal environment */ setenv ("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1); /* check that we are setuid root */ if (geteuid () != 0) { fprintf (stderr, "polkit-agent-helper-1: needs to be setuid root\n"); goto error; } openlog ("polkit-agent-helper-1", LOG_CONS | LOG_PID, LOG_AUTHPRIV); /* check for correct invocation */ if (!(argc == 2 || argc == 3)) { syslog (LOG_NOTICE, "inappropriate use of helper, wrong number of arguments [uid=%d]", getuid ()); fprintf (stderr, "polkit-agent-helper-1: wrong number of arguments. This incident has been logged.\n"); goto error; } if (getuid () != 0) { /* check we're running with a non-tty stdin */ if (isatty (STDIN_FILENO) != 0) { syslog (LOG_NOTICE, "inappropriate use of helper, stdin is a tty [uid=%d]", getuid ()); fprintf (stderr, "polkit-agent-helper-1: inappropriate use of helper, stdin is a tty. This incident has been logged.\n"); goto error; } } user_to_auth = argv[1]; cookie = read_cookie (argc, argv); if (!cookie) goto error; #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: user to auth is '%s'.\n", user_to_auth); #endif /* PAH_DEBUG */ /* Ask shadow about the user requesting authentication */ shadow = getspnam (user_to_auth); if (shadow == NULL) { syslog (LOG_NOTICE, "shadow file data information request for user '%s' [uid=%d] failed", user_to_auth, getuid ()); fprintf(stderr, "polkit-agent-helper-1: could not get shadow information for '%s'", user_to_auth); goto error; } /* Check the user's identity */ if (shadow_authenticate (shadow) == FALSE) { syslog (LOG_NOTICE, "authentication failure [uid=%d] trying to authenticate '%s'", getuid (), user_to_auth); fprintf (stderr, "polkit-agent-helper-1: authentication failure. This incident has been logged.\n"); goto error; } /* Check whether the user's password has expired */ now = time (NULL); if (shadow->sp_max >= 0 && (shadow->sp_lstchg + shadow->sp_max) * 60 * 60 * 24 <= now) { syslog (LOG_NOTICE, "password expired for user '%s' [uid=%d] trying to authenticate", user_to_auth, getuid ()); fprintf (stderr, "polkit-agent-helper-1: authorization failure. This incident has been logged.\n"); goto error; } /* Check whether the user's password has aged (and account expired along * with it) */ if (shadow->sp_inact >= 0 && (shadow->sp_lstchg + shadow->sp_max + shadow->sp_inact) * 60 * 60 * 24 <= now) { syslog (LOG_NOTICE, "password aged for user '%s' [uid=%d] trying to authenticate", user_to_auth, getuid ()); fprintf (stderr, "polkit-agent-helper-1: authorization failure. This incident has been logged.\n"); goto error; } /* Check whether the user's account has expired */ if (shadow->sp_expire >= 0 && shadow->sp_expire * 60 * 60 * 24 <= now) { syslog (LOG_NOTICE, "account expired for user '%s' [uid=%d] trying to authenticate", user_to_auth, getuid ()); fprintf (stderr, "polkit-agent-helper-1: authorization failure. This incident has been logged.\n"); goto error; } #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: sending D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ /* now send a D-Bus message to the PolicyKit daemon that * includes a) the cookie; and b) the user we authenticated */ if (!send_dbus_message (cookie, user_to_auth)) { #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: error sending D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ goto error; } free (cookie); #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: successfully sent D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ fprintf (stdout, "SUCCESS\n"); flush_and_wait (); return 0; error: free (cookie); fprintf (stdout, "FAILURE\n"); flush_and_wait (); return 1; } static gboolean shadow_authenticate (struct spwd *shadow) { char passwd[512], *crypt_pass; fprintf (stdout, "PAM_PROMPT_ECHO_OFF password:\n"); fflush (stdout); usleep (10 * 1000); /* since fflush(3) seems buggy */ if (fgets (passwd, sizeof (passwd), stdin) == NULL) goto error; if (strlen (passwd) > 0 && passwd[strlen (passwd) - 1] == '\n') passwd[strlen (passwd) - 1] = '\0'; /* Use the encrypted password as the salt, according to the crypt(3) man page, * it will perform whatever encryption method is specified in /etc/shadow */ crypt_pass = crypt (passwd, shadow->sp_pwdp); if (crypt_pass == NULL) goto error; if (strcmp (shadow->sp_pwdp, crypt (passwd, shadow->sp_pwdp)) != 0) goto error; return 1; error: return 0; } polkit-126/src/polkitagent/polkitagenthelperprivate.c000066400000000000000000000067061474122443600232760ustar00rootroot00000000000000/* * Copyright (C) 2009-2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * Authors: David Zeuthen , * Andrew Psaltis */ #include "polkitagenthelperprivate.h" #include #include #include #include #ifndef HAVE_CLEARENV extern char **environ; int _polkit_clearenv (void) { if (environ != NULL) environ[0] = NULL; return 0; } #else int _polkit_clearenv (void) { return clearenv (); } #endif char * read_cookie (int argc, char **argv) { /* As part of CVE-2015-4625, we started passing the cookie * on standard input, to ensure it's not visible to other * processes. However, to ensure that things continue * to work if the setuid binary is upgraded while old * agents are still running (this will be common with * package managers), we support both modes. */ if (argc == 3) return strdup (argv[2]); else { char *ret = NULL; size_t n = 0; ssize_t r = getline (&ret, &n, stdin); if (r == -1) { if (!feof (stdin)) perror ("getline"); free (ret); return NULL; } else { g_strchomp (ret); return ret; } } } gboolean send_dbus_message (const char *cookie, const char *user) { PolkitAuthority *authority = NULL; PolkitIdentity *identity = NULL; GError *error; gboolean ret; ret = FALSE; error = NULL; authority = polkit_authority_get_sync (NULL /* GCancellable* */, &error); if (authority == NULL) { g_printerr ("Error getting authority: %s\n", error->message); g_error_free (error); goto out; } identity = polkit_unix_user_new_for_name (user, &error); if (identity == NULL) { g_printerr ("Error constructing identity: %s\n", error->message); g_error_free (error); goto out; } if (!polkit_authority_authentication_agent_response_sync (authority, cookie, identity, NULL, &error)) { g_printerr ("polkit-agent-helper-1: error response to PolicyKit daemon: %s\n", error->message); g_error_free (error); goto out; } ret = TRUE; out: if (identity != NULL) g_object_unref (identity); if (authority != NULL) g_object_unref (authority); return ret; } void flush_and_wait (void) { fflush (stdout); fflush (stderr); #ifdef HAVE_FDATASYNC fdatasync (fileno(stdout)); fdatasync (fileno(stderr)); #else fsync (fileno(stdout)); fsync (fileno(stderr)); #endif usleep (100 * 1000); } polkit-126/src/polkitagent/polkitagenthelperprivate.h000066400000000000000000000027231474122443600232760ustar00rootroot00000000000000/* * Copyright (C) 2009-2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * Authors: David Zeuthen , * Andrew Psaltis */ #ifndef __POLKIT_AGENT_HELPER_PRIVATE_H #define __POLKIT_AGENT_HELPER_PRIVATE_H #include /* Development aid: define PAH_DEBUG to get debugging output. Do _NOT_ * enable this in production builds; it may leak passwords and other * sensitive information. */ #undef PAH_DEBUG /* #define PAH_DEBUG */ #ifdef HAVE_SOLARIS # define LOG_AUTHPRIV (10<<3) #endif int _polkit_clearenv (void); char *read_cookie (int argc, char **argv); gboolean send_dbus_message (const char *cookie, const char *user); void flush_and_wait (void); #endif /* __POLKIT_AGENT_HELPER_PRIVATE_H */ polkit-126/src/polkitagent/polkitagentlistener.c000066400000000000000000000705361474122443600222530ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include "polkitagentlistener.h" /** * SECTION:polkitagentlistener * @title: PolkitAgentListener * @short_description: Abstract base class for Authentication Agents * @stability: Unstable * * #PolkitAgentListener is an abstract base class used for implementing authentication * agents. To implement an authentication agent, simply subclass #PolkitAgentListener and * implement the @initiate_authentication and @initiate_authentication_finish methods. * * Typically authentication agents use #PolkitAgentSession to * authenticate users (via passwords) and communicate back the * authentication result to the PolicyKit daemon. * * To register a #PolkitAgentListener with the PolicyKit daemon, use * polkit_agent_listener_register() or * polkit_agent_listener_register_with_options(). */ typedef struct { GObject parent_instance; GDBusConnection *system_bus; guint auth_agent_registration_id; GDBusInterfaceInfo *interface_info; PolkitAuthority *authority; gulong notify_owner_handler_id; gboolean is_registered; PolkitAgentListener *listener; GVariant *registration_options; PolkitSubject *subject; gchar *object_path; GHashTable *cookie_to_pending_auth; GThread *thread; GError *thread_initialization_error; gboolean thread_initialized; GMainContext *thread_context; GMainLoop *thread_loop; } Server; static void server_free (Server *server) { if (server->is_registered) { GError *error; error = NULL; if (!polkit_authority_unregister_authentication_agent_sync (server->authority, server->subject, server->object_path, NULL, &error)) { g_warning ("Error unregistering authentication agent: %s", error->message); g_error_free (error); } } if (server->thread_initialization_error != NULL) g_error_free (server->thread_initialization_error); if (server->thread_context != NULL) g_main_context_unref (server->thread_context); if (server->thread_loop != NULL) g_main_loop_unref (server->thread_loop); if (server->interface_info != NULL) g_dbus_interface_info_unref (server->interface_info); if (server->registration_options != NULL) g_variant_unref (server->registration_options); if (server->listener != NULL) g_object_unref (server->listener); if (server->auth_agent_registration_id > 0) g_dbus_connection_unregister_object (server->system_bus, server->auth_agent_registration_id); if (server->notify_owner_handler_id > 0) g_signal_handler_disconnect (server->authority, server->notify_owner_handler_id); if (server->authority != NULL) g_object_unref (server->authority); if (server->system_bus != NULL) g_object_unref (server->system_bus); if (server->cookie_to_pending_auth != NULL) g_hash_table_unref (server->cookie_to_pending_auth); if (server->subject != NULL) g_object_unref (server->subject); g_free (server->object_path); g_free (server); } static gboolean server_register (Server *server, GError **error) { GError *local_error; gboolean ret; const gchar *locale; ret = FALSE; locale = g_getenv ("LANG"); if (locale == NULL) locale = "en_US.UTF-8"; local_error = NULL; if (!polkit_authority_register_authentication_agent_with_options_sync (server->authority, server->subject, locale, server->object_path, server->registration_options, NULL, &local_error)) { g_propagate_error (error, local_error); } else { server->is_registered = TRUE; ret = TRUE; } return ret; } static void on_notify_authority_owner (GObject *object, GParamSpec *pspec, gpointer user_data) { Server *server = user_data; gchar *owner; owner = polkit_authority_get_owner (server->authority); if (owner == NULL) { g_debug ("PolicyKit daemon disconnected from the bus.\n"); if (server->is_registered) g_debug ("We are no longer a registered authentication agent.\n"); server->is_registered = FALSE; } else { /* only register if there is a name owner */ if (!server->is_registered) { GError *error; g_debug ("PolicyKit daemon reconnected to bus.\n"); g_debug ("Attempting to re-register as an authentication agent.\n"); error = NULL; if (server_register (server, &error)) { g_debug ("We are now a registered authentication agent.\n"); } else { g_debug ("Failed to register as an authentication agent: %s\n", error->message); g_error_free (error); } } } g_free (owner); } static gboolean server_init_sync (Server *server, GCancellable *cancellable, GError **error) { gboolean ret; ret = FALSE; server->system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error); if (server->system_bus == NULL) goto out; server->authority = polkit_authority_get_sync (cancellable, error); if (server->authority == NULL) goto out; /* the only use of this proxy is to re-register with the polkit daemon * if it jumps off the bus and comes back (which is useful for debugging) */ server->notify_owner_handler_id = g_signal_connect (server->authority, "notify::owner", G_CALLBACK (on_notify_authority_owner), server); ret = TRUE; out: return ret; } static Server * server_new (PolkitSubject *subject, const gchar *object_path, GCancellable *cancellable, GError **error) { Server *server; server = g_new0 (Server, 1); server->subject = g_object_ref (subject); server->object_path = object_path != NULL ? g_strdup (object_path) : g_strdup ("/org/freedesktop/PolicyKit1/AuthenticationAgent"); server->cookie_to_pending_auth = g_hash_table_new (g_str_hash, g_str_equal); if (!server_init_sync (server, cancellable, error)) { server_free (server); return NULL; } return server; } static void auth_agent_handle_begin_authentication (Server *server, GVariant *parameters, GDBusMethodInvocation *invocation); static void auth_agent_handle_cancel_authentication (Server *server, GVariant *parameters, GDBusMethodInvocation *invocation); static void auth_agent_handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { Server *server = user_data; /* The shipped D-Bus policy also ensures that only uid 0 can invoke * methods on our interface. So no need to check the caller. */ if (g_strcmp0 (method_name, "BeginAuthentication") == 0) auth_agent_handle_begin_authentication (server, parameters, invocation); else if (g_strcmp0 (method_name, "CancelAuthentication") == 0) auth_agent_handle_cancel_authentication (server, parameters, invocation); else g_assert_not_reached (); } static const gchar *auth_agent_introspection_data = "" " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static const GDBusInterfaceVTable auth_agent_vtable = { auth_agent_handle_method_call, NULL, /* _handle_get_property */ NULL /* _handle_set_property */ }; static gboolean server_export_object (Server *server, GError **error) { gboolean ret; ret = FALSE; server->auth_agent_registration_id = g_dbus_connection_register_object (server->system_bus, server->object_path, server->interface_info, &auth_agent_vtable, server, NULL, /* user_data GDestroyNotify */ error); if (server->auth_agent_registration_id > 0) ret = TRUE; return ret; } static gpointer server_thread_func (gpointer user_data) { Server *server = user_data; server->thread_context = g_main_context_new (); server->thread_loop = g_main_loop_new (server->thread_context, FALSE); g_main_context_push_thread_default (server->thread_context); if (!server_export_object (server, &server->thread_initialization_error)) { server->thread_initialized = TRUE; goto out; } server->thread_initialized = TRUE; g_main_loop_run (server->thread_loop); out: g_main_context_pop_thread_default (server->thread_context); return NULL; } /** * polkit_agent_listener_register_with_options: * @listener: A #PolkitAgentListener. * @flags: A set of flags from the #PolkitAgentRegisterFlags enumeration. * @subject: The subject to become an authentication agent for, typically a #PolkitUnixSession object. * @object_path: The D-Bus object path to use for the authentication agent or %NULL for the default object path. * @options: (allow-none): A #GVariant with options or %NULL. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error. * * Like polkit_agent_listener_register() but takes options to influence registration. See the * RegisterAuthenticationAgentWithOptions() D-Bus method for details. * * Returns: (transfer full): %NULL if @error is set, otherwise a * registration handle that can be used with * polkit_agent_listener_unregister(). */ gpointer polkit_agent_listener_register_with_options (PolkitAgentListener *listener, PolkitAgentRegisterFlags flags, PolkitSubject *subject, const gchar *object_path, GVariant *options, GCancellable *cancellable, GError **error) { Server *server; GDBusNodeInfo *node_info; g_return_val_if_fail (POLKIT_AGENT_IS_LISTENER (listener), NULL); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), NULL); g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), NULL); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (object_path == NULL) object_path = "/org/freedesktop/PolicyKit1/AuthenticationAgent"; server = server_new (subject, object_path, cancellable, error); if (server == NULL) goto out; node_info = g_dbus_node_info_new_for_xml (auth_agent_introspection_data, error); if (node_info == NULL) { server_free (server); server = NULL; goto out; } server->interface_info = g_dbus_interface_info_ref (g_dbus_node_info_lookup_interface (node_info, "org.freedesktop.PolicyKit1.AuthenticationAgent")); g_dbus_node_info_unref (node_info); server->listener = g_object_ref (listener); server->registration_options = options != NULL ? g_variant_ref_sink (options) : NULL; if (flags & POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD) { server->thread = g_thread_try_new ("polkit agent listener", server_thread_func, server, error); if (server->thread == NULL) { server_free (server); server = NULL; goto out; } /* wait for the thread to export and object (TODO: probably use a condition variable instead) */ while (!server->thread_initialized) g_thread_yield (); if (server->thread_initialization_error != NULL) { g_propagate_error (error, server->thread_initialization_error); server->thread_initialization_error = NULL; g_thread_join (server->thread); server_free (server); server = NULL; goto out; } } else { if (!server_export_object (server, error)) { server_free (server); server = NULL; goto out; } } if (!server_register (server, error)) { server_free (server); server = NULL; goto out; } out: return server; } /** * polkit_agent_listener_register: * @listener: A #PolkitAgentListener. * @flags: A set of flags from the #PolkitAgentRegisterFlags enumeration. * @subject: The subject to become an authentication agent for, typically a #PolkitUnixSession object. * @object_path: The D-Bus object path to use for the authentication agent or %NULL for the default object path. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error. * * Registers @listener with the PolicyKit daemon as an authentication * agent for @subject. This is implemented by registering a D-Bus * object at @object_path on the unique name assigned by the system * message bus. * * Whenever the PolicyKit daemon needs to authenticate a processes * that is related to @subject, the methods * polkit_agent_listener_initiate_authentication() and * polkit_agent_listener_initiate_authentication_finish() will be * invoked on @listener. * * Note that registration of an authentication agent can fail; for * example another authentication agent may already be registered for * @subject. * * Note that the calling thread is blocked until a reply is received. * * Returns: (transfer full): %NULL if @error is set, otherwise a * registration handle that can be used with * polkit_agent_listener_unregister(). */ gpointer polkit_agent_listener_register (PolkitAgentListener *listener, PolkitAgentRegisterFlags flags, PolkitSubject *subject, const gchar *object_path, GCancellable *cancellable, GError **error) { return polkit_agent_listener_register_with_options (listener, flags, subject, object_path, NULL, cancellable, error); } /** * polkit_agent_listener_unregister: * @registration_handle: A handle obtained from polkit_agent_listener_register(). * * Unregisters @listener. */ void polkit_agent_listener_unregister (gpointer registration_handle) { Server *server = registration_handle; if (server->thread != NULL) { g_main_loop_quit (server->thread_loop); g_thread_join (server->thread); } server_free (server); } static void listener_died (gpointer user_data, GObject *where_the_object_was) { Server *server = user_data; server_free (server); } /** * polkit_agent_register_listener: * @listener: A #PolkitAgentListener. * @subject: The subject to become an authentication agent for, typically a #PolkitUnixSession object. * @object_path: The D-Bus object path to use for the authentication agent or %NULL for the default object path. * @error: Return location for error. * * (deprecated) */ gboolean polkit_agent_register_listener (PolkitAgentListener *listener, PolkitSubject *subject, const gchar *object_path, GError **error) { Server *server; gboolean ret; ret = FALSE; server = polkit_agent_listener_register (listener, POLKIT_AGENT_REGISTER_FLAGS_NONE, subject, object_path, NULL, error); if (server == NULL) goto out; /* drop the ref that server took */ g_object_unref (server->listener); /* take a weak ref and kill server when listener dies */ g_object_weak_ref (G_OBJECT (server->listener), listener_died, server); ret = TRUE; out: return ret; } typedef struct { gchar *cookie; GHashTable *cookie_to_pending_auth; GDBusMethodInvocation *invocation; GCancellable *cancellable; } AuthData; static void auth_data_free (AuthData *data) { g_free (data->cookie); g_object_unref (data->invocation); g_object_unref (data->cancellable); g_hash_table_unref (data->cookie_to_pending_auth); g_free (data); } /* ---------------------------------------------------------------------------------------------------- */ static void auth_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { AuthData *data = user_data; GError *error; error = NULL; if (!polkit_agent_listener_initiate_authentication_finish (POLKIT_AGENT_LISTENER (source_object), res, &error)) { g_dbus_method_invocation_return_gerror (data->invocation, error); g_error_free (error); } else { g_dbus_method_invocation_return_value (data->invocation, NULL); } g_hash_table_remove (data->cookie_to_pending_auth, data->cookie); auth_data_free (data); } static void auth_agent_handle_begin_authentication (Server *server, GVariant *parameters, GDBusMethodInvocation *invocation) { const gchar *action_id; const gchar *message; const gchar *icon_name; GVariant *details_gvariant; const gchar *cookie; GVariant *identities_gvariant; GList *identities; PolkitDetails *details; GVariantIter iter; GVariant *child; guint n; AuthData *data; identities = NULL; details = NULL; g_variant_get (parameters, "(&s&s&s@a{ss}&s@a(sa{sv}))", &action_id, &message, &icon_name, &details_gvariant, &cookie, &identities_gvariant); details = polkit_details_new_for_gvariant (details_gvariant); g_variant_iter_init (&iter, identities_gvariant); n = 0; while ((child = g_variant_iter_next_value (&iter)) != NULL) { PolkitIdentity *identity; GError *error; error = NULL; identity = polkit_identity_new_for_gvariant (child, &error); g_variant_unref (child); if (identity == NULL) { g_prefix_error (&error, "Error extracting identity %d: ", n); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } n++; identities = g_list_prepend (identities, identity); } identities = g_list_reverse (identities); data = g_new0 (AuthData, 1); data->cookie_to_pending_auth = g_hash_table_ref (server->cookie_to_pending_auth); data->cookie = g_strdup (cookie); data->invocation = g_object_ref (invocation); data->cancellable = g_cancellable_new (); g_hash_table_insert (server->cookie_to_pending_auth, (gpointer) cookie, data); polkit_agent_listener_initiate_authentication (server->listener, action_id, message, icon_name, details, cookie, identities, data->cancellable, auth_cb, data); out: g_list_foreach (identities, (GFunc) g_object_unref, NULL); g_list_free (identities); g_object_unref (details); g_variant_unref (details_gvariant); g_variant_unref (identities_gvariant); } /* ---------------------------------------------------------------------------------------------------- */ static void auth_agent_handle_cancel_authentication (Server *server, GVariant *parameters, GDBusMethodInvocation *invocation) { AuthData *data; const gchar *cookie; g_variant_get (parameters, "(&s)", &cookie); data = g_hash_table_lookup (server->cookie_to_pending_auth, cookie); if (data == NULL) { g_dbus_method_invocation_return_error (invocation, POLKIT_ERROR, POLKIT_ERROR_FAILED, "No pending authentication request for cookie '%s'", cookie); } else { g_cancellable_cancel (data->cancellable); g_dbus_method_invocation_return_value (invocation, NULL); } } /* ---------------------------------------------------------------------------------------------------- */ G_DEFINE_ABSTRACT_TYPE (PolkitAgentListener, polkit_agent_listener, G_TYPE_OBJECT); static void polkit_agent_listener_init (PolkitAgentListener *listener) { } static void polkit_agent_listener_class_init (PolkitAgentListenerClass *klass) { } /** * polkit_agent_listener_initiate_authentication: * @listener: A #PolkitAgentListener. * @action_id: The action to authenticate for. * @message: The message to present to the user. * @icon_name: A themed icon name representing the action or %NULL. * @details: Details describing the action. * @cookie: The cookie for the authentication request. * @identities: (element-type Polkit.Identity): A list of #PolkitIdentity objects that the user can choose to authenticate as. * @cancellable: A #GCancellable. * @callback: Function to call when the user is done authenticating. * @user_data: Data to pass to @callback. * * Called on a registered authentication agent (see * polkit_agent_listener_register()) when the user owning the session * needs to prove he is one of the identities listed in @identities. * * When the user is done authenticating (for example by dismissing an * authentication dialog or by successfully entering a password or * otherwise proving the user is one of the identities in * @identities), @callback will be invoked. The caller then calls * polkit_agent_listener_initiate_authentication_finish() to get the * result. * * #PolkitAgentListener derived subclasses imlementing this method * MUST not ignore @cancellable; callers of this * function can and will use it. Additionally, @callback must be * invoked in the thread-default main * loop of the thread that this method is called from. */ void polkit_agent_listener_initiate_authentication (PolkitAgentListener *listener, const gchar *action_id, const gchar *message, const gchar *icon_name, PolkitDetails *details, const gchar *cookie, GList *identities, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (POLKIT_AGENT_IS_LISTENER (listener)); g_return_if_fail (details == NULL || POLKIT_IS_DETAILS (details)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_return_if_fail (action_id != NULL); g_return_if_fail (message != NULL); g_return_if_fail (cookie != NULL); g_return_if_fail (identities != NULL); POLKIT_AGENT_LISTENER_GET_CLASS (listener)->initiate_authentication (listener, action_id, message, icon_name, details, cookie, identities, cancellable, callback, user_data); } /** * polkit_agent_listener_initiate_authentication_finish: * @listener: A #PolkitAgentListener. * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed to polkit_agent_listener_initiate_authentication(). * @error: Return location for error. * * Finishes an authentication request from the PolicyKit daemon, see * polkit_agent_listener_initiate_authentication() for details. * * Returns: %TRUE if @error is set. **/ gboolean polkit_agent_listener_initiate_authentication_finish (PolkitAgentListener *listener, GAsyncResult *res, GError **error) { g_return_val_if_fail (POLKIT_AGENT_IS_LISTENER (listener), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return POLKIT_AGENT_LISTENER_GET_CLASS (listener)->initiate_authentication_finish (listener, res, error); } polkit-126/src/polkitagent/polkitagentlistener.h000066400000000000000000000170161474122443600222520ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined(_POLKIT_AGENT_INSIDE_POLKIT_AGENT_H) && !defined (_POLKIT_AGENT_COMPILATION) #error "Only can be included directly, this file may disappear or change contents" #endif #ifndef __POLKIT_AGENT_LISTENER_H #define __POLKIT_AGENT_LISTENER_H #include #include G_BEGIN_DECLS #define POLKIT_AGENT_TYPE_LISTENER (polkit_agent_listener_get_type ()) #define POLKIT_AGENT_LISTENER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_AGENT_TYPE_LISTENER, PolkitAgentListener)) #define POLKIT_AGENT_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), POLKIT_AGENT_TYPE_LISTENER, PolkitAgentListenerClass)) #define POLKIT_AGENT_LISTENER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_AGENT_TYPE_LISTENER,PolkitAgentListenerClass)) #define POLKIT_AGENT_IS_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_AGENT_TYPE_LISTENER)) #define POLKIT_AGENT_IS_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_AGENT_TYPE_LISTENER)) struct _PolkitAgentListenerClass; typedef struct _PolkitAgentListenerClass PolkitAgentListenerClass; /** * PolkitAgentListener: * * The #PolkitAgentListener struct should not be accessed directly. */ struct _PolkitAgentListener { GObject parent_instance; }; /** * PolkitAgentListenerClass: * @parent_class: The parent class. * @initiate_authentication: Handle an authentication request, see polkit_agent_listener_initiate_authentication(). * @initiate_authentication_finish: Finishes handling an authentication request, see polkit_agent_listener_initiate_authentication_finish(). * * VFuncs that authentication agents needs to implement. */ struct _PolkitAgentListenerClass { /*< public >*/ GObjectClass parent_class; /* Vtable */ void (*initiate_authentication) (PolkitAgentListener *listener, const gchar *action_id, const gchar *message, const gchar *icon_name, PolkitDetails *details, const gchar *cookie, GList *identities, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (*initiate_authentication_finish) (PolkitAgentListener *listener, GAsyncResult *res, GError **error); /*< private >*/ /* Padding for future expansion */ void (*_polkit_reserved0) (void); void (*_polkit_reserved1) (void); void (*_polkit_reserved2) (void); void (*_polkit_reserved3) (void); void (*_polkit_reserved4) (void); void (*_polkit_reserved5) (void); void (*_polkit_reserved6) (void); void (*_polkit_reserved7) (void); }; GType polkit_agent_listener_get_type (void) G_GNUC_CONST; void polkit_agent_listener_initiate_authentication (PolkitAgentListener *listener, const gchar *action_id, const gchar *message, const gchar *icon_name, PolkitDetails *details, const gchar *cookie, GList *identities, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean polkit_agent_listener_initiate_authentication_finish (PolkitAgentListener *listener, GAsyncResult *res, GError **error); gboolean polkit_agent_register_listener (PolkitAgentListener *listener, PolkitSubject *subject, const gchar *object_path, GError **error) G_GNUC_DEPRECATED_FOR (polkit_agent_listener_register); /** * PolkitAgentRegisterFlags: * @POLKIT_AGENT_REGISTER_FLAGS_NONE: No flags are set. * @POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD: Run the listener in a dedicated thread. * * Flags used in polkit_agent_listener_register(). */ typedef enum { POLKIT_AGENT_REGISTER_FLAGS_NONE = 0, POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD = (1<<0) } PolkitAgentRegisterFlags; gpointer polkit_agent_listener_register (PolkitAgentListener *listener, PolkitAgentRegisterFlags flags, PolkitSubject *subject, const gchar *object_path, GCancellable *cancellable, GError **error); gpointer polkit_agent_listener_register_with_options (PolkitAgentListener *listener, PolkitAgentRegisterFlags flags, PolkitSubject *subject, const gchar *object_path, GVariant *options, GCancellable *cancellable, GError **error); void polkit_agent_listener_unregister (gpointer registration_handle); G_END_DECLS #endif /* __POLKIT_AGENT_LISTENER_H */ polkit-126/src/polkitagent/polkitagentmarshal.list000066400000000000000000000000241474122443600225670ustar00rootroot00000000000000VOID:STRING,BOOLEAN polkit-126/src/polkitagent/polkitagentsession.c000066400000000000000000000531431474122443600221040ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ /** * SECTION:polkitagentsession * @title: PolkitAgentSession * @short_description: Authentication Session * @stability: Unstable * * The #PolkitAgentSession class is an abstraction used for interacting with the * native authentication system (for example PAM) for obtaining authorizations. * This class is typically used together with instances that are derived from * the #PolkitAgentListener abstract base class. * * To perform the actual authentication, #PolkitAgentSession uses a trusted suid helper. * The authentication conversation is done through a pipe. This is transparent; the user * only need to handle the * #PolkitAgentSession::request, * #PolkitAgentSession::show-info, * #PolkitAgentSession::show-error and * #PolkitAgentSession::completed * signals and invoke polkit_agent_session_response() in response to requests. * * If the user successfully authenticates, the authentication helper will invoke * a method on the PolicyKit daemon (see polkit_authority_authentication_agent_response_sync()) * with the given @cookie. Upon receiving a positive response from the PolicyKit daemon (via * the authentication helper), the #PolkitAgentSession::completed signal will be emitted * with the @gained_authorization paramter set to %TRUE. * * If the user is unable to authenticate, the #PolkitAgentSession::completed signal will * be emitted with the @gained_authorization paramter set to %FALSE. */ #include #include #include #include #include #include #include #include "polkitagentmarshal.h" #include "polkitagentsession.h" static gboolean _show_debug (void) { static volatile gsize has_show_debug = 0; static gboolean show_debug_value = FALSE; if (g_once_init_enter ((gsize*) &has_show_debug)) { show_debug_value = (g_getenv ("POLKIT_DEBUG") != NULL); g_once_init_leave (&has_show_debug, 1); } return show_debug_value; } /** * PolkitAgentSession: * * The #PolkitAgentSession struct should not be accessed directly. */ struct _PolkitAgentSession { /*< private >*/ GObject parent_instance; gchar *cookie; PolkitIdentity *identity; GOutputStream *child_stdin; int child_stdout; GPid child_pid; GSource *child_stdout_watch_source; GIOChannel *child_stdout_channel; gboolean success; gboolean helper_is_running; gboolean have_emitted_completed; }; struct _PolkitAgentSessionClass { GObjectClass parent_class; }; enum { PROP_0, PROP_IDENTITY, PROP_COOKIE }; enum { REQUEST_SIGNAL, SHOW_INFO_SIGNAL, SHOW_ERROR_SIGNAL, COMPLETED_SIGNAL, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; G_DEFINE_TYPE (PolkitAgentSession, polkit_agent_session, G_TYPE_OBJECT); static void polkit_agent_session_init (PolkitAgentSession *session) { session->child_stdout = -1; } static void kill_helper (PolkitAgentSession *session); static void polkit_agent_session_finalize (GObject *object) { PolkitAgentSession *session; session = POLKIT_AGENT_SESSION (object); /* this releases resources related to the helper */ kill_helper (session); g_free (session->cookie); if (session->identity != NULL) g_object_unref (session->identity); if (G_OBJECT_CLASS (polkit_agent_session_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_agent_session_parent_class)->finalize (object); } static void polkit_agent_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitAgentSession *session = POLKIT_AGENT_SESSION (object); switch (prop_id) { case PROP_IDENTITY: g_value_set_object (value, session->identity); break; case PROP_COOKIE: g_value_set_string (value, session->cookie); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_agent_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitAgentSession *session = POLKIT_AGENT_SESSION (object); switch (prop_id) { case PROP_IDENTITY: session->identity = g_value_dup_object (value); break; case PROP_COOKIE: session->cookie = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_agent_session_class_init (PolkitAgentSessionClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_agent_session_finalize; gobject_class->get_property = polkit_agent_session_get_property; gobject_class->set_property = polkit_agent_session_set_property; /** * PolkitAgentSession:identity: * * The identity to authenticate. */ g_object_class_install_property (gobject_class, PROP_IDENTITY, g_param_spec_object ("identity", "Identity", "The identity to authenticate", POLKIT_TYPE_IDENTITY, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitAgentSession:cookie: * * The cookie obtained from the PolicyKit daemon */ g_object_class_install_property (gobject_class, PROP_COOKIE, g_param_spec_string ("cookie", "Cookie", "The cookie obtained from the PolicyKit daemon", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); /** * PolkitAgentSession::request: * @session: A #PolkitAgentSession. * @request: The request to show the user, e.g. "name: " or "password: ". * @echo_on: %TRUE if the response to the request SHOULD be echoed on the * screen, %FALSE if the response MUST NOT be echoed to the screen. * * Emitted when the user is requested to answer a question. * * When the response has been collected from the user, call polkit_agent_session_response(). */ signals[REQUEST_SIGNAL] = g_signal_new ("request", POLKIT_AGENT_TYPE_SESSION, G_SIGNAL_RUN_LAST, 0, /* class offset */ NULL, /* accumulator */ NULL, /* accumulator data */ _polkit_agent_marshal_VOID__STRING_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN); /** * PolkitAgentSession::show-info: * @session: A #PolkitAgentSession. * @text: A string to display to the user. * * Emitted when there is information to be displayed to the user. */ signals[SHOW_INFO_SIGNAL] = g_signal_new ("show-info", POLKIT_AGENT_TYPE_SESSION, G_SIGNAL_RUN_LAST, 0, /* class offset */ NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * PolkitAgentSession::show-error: * @session: A #PolkitAgentSession. * @text: An error string to display to the user. * * Emitted when there is information related to an error condition to be displayed to the user. */ signals[SHOW_ERROR_SIGNAL] = g_signal_new ("show-error", POLKIT_AGENT_TYPE_SESSION, G_SIGNAL_RUN_LAST, 0, /* class offset */ NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * PolkitAgentSession::completed: * @session: A #PolkitAgentSession. * @gained_authorization: %TRUE only if the authorization was successfully obtained. * * Emitted when the authentication session has been completed or * cancelled. The @gained_authorization parameter is %TRUE only if * the user successfully authenticated. * * Upon receiving this signal, the user should free @session using g_object_unref(). */ signals[COMPLETED_SIGNAL] = g_signal_new ("completed", POLKIT_AGENT_TYPE_SESSION, G_SIGNAL_RUN_LAST, 0, /* class offset */ NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } /** * polkit_agent_session_new: * @identity: The identity to authenticate. * @cookie: The cookie obtained from the PolicyKit daemon * * Creates a new authentication session. * * The caller should connect to the * #PolkitAgentSession::request, * #PolkitAgentSession::show-info, * #PolkitAgentSession::show-error and * #PolkitAgentSession::completed * signals and then call polkit_agent_session_initiate() to initiate the authentication session. * * Returns: A #PolkitAgentSession. Free with g_object_unref(). **/ PolkitAgentSession * polkit_agent_session_new (PolkitIdentity *identity, const gchar *cookie) { PolkitAgentSession *session; g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), NULL); g_return_val_if_fail (cookie != NULL, NULL); session = POLKIT_AGENT_SESSION (g_object_new (POLKIT_AGENT_TYPE_SESSION, "identity", identity, "cookie", cookie, NULL)); return session; } static void kill_helper (PolkitAgentSession *session) { if (!session->helper_is_running) goto out; if (session->child_pid > 0) { gint status; //g_debug ("Sending SIGTERM to helper"); kill (session->child_pid, SIGTERM); waitpid (session->child_pid, &status, 0); session->child_pid = 0; } if (session->child_stdout_watch_source != NULL) { g_source_destroy (session->child_stdout_watch_source); g_source_unref (session->child_stdout_watch_source); session->child_stdout_watch_source = NULL; } if (session->child_stdout_channel != NULL) { g_io_channel_unref (session->child_stdout_channel); session->child_stdout_channel = NULL; } if (session->child_stdout != -1) { g_warn_if_fail (close (session->child_stdout) == 0); session->child_stdout = -1; } g_clear_object (&session->child_stdin); session->helper_is_running = FALSE; out: ; } static void complete_session (PolkitAgentSession *session, gboolean result) { kill_helper (session); if (!session->have_emitted_completed) { if (G_UNLIKELY (_show_debug ())) g_print ("PolkitAgentSession: emitting ::completed(%s)\n", result ? "TRUE" : "FALSE"); session->have_emitted_completed = TRUE; /* Note that the signal handler may drop the last reference to session. */ g_signal_emit_by_name (session, "completed", result); } } static gboolean io_watch_have_data (GIOChannel *channel, GIOCondition condition, gpointer user_data) { PolkitAgentSession *session = POLKIT_AGENT_SESSION (user_data); gchar *line, *unescaped; GError *error; error = NULL; line = NULL; unescaped = NULL; if (!session->helper_is_running) { g_warning ("in io_watch_have_data() but helper is not supposed to be running"); complete_session (session, FALSE); goto out; } g_io_channel_read_line (channel, &line, NULL, NULL, &error); if (error != NULL || line == NULL) { /* In case we get just G_IO_HUP, line is NULL but error is unset.*/ g_warning ("Error reading line from helper: %s", error ? error->message : "nothing to read"); g_clear_error (&error); complete_session (session, FALSE); goto out; } /* remove terminator */ if (strlen (line) > 0 && line[strlen (line) - 1] == '\n') line[strlen (line) - 1] = '\0'; unescaped = g_strcompress (line); if (G_UNLIKELY (_show_debug ())) g_print ("PolkitAgentSession: read `%s' from helper\n", unescaped); if (g_str_has_prefix (unescaped, "PAM_PROMPT_ECHO_OFF ")) { const gchar *s = unescaped + sizeof "PAM_PROMPT_ECHO_OFF " - 1; if (G_UNLIKELY (_show_debug ())) g_print ("PolkitAgentSession: emitting ::request('%s', FALSE)\n", s); g_signal_emit_by_name (session, "request", s, FALSE); } else if (g_str_has_prefix (unescaped, "PAM_PROMPT_ECHO_ON ")) { const gchar *s = unescaped + sizeof "PAM_PROMPT_ECHO_ON " - 1; if (G_UNLIKELY (_show_debug ())) g_print ("PolkitAgentSession: emitting ::request('%s', TRUE)\n", s); g_signal_emit_by_name (session, "request", s, TRUE); } else if (g_str_has_prefix (unescaped, "PAM_ERROR_MSG ")) { const gchar *s = unescaped + sizeof "PAM_ERROR_MSG " - 1; if (G_UNLIKELY (_show_debug ())) g_print ("PolkitAgentSession: emitting ::show-error('%s')\n", s); g_signal_emit_by_name (session, "show-error", s); } else if (g_str_has_prefix (unescaped, "PAM_TEXT_INFO ")) { const gchar *s = unescaped + sizeof "PAM_TEXT_INFO " - 1; if (G_UNLIKELY (_show_debug ())) g_print ("PolkitAgentSession: emitting ::show-info('%s')\n", s); g_signal_emit_by_name (session, "show-info", s); } else if (g_str_has_prefix (unescaped, "SUCCESS")) { complete_session (session, TRUE); } else if (g_str_has_prefix (unescaped, "FAILURE")) { complete_session (session, FALSE); } else { g_warning ("Unknown line '%s' from helper", line); complete_session (session, FALSE); goto out; } out: g_free (line); g_free (unescaped); if (condition & (G_IO_ERR | G_IO_HUP)) complete_session (session, FALSE); /* keep the IOChannel around */ return TRUE; } /** * polkit_agent_session_response: * @session: A #PolkitAgentSession. * @response: Response from the user, typically a password. * * Function for providing response to requests received * via the #PolkitAgentSession::request signal. **/ void polkit_agent_session_response (PolkitAgentSession *session, const gchar *response) { gboolean add_newline; size_t response_len; const char newline[] = "\n"; g_return_if_fail (POLKIT_AGENT_IS_SESSION (session)); g_return_if_fail (response != NULL); response_len = strlen (response); add_newline = (response_len == 0 || response[response_len - 1] != '\n'); (void) g_output_stream_write_all (session->child_stdin, response, response_len, NULL, NULL, NULL); if (add_newline) (void) g_output_stream_write_all (session->child_stdin, newline, 1, NULL, NULL, NULL); } /** * polkit_agent_session_initiate: * @session: A #PolkitAgentSession. * * Initiates the authentication session. Before calling this method, * make sure to connect to the various signals. The signals will be * emitted in the thread-default main * loop that this method is invoked from. * * Use polkit_agent_session_cancel() to cancel the session. **/ void polkit_agent_session_initiate (PolkitAgentSession *session) { uid_t uid; GError *error; gchar *helper_argv[3]; struct passwd *passwd; int stdin_fd = -1; g_return_if_fail (POLKIT_AGENT_IS_SESSION (session)); if (G_UNLIKELY (_show_debug ())) { gchar *s; s = polkit_identity_to_string (session->identity); g_print ("PolkitAgentSession: initiating authentication for identity `%s', cookie %s\n", s, session->cookie); g_free (s); } /* TODO: also support authorization for other kinds of identities */ if (!POLKIT_IS_UNIX_USER (session->identity)) { g_warning ("Unsupported identity type"); goto error; } uid = polkit_unix_user_get_uid (POLKIT_UNIX_USER (session->identity)); passwd = getpwuid (uid); if (passwd == NULL) { g_warning ("No user with uid %d", uid); goto error; } helper_argv[0] = PACKAGE_PREFIX "/lib/polkit-1/polkit-agent-helper-1"; helper_argv[1] = passwd->pw_name; helper_argv[2] = NULL; session->child_stdout = -1; error = NULL; if (!g_spawn_async_with_pipes (NULL, (char **) helper_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | 0,//G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, &session->child_pid, &stdin_fd, &session->child_stdout, NULL, &error)) { g_warning ("Cannot spawn helper: %s\n", error->message); g_error_free (error); goto error; } if (G_UNLIKELY (_show_debug ())) g_print ("PolkitAgentSession: spawned helper with pid %d\n", (gint) session->child_pid); session->child_stdin = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE); /* Write the cookie on stdin so it can't be seen by other processes */ (void) g_output_stream_write_all (session->child_stdin, session->cookie, strlen (session->cookie), NULL, NULL, NULL); (void) g_output_stream_write_all (session->child_stdin, "\n", 1, NULL, NULL, NULL); session->child_stdout_channel = g_io_channel_unix_new (session->child_stdout); session->child_stdout_watch_source = g_io_create_watch (session->child_stdout_channel, G_IO_IN | G_IO_ERR | G_IO_HUP); g_source_set_callback (session->child_stdout_watch_source, (GSourceFunc) io_watch_have_data, session, NULL); g_source_attach (session->child_stdout_watch_source, g_main_context_get_thread_default ()); session->success = FALSE; session->helper_is_running = TRUE; return; error: complete_session (session, FALSE); } /** * polkit_agent_session_cancel: * @session: A #PolkitAgentSession. * * Cancels an authentication session. This will make @session emit the #PolkitAgentSession::completed * signal. **/ void polkit_agent_session_cancel (PolkitAgentSession *session) { g_return_if_fail (POLKIT_AGENT_IS_SESSION (session)); if (G_UNLIKELY (_show_debug ())) g_print ("PolkitAgentSession: canceling authentication\n"); complete_session (session, FALSE); } polkit-126/src/polkitagent/polkitagentsession.h000066400000000000000000000050471474122443600221110ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined(_POLKIT_AGENT_INSIDE_POLKIT_AGENT_H) && !defined (_POLKIT_AGENT_COMPILATION) #error "Only can be included directly, this file may disappear or change contents" #endif #ifndef __POLKIT_AGENT_SESSION_H #define __POLKIT_AGENT_SESSION_H #include #include G_BEGIN_DECLS #define POLKIT_AGENT_TYPE_SESSION (polkit_agent_session_get_type()) #define POLKIT_AGENT_SESSION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_AGENT_TYPE_SESSION, PolkitAgentSession)) #define POLKIT_AGENT_SESSION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_AGENT_TYPE_SESSION, PolkitAgentSessionClass)) #define POLKIT_AGENT_SESSION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_AGENT_TYPE_SESSION, PolkitAgentSessionClass)) #define POLKIT_AGENT_IS_SESSION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_AGENT_TYPE_SESSION)) #define POLKIT_AGENT_IS_SESSION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_AGENT_TYPE_SESSION)) struct _PolkitAgentSessionClass; typedef struct _PolkitAgentSessionClass PolkitAgentSessionClass; GType polkit_agent_session_get_type (void) G_GNUC_CONST; PolkitAgentSession *polkit_agent_session_new (PolkitIdentity *identity, const gchar *cookie); void polkit_agent_session_initiate (PolkitAgentSession *session); void polkit_agent_session_response (PolkitAgentSession *session, const gchar *response); void polkit_agent_session_cancel (PolkitAgentSession *session); G_END_DECLS #endif /* __POLKIT_AGENT_SESSION_H */ polkit-126/src/polkitagent/polkitagenttextlistener.c000066400000000000000000000534731474122443600231610ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #include #include #include #include #include #include "polkitagentlistener.h" #include "polkitagenttextlistener.h" #include "polkitagentsession.h" /** * SECTION:polkitagenttextlistener * @title: PolkitAgentTextListener * @short_description: Text-based Authentication Agent * @stability: Unstable * * #PolkitAgentTextListener is an #PolkitAgentListener implementation * that interacts with the user using a textual interface. */ /** * PolkitAgentTextListener: * * The #PolkitAgentTextListener struct should not be accessed directly. */ struct _PolkitAgentTextListener { PolkitAgentListener parent_instance; GSimpleAsyncResult *simple; PolkitAgentSession *active_session; gulong cancel_id; GCancellable *cancellable; FILE *tty; gboolean use_color; gboolean use_alternate_buffer; guint delay; }; enum { PROP_ZERO, PROP_USE_COLOR, PROP_USE_ALTERNATE_BUFFER, PROP_DELAY }; typedef struct { PolkitAgentListenerClass parent_class; } PolkitAgentTextListenerClass; static void polkit_agent_text_listener_initiate_authentication (PolkitAgentListener *_listener, const gchar *action_id, const gchar *message, const gchar *icon_name, PolkitDetails *details, const gchar *cookie, GList *identities, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static gboolean polkit_agent_text_listener_initiate_authentication_finish (PolkitAgentListener *_listener, GAsyncResult *res, GError **error); static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (PolkitAgentTextListener, polkit_agent_text_listener, POLKIT_AGENT_TYPE_LISTENER, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); static void polkit_agent_text_listener_init (PolkitAgentTextListener *listener) { listener->use_color = TRUE; listener->use_alternate_buffer = FALSE; listener->delay = 1; } static void polkit_agent_text_listener_finalize (GObject *object) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (object); if (listener->tty != NULL) fclose (listener->tty); if (listener->active_session != NULL) g_object_unref (listener->active_session); if (G_OBJECT_CLASS (polkit_agent_text_listener_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_agent_text_listener_parent_class)->finalize (object); } static void polkit_agent_text_listener_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (object); switch (prop_id) { case PROP_USE_COLOR: listener->use_color = g_value_get_boolean (value); break; case PROP_USE_ALTERNATE_BUFFER: listener->use_alternate_buffer = g_value_get_boolean (value); break; case PROP_DELAY: listener->delay = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_agent_text_listener_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (object); switch (prop_id) { case PROP_USE_COLOR: g_value_set_boolean (value, listener->use_color); break; case PROP_USE_ALTERNATE_BUFFER: g_value_set_boolean (value, listener->use_alternate_buffer); break; case PROP_DELAY: g_value_set_uint (value, listener->delay); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_agent_text_listener_class_init (PolkitAgentTextListenerClass *klass) { GObjectClass *gobject_class; PolkitAgentListenerClass *listener_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_agent_text_listener_finalize; gobject_class->get_property = polkit_agent_text_listener_get_property; gobject_class->set_property = polkit_agent_text_listener_set_property; listener_class = POLKIT_AGENT_LISTENER_CLASS (klass); listener_class->initiate_authentication = polkit_agent_text_listener_initiate_authentication; listener_class->initiate_authentication_finish = polkit_agent_text_listener_initiate_authentication_finish; g_object_class_install_property (gobject_class, PROP_USE_COLOR, g_param_spec_boolean ("use-color", "", "", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_USE_ALTERNATE_BUFFER, g_param_spec_boolean ("use-alternate-buffer", "", "", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_DELAY, g_param_spec_uint ("delay", "", "", 0, G_MAXUINT, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_signal_new("tty_attrs_changed", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } /** * polkit_agent_text_listener_new: * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Creates a new #PolkitAgentTextListener for authenticating the user * via an textual interface on the controlling terminal * (e.g. /dev/tty). This can fail if e.g. the * current process has no controlling terminal. * * Returns: A #PolkitAgentTextListener or %NULL if @error is set. Free with g_object_unref() when done with it. */ PolkitAgentListener * polkit_agent_text_listener_new (GCancellable *cancellable, GError **error) { g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); return POLKIT_AGENT_LISTENER (g_initable_new (POLKIT_AGENT_TYPE_TEXT_LISTENER, cancellable, error, NULL)); } /* ---------------------------------------------------------------------------------------------------- */ static gboolean initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (initable); gboolean ret; const gchar *tty_name; ret = FALSE; tty_name = ctermid (NULL); if (tty_name == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine pathname for current controlling terminal for the process: %s", strerror (errno)); goto out; } listener->tty = fopen (tty_name, "r+"); if (listener->tty == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error opening current controlling terminal for the process (`%s'): %s", tty_name, strerror (errno)); goto out; } ret = TRUE; out: return ret; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = initable_init; } /* ---------------------------------------------------------------------------------------------------- */ static void on_completed (PolkitAgentSession *session, gboolean gained_authorization, gpointer user_data) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (user_data); if (listener->use_color) fprintf (listener->tty, "\x1B[1;31m"); if (gained_authorization) fprintf (listener->tty, "==== AUTHENTICATION COMPLETE ====\n"); else fprintf (listener->tty, "==== AUTHENTICATION FAILED ====\n"); if (listener->use_color) fprintf (listener->tty, "\x1B[0m"); if (listener->use_alternate_buffer) { sleep (listener->delay); fprintf (listener->tty, "\x1B[?1049l"); } fflush (listener->tty); g_simple_async_result_complete_in_idle (listener->simple); g_object_unref (listener->simple); g_object_unref (listener->active_session); g_cancellable_disconnect (listener->cancellable, listener->cancel_id); g_object_unref (listener->cancellable); listener->simple = NULL; listener->active_session = NULL; listener->cancel_id = 0; } static void on_request (PolkitAgentSession *session, const gchar *request, gboolean echo_on, gpointer user_data) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (user_data); struct termios ts, ots; GString *str; gint c; fprintf (listener->tty, "%s", request); fflush (listener->tty); setbuf (listener->tty, NULL); /* TODO: We really ought to block SIGINT and STGSTP (and probably * other signals too) so we can restore the terminal (since we * turn off echoing). See e.g. Advanced Programming in the * UNIX Environment 2nd edition (Steves and Rago) section * 18.10, pg 660 where this is suggested. See also various * getpass(3) implementations * * However, since we are a library routine the user could have * multiple threads - in fact, typical usage of * PolkitAgentTextListener is to run it in a thread. And * unfortunately threads and POSIX signals is a royal PITA. * * Maybe we could fork(2) and ask for the password in the * child and send it back to the parent over a pipe? (we are * guaranteed that there is only one thread in the child * process). * * (Side benefit of doing this in a child process is that we * could avoid blocking the thread where the * PolkitAgentTextListener object is being serviced from. But * since this class is normally used in a dedicated thread * it doesn't really matter *anyway*.) * * Anyway, On modern Linux not doing this doesn't seem to be a * problem - looks like modern shells restore echoing anyway * on the first input. So maybe it's not even worth solving * the problem. */ g_signal_emit_by_name(listener, "tty_attrs_changed", TRUE); tcgetattr (fileno (listener->tty), &ts); ots = ts; ts.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); tcsetattr (fileno (listener->tty), TCSAFLUSH, &ts); str = g_string_new (NULL); while (TRUE) { c = getc (listener->tty); if (c == '\n') { /* ok, done */ break; } else if (c == EOF) { tcsetattr (fileno (listener->tty), TCSAFLUSH, &ots); g_warning ("Got unexpected EOF while reading from controlling terminal."); break; } else { g_string_append_c (str, c); } } tcsetattr (fileno (listener->tty), TCSAFLUSH, &ots); g_signal_emit_by_name(listener, "tty_attrs_changed", FALSE); putc ('\n', listener->tty); if (c == EOF) { polkit_agent_session_cancel (listener->active_session); } else { polkit_agent_session_response (session, str->str); } memset (str->str, '\0', str->len); g_string_free (str, TRUE); } static void on_show_error (PolkitAgentSession *session, const gchar *text, gpointer user_data) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (user_data); fprintf (listener->tty, "Error: %s\n", text); fflush (listener->tty); } static void on_show_info (PolkitAgentSession *session, const gchar *text, gpointer user_data) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (user_data); fprintf (listener->tty, "Info: %s\n", text); fflush (listener->tty); } static void on_cancelled (GCancellable *cancellable, gpointer user_data) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (user_data); fprintf (listener->tty, "Cancelled\n"); fflush (listener->tty); polkit_agent_session_cancel (listener->active_session); } static gchar * identity_to_human_readable_string (PolkitIdentity *identity) { gchar *ret; g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), NULL); ret = NULL; if (POLKIT_IS_UNIX_USER (identity)) { struct passwd pw; struct passwd *ppw; char buf[2048]; int res; res = getpwuid_r (polkit_unix_user_get_uid (POLKIT_UNIX_USER (identity)), &pw, buf, sizeof buf, &ppw); if (res != 0) { g_warning ("Error calling getpwuid_r: %s", strerror (res)); } else { if (ppw->pw_gecos == NULL || strlen (ppw->pw_gecos) == 0 || strcmp (ppw->pw_gecos, ppw->pw_name) == 0) { ret = g_strdup_printf ("%s", ppw->pw_name); } else { ret = g_strdup_printf ("%s (%s)", ppw->pw_gecos, ppw->pw_name); } } } if (ret == NULL) ret = polkit_identity_to_string (identity); return ret; } static PolkitIdentity * choose_identity (PolkitAgentTextListener *listener, GList *identities) { GList *l; guint n; guint num_identities; GString *str; PolkitIdentity *ret; guint num; gchar *endp; ret = NULL; fprintf (listener->tty, "Multiple identities can be used for authentication:\n"); for (l = identities, n = 0; l != NULL; l = l->next, n++) { PolkitIdentity *identity = POLKIT_IDENTITY (l->data); gchar *s; s = identity_to_human_readable_string (identity); fprintf (listener->tty, " %d. %s\n", n + 1, s); g_free (s); } num_identities = n; fprintf (listener->tty, "Choose identity to authenticate as (1-%d): ", num_identities); fflush (listener->tty); str = g_string_new (NULL); while (TRUE) { gint c; c = getc (listener->tty); if (c == '\n') { /* ok, done */ break; } else if (c == EOF) { g_warning ("Got unexpected EOF while reading from controlling terminal."); ret = NULL; /* let' be defensive */ goto out; } else { g_string_append_c (str, c); } } num = strtol (str->str, &endp, 10); if (str->len == 0 || *endp != '\0' || (num < 1 || num > num_identities)) { fprintf (listener->tty, "Invalid response `%s'.\n", str->str); goto out; } ret = g_list_nth_data (identities, num-1); out: g_string_free (str, TRUE); return ret; } static void polkit_agent_text_listener_initiate_authentication (PolkitAgentListener *_listener, const gchar *action_id, const gchar *message, const gchar *icon_name, PolkitDetails *details, const gchar *cookie, GList *identities, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (_listener); GSimpleAsyncResult *simple; PolkitIdentity *identity; simple = g_simple_async_result_new (G_OBJECT (listener), callback, user_data, polkit_agent_text_listener_initiate_authentication); if (listener->active_session != NULL) { g_simple_async_result_set_error (simple, POLKIT_ERROR, POLKIT_ERROR_FAILED, "An authentication session is already underway."); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); goto out; } g_assert (g_list_length (identities) >= 1); if (listener->use_alternate_buffer) fprintf (listener->tty, "\x1B[?1049h"); if (listener->use_color) fprintf (listener->tty, "\x1B[1;31m"); fprintf (listener->tty, "==== AUTHENTICATING FOR %s ====\n", action_id); if (listener->use_color) fprintf (listener->tty, "\x1B[0m"); fprintf (listener->tty, "%s\n", message); /* handle multiple identies by asking which one to use */ if (g_list_length (identities) > 1) { identity = choose_identity (listener, identities); if (identity == NULL) { if (listener->use_color) fprintf (listener->tty, "\x1B[1;31m"); fprintf (listener->tty, "==== AUTHENTICATION CANCELED ====\n"); if (listener->use_color) fprintf (listener->tty, "\x1B[0m"); fflush (listener->tty); g_simple_async_result_set_error (simple, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Authentication was canceled."); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); goto out; } } else { gchar *s; identity = identities->data; s = identity_to_human_readable_string (identity); fprintf (listener->tty, "Authenticating as: %s\n", s); g_free (s); } listener->active_session = polkit_agent_session_new (identity, cookie); g_signal_connect (listener->active_session, "completed", G_CALLBACK (on_completed), listener); g_signal_connect (listener->active_session, "request", G_CALLBACK (on_request), listener); g_signal_connect (listener->active_session, "show-info", G_CALLBACK (on_show_info), listener); g_signal_connect (listener->active_session, "show-error", G_CALLBACK (on_show_error), listener); listener->simple = simple; listener->cancellable = g_object_ref (cancellable); listener->cancel_id = g_cancellable_connect (cancellable, G_CALLBACK (on_cancelled), listener, NULL); polkit_agent_session_initiate (listener->active_session); out: ; } static gboolean polkit_agent_text_listener_initiate_authentication_finish (PolkitAgentListener *_listener, GAsyncResult *res, GError **error) { gboolean ret; g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == polkit_agent_text_listener_initiate_authentication); ret = FALSE; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) goto out; ret = TRUE; out: return ret; } polkit-126/src/polkitagent/polkitagenttextlistener.h000066400000000000000000000035041474122443600231540ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined(_POLKIT_AGENT_INSIDE_POLKIT_AGENT_H) && !defined (_POLKIT_AGENT_COMPILATION) #error "Only can be included directly, this file may disappear or change contents" #endif #ifndef __POLKIT_AGENT_TEXT_LISTENER_H #define __POLKIT_AGENT_TEXT_LISTENER_H #include #include G_BEGIN_DECLS #define POLKIT_AGENT_TYPE_TEXT_LISTENER (polkit_agent_text_listener_get_type()) #define POLKIT_AGENT_TEXT_LISTENER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_AGENT_TYPE_TEXT_LISTENER, PolkitAgentTextListener)) #define POLKIT_AGENT_IS_TEXT_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_AGENT_TYPE_TEXT_LISTENER)) GType polkit_agent_text_listener_get_type (void) G_GNUC_CONST; PolkitAgentListener *polkit_agent_text_listener_new (GCancellable *cancellable, GError **error); G_END_DECLS #endif /* __POLKIT_AGENT_TEXT_LISTENER_H */ polkit-126/src/polkitagent/polkitagenttypes.h000066400000000000000000000032601474122443600215650ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined(_POLKIT_AGENT_INSIDE_POLKIT_AGENT_H) && !defined (_POLKIT_AGENT_COMPILATION) #error "Only can be included directly, this file may disappear or change contents" #endif #ifndef __POLKIT_AGENT_TYPES_H #define __POLKIT_AGENT_TYPES_H #include G_BEGIN_DECLS struct _PolkitAgentListener; typedef struct _PolkitAgentListener PolkitAgentListener; struct _PolkitAgentTextListener; typedef struct _PolkitAgentTextListener PolkitAgentTextListener; struct _PolkitAgentSession; typedef struct _PolkitAgentSession PolkitAgentSession; #if GLIB_CHECK_VERSION(2, 44, 0) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAgentListener, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAgentTextListener, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAgentSession, g_object_unref) #endif G_END_DECLS #endif /* __POLKIT_AGENT_TYPES_H */ polkit-126/src/polkitbackend/000077500000000000000000000000001474122443600162765ustar00rootroot00000000000000polkit-126/src/polkitbackend/50-default.rules.in000066400000000000000000000005231474122443600216250ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- */ // DO NOT EDIT THIS FILE, it will be overwritten on update // // Default rules for polkit // // See the polkit(8) man page for more information // about configuring polkit. polkit.addAdminRule(function(action, subject) { return ["unix-group:@PRIVILEGED_GROUP@"]; }); polkit-126/src/polkitbackend/init.js000066400000000000000000000046731474122443600176110ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- */ function Action() { this.lookup = function(name) { return this["_detail_" + name]; }, this.toString = function() { var ret = "[Action id='" + this.id + "'"; for (var i in this) { if (i.indexOf("_detail_") == 0) { var key = i.substr(8); var value = this[i]; ret += " " + key + "='" + value + "'"; } } ret += "]"; return ret; }; }; function Subject() { this.isInGroup = function(group) { for (var n = 0; n < this.groups.length; n++) { if (this.groups[n] == group) return true; } return false; }; this.isInNetGroup = function(netGroup) { return polkit._userIsInNetGroup(this.user, netGroup); }; this.toString = function() { var ret = "[Subject"; for (var i in this) { if (typeof this[i] != "function") { if (typeof this[i] == "string") ret += " " + i + "='" + this[i] + "'"; else ret += " " + i + "=" + this[i]; } } ret += "]"; return ret; }; }; polkit._adminRuleFuncs = []; polkit.addAdminRule = function(callback) {this._adminRuleFuncs.push(callback);}; polkit._runAdminRules = function(action, subject) { var ret = null; for (var n = 0; n < this._adminRuleFuncs.length; n++) { var func = this._adminRuleFuncs[n]; var func_ret = func(action, subject); if (func_ret) { ret = func_ret; break } } return ret ? ret.join(",") : ""; }; polkit._ruleFuncs = []; polkit.addRule = function(callback) {this._ruleFuncs.push(callback);}; polkit._runRules = function(action, subject) { var ret = null; for (var n = 0; n < this._ruleFuncs.length; n++) { var func = this._ruleFuncs[n]; var func_ret = func(action, subject); if (func_ret) { ret = func_ret; break } } return ret; }; polkit._deleteRules = function() { this._adminRuleFuncs = []; this._ruleFuncs = []; }; polkit.Result = { NO : "no", YES : "yes", AUTH_SELF : "auth_self", AUTH_SELF_KEEP : "auth_self_keep", AUTH_ADMIN : "auth_admin", AUTH_ADMIN_KEEP : "auth_admin_keep", NOT_HANDLED : null }; polkit-126/src/polkitbackend/meson.build000066400000000000000000000032561474122443600204460ustar00rootroot00000000000000name = '@0@-backend-@1@'.format(meson.project_name(), pk_api_version) sources = files( 'polkitbackendactionlookup.c', 'polkitbackendactionpool.c', 'polkitbackendauthority.c', 'polkitbackendcommon.c', 'polkitbackendinteractiveauthority.c', ) output = 'initjs.h' sources += custom_target( output, input: 'init.js', output: output, capture: true, command: [find_program('perl'), meson.current_source_dir() / 'toarray.pl', '@INPUT@', 'init_js'], ) deps = [ expat_dep, libpolkit_gobject_dep, js_dep, ] c_flags = [ '-D_POLKIT_COMPILATION', '-D_POLKIT_BACKEND_COMPILATION', '-DPACKAGE_DATA_DIR="@0@"'.format(pk_prefix / pk_datadir), '-DPACKAGE_SYSCONF_DIR="@0@"'.format(pk_prefix / pk_sysconfdir), ] sources += files('polkitbackendduktapeauthority.c') deps += libm_dep deps += thread_dep if enable_logind sources += files('polkitbackendsessionmonitor-systemd.c') deps += logind_dep else sources += files('polkitbackendsessionmonitor.c') endif libpolkit_backend = static_library( name, sources: sources, include_directories: top_inc, dependencies: deps, c_args: c_flags, cpp_args: c_flags, ) configure_file( input: '50-default.rules.in', output: '@BASENAME@', configuration: { 'PRIVILEGED_GROUP': privileged_group, }, install: true, install_dir: pk_pkgdatadir / 'rules.d', ) program = 'polkitd' c_flags = [ '-DG_LOG_DOMAIN="@0@-@1@"'.format(program, pk_api_version), '-DPOLKIT_BACKEND_I_KNOW_API_IS_SUBJECT_TO_CHANGE', ] executable( program, program + '.c', include_directories: top_inc, dependencies: libpolkit_gobject_dep, c_args: c_flags, link_with: libpolkit_backend, install: true, install_dir: pk_libprivdir, ) polkit-126/src/polkitbackend/polkitbackend.h000066400000000000000000000027561474122443600212730ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #ifndef __POLKIT_BACKEND_H #define __POLKIT_BACKEND_H #include #if !defined (POLKIT_BACKEND_I_KNOW_API_IS_SUBJECT_TO_CHANGE) && !defined (_POLKIT_BACKEND_COMPILATION) #error "libpolkitbackend is unstable API and subject to change. You must define POLKIT_BACKEND_I_KNOW_API_IS_SUBJECT_TO_CHANGE to acknowledge this." #endif #define _POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H 1 #include #include #include #include #undef _POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H #endif /* __POLKIT_BACKEND_H */ polkit-126/src/polkitbackend/polkitbackendactionlookup.c000066400000000000000000000153531474122443600237130ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #define _POLKIT_BACKEND_ACTION_LOOKUP_NO_DEPRECATED_WARNING #include #include #include "polkitbackendactionlookup.h" #include "polkitbackendprivate.h" /** * SECTION:polkitbackendactionlookup * @title: PolkitBackendActionLookup * @short_description: Interface used to provide data to authentication dialogs * @stability: Unstable * * An interface that is used by backends to provide localized data * shown in authentication dialogs. * * This inteface is intended for mechanisms to customize the message * to show - a mechanism can provide a #GIOModule that registers one * or more extensions that implement this interface. Every time an * authentication dialog is shown, the registered extensions are * consulted in priority order. * * This is useful if your mechanism wants to put up a message such as * "Authentication is required to install 'Totem Movie Player'", * e.g. messages that include more information than just the action * name. * * Code implementing this interface cannot block * or do any IO when methods are invoked. If information is needed to * format the message or details, prepare it in advance and pass it as * part of the @details object when doing the * polkit_authority_check_authorization() call. Then the code in this * interface can use that information to return localized data. * * Note that setlocale() and the LANG environment * variable will be set up to match the locale of the authentication * agent that is the receiver of the information. This means that code * implementing this interface can use dgettext() or similar machinery * to look up translations. */ static void base_init (gpointer g_iface) { } GType polkit_backend_action_lookup_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; if (g_once_init_enter ((gsize*) &g_define_type_id__volatile)) { static const GTypeInfo info = { sizeof (PolkitBackendActionLookupIface), base_init, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, /* instance_size */ 0, /* n_preallocs */ NULL, /* instance_init */ NULL /* value_table */ }; GType iface_type = g_type_register_static (G_TYPE_INTERFACE, "PolkitBackendActionLookup", &info, 0); g_type_interface_add_prerequisite (iface_type, G_TYPE_OBJECT); g_once_init_leave (&g_define_type_id__volatile, iface_type); } return g_define_type_id__volatile; } /** * polkit_backend_action_lookup_get_message: * @lookup: A #PolkitBackendActionLookup. * @action_id: The action to get the message for. * @details: Details passed to polkit_authority_check_authorization(). * @action_description: A #PolkitActionDescription object for @action_id. * * Computes a message to show in an authentication dialog for * @action_id and @details. * * Returns: A localized string to show in the authentication dialog or %NULL. Caller must free this string. **/ gchar * polkit_backend_action_lookup_get_message (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description) { PolkitBackendActionLookupIface *iface = POLKIT_BACKEND_ACTION_LOOKUP_GET_IFACE (lookup); if (iface->get_message == NULL) return NULL; else return iface->get_message (lookup, action_id, details, action_description); } /** * polkit_backend_action_lookup_get_icon_name: * @lookup: A #PolkitBackendActionLookup. * @action_id: The action to get the themed icon for. * @details: Details passed to polkit_authority_check_authorization(). * @action_description: A #PolkitActionDescription object for @action_id. * * Computes a themed icon name to show in an authentication dialog for * @action_id and @details. * * Returns: A themed icon name or %NULL. Caller must free this string. **/ gchar * polkit_backend_action_lookup_get_icon_name (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description) { PolkitBackendActionLookupIface *iface = POLKIT_BACKEND_ACTION_LOOKUP_GET_IFACE (lookup); if (iface->get_icon_name == NULL) return NULL; else return iface->get_icon_name (lookup, action_id, details, action_description); } /** * polkit_backend_action_lookup_get_details: * @lookup: A #PolkitBackendActionLookup. * @action_id: The action to get the details for. * @details: Details passed to polkit_authority_check_authorization(). * @action_description: A #PolkitActionDescription object for @action_id. * * Computes localized details to show in an authentication dialog for * @action_id and @details. * * Returns: A #PolkitDetails object with localized details or %NULL. Caller must free the result. **/ PolkitDetails * polkit_backend_action_lookup_get_details (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description) { PolkitBackendActionLookupIface *iface = POLKIT_BACKEND_ACTION_LOOKUP_GET_IFACE (lookup); if (iface->get_details == NULL) return NULL; else return iface->get_details (lookup, action_id, details, action_description); } polkit-126/src/polkitbackend/polkitbackendactionlookup.h000066400000000000000000000145531474122443600237210ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_BACKEND_ACTION_LOOKUP_H #define __POLKIT_BACKEND_ACTION_LOOKUP_H #include #include #include G_BEGIN_DECLS /** * POLKIT_BACKEND_ACTION_LOOKUP_EXTENSION_POINT_NAME: * * Extension point name for looking up action information. */ #define POLKIT_BACKEND_ACTION_LOOKUP_EXTENSION_POINT_NAME "polkit-backend-action-lookup-1" #define POLKIT_BACKEND_TYPE_ACTION_LOOKUP (polkit_backend_action_lookup_get_type()) #define POLKIT_BACKEND_ACTION_LOOKUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_BACKEND_TYPE_ACTION_LOOKUP, PolkitBackendActionLookup)) #define POLKIT_BACKEND_IS_ACTION_LOOKUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_BACKEND_TYPE_ACTION_LOOKUP)) #define POLKIT_BACKEND_ACTION_LOOKUP_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), POLKIT_BACKEND_TYPE_ACTION_LOOKUP, PolkitBackendActionLookupIface)) #if 0 /** * PolkitBackendActionLookup: * * Opaque object for #PolkitBackendActionLookupIface. */ typedef struct _PolkitBackendActionLookup PolkitBackendActionLookup; /* Dummy typedef */ #endif typedef struct _PolkitBackendActionLookupIface PolkitBackendActionLookupIface; /** * PolkitBackendActionLookupIface: * @get_message: See polkit_backend_action_lookup_get_message(). * @get_icon_name: See polkit_backend_action_lookup_get_icon_name(). * @get_details: See polkit_backend_action_lookup_get_details(). * * Interface that is used by backends to provide localized data shown * in authentication dialogs. */ struct _PolkitBackendActionLookupIface { /*< private >*/ GTypeInterface parent_iface; /*< public >*/ /* VTable */ gchar * (*get_message) (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description); gchar * (*get_icon_name) (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description); PolkitDetails * (*get_details) (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description); }; #ifdef _POLKIT_BACKEND_ACTION_LOOKUP_NO_DEPRECATED_WARNING GType polkit_backend_action_lookup_get_type (void) G_GNUC_CONST; gchar *polkit_backend_action_lookup_get_message (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description); gchar *polkit_backend_action_lookup_get_icon_name (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description); PolkitDetails *polkit_backend_action_lookup_get_details (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description); #else GType polkit_backend_action_lookup_get_type (void) G_GNUC_CONST G_GNUC_DEPRECATED_FOR (use_PolkitDetails_instead); gchar *polkit_backend_action_lookup_get_message (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description) G_GNUC_DEPRECATED_FOR (use_PolkitDetails_instead); gchar *polkit_backend_action_lookup_get_icon_name (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description) G_GNUC_DEPRECATED_FOR (use_PolkitDetails_instead); PolkitDetails *polkit_backend_action_lookup_get_details (PolkitBackendActionLookup *lookup, const gchar *action_id, PolkitDetails *details, PolkitActionDescription *action_description) G_GNUC_DEPRECATED_FOR (use_PolkitDetails_instead); #endif G_END_DECLS #endif /* __POLKIT_BACKEND_ACTION_LOOKUP_H */ polkit-126/src/polkitbackend/polkitbackendactionpool.c000066400000000000000000001051251474122443600233500ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #ifdef ENABLE_GETTEXT #include #include #endif #include #include #include "polkitbackendactionpool.h" /* * SECTION:polkitbackendactionpool * @title: PolkitBackendActionPool * @short_description: Registered actions * * The #PolkitBackendActionPool class is a utility class to look up registered PolicyKit actions. */ typedef struct { gchar *vendor_name; gchar *vendor_url; gchar *icon_name; gchar *description; gchar *description_domain; gchar *message; gchar *message_domain; PolkitImplicitAuthorization implicit_authorization_any; PolkitImplicitAuthorization implicit_authorization_inactive; PolkitImplicitAuthorization implicit_authorization_active; /* each of these map from the locale identifer (e.g. da_DK) to the localized value */ GHashTable *localized_description; GHashTable *localized_message; /* this maps from annotation key (string) to annotation value (also a string) */ GHashTable *annotations; } ParsedAction; static void parsed_action_free (ParsedAction *action) { g_free (action->vendor_name); g_free (action->vendor_url); g_free (action->icon_name); g_free (action->description); g_free (action->description_domain); g_free (action->message); g_free (action->message_domain); g_hash_table_unref (action->localized_description); g_hash_table_unref (action->localized_message); g_hash_table_unref (action->annotations); g_free (action); } static gboolean process_policy_file (PolkitBackendActionPool *pool, const gchar *xml, GError **error); static void ensure_file (PolkitBackendActionPool *pool, GFile *file); static void ensure_all_files (PolkitBackendActionPool *pool); static const gchar *_localize (GHashTable *translations, const gchar *untranslated, const gchar *domain, const gchar *lang); typedef struct { /* directories with .policy files, e.g. /usr/share/polkit-1/actions */ GList *directories; /* GFileMonitor instances for directories */ GList *dir_monitors; /* maps from action_id to a ParsedAction struct */ GHashTable *parsed_actions; /* maps from basename of parsed file to nothing */ GHashTable *parsed_files; /* is TRUE only when we've read all files */ gboolean has_loaded_all_files; } PolkitBackendActionPoolPrivate; enum { PROP_0, PROP_DIRECTORIES, }; enum { CHANGED_SIGNAL, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; G_DEFINE_TYPE_WITH_PRIVATE (PolkitBackendActionPool, polkit_backend_action_pool, G_TYPE_OBJECT); static void polkit_backend_action_pool_init (PolkitBackendActionPool *pool) { PolkitBackendActionPoolPrivate *priv; priv = polkit_backend_action_pool_get_instance_private (pool); priv->parsed_actions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) parsed_action_free); priv->parsed_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } static void polkit_backend_action_pool_finalize (GObject *object) { PolkitBackendActionPool *pool; PolkitBackendActionPoolPrivate *priv; pool = POLKIT_BACKEND_ACTION_POOL (object); priv = polkit_backend_action_pool_get_instance_private (pool); if (priv->directories != NULL) g_list_free_full (priv->directories, g_object_unref); if (priv->dir_monitors != NULL) g_list_free_full (priv->dir_monitors, g_object_unref); if (priv->parsed_actions != NULL) g_hash_table_unref (priv->parsed_actions); if (priv->parsed_files != NULL) g_hash_table_unref (priv->parsed_files); G_OBJECT_CLASS (polkit_backend_action_pool_parent_class)->finalize (object); } static void polkit_backend_action_pool_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PolkitBackendActionPool *pool; PolkitBackendActionPoolPrivate *priv; pool = POLKIT_BACKEND_ACTION_POOL (object); priv = polkit_backend_action_pool_get_instance_private (pool); switch (prop_id) { case PROP_DIRECTORIES: g_value_set_object (value, priv->directories); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void dir_monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data) { PolkitBackendActionPool *pool; PolkitBackendActionPoolPrivate *priv; pool = POLKIT_BACKEND_ACTION_POOL (user_data); priv = polkit_backend_action_pool_get_instance_private (pool); /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution? * Because when editing a file with emacs we get 4-8 events.. */ if (file != NULL) { gchar *name; name = g_file_get_basename (file); //g_debug ("event_type=%d file=%p name=%s", event_type, file, name); if (!g_str_has_prefix (name, ".") && !g_str_has_prefix (name, "#") && g_str_has_suffix (name, ".policy") && (event_type == G_FILE_MONITOR_EVENT_CREATED || event_type == G_FILE_MONITOR_EVENT_DELETED || event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)) { //g_debug ("match"); /* now throw away all caches */ g_hash_table_remove_all (priv->parsed_files); g_hash_table_remove_all (priv->parsed_actions); priv->has_loaded_all_files = FALSE; g_signal_emit_by_name (pool, "changed"); } g_free (name); } } static void polkit_backend_action_pool_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PolkitBackendActionPool *pool; PolkitBackendActionPoolPrivate *priv; pool = POLKIT_BACKEND_ACTION_POOL (object); priv = polkit_backend_action_pool_get_instance_private (pool); switch (prop_id) { case PROP_DIRECTORIES: { const gchar **dir_names = (const gchar**) g_value_get_boxed (value); for (int n = 0; dir_names[n] != NULL; n++) { GFile *file; GFileMonitor *monitor; GError *error = NULL; const gchar *dir_name = dir_names[n]; file = g_file_new_for_path (dir_name); priv->directories = g_list_prepend (priv->directories, file); monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &error); if (monitor == NULL) { g_warning ("Error monitoring actions directory: %s", error->message); g_error_free (error); } else { g_signal_connect (monitor, "changed", (GCallback) dir_monitor_changed, pool); priv->dir_monitors = g_list_prepend (priv->dir_monitors, monitor); } } priv->directories = g_list_reverse(priv->directories); priv->dir_monitors = g_list_reverse(priv->dir_monitors); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void polkit_backend_action_pool_class_init (PolkitBackendActionPoolClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = polkit_backend_action_pool_get_property; gobject_class->set_property = polkit_backend_action_pool_set_property; gobject_class->finalize = polkit_backend_action_pool_finalize; /** * PolkitBackendActionPool:directory: * * The directory to load action description files from. */ g_object_class_install_property (gobject_class, PROP_DIRECTORIES, g_param_spec_boxed ("directories", "Directories", "Directories to load action description files from", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * PolkitBackendActionPool::changed: * @action_pool: A #PolkitBackendActionPool. * * Emitted when action files in the supplied directory changes. */ signals[CHANGED_SIGNAL] = g_signal_new ("changed", POLKIT_BACKEND_TYPE_ACTION_POOL, G_SIGNAL_RUN_LAST, 0, /* class offset */ NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /** * polkit_backend_action_pool_new: * @directory: A #GFile for the directory holding PolicyKit action description files. * * Creates a new #PolkitBackendPool that can be used for looking up #PolkitActionDescription objects. * * Returns: A #PolkitBackendActionPool. Free with g_object_unref(). **/ PolkitBackendActionPool * polkit_backend_action_pool_new (const gchar **directories) { PolkitBackendActionPool *pool; pool = POLKIT_BACKEND_ACTION_POOL (g_object_new (POLKIT_BACKEND_TYPE_ACTION_POOL, "directories", directories, NULL)); return pool; } /** * polkit_backend_action_pool_get_action: * @pool: A #PolkitBackendActionPool. * @action_id: A PolicyKit action identifier. * @locale: The locale to get descriptions for or %NULL for system locale. * * Gets a #PolkitActionDescription object describing the action with identifier @action_id. * * Returns: A #PolkitActionDescription (free with g_object_unref()) or %NULL * if @action_id isn't registered or valid. **/ PolkitActionDescription * polkit_backend_action_pool_get_action (PolkitBackendActionPool *pool, const gchar *action_id, const gchar *locale) { PolkitBackendActionPoolPrivate *priv; PolkitActionDescription *ret; ParsedAction *parsed_action; const gchar *description; const gchar *message; g_return_val_if_fail (POLKIT_BACKEND_IS_ACTION_POOL (pool), NULL); priv = polkit_backend_action_pool_get_instance_private (pool); /* TODO: just compute the name of the expected file and ensure it's parsed */ ensure_all_files (pool); ret = NULL; parsed_action = g_hash_table_lookup (priv->parsed_actions, action_id); if (parsed_action == NULL) { g_warning ("Unknown action_id '%s'", action_id); goto out; } description = _localize (parsed_action->localized_description, parsed_action->description, parsed_action->description_domain, locale); message = _localize (parsed_action->localized_message, parsed_action->message, parsed_action->message_domain, locale); ret = polkit_action_description_new (action_id, description, message, parsed_action->vendor_name, parsed_action->vendor_url, parsed_action->icon_name, parsed_action->implicit_authorization_any, parsed_action->implicit_authorization_inactive, parsed_action->implicit_authorization_active, parsed_action->annotations); out: return ret; } /** * polkit_backend_action_pool_get_all_actions: * @pool: A #PolkitBackendActionPool. * @locale: The locale to get descriptions for or %NULL for system locale. * * Gets all registered PolicyKit action descriptions from @pool with strings for @locale. * * Returns: A #GList of #PolkitActionDescription objects. This list * should be freed with g_list_free() after each element have * been unreffed with g_object_unref(). **/ GList * polkit_backend_action_pool_get_all_actions (PolkitBackendActionPool *pool, const gchar *locale) { GList *ret; PolkitBackendActionPoolPrivate *priv; GHashTableIter hash_iter; const gchar *action_id; g_return_val_if_fail (POLKIT_BACKEND_IS_ACTION_POOL (pool), NULL); priv = polkit_backend_action_pool_get_instance_private (pool); ensure_all_files (pool); ret = NULL; g_hash_table_iter_init (&hash_iter, priv->parsed_actions); while (g_hash_table_iter_next (&hash_iter, (gpointer) &action_id, NULL)) { PolkitActionDescription *action_desc; action_desc = polkit_backend_action_pool_get_action (pool, action_id, locale); if (action_desc != NULL) ret = g_list_prepend (ret, action_desc); } ret = g_list_reverse (ret); return ret; } /** * polkit_backend_action_pool_reload: * @pool: A #PolkitBackendActionPool. * * Reload all PolicyKit actions in @pool. **/ void polkit_backend_action_pool_reload (PolkitBackendActionPool *pool) { PolkitBackendActionPoolPrivate *priv; if (!POLKIT_BACKEND_IS_ACTION_POOL (pool)) return; priv = polkit_backend_action_pool_get_instance_private (pool); g_hash_table_remove_all (priv->parsed_files); g_hash_table_remove_all (priv->parsed_actions); priv->has_loaded_all_files = FALSE; ensure_all_files (pool); } /* ---------------------------------------------------------------------------------------------------- */ static void ensure_file (PolkitBackendActionPool *pool, GFile *file) { PolkitBackendActionPoolPrivate *priv; gchar *contents; GError *error = NULL; gchar *path; gchar *basename; priv = polkit_backend_action_pool_get_instance_private (pool); path = g_file_get_path (file); basename = g_file_get_basename (file); if (g_hash_table_lookup_extended (priv->parsed_files, basename, NULL, NULL) == TRUE) goto out; if (!g_file_load_contents (file, NULL, &contents, NULL, NULL, &error)) { g_warning ("Error loading file with path '%s': %s", path, error->message); goto out; } if (!process_policy_file (pool, contents, &error)) { g_warning ("Error parsing file with path '%s': %s", path, error->message); g_free (contents); goto out; } g_free (contents); /* steal basename */ g_hash_table_insert (priv->parsed_files, basename, NULL); basename = NULL; out: g_free (basename); g_free (path); } static void ensure_all_files (PolkitBackendActionPool *pool) { PolkitBackendActionPoolPrivate *priv; GList *files = NULL; priv = polkit_backend_action_pool_get_instance_private (pool); if (priv->has_loaded_all_files) return; for (GList *l = priv->directories; l != NULL; l = l->next) { GError *error = NULL; GFileEnumerator *enumerator; GFile* file = l->data; char *dir_name = g_file_get_path(file); enumerator = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NONE, NULL, &error); if (error != NULL) { g_warning ("Error enumerating files in %s: %s", dir_name, error->message); g_error_free (error); } else { GFileInfo *file_info; while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL) { const gchar *name = g_file_info_get_name (file_info); /* only consider files with the right suffix */ if (g_str_has_suffix (name, ".policy")) files = g_list_prepend (files, g_strdup_printf ("%s/%s", dir_name, name)); g_object_unref (file_info); } /* for all files */ } g_object_unref (enumerator); g_free (dir_name); } /* standard sorting places /etc before /usr as desired */ files = g_list_sort (files, (GCompareFunc) g_strcmp0); for (GList *l = files; l != NULL; l = l->next) { GFile *file = g_file_new_for_path((gchar *)l->data); ensure_file (pool, file); g_object_unref (file); } g_list_free_full (files, g_free); priv->has_loaded_all_files = TRUE; } /* ---------------------------------------------------------------------------------------------------- */ enum { STATE_NONE, STATE_UNKNOWN_TAG, STATE_IN_POLICY_CONFIG, STATE_IN_POLICY_VENDOR, STATE_IN_POLICY_VENDOR_URL, STATE_IN_POLICY_ICON_NAME, STATE_IN_ACTION, STATE_IN_ACTION_DESCRIPTION, STATE_IN_ACTION_MESSAGE, STATE_IN_ACTION_VENDOR, STATE_IN_ACTION_VENDOR_URL, STATE_IN_ACTION_ICON_NAME, STATE_IN_DEFAULTS, STATE_IN_DEFAULTS_ALLOW_ANY, STATE_IN_DEFAULTS_ALLOW_INACTIVE, STATE_IN_DEFAULTS_ALLOW_ACTIVE, STATE_IN_ANNOTATE }; #define PARSER_MAX_DEPTH 32 typedef struct { XML_Parser parser; int state; int state_stack[PARSER_MAX_DEPTH]; int stack_depth; char *global_vendor; char *global_vendor_url; char *global_icon_name; char *action_id; char *vendor; char *vendor_url; char *icon_name; PolkitImplicitAuthorization implicit_authorization_any; PolkitImplicitAuthorization implicit_authorization_inactive; PolkitImplicitAuthorization implicit_authorization_active; GHashTable *policy_descriptions; GHashTable *policy_messages; char *policy_description_nolang; char *policy_description_domain; char *policy_message_nolang; char *policy_message_domain; /* the value of xml:lang for the thing we're reading in _cdata() */ char *elem_lang; /* the value of gettext-domain for the thing we're reading in _cdata() */ char *elem_domain; char *annotate_key; GHashTable *annotations; PolkitBackendActionPool *pool; } ParserData; static void pd_unref_action_data (ParserData *pd) { g_free (pd->action_id); pd->action_id = NULL; g_free (pd->vendor); pd->vendor = NULL; g_free (pd->vendor_url); pd->vendor_url = NULL; g_free (pd->icon_name); pd->icon_name = NULL; g_free (pd->policy_description_nolang); pd->policy_description_nolang = NULL; g_free (pd->policy_description_domain); pd->policy_description_domain = NULL; g_free (pd->policy_message_nolang); pd->policy_message_nolang = NULL; g_free (pd->policy_message_domain); pd->policy_message_domain = NULL; if (pd->policy_descriptions != NULL) { g_hash_table_unref (pd->policy_descriptions); pd->policy_descriptions = NULL; } if (pd->policy_messages != NULL) { g_hash_table_unref (pd->policy_messages); pd->policy_messages = NULL; } g_free (pd->annotate_key); pd->annotate_key = NULL; if (pd->annotations != NULL) { g_hash_table_unref (pd->annotations); pd->annotations = NULL; } g_free (pd->elem_lang); pd->elem_lang = NULL; g_free (pd->elem_domain); pd->elem_domain = NULL; } static void pd_unref_data (ParserData *pd) { pd_unref_action_data (pd); g_free (pd->global_vendor); pd->global_vendor = NULL; g_free (pd->global_vendor_url); pd->global_vendor_url = NULL; g_free (pd->global_icon_name); pd->global_icon_name = NULL; } static void _start (void *data, const char *el, const char **attr) { guint state; guint num_attr; ParserData *pd = data; for (num_attr = 0; attr[num_attr] != NULL; num_attr++) ; state = STATE_NONE; switch (pd->state) { case STATE_NONE: if (strcmp (el, "policyconfig") == 0) { state = STATE_IN_POLICY_CONFIG; } break; case STATE_IN_POLICY_CONFIG: if (strcmp (el, "action") == 0) { if (num_attr != 2 || strcmp (attr[0], "id") != 0) goto error; state = STATE_IN_ACTION; //if (!polkit_action_validate_id (attr[1])) // goto error; pd_unref_action_data (pd); pd->action_id = g_strdup (attr[1]); pd->policy_descriptions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); pd->policy_messages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); pd->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); /* initialize defaults */ pd->implicit_authorization_any = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; pd->implicit_authorization_inactive = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; pd->implicit_authorization_active = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; } else if (strcmp (el, "vendor") == 0 && num_attr == 0) { state = STATE_IN_POLICY_VENDOR; } else if (strcmp (el, "vendor_url") == 0 && num_attr == 0) { state = STATE_IN_POLICY_VENDOR_URL; } else if (strcmp (el, "icon_name") == 0 && num_attr == 0) { state = STATE_IN_POLICY_ICON_NAME; } break; case STATE_IN_ACTION: if (strcmp (el, "defaults") == 0) { state = STATE_IN_DEFAULTS; } else if (strcmp (el, "description") == 0) { if (num_attr == 2 && strcmp (attr[0], "xml:lang") == 0) { pd->elem_lang = g_strdup (attr[1]); } if (num_attr == 2 && strcmp (attr[0], "gettext-domain") == 0) { pd->elem_domain = g_strdup (attr[1]); } state = STATE_IN_ACTION_DESCRIPTION; } else if (strcmp (el, "message") == 0) { if (num_attr == 2 && strcmp (attr[0], "xml:lang") == 0) { pd->elem_lang = g_strdup (attr[1]); } if (num_attr == 2 && strcmp (attr[0], "gettext-domain") == 0) { pd->elem_domain = g_strdup (attr[1]); } state = STATE_IN_ACTION_MESSAGE; } else if (strcmp (el, "vendor") == 0 && num_attr == 0) { state = STATE_IN_ACTION_VENDOR; } else if (strcmp (el, "vendor_url") == 0 && num_attr == 0) { state = STATE_IN_ACTION_VENDOR_URL; } else if (strcmp (el, "icon_name") == 0 && num_attr == 0) { state = STATE_IN_ACTION_ICON_NAME; } else if (strcmp (el, "annotate") == 0) { if (num_attr != 2 || strcmp (attr[0], "key") != 0) goto error; state = STATE_IN_ANNOTATE; g_free (pd->annotate_key); pd->annotate_key = g_strdup (attr[1]); } break; case STATE_IN_DEFAULTS: if (strcmp (el, "allow_any") == 0) state = STATE_IN_DEFAULTS_ALLOW_ANY; else if (strcmp (el, "allow_inactive") == 0) state = STATE_IN_DEFAULTS_ALLOW_INACTIVE; else if (strcmp (el, "allow_active") == 0) state = STATE_IN_DEFAULTS_ALLOW_ACTIVE; break; default: break; } if (state == STATE_NONE) { g_warning ("skipping unknown tag <%s> at line %d", el, (int) XML_GetCurrentLineNumber (pd->parser)); state = STATE_UNKNOWN_TAG; } pd->state = state; pd->state_stack[pd->stack_depth] = pd->state; pd->stack_depth++; return; error: XML_StopParser (pd->parser, FALSE); } static gboolean _validate_icon_name (const gchar *icon_name) { guint n; gboolean ret; gsize len; ret = FALSE; len = strlen (icon_name); /* check for common suffixes */ if (g_str_has_suffix (icon_name, ".png")) goto out; if (g_str_has_suffix (icon_name, ".jpg")) goto out; /* icon name cannot be a path */ for (n = 0; n < len; n++) { if (icon_name [n] == '/') { goto out; } } ret = TRUE; out: return ret; } static void _cdata (void *data, const char *s, int len) { gchar *str; ParserData *pd = data; str = g_strndup (s, len); switch (pd->state) { case STATE_IN_ACTION_DESCRIPTION: if (pd->elem_lang == NULL) { g_free (pd->policy_description_nolang); pd->policy_description_nolang = str; pd->policy_description_domain = g_strdup (pd->elem_domain); str = NULL; } else { g_hash_table_insert (pd->policy_descriptions, g_strdup (pd->elem_lang), str); str = NULL; } break; case STATE_IN_ACTION_MESSAGE: if (pd->elem_lang == NULL) { g_free (pd->policy_message_nolang); pd->policy_message_nolang = str; pd->policy_message_domain = g_strdup (pd->elem_domain); str = NULL; } else { g_hash_table_insert (pd->policy_messages, g_strdup (pd->elem_lang), str); str = NULL; } break; case STATE_IN_POLICY_VENDOR: g_free (pd->global_vendor); pd->global_vendor = str; str = NULL; break; case STATE_IN_POLICY_VENDOR_URL: g_free (pd->global_vendor_url); pd->global_vendor_url = str; str = NULL; break; case STATE_IN_POLICY_ICON_NAME: if (! _validate_icon_name (str)) { g_warning ("Icon name '%s' is invalid", str); goto error; } g_free (pd->global_icon_name); pd->global_icon_name = str; str = NULL; break; case STATE_IN_ACTION_VENDOR: g_free (pd->vendor); pd->vendor = str; str = NULL; break; case STATE_IN_ACTION_VENDOR_URL: g_free (pd->vendor_url); pd->vendor_url = str; str = NULL; break; case STATE_IN_ACTION_ICON_NAME: if (! _validate_icon_name (str)) { g_warning ("Icon name '%s' is invalid", str); goto error; } g_free (pd->icon_name); pd->icon_name = str; str = NULL; break; case STATE_IN_DEFAULTS_ALLOW_ANY: if (!polkit_implicit_authorization_from_string (str, &pd->implicit_authorization_any)) goto error; break; case STATE_IN_DEFAULTS_ALLOW_INACTIVE: if (!polkit_implicit_authorization_from_string (str, &pd->implicit_authorization_inactive)) goto error; break; case STATE_IN_DEFAULTS_ALLOW_ACTIVE: if (!polkit_implicit_authorization_from_string (str, &pd->implicit_authorization_active)) goto error; break; case STATE_IN_ANNOTATE: g_hash_table_insert (pd->annotations, g_strdup (pd->annotate_key), str); str = NULL; break; default: break; } g_free (str); return; error: g_free (str); XML_StopParser (pd->parser, FALSE); } static void _end (void *data, const char *el) { ParserData *pd = data; g_free (pd->elem_lang); pd->elem_lang = NULL; g_free (pd->elem_domain); pd->elem_domain = NULL; switch (pd->state) { case STATE_IN_ACTION: { gchar *vendor; gchar *vendor_url; gchar *icon_name; ParsedAction *action; PolkitBackendActionPoolPrivate *priv; priv = polkit_backend_action_pool_get_instance_private (pd->pool); vendor = pd->vendor; if (vendor == NULL) vendor = pd->global_vendor; vendor_url = pd->vendor_url; if (vendor_url == NULL) vendor_url = pd->global_vendor_url; icon_name = pd->icon_name; if (icon_name == NULL) icon_name = pd->global_icon_name; action = g_new0 (ParsedAction, 1); action->vendor_name = g_strdup (vendor); action->vendor_url = g_strdup (vendor_url); action->icon_name = g_strdup (icon_name); action->description = g_strdup (pd->policy_description_nolang); action->description_domain = g_strdup (pd->policy_description_domain); action->message = g_strdup (pd->policy_message_nolang); action->message_domain = g_strdup (pd->policy_message_domain); action->localized_description = pd->policy_descriptions; action->localized_message = pd->policy_messages; action->annotations = pd->annotations; action->implicit_authorization_any = pd->implicit_authorization_any; action->implicit_authorization_inactive = pd->implicit_authorization_inactive; action->implicit_authorization_active = pd->implicit_authorization_active; g_hash_table_insert (priv->parsed_actions, g_strdup (pd->action_id), action); /* we steal these hash tables */ pd->annotations = NULL; pd->policy_descriptions = NULL; pd->policy_messages = NULL; break; } default: break; } --pd->stack_depth; if (pd->stack_depth < 0 || pd->stack_depth >= PARSER_MAX_DEPTH) { g_warning ("reached max depth?"); goto error; } if (pd->stack_depth > 0) pd->state = pd->state_stack[pd->stack_depth - 1]; else pd->state = STATE_NONE; return; error: XML_StopParser (pd->parser, FALSE); } /* ---------------------------------------------------------------------------------------------------- */ static gboolean process_policy_file (PolkitBackendActionPool *pool, const gchar *xml, GError **error) { ParserData pd; int xml_res; /* clear parser data */ memset (&pd, 0, sizeof (ParserData)); pd.pool = pool; pd.parser = XML_ParserCreate (NULL); pd.stack_depth = 0; XML_SetUserData (pd.parser, &pd); XML_SetElementHandler (pd.parser, _start, _end); XML_SetCharacterDataHandler (pd.parser, _cdata); /* init parser data */ pd.state = STATE_NONE; xml_res = XML_Parse (pd.parser, xml, strlen (xml), 1); if (xml_res == 0) { if (XML_GetErrorCode (pd.parser) == XML_ERROR_NO_MEMORY) { abort (); } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "%d: parse error: %s", (int) XML_GetCurrentLineNumber (pd.parser), XML_ErrorString (XML_GetErrorCode (pd.parser))); } XML_ParserFree (pd.parser); goto error; } XML_ParserFree (pd.parser); pd_unref_data (&pd); return TRUE; error: pd_unref_data (&pd); return FALSE; } /** * _localize: * @translations: a mapping from xml:lang to the value, e.g. 'da' -> 'Smadre', 'en_CA' -> 'Punch, Aye!' * @untranslated: the untranslated value, e.g. 'Punch' * @domain: the gettext domain for this string. May be NULL. * @lang: the locale we're interested in, e.g. 'da_DK', 'da', 'en_CA', 'en_US'; basically just $LANG * with the encoding cut off. Maybe be NULL. * * Pick the correct translation to use. * * Returns: the localized string to use */ static const gchar * _localize (GHashTable *translations, const gchar *untranslated, const gchar *domain, const gchar *lang) { const gchar *result; gchar **langs; guint n; if (lang == NULL) { result = untranslated; goto out; } /* If configured at build time, check gettext */ #ifdef ENABLE_GETTEXT if (domain != NULL) { gchar *old_locale; old_locale = g_strdup (setlocale (LC_ALL, NULL)); setlocale (LC_ALL, lang); result = dgettext (domain, untranslated); setlocale (LC_ALL, old_locale); g_free (old_locale); if (result != NULL) goto out; } #endif /* first see if we have the translation */ result = (const char *) g_hash_table_lookup (translations, (void *) lang); if (result != NULL) goto out; /* we could have a translation for 'da' but lang=='da_DK'; cut off the last part and try again */ langs = g_get_locale_variants (lang); for (n = 0; langs[n] != NULL; n++) { result = (const char *) g_hash_table_lookup (translations, (void *) langs[n]); if (result != NULL) break; } g_strfreev (langs); if (result != NULL) goto out; /* fall back to untranslated */ result = untranslated; out: return result; } polkit-126/src/polkitbackend/polkitbackendactionpool.h000066400000000000000000000064311474122443600233550ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_BACKEND_COMPILATION) || defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) #error "This is a private header file." #endif #include #ifndef __POLKIT_BACKEND_ACTION_POOL_H #define __POLKIT_BACKEND_ACTION_POOL_H G_BEGIN_DECLS #define POLKIT_BACKEND_TYPE_ACTION_POOL (polkit_backend_action_pool_get_type ()) #define POLKIT_BACKEND_ACTION_POOL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_BACKEND_TYPE_ACTION_POOL, PolkitBackendActionPool)) #define POLKIT_BACKEND_ACTION_POOL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), POLKIT_BACKEND_TYPE_ACTION_POOL, PolkitBackendActionPoolClass)) #define POLKIT_BACKEND_ACTION_POOL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_BACKEND_TYPE_ACTION_POOL,PolkitBackendActionPoolClass)) #define POLKIT_BACKEND_IS_ACTION_POOL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_BACKEND_TYPE_ACTION_POOL)) #define POLKIT_BACKEND_IS_ACTION_POOL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_BACKEND_TYPE_ACTION_POOL)) typedef struct _PolkitBackendActionPool PolkitBackendActionPool; typedef struct _PolkitBackendActionPoolClass PolkitBackendActionPoolClass; struct _PolkitBackendActionPool { GObject parent_instance; }; struct _PolkitBackendActionPoolClass { GObjectClass parent_class; /*< public >*/ /*< private >*/ /* Padding for future expansion */ void (*_polkit_reserved1) (void); void (*_polkit_reserved2) (void); void (*_polkit_reserved3) (void); void (*_polkit_reserved4) (void); void (*_polkit_reserved5) (void); void (*_polkit_reserved6) (void); void (*_polkit_reserved7) (void); void (*_polkit_reserved8) (void); }; GType polkit_backend_action_pool_get_type (void) G_GNUC_CONST; PolkitBackendActionPool *polkit_backend_action_pool_new (const gchar **); GList *polkit_backend_action_pool_get_all_actions (PolkitBackendActionPool *pool, const gchar *locale); PolkitActionDescription *polkit_backend_action_pool_get_action (PolkitBackendActionPool *pool, const gchar *action_id, const gchar *locale); void polkit_backend_action_pool_reload (PolkitBackendActionPool *pool); G_END_DECLS #endif /* __POLKIT_BACKEND_ACTION_POOL_H */ polkit-126/src/polkitbackend/polkitbackendauthority.c000066400000000000000000001744541474122443600232440ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #include #include #include #include "polkitbackendauthority.h" #include "polkitbackendjsauthority.h" #include "polkitbackendprivate.h" /** * SECTION:polkitbackendauthority * @title: PolkitBackendAuthority * @short_description: Abstract base class for authority backends * @stability: Unstable * @see_also: PolkitBackendJsAuthority * * To implement an authority backend, simply subclass #PolkitBackendAuthority * and implement the required VFuncs. */ enum { CHANGED_SIGNAL, SESSIONS_CHANGED_SIGNAL, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; static guint polkit_authority_log_level = LOG_LEVEL_NOTICE; G_DEFINE_ABSTRACT_TYPE (PolkitBackendAuthority, polkit_backend_authority, G_TYPE_OBJECT); static void polkit_backend_authority_init (PolkitBackendAuthority *authority) { } static void polkit_backend_authority_class_init (PolkitBackendAuthorityClass *klass) { /** * PolkitBackendAuthority::changed: * @authority: A #PolkitBackendAuthority. * * Emitted when actions and/or authorizations change. */ signals[CHANGED_SIGNAL] = g_signal_new ("changed", POLKIT_BACKEND_TYPE_AUTHORITY, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (PolkitBackendAuthorityClass, changed), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[SESSIONS_CHANGED_SIGNAL] = g_signal_new ("sessions-changed", POLKIT_BACKEND_TYPE_AUTHORITY, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (PolkitBackendAuthorityClass, changed), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /** * polkit_backend_authority_get_name: * @authority: A #PolkitBackendAuthority. * * Gets the name of the authority backend. * * Returns: The name of the backend. */ const gchar * polkit_backend_authority_get_name (PolkitBackendAuthority *authority) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->get_name == NULL) return "(not set)"; return klass->get_name (authority); } /** * polkit_backend_authority_get_version: * @authority: A #PolkitBackendAuthority. * * Gets the version of the authority backend. * * Returns: The name of the backend. */ const gchar * polkit_backend_authority_get_version (PolkitBackendAuthority *authority) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->get_version == NULL) return "(not set)"; return klass->get_version (authority); } /** * polkit_backend_authority_get_features: * @authority: A #PolkitBackendAuthority. * * Gets the features supported by the authority backend. * * Returns: Flags from #PolkitAuthorityFeatures. */ PolkitAuthorityFeatures polkit_backend_authority_get_features (PolkitBackendAuthority *authority) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->get_features == NULL) return POLKIT_AUTHORITY_FEATURES_NONE; return klass->get_features (authority); } /** * polkit_backend_authority_enumerate_actions: * @authority: A #PolkitBackendAuthority. * @caller: The system bus name that initiated the query. * @locale: The locale to retrieve descriptions for. * @error: Return location for error or %NULL. * * Retrieves all registered actions. * * Returns: A list of #PolkitActionDescription objects or %NULL if @error is set. The returned list * should be freed with g_list_free() after each element have been freed with g_object_unref(). **/ GList * polkit_backend_authority_enumerate_actions (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *locale, GError **error) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->enumerate_actions == NULL) { g_warning ("enumerate_actions is not implemented (it is not optional)"); g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Operation not supported (bug in backend)"); return NULL; } else { return klass->enumerate_actions (authority, caller, locale, error); } } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_backend_authority_check_authorization: * @authority: A #PolkitBackendAuthority. * @caller: The system bus name that initiated the query. * @subject: A #PolkitSubject. * @action_id: The action to check for. * @details: Details about the action or %NULL. * @flags: A set of #PolkitCheckAuthorizationFlags. * @cancellable: A #GCancellable. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. * * Asynchronously checks if @subject is authorized to perform the action represented * by @action_id. * * When the operation is finished, @callback will be invoked. You can then * call polkit_backend_authority_check_authorization_finish() to get the result of * the operation. **/ void polkit_backend_authority_check_authorization (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->check_authorization == NULL) { GSimpleAsyncResult *simple; g_warning ("check_authorization is not implemented (it is not optional)"); simple = g_simple_async_result_new_error (G_OBJECT (authority), callback, user_data, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Operation not supported (bug in backend)"); g_simple_async_result_complete (simple); g_object_unref (simple); } else { klass->check_authorization (authority, caller, subject, action_id, details, flags, cancellable, callback, user_data); } } /** * polkit_backend_authority_check_authorization_finish: * @authority: A #PolkitBackendAuthority. * @res: A #GAsyncResult obtained from the callback. * @error: Return location for error or %NULL. * * Finishes checking if a subject is authorized for an action. * * Returns: A #PolkitAuthorizationResult or %NULL if @error is set. Free with g_object_unref(). **/ PolkitAuthorizationResult * polkit_backend_authority_check_authorization_finish (PolkitBackendAuthority *authority, GAsyncResult *res, GError **error) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->check_authorization_finish == NULL) { g_warning ("check_authorization_finish is not implemented (it is not optional)"); g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); return NULL; } else { return klass->check_authorization_finish (authority, res, error); } } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_backend_authority_register_authentication_agent: * @authority: A #PolkitBackendAuthority. * @caller: The system bus name that initiated the query. * @subject: The subject the authentication agent wants to register for. * @locale: The locale of the authentication agent. * @object_path: The object path for the authentication agent. * @options: A #GVariant with options or %NULL. * @error: Return location for error or %NULL. * * Registers an authentication agent. * * Returns: %TRUE if the authentication agent was successfully registered, %FALSE if @error is set. **/ gboolean polkit_backend_authority_register_authentication_agent (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GError **error) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->register_authentication_agent == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Operation not supported"); return FALSE; } else { return klass->register_authentication_agent (authority, caller, subject, locale, object_path, options, error); } } /** * polkit_backend_authority_unregister_authentication_agent: * @authority: A #PolkitBackendAuthority. * @caller: The system bus name that initiated the query. * @subject: The subject the agent claims to be registered at. * @object_path: The object path that the authentication agent is registered at. * @error: Return location for error or %NULL. * * Unregisters an authentication agent. * * Returns: %TRUE if the authentication agent was successfully unregistered, %FALSE if @error is set. **/ gboolean polkit_backend_authority_unregister_authentication_agent (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *object_path, GError **error) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->unregister_authentication_agent == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Operation not supported"); return FALSE; } else { return klass->unregister_authentication_agent (authority, caller, subject, object_path, error); } } /** * polkit_backend_authority_authentication_agent_response: * @authority: A #PolkitBackendAuthority. * @caller: The system bus name that initiated the query. * @uid: The real UID of the registered agent, or (uid_t)-1 if unknown. * @cookie: The cookie passed to the authentication agent from the authority. * @identity: The identity that was authenticated. * @error: Return location for error or %NULL. * * Provide response that @identity successfully authenticated for the * authentication request identified by @cookie. * * Returns: %TRUE if @authority acknowledged the call, %FALSE if @error is set. **/ gboolean polkit_backend_authority_authentication_agent_response (PolkitBackendAuthority *authority, PolkitSubject *caller, uid_t uid, const gchar *cookie, PolkitIdentity *identity, GError **error) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->authentication_agent_response == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Operation not supported"); return FALSE; } else { return klass->authentication_agent_response (authority, caller, uid, cookie, identity, error); } } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_backend_authority_enumerate_temporary_authorizations: * @authority: A #PolkitBackendAuthority. * @caller: The system bus name that initiated the query. * @subject: The subject to get temporary authorizations for. * @error: Return location for error. * * Gets temporary authorizations for @subject. * * Returns: A list of #PolkitTemporaryAuthorization objects or %NULL if @error is set. The returned list * should be freed with g_list_free() after each element have been freed with g_object_unref(). */ GList * polkit_backend_authority_enumerate_temporary_authorizations (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->enumerate_temporary_authorizations == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Operation not supported"); return NULL; } else { return klass->enumerate_temporary_authorizations (authority, caller, subject, error); } } /** * polkit_backend_authority_revoke_temporary_authorizations: * @authority: A #PolkitBackendAuthority. * @caller: The system bus name that initiated the query. * @subject: The subject to revoke temporary authorizations for. * @error: Return location for error. * * Revokes temporary authorizations for @subject. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. **/ gboolean polkit_backend_authority_revoke_temporary_authorizations (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->revoke_temporary_authorizations == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Operation not supported"); return FALSE; } else { return klass->revoke_temporary_authorizations (authority, caller, subject, error); } } /** * polkit_backend_authority_revoke_temporary_authorization_by_id: * @authority: A #PolkitBackendAuthority. * @caller: The system bus name that initiated the query. * @id: The opaque identifier of the temporary authorization. * @error: Return location for error. * * Revokes a temporary authorizations with opaque identifier @id. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. **/ gboolean polkit_backend_authority_revoke_temporary_authorization_by_id (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *id, GError **error) { PolkitBackendAuthorityClass *klass; klass = POLKIT_BACKEND_AUTHORITY_GET_CLASS (authority); if (klass->revoke_temporary_authorization_by_id == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Operation not supported"); return FALSE; } else { return klass->revoke_temporary_authorization_by_id (authority, caller, id, error); } } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { guint authority_registration_id; guint log_control_registration_id; GDBusNodeInfo *introspection_info; PolkitBackendAuthority *authority; GDBusConnection *connection; gulong authority_changed_id; gulong authority_session_monitor_signaller; gchar *object_path; GHashTable *cancellation_id_to_check_auth_data; } Server; static void server_free (Server *server) { g_free (server->object_path); if (server->authority_registration_id > 0) g_dbus_connection_unregister_object (server->connection, server->authority_registration_id); if (server->log_control_registration_id > 0) g_dbus_connection_unregister_object (server->connection, server->log_control_registration_id); if (server->connection != NULL) g_object_unref (server->connection); if (server->introspection_info != NULL) g_dbus_node_info_unref (server->introspection_info); if (server->authority != NULL && server->authority_changed_id > 0) g_signal_handler_disconnect (server->authority, server->authority_changed_id); if (server->authority != NULL && server->authority_session_monitor_signaller > 0) g_signal_handler_disconnect (server->authority, server->authority_session_monitor_signaller); if (server->cancellation_id_to_check_auth_data != NULL) g_hash_table_unref (server->cancellation_id_to_check_auth_data); g_object_unref (server->authority); g_free (server); } static void changed_dbus_call_handler(PolkitBackendAuthority *authority, gpointer user_data, guint16 msg_mask) { Server *server = user_data; GError *error; GVariant *parameters; error = NULL; parameters = g_variant_new("(q)", msg_mask); if (!g_dbus_connection_emit_signal (server->connection, NULL, /* destination bus name */ server->object_path, "org.freedesktop.PolicyKit1.Authority", "Changed", parameters, &error)) { g_warning ("Error emitting Changed() signal: %s", error->message); g_error_free (error); } } static void on_authority_changed (PolkitBackendAuthority *authority, gpointer user_data) { guint16 msg_mask; msg_mask = (guint16) CHANGED_SIGNAL; changed_dbus_call_handler(authority, user_data, msg_mask); } static void on_sessions_changed (PolkitBackendAuthority *authority, gpointer user_data) { guint16 msg_mask; msg_mask = (guint16) SESSIONS_CHANGED_SIGNAL; changed_dbus_call_handler(authority, user_data, msg_mask); } static const gchar *server_introspection_data = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_enumerate_actions (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { GVariantBuilder builder; GError *error; GList *actions; GList *l; const gchar *locale; actions = NULL; g_variant_get (parameters, "(&s)", &locale); error = NULL; actions = polkit_backend_authority_enumerate_actions (server->authority, caller, locale, &error); if (error != NULL) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssssssuuua{ss})")); for (l = actions; l != NULL; l = l->next) { PolkitActionDescription *ad = POLKIT_ACTION_DESCRIPTION (l->data); g_variant_builder_add_value (&builder, polkit_action_description_to_gvariant (ad)); /* A floating value */ } g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(ssssssuuua{ss}))", &builder)); out: g_list_foreach (actions, (GFunc) g_object_unref, NULL); g_list_free (actions); } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { GDBusMethodInvocation *invocation; Server *server; PolkitSubject *caller; PolkitSubject *subject; GCancellable *cancellable; gchar *cancellation_id; } CheckAuthData; static void check_auth_data_free (CheckAuthData *data) { if (data->invocation != NULL) g_object_unref (data->invocation); if (data->caller != NULL) g_object_unref (data->caller); if (data->subject != NULL) g_object_unref (data->subject); if (data->cancellable != NULL) g_object_unref (data->cancellable); g_free (data->cancellation_id); g_free (data); } static void check_auth_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { CheckAuthData *data = user_data; PolkitAuthorizationResult *result; GError *error; error = NULL; result = polkit_backend_authority_check_authorization_finish (POLKIT_BACKEND_AUTHORITY (source_object), res, &error); if (data->cancellation_id != NULL) g_hash_table_remove (data->server->cancellation_id_to_check_auth_data, data->cancellation_id); if (error != NULL) { g_dbus_method_invocation_return_gerror (data->invocation, error); g_error_free (error); } else { g_dbus_method_invocation_return_value (data->invocation, g_variant_new ("(@(bba{ss}))", polkit_authorization_result_to_gvariant (result))); /* A floating value */ g_object_unref (result); } check_auth_data_free (data); } static void server_handle_check_authorization (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { GVariant *subject_gvariant; const gchar *action_id; GVariant *details_gvariant; guint32 flags; const gchar *cancellation_id; GError *error; PolkitSubject *subject; PolkitDetails *details; subject = NULL; details = NULL; g_variant_get (parameters, "(@(sa{sv})&s@a{ss}u&s)", &subject_gvariant, &action_id, &details_gvariant, &flags, &cancellation_id); error = NULL; subject = polkit_subject_new_for_gvariant_invocation (subject_gvariant, invocation, &error); if (subject == NULL) { g_prefix_error (&error, "Error getting subject: "); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } details = polkit_details_new_for_gvariant (details_gvariant); CheckAuthData *data; data = g_new0 (CheckAuthData, 1); data->server = server; data->caller = g_object_ref (caller); data->subject = g_object_ref (subject); data->invocation = g_object_ref (invocation); if (strlen (cancellation_id) > 0) { data->cancellation_id = g_strdup_printf ("%s-%s", g_dbus_method_invocation_get_sender (invocation), cancellation_id); if (g_hash_table_lookup (server->cancellation_id_to_check_auth_data, data->cancellation_id) != NULL) { gchar *message; message = g_strdup_printf ("Given cancellation_id %s is already in use for name %s", cancellation_id, g_dbus_method_invocation_get_sender (invocation)); /* Don't want this error in our GError enum since libpolkit-gobject-1 users will never see it */ g_dbus_method_invocation_return_dbus_error (invocation, "org.freedesktop.PolicyKit1.Error.CancellationIdNotUnique", message); g_free (message); check_auth_data_free (data); goto out; } data->cancellable = g_cancellable_new (); g_hash_table_insert (server->cancellation_id_to_check_auth_data, data->cancellation_id, data); } polkit_backend_authority_check_authorization (server->authority, caller, subject, action_id, details, flags, data->cancellable, check_auth_cb, data); out: g_variant_unref (subject_gvariant); g_variant_unref (details_gvariant); if (details != NULL) g_object_unref (details); if (subject != NULL) g_object_unref (subject); } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_cancel_check_authorization (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { CheckAuthData *data; const gchar *cancellation_id; gchar *full_cancellation_id; g_variant_get (parameters, "(&s)", &cancellation_id); full_cancellation_id = g_strdup_printf ("%s-%s", g_dbus_method_invocation_get_sender (invocation), cancellation_id); data = g_hash_table_lookup (server->cancellation_id_to_check_auth_data, full_cancellation_id); if (data == NULL) { g_dbus_method_invocation_return_error (invocation, POLKIT_ERROR, POLKIT_ERROR_FAILED, "No such cancellation_id `%s' for name %s", cancellation_id, g_dbus_method_invocation_get_sender (invocation)); goto out; } g_cancellable_cancel (data->cancellable); g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); out: g_free (full_cancellation_id); } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_register_authentication_agent (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { GVariant *subject_gvariant; GError *error; PolkitSubject *subject; const gchar *locale; const gchar *object_path; subject = NULL; g_variant_get (parameters, "(@(sa{sv})&s&s)", &subject_gvariant, &locale, &object_path); error = NULL; subject = polkit_subject_new_for_gvariant (subject_gvariant, &error); if (subject == NULL) { g_prefix_error (&error, "Error getting subject: "); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } error = NULL; if (!polkit_backend_authority_register_authentication_agent (server->authority, caller, subject, locale, object_path, NULL, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); out: g_variant_unref (subject_gvariant); if (subject != NULL) g_object_unref (subject); } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_register_authentication_agent_with_options (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { GVariant *subject_gvariant; GError *error; PolkitSubject *subject; const gchar *locale; const gchar *object_path; GVariant *options; subject = NULL; g_variant_get (parameters, "(@(sa{sv})&s&s@a{sv})", &subject_gvariant, &locale, &object_path, &options); error = NULL; subject = polkit_subject_new_for_gvariant (subject_gvariant, &error); if (subject == NULL) { g_prefix_error (&error, "Error getting subject: "); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } error = NULL; if (!polkit_backend_authority_register_authentication_agent (server->authority, caller, subject, locale, object_path, options, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); out: g_variant_unref (subject_gvariant); if (options != NULL) g_variant_unref (options); if (subject != NULL) g_object_unref (subject); } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_unregister_authentication_agent (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { GVariant *subject_gvariant; GError *error; PolkitSubject *subject; const gchar *object_path; subject = NULL; g_variant_get (parameters, "(@(sa{sv})&s)", &subject_gvariant, &object_path); error = NULL; subject = polkit_subject_new_for_gvariant (subject_gvariant, &error); if (subject == NULL) { g_prefix_error (&error, "Error getting subject: "); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } error = NULL; if (!polkit_backend_authority_unregister_authentication_agent (server->authority, caller, subject, object_path, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); out: g_variant_unref (subject_gvariant); if (subject != NULL) g_object_unref (subject); } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_authentication_agent_response (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { const gchar *cookie; GVariant *identity_gvariant; PolkitIdentity *identity; GError *error; identity = NULL; g_variant_get (parameters, "(&s@(sa{sv}))", &cookie, &identity_gvariant); error = NULL; identity = polkit_identity_new_for_gvariant (identity_gvariant, &error); if (identity == NULL) { g_prefix_error (&error, "Error getting identity: "); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } error = NULL; if (!polkit_backend_authority_authentication_agent_response (server->authority, caller, (uid_t)-1, cookie, identity, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); out: g_variant_unref (identity_gvariant); if (identity != NULL) g_object_unref (identity); } static void server_handle_authentication_agent_response2 (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { const gchar *cookie; GVariant *identity_gvariant; PolkitIdentity *identity; GError *error; guint32 uid; identity = NULL; g_variant_get (parameters, "(u&s@(sa{sv}))", &uid, &cookie, &identity_gvariant); error = NULL; identity = polkit_identity_new_for_gvariant (identity_gvariant, &error); if (identity == NULL) { g_prefix_error (&error, "Error getting identity: "); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } error = NULL; if (!polkit_backend_authority_authentication_agent_response (server->authority, caller, (uid_t)uid, cookie, identity, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); out: g_variant_unref (identity_gvariant); if (identity != NULL) g_object_unref (identity); } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_enumerate_temporary_authorizations (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { GVariant *subject_gvariant; GError *error; PolkitSubject *subject; GList *authorizations; GList *l; GVariantBuilder builder; subject = NULL; g_variant_get (parameters, "(@(sa{sv}))", &subject_gvariant); error = NULL; subject = polkit_subject_new_for_gvariant (subject_gvariant, &error); if (subject == NULL) { g_prefix_error (&error, "Error getting subject: "); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } error = NULL; authorizations = polkit_backend_authority_enumerate_temporary_authorizations (server->authority, caller, subject, &error); if (error != NULL) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss(sa{sv})tt)")); for (l = authorizations; l != NULL; l = l->next) { PolkitTemporaryAuthorization *a = POLKIT_TEMPORARY_AUTHORIZATION (l->data); g_variant_builder_add_value (&builder, polkit_temporary_authorization_to_gvariant (a)); /* A floating value */ } g_list_foreach (authorizations, (GFunc) g_object_unref, NULL); g_list_free (authorizations); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(ss(sa{sv})tt))", &builder)); out: g_variant_unref (subject_gvariant); if (subject != NULL) g_object_unref (subject); } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_revoke_temporary_authorizations (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { GVariant *subject_gvariant; GError *error; PolkitSubject *subject; subject = NULL; g_variant_get (parameters, "(@(sa{sv}))", &subject_gvariant); error = NULL; subject = polkit_subject_new_for_gvariant (subject_gvariant, &error); if (subject == NULL) { g_prefix_error (&error, "Error getting subject: "); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } error = NULL; if (!polkit_backend_authority_revoke_temporary_authorizations (server->authority, caller, subject, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); out: g_variant_unref (subject_gvariant); if (subject != NULL) g_object_unref (subject); } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_revoke_temporary_authorization_by_id (Server *server, GVariant *parameters, PolkitSubject *caller, GDBusMethodInvocation *invocation) { GError *error; const gchar *id; g_variant_get (parameters, "(&s)", &id); error = NULL; if (!polkit_backend_authority_revoke_temporary_authorization_by_id (server->authority, caller, id, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); goto out; } g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); out: ; } /* ---------------------------------------------------------------------------------------------------- */ static void server_handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { Server *server = user_data; PolkitSubject *caller; caller = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation)); if (g_strcmp0 (method_name, "EnumerateActions") == 0) server_handle_enumerate_actions (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "CheckAuthorization") == 0) server_handle_check_authorization (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "CancelCheckAuthorization") == 0) server_handle_cancel_check_authorization (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "RegisterAuthenticationAgent") == 0) server_handle_register_authentication_agent (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "RegisterAuthenticationAgentWithOptions") == 0) server_handle_register_authentication_agent_with_options (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "UnregisterAuthenticationAgent") == 0) server_handle_unregister_authentication_agent (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "AuthenticationAgentResponse") == 0) server_handle_authentication_agent_response (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "AuthenticationAgentResponse2") == 0) server_handle_authentication_agent_response2 (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "EnumerateTemporaryAuthorizations") == 0) server_handle_enumerate_temporary_authorizations (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "RevokeTemporaryAuthorizations") == 0) server_handle_revoke_temporary_authorizations (server, parameters, caller, invocation); else if (g_strcmp0 (method_name, "RevokeTemporaryAuthorizationById") == 0) server_handle_revoke_temporary_authorization_by_id (server, parameters, caller, invocation); else g_assert_not_reached (); g_object_unref (caller); } static GVariant * server_handle_get_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { Server *server = user_data; GVariant *result; result = NULL; if (g_strcmp0 (interface_name, "org.freedesktop.PolicyKit1.Authority") == 0) { if (g_strcmp0 (property_name, "BackendName") == 0) { result = g_variant_new_string (polkit_backend_authority_get_name (server->authority)); } else if (g_strcmp0 (property_name, "BackendVersion") == 0) { result = g_variant_new_string (polkit_backend_authority_get_version (server->authority)); } else if (g_strcmp0 (property_name, "BackendFeatures") == 0) { result = g_variant_new_uint32 (polkit_backend_authority_get_features (server->authority)); } else g_assert_not_reached (); } else if (g_strcmp0 (interface_name, "org.freedesktop.LogControl1") == 0) { if (g_strcmp0 (property_name, "LogLevel") == 0) { switch (polkit_authority_log_level) { case LOG_LEVEL_EMERG: result = g_variant_new_string ("emerg"); break; case LOG_LEVEL_ALERT: result = g_variant_new_string ("alert"); break; case LOG_LEVEL_CRIT: result = g_variant_new_string ("crit"); break; case LOG_LEVEL_ERROR: result = g_variant_new_string ("err"); break; case LOG_LEVEL_WARNING: result = g_variant_new_string ("warn"); break; case LOG_LEVEL_NOTICE: result = g_variant_new_string ("notice"); break; case LOG_LEVEL_INFO: result = g_variant_new_string ("info"); break; case LOG_LEVEL_DEBUG: result = g_variant_new_string ("debug"); break; } } else if (g_strcmp0 (property_name, "LogTarget") == 0) { result = g_variant_new_string ("syslog"); } else if (g_strcmp0 (property_name, "SyslogIdentifier") == 0) { result = g_variant_new_string ("polkitd"); } else g_assert_not_reached (); } return result; } static gboolean server_handle_set_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error, gpointer user_data) { PolkitSubject *caller_subject; PolkitUnixUser *caller_user; const gchar *level; if (g_strcmp0 (interface_name, "org.freedesktop.LogControl1") != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Only properties of org.freedesktop.LogControl1 can be modified"); return FALSE; } if (g_strcmp0 (property_name, "LogLevel") != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Only LogLevel can be modified"); return FALSE; } caller_subject = polkit_system_bus_name_new (sender); if (!caller_subject) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not obtain caller's credentials"); return FALSE; } caller_user = polkit_system_bus_name_get_user_sync (POLKIT_SYSTEM_BUS_NAME (caller_subject), NULL, error); if (!caller_user) { g_object_unref (caller_subject); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not obtain caller's credentials"); return FALSE; } if ((uid_t)polkit_unix_user_get_uid (caller_user) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, "Only root can change the log level"); g_object_unref (caller_user); g_object_unref (caller_subject); return FALSE; } g_variant_get (value, "&s", &level); polkit_backend_authority_set_log_level (level); g_object_unref (caller_user); g_object_unref (caller_subject); return TRUE; } /* ---------------------------------------------------------------------------------------------------- */ static const GDBusInterfaceVTable server_vtable = { server_handle_method_call, server_handle_get_property, NULL, /* server_handle_set_property */ }; static const GDBusInterfaceVTable logcontrol_vtable = { NULL, /* server_handle_method_call */ server_handle_get_property, server_handle_set_property, }; /** * polkit_backend_authority_unregister: * @registration_id: A #gpointer obtained from polkit_backend_authority_register(). * * Unregisters a #PolkitBackendAuthority registered with polkit_backend_authority_register(). */ void polkit_backend_authority_unregister (gpointer registration_id) { Server *server = registration_id; server_free (server); } /** * polkit_backend_authority_register: * @connection: The #GDBusConnection to register the authority on. * @authority: A #PolkitBackendAuthority. * @object_path: Object path of the authority. * @error: Return location for error. * * Registers @authority on a #GDBusConnection. * * Returns: A #gpointer that can be used with polkit_backend_authority_unregister() or %NULL if @error is set. */ gpointer polkit_backend_authority_register (PolkitBackendAuthority *authority, GDBusConnection *connection, const gchar *object_path, GError **error) { Server *server; server = g_new0 (Server, 1); server->cancellation_id_to_check_auth_data = g_hash_table_new (g_str_hash, g_str_equal); server->connection = g_object_ref (connection); server->object_path = g_strdup (object_path); server->introspection_info = g_dbus_node_info_new_for_xml (server_introspection_data, error); if (server->introspection_info == NULL) goto error; server->authority_registration_id = g_dbus_connection_register_object (server->connection, object_path, g_dbus_node_info_lookup_interface (server->introspection_info, "org.freedesktop.PolicyKit1.Authority"), &server_vtable, server, NULL, error); if (server->authority_registration_id == 0) { goto error; } server->log_control_registration_id = g_dbus_connection_register_object (server->connection, "/org/freedesktop/LogControl1", g_dbus_node_info_lookup_interface (server->introspection_info, "org.freedesktop.LogControl1"), &logcontrol_vtable, server, NULL, error); if (server->log_control_registration_id == 0) { goto error; } server->authority = g_object_ref (authority); server->authority_changed_id = g_signal_connect (server->authority, "changed", G_CALLBACK (on_authority_changed), server); server->authority_session_monitor_signaller = g_signal_connect (server->authority, "sessions-changed", G_CALLBACK (on_sessions_changed), server); return server; error: server_free (server); return NULL; } /** * polkit_backend_authority_get: * * Gets the #PolkitBackendAuthority to use. * * Returns: A #PolkitBackendAuthority. Free with g_object_unref(). */ PolkitBackendAuthority * polkit_backend_authority_get (void) { PolkitBackendAuthority *authority; /* TODO: move to polkitd/main.c */ /* Announce that we've started in the generic log */ openlog ("polkitd", LOG_PID, LOG_DAEMON); /* system daemons without separate facility value */ syslog (LOG_INFO, "Started polkitd version %s", VERSION); closelog (); /* then start logging to the secure log */ openlog ("polkitd", LOG_PID, LOG_AUTHPRIV); /* security/authorization messages (private) */ authority = POLKIT_BACKEND_AUTHORITY (g_object_new (POLKIT_BACKEND_TYPE_JS_AUTHORITY, NULL)); return authority; } /* ---------------------------------------------------------------------------------------------------- */ typedef enum { _COLOR_RESET, _COLOR_BOLD_ON, _COLOR_INVERSE_ON, _COLOR_BOLD_OFF, _COLOR_FG_BLACK, _COLOR_FG_RED, _COLOR_FG_GREEN, _COLOR_FG_YELLOW, _COLOR_FG_BLUE, _COLOR_FG_MAGENTA, _COLOR_FG_CYAN, _COLOR_FG_WHITE, _COLOR_BG_RED, _COLOR_BG_GREEN, _COLOR_BG_YELLOW, _COLOR_BG_BLUE, _COLOR_BG_MAGENTA, _COLOR_BG_CYAN, _COLOR_BG_WHITE } _Color; static gboolean _color_stdin_is_tty = FALSE; static gboolean _color_initialized = FALSE; static void _color_init (void) { if (_color_initialized) return; _color_initialized = TRUE; _color_stdin_is_tty = (isatty (STDIN_FILENO) != 0 && isatty (STDOUT_FILENO) != 0); } static const gchar * _color_get (_Color color) { const gchar *str; _color_init (); if (!_color_stdin_is_tty) return ""; str = NULL; switch (color) { case _COLOR_RESET: str="\x1b[0m"; break; case _COLOR_BOLD_ON: str="\x1b[1m"; break; case _COLOR_INVERSE_ON: str="\x1b[7m"; break; case _COLOR_BOLD_OFF: str="\x1b[22m"; break; case _COLOR_FG_BLACK: str="\x1b[30m"; break; case _COLOR_FG_RED: str="\x1b[31m"; break; case _COLOR_FG_GREEN: str="\x1b[32m"; break; case _COLOR_FG_YELLOW: str="\x1b[33m"; break; case _COLOR_FG_BLUE: str="\x1b[34m"; break; case _COLOR_FG_MAGENTA: str="\x1b[35m"; break; case _COLOR_FG_CYAN: str="\x1b[36m"; break; case _COLOR_FG_WHITE: str="\x1b[37m"; break; case _COLOR_BG_RED: str="\x1b[41m"; break; case _COLOR_BG_GREEN: str="\x1b[42m"; break; case _COLOR_BG_YELLOW: str="\x1b[43m"; break; case _COLOR_BG_BLUE: str="\x1b[44m"; break; case _COLOR_BG_MAGENTA: str="\x1b[45m"; break; case _COLOR_BG_CYAN: str="\x1b[46m"; break; case _COLOR_BG_WHITE: str="\x1b[47m"; break; default: g_assert_not_reached (); break; } return str; } /* ---------------------------------------------------------------------------------------------------- */ G_GNUC_PRINTF(3, 4) void polkit_backend_authority_log (PolkitBackendAuthority *authority, const guint message_log_level, const gchar *format, ...) { guint64 now; time_t now_time; struct tm *now_tm; gchar time_buf[128]; gchar *message; va_list var_args; if (message_log_level > polkit_authority_log_level) { return; } g_return_if_fail (POLKIT_BACKEND_IS_AUTHORITY (authority)); va_start (var_args, format); message = g_strdup_vprintf (format, var_args); va_end (var_args); syslog (message_log_level, "%s", message); now = g_get_real_time (); now_time = (time_t) now / G_TIME_SPAN_SECOND; now_tm = localtime (&now_time); strftime (time_buf, sizeof time_buf, "%H:%M:%S", now_tm); g_print ("%s%s%s.%03d%s: %s\n", _color_get (_COLOR_BOLD_ON), _color_get (_COLOR_FG_YELLOW), time_buf, (gint) (now % G_TIME_SPAN_SECOND / G_TIME_SPAN_MILLISECOND), _color_get (_COLOR_RESET), message); g_free (message); } void polkit_backend_authority_set_log_level (const gchar *level) { /* Match syslog names so that they are the same across journalct, systemctl * et al, but also accept more readable aliases for abbreviated levels. */ if (g_strcmp0 (level, "debug") == 0) polkit_authority_log_level = (guint) LOG_LEVEL_DEBUG; else if (g_strcmp0 (level, "info") == 0) polkit_authority_log_level = (guint) LOG_LEVEL_INFO; else if (g_strcmp0 (level, "notice") == 0) polkit_authority_log_level = (guint) LOG_LEVEL_NOTICE; else if (g_strcmp0 (level, "warning") == 0) polkit_authority_log_level = (guint) LOG_LEVEL_WARNING; else if (g_strcmp0 (level, "err") == 0 || g_strcmp0 (level, "error") == 0) polkit_authority_log_level = (guint) LOG_LEVEL_ERROR; else if (g_strcmp0 (level, "crit") == 0 || g_strcmp0 (level, "critical") == 0) polkit_authority_log_level = (guint) LOG_LEVEL_CRIT; else if (g_strcmp0 (level, "alert") == 0) polkit_authority_log_level = (guint) LOG_LEVEL_ALERT; else if (g_strcmp0 (level, "emerg") == 0 || g_strcmp0 (level, "emergency") == 0) polkit_authority_log_level = (guint) LOG_LEVEL_EMERG; } polkit-126/src/polkitbackend/polkitbackendauthority.h000066400000000000000000000372431474122443600232430ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_BACKEND_AUTHORITY_H #define __POLKIT_BACKEND_AUTHORITY_H #include #include #include G_BEGIN_DECLS #define POLKIT_BACKEND_TYPE_AUTHORITY (polkit_backend_authority_get_type ()) #define POLKIT_BACKEND_AUTHORITY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_BACKEND_TYPE_AUTHORITY, PolkitBackendAuthority)) #define POLKIT_BACKEND_AUTHORITY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), POLKIT_BACKEND_TYPE_AUTHORITY, PolkitBackendAuthorityClass)) #define POLKIT_BACKEND_AUTHORITY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_BACKEND_TYPE_AUTHORITY,PolkitBackendAuthorityClass)) #define POLKIT_BACKEND_IS_AUTHORITY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_BACKEND_TYPE_AUTHORITY)) #define POLKIT_BACKEND_IS_AUTHORITY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_BACKEND_TYPE_AUTHORITY)) typedef struct _PolkitBackendAuthorityClass PolkitBackendAuthorityClass; /** * PolkitBackendAuthority: * * The #PolkitBackendAuthority struct should not be accessed directly. */ struct _PolkitBackendAuthority { GObject parent_instance; }; /** * Log levels aligned with those used in syslog and LogControl */ enum { LOG_LEVEL_EMERG, LOG_LEVEL_ALERT, LOG_LEVEL_CRIT, LOG_LEVEL_ERROR, LOG_LEVEL_WARNING, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG }; /** * PolkitBackendAuthorityClass: * @parent_class: The parent class. * @get_name: Function pointer for the polkit_backend_authority_get_name() function. * @get_version: Function pointer for the polkit_backend_authority_get_version() function. * @get_features: Function pointer for the polkit_backend_authority_get_features() function. * @changed: Function pointer for #PolkitBackendAuthority::changed signal. * @enumerate_actions: Enumerates registered actions on the * system. See polkit_backend_authority_enumerate_actions() for * details. * @check_authorization: Called to initiate an asynchronous * authorization check. See * polkit_backend_authority_check_authorization() for details. * @check_authorization_finish: Called when finishing an authorization * check. See polkit_backend_authority_check_authorization_finish() * for details. * @register_authentication_agent: Called when an authentication agent * is attempting to register or %NULL if the backend doesn't support * the operation. See * polkit_backend_authority_register_authentication_agent() for * details. * @unregister_authentication_agent: Called when an authentication * agent is attempting to unregister or %NULL if the backend doesn't * support the operation. See * polkit_backend_authority_unregister_authentication_agent() for * details. * @authentication_agent_response: Called by an authentication agent * when the user successfully authenticates or %NULL if the backend * doesn't support the operation. See * polkit_backend_authority_authentication_agent_response() for * details. * @enumerate_temporary_authorizations: Called to enumerate temporary * authorizations or %NULL if the backend doesn't support the operation. * See polkit_backend_authority_enumerate_temporary_authorizations() * for details. * @revoke_temporary_authorizations: Called to revoke temporary * authorizations or %NULL if the backend doesn't support the operation. * See polkit_backend_authority_revoke_temporary_authorizations() * for details. * @revoke_temporary_authorization_by_id: Called to revoke a temporary * authorization identified by id or %NULL if the backend doesn't support * the operation. See polkit_backend_authority_revoke_temporary_authorization_by_id() * for details. * * Class structure for #PolkitBackendAuthority. */ struct _PolkitBackendAuthorityClass { /*< public >*/ GObjectClass parent_class; /* Signals */ void (*changed) (PolkitBackendAuthority *authority); /* VTable */ const gchar *(*get_name) (PolkitBackendAuthority *authority); const gchar *(*get_version) (PolkitBackendAuthority *authority); PolkitAuthorityFeatures (*get_features) (PolkitBackendAuthority *authority); GList *(*enumerate_actions) (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *locale, GError **error); void (*check_authorization) (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); PolkitAuthorizationResult * (*check_authorization_finish) (PolkitBackendAuthority *authority, GAsyncResult *res, GError **error); gboolean (*register_authentication_agent) (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GError **error); gboolean (*unregister_authentication_agent) (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *object_path, GError **error); gboolean (*authentication_agent_response) (PolkitBackendAuthority *authority, PolkitSubject *caller, uid_t uid, const gchar *cookie, PolkitIdentity *identity, GError **error); GList *(*enumerate_temporary_authorizations) (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error); gboolean (*revoke_temporary_authorizations) (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error); gboolean (*revoke_temporary_authorization_by_id) (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *id, GError **error); /*< private >*/ /* Padding for future expansion */ void (*_polkit_reserved1) (void); void (*_polkit_reserved2) (void); void (*_polkit_reserved3) (void); void (*_polkit_reserved4) (void); void (*_polkit_reserved5) (void); void (*_polkit_reserved6) (void); void (*_polkit_reserved7) (void); void (*_polkit_reserved8) (void); void (*_polkit_reserved9) (void); void (*_polkit_reserved10) (void); void (*_polkit_reserved11) (void); void (*_polkit_reserved12) (void); void (*_polkit_reserved13) (void); void (*_polkit_reserved14) (void); void (*_polkit_reserved15) (void); void (*_polkit_reserved16) (void); void (*_polkit_reserved17) (void); void (*_polkit_reserved18) (void); void (*_polkit_reserved19) (void); void (*_polkit_reserved20) (void); void (*_polkit_reserved21) (void); void (*_polkit_reserved22) (void); void (*_polkit_reserved23) (void); void (*_polkit_reserved24) (void); void (*_polkit_reserved25) (void); void (*_polkit_reserved26) (void); void (*_polkit_reserved27) (void); void (*_polkit_reserved28) (void); void (*_polkit_reserved29) (void); void (*_polkit_reserved30) (void); void (*_polkit_reserved31) (void); void (*_polkit_reserved32) (void); }; GType polkit_backend_authority_get_type (void) G_GNUC_CONST; /* --- */ const gchar *polkit_backend_authority_get_name (PolkitBackendAuthority *authority); const gchar *polkit_backend_authority_get_version (PolkitBackendAuthority *authority); PolkitAuthorityFeatures polkit_backend_authority_get_features (PolkitBackendAuthority *authority); void polkit_backend_authority_log (PolkitBackendAuthority *authority, const guint message_log_level, const gchar *format, ...); void polkit_backend_authority_set_log_level (const gchar *level); GList *polkit_backend_authority_enumerate_actions (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *locale, GError **error); void polkit_backend_authority_check_authorization (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); PolkitAuthorizationResult *polkit_backend_authority_check_authorization_finish (PolkitBackendAuthority *authority, GAsyncResult *res, GError **error); gboolean polkit_backend_authority_register_authentication_agent (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GError **error); gboolean polkit_backend_authority_unregister_authentication_agent (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *object_path, GError **error); gboolean polkit_backend_authority_authentication_agent_response (PolkitBackendAuthority *authority, PolkitSubject *caller, uid_t uid, const gchar *cookie, PolkitIdentity *identity, GError **error); GList *polkit_backend_authority_enumerate_temporary_authorizations (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error); gboolean polkit_backend_authority_revoke_temporary_authorizations (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error); gboolean polkit_backend_authority_revoke_temporary_authorization_by_id (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *id, GError **error); /* --- */ PolkitBackendAuthority *polkit_backend_authority_get (void); gpointer polkit_backend_authority_register (PolkitBackendAuthority *authority, GDBusConnection *connection, const gchar *object_path, GError **error); void polkit_backend_authority_unregister (gpointer registration_id); G_END_DECLS #endif /* __POLKIT_BACKEND_AUTHORITY_H */ polkit-126/src/polkitbackend/polkitbackendcommon.c000066400000000000000000000530071474122443600224720ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include "polkitbackendcommon.h" static void utils_child_watch_from_release_cb (GPid pid, gint status, gpointer user_data) { } static void utils_spawn_data_free (UtilsSpawnData *data) { if (data->timeout_source != NULL) { g_source_destroy (data->timeout_source); data->timeout_source = NULL; } /* Nuke the child, if necessary */ if (data->child_watch_source != NULL) { g_source_destroy (data->child_watch_source); data->child_watch_source = NULL; } if (data->child_pid != 0) { GSource *source; kill (data->child_pid, SIGTERM); /* OK, we need to reap for the child ourselves - we don't want * to use waitpid() because that might block the calling * thread (the child might handle SIGTERM and use several * seconds for cleanup/rollback). * * So we use GChildWatch instead. * * Avoid taking a references to ourselves. but note that we need * to pass the GSource so we can nuke it once handled. */ source = g_child_watch_source_new (data->child_pid); g_source_set_callback (source, (GSourceFunc) utils_child_watch_from_release_cb, source, (GDestroyNotify) g_source_destroy); g_source_attach (source, data->main_context); g_source_unref (source); data->child_pid = 0; } if (data->child_stdout != NULL) { g_string_free (data->child_stdout, TRUE); data->child_stdout = NULL; } if (data->child_stderr != NULL) { g_string_free (data->child_stderr, TRUE); data->child_stderr = NULL; } if (data->child_stdout_channel != NULL) { g_io_channel_unref (data->child_stdout_channel); data->child_stdout_channel = NULL; } if (data->child_stderr_channel != NULL) { g_io_channel_unref (data->child_stderr_channel); data->child_stderr_channel = NULL; } if (data->child_stdout_source != NULL) { g_source_destroy (data->child_stdout_source); data->child_stdout_source = NULL; } if (data->child_stderr_source != NULL) { g_source_destroy (data->child_stderr_source); data->child_stderr_source = NULL; } if (data->child_stdout_fd != -1) { g_warn_if_fail (close (data->child_stdout_fd) == 0); data->child_stdout_fd = -1; } if (data->child_stderr_fd != -1) { g_warn_if_fail (close (data->child_stderr_fd) == 0); data->child_stderr_fd = -1; } if (data->cancellable_handler_id > 0) { g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id); data->cancellable_handler_id = 0; } if (data->main_context != NULL) g_main_context_unref (data->main_context); if (data->cancellable != NULL) g_object_unref (data->cancellable); g_slice_free (UtilsSpawnData, data); } /* called in the thread where @cancellable was cancelled */ static void utils_on_cancelled (GCancellable *cancellable, gpointer user_data) { UtilsSpawnData *data = (UtilsSpawnData *)user_data; GError *error; error = NULL; g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error)); g_simple_async_result_take_error (data->simple, error); g_simple_async_result_complete_in_idle (data->simple); g_object_unref (data->simple); } static gboolean utils_timeout_cb (gpointer user_data) { UtilsSpawnData *data = (UtilsSpawnData *)user_data; data->timed_out = TRUE; /* ok, timeout is history, make sure we don't free it in spawn_data_free() */ data->timeout_source = NULL; /* we're done */ g_simple_async_result_complete_in_idle (data->simple); g_object_unref (data->simple); return FALSE; /* remove source */ } static void utils_child_watch_cb (GPid pid, gint status, gpointer user_data) { UtilsSpawnData *data = (UtilsSpawnData *)user_data; gchar *buf; gsize buf_size; if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL) { g_string_append_len (data->child_stdout, buf, buf_size); } g_free (buf); if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL) { g_string_append_len (data->child_stderr, buf, buf_size); } g_free (buf); data->exit_status = status; /* ok, child watch is history, make sure we don't free it in spawn_data_free() */ data->child_pid = 0; data->child_watch_source = NULL; /* we're done */ g_simple_async_result_complete_in_idle (data->simple); g_object_unref (data->simple); } static gboolean utils_read_child_stderr (GIOChannel *channel, GIOCondition condition, gpointer user_data) { UtilsSpawnData *data = (UtilsSpawnData *)user_data; gchar buf[1024]; gsize bytes_read; g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL); g_string_append_len (data->child_stderr, buf, bytes_read); return TRUE; } static gboolean utils_read_child_stdout (GIOChannel *channel, GIOCondition condition, gpointer user_data) { UtilsSpawnData *data = (UtilsSpawnData *)user_data; gchar buf[1024]; gsize bytes_read; g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL); g_string_append_len (data->child_stdout, buf, bytes_read); return TRUE; } void polkit_backend_common_spawn (const gchar *const *argv, guint timeout_seconds, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { UtilsSpawnData *data; GError *error; data = g_slice_new0 (UtilsSpawnData); data->timeout_seconds = timeout_seconds; data->simple = g_simple_async_result_new (NULL, callback, user_data, (gpointer*)polkit_backend_common_spawn); data->main_context = g_main_context_get_thread_default (); if (data->main_context != NULL) g_main_context_ref (data->main_context); data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL; data->child_stdout = g_string_new (NULL); data->child_stderr = g_string_new (NULL); data->child_stdout_fd = -1; data->child_stderr_fd = -1; /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */ g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free); error = NULL; if (data->cancellable != NULL) { /* could already be cancelled */ error = NULL; if (g_cancellable_set_error_if_cancelled (data->cancellable, &error)) { g_simple_async_result_take_error (data->simple, error); g_simple_async_result_complete_in_idle (data->simple); g_object_unref (data->simple); goto out; } data->cancellable_handler_id = g_cancellable_connect (data->cancellable, G_CALLBACK (utils_on_cancelled), data, NULL); } error = NULL; if (!g_spawn_async_with_pipes (NULL, /* working directory */ (gchar **) argv, NULL, /* envp */ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, /* child_setup */ NULL, /* child_setup's user_data */ &(data->child_pid), NULL, /* gint *stdin_fd */ &(data->child_stdout_fd), &(data->child_stderr_fd), &error)) { g_prefix_error (&error, "Error spawning: "); g_simple_async_result_take_error (data->simple, error); g_simple_async_result_complete_in_idle (data->simple); g_object_unref (data->simple); goto out; } if (timeout_seconds > 0) { data->timeout_source = g_timeout_source_new_seconds (timeout_seconds); g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT); g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL); g_source_attach (data->timeout_source, data->main_context); g_source_unref (data->timeout_source); } data->child_watch_source = g_child_watch_source_new (data->child_pid); g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL); g_source_attach (data->child_watch_source, data->main_context); g_source_unref (data->child_watch_source); data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd); g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL); data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN); g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL); g_source_attach (data->child_stdout_source, data->main_context); g_source_unref (data->child_stdout_source); data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd); g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL); data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN); g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL); g_source_attach (data->child_stderr_source, data->main_context); g_source_unref (data->child_stderr_source); out: ; } void polkit_backend_common_on_dir_monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data) { PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data); /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution? * Because when editing a file with emacs we get 4-8 events.. */ if (file != NULL) { gchar *name; name = g_file_get_basename (file); /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */ if (!g_str_has_prefix (name, ".") && !g_str_has_prefix (name, "#") && g_str_has_suffix (name, ".rules") && (event_type == G_FILE_MONITOR_EVENT_CREATED || event_type == G_FILE_MONITOR_EVENT_DELETED || event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Reloading rules"); polkit_backend_common_reload_scripts (authority); } g_free (name); } } gboolean polkit_backend_common_spawn_finish (GAsyncResult *res, gint *out_exit_status, gchar **out_standard_output, gchar **out_standard_error, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); UtilsSpawnData *data; gboolean ret = FALSE; g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_backend_common_spawn); if (g_simple_async_result_propagate_error (simple, error)) goto out; data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple); if (data->timed_out) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "Timed out after %d seconds", data->timeout_seconds); goto out; } if (out_exit_status != NULL) *out_exit_status = data->exit_status; if (out_standard_output != NULL) *out_standard_output = g_strdup (data->child_stdout->str); if (out_standard_error != NULL) *out_standard_error = g_strdup (data->child_stderr->str); ret = TRUE; out: return ret; } static const gchar * polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority) { return "js"; } static const gchar * polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority) { return PACKAGE_VERSION; } static PolkitAuthorityFeatures polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority) { return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION; } void polkit_backend_common_js_authority_class_init_common (PolkitBackendJsAuthorityClass *klass) { GObjectClass *gobject_class; PolkitBackendAuthorityClass *authority_class; PolkitBackendInteractiveAuthorityClass *interactive_authority_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_backend_common_js_authority_finalize; gobject_class->set_property = polkit_backend_common_js_authority_set_property; gobject_class->constructed = polkit_backend_common_js_authority_constructed; authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass); authority_class->get_name = polkit_backend_js_authority_get_name; authority_class->get_version = polkit_backend_js_authority_get_version; authority_class->get_features = polkit_backend_js_authority_get_features; interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass); interactive_authority_class->get_admin_identities = polkit_backend_common_js_authority_get_admin_auth_identities; interactive_authority_class->check_authorization_sync = polkit_backend_common_js_authority_check_authorization_sync; g_object_class_install_property (gobject_class, PROP_RULES_DIRS, g_param_spec_boxed ("rules-dirs", NULL, NULL, G_TYPE_STRV, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); } gint polkit_backend_common_rules_file_name_cmp (const gchar *a, const gchar *b) { gint ret; const gchar *a_base; const gchar *b_base; a_base = strrchr (a, '/'); b_base = strrchr (b, '/'); g_assert (a_base != NULL); g_assert (b_base != NULL); a_base += 1; b_base += 1; ret = g_strcmp0 (a_base, b_base); if (ret == 0) { /* /etc wins over /usr */ ret = g_strcmp0 (a, b); g_assert (ret != 0); } return ret; } const gchar * polkit_backend_common_get_signal_name (gint signal_number) { switch (signal_number) { #define _HANDLE_SIG(sig) case sig: return #sig; _HANDLE_SIG (SIGHUP); _HANDLE_SIG (SIGINT); _HANDLE_SIG (SIGQUIT); _HANDLE_SIG (SIGILL); _HANDLE_SIG (SIGABRT); _HANDLE_SIG (SIGFPE); _HANDLE_SIG (SIGKILL); _HANDLE_SIG (SIGSEGV); _HANDLE_SIG (SIGPIPE); _HANDLE_SIG (SIGALRM); _HANDLE_SIG (SIGTERM); _HANDLE_SIG (SIGUSR1); _HANDLE_SIG (SIGUSR2); _HANDLE_SIG (SIGCHLD); _HANDLE_SIG (SIGCONT); _HANDLE_SIG (SIGSTOP); _HANDLE_SIG (SIGTSTP); _HANDLE_SIG (SIGTTIN); _HANDLE_SIG (SIGTTOU); _HANDLE_SIG (SIGBUS); #ifdef SIGPOLL _HANDLE_SIG (SIGPOLL); #endif _HANDLE_SIG (SIGPROF); _HANDLE_SIG (SIGSYS); _HANDLE_SIG (SIGTRAP); _HANDLE_SIG (SIGURG); _HANDLE_SIG (SIGVTALRM); _HANDLE_SIG (SIGXCPU); _HANDLE_SIG (SIGXFSZ); #undef _HANDLE_SIG default: break; } return "UNKNOWN_SIGNAL"; } void polkit_backend_common_spawn_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { SpawnData *data = (SpawnData *)user_data; data->res = (GAsyncResult*)g_object_ref (res); g_main_loop_quit (data->loop); } void polkit_backend_common_pidfd_to_systemd_unit (gint pidfd, gchar **ret_unit, gboolean *ret_no_new_privs) { static int cached_has_pidfd_support = -1; GError *error = NULL; GDBusConnection *connection = NULL; GMainContext *tmp_context = NULL; GVariant *result = NULL, *no_new_privs_result = NULL, *no_new_privis_value; GUnixFDList *fd_list = NULL; const char *unit_path, *unit; int fd_id; /* Try to lookup using a PIDFD, so that we do not have issues with PIDs being * recycled under our nose. For that we need both a kernel that supports the * PIDFD syscalls (no wrapper from glibc, so need to call it directly) and a * version of systemd with the new GetUnitByPIDFD method. If either are not * available, then return nothing, as we don't want to be open to PID recycle * attacks. */ g_assert (ret_unit != NULL); g_assert (ret_no_new_privs != NULL); if (pidfd < 0 || cached_has_pidfd_support == 0) return; connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (connection == NULL) { g_warning ("Error getting system bus: %s", error->message); goto out; } tmp_context = g_main_context_new (); g_main_context_push_thread_default (tmp_context); fd_list = g_unix_fd_list_new (); if (fd_list == NULL) goto out; fd_id = g_unix_fd_list_append (fd_list, pidfd, &error); if (fd_id < 0) { g_warning ("Error appending PID FD to fd list: %s", error->message); goto out; } result = g_dbus_connection_call_with_unix_fd_list_sync (connection, "org.freedesktop.systemd1", /* name */ "/org/freedesktop/systemd1", /* object path */ "org.freedesktop.systemd1.Manager", /* interface name */ "GetUnitByPIDFD", /* method */ g_variant_new ("(h)", fd_id), G_VARIANT_TYPE ("(osay)"), G_DBUS_CALL_FLAGS_NONE, -1, fd_list, NULL, NULL, &error); if (result == NULL) { if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) cached_has_pidfd_support = 0; g_warning ("Error calling GetUnitByPIDFD: %s", error->message); goto out; } g_variant_get (result, "(&o&say)", &unit_path, &unit, NULL); if (unit == NULL) goto out; /* Check for NoNewPrivileges property being set on the unit via D-Bus, and * return if it is not. This protects against PID changes, as if unset the * unit could use a setuid binary. It is only valid for service units. */ if (g_str_has_suffix (unit, ".service")) { no_new_privs_result = g_dbus_connection_call_sync (connection, "org.freedesktop.systemd1", /* name */ unit_path, /* object path */ "org.freedesktop.DBus.Properties", /* interface name */ "Get", /* method */ g_variant_new ("(ss)", "org.freedesktop.systemd1.Service", "NoNewPrivileges"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (no_new_privs_result == NULL) { g_warning ("Error calling Get on NoNewPrivileges property for unit %s: %s", unit, error->message); goto out; } g_variant_get (no_new_privs_result, "(v)", &no_new_privis_value); if (no_new_privis_value == NULL) { g_warning ("Error getting value for NoNewPrivileges property for unit %s", unit); goto out; } *ret_no_new_privs = g_variant_get_boolean (no_new_privis_value); } else *ret_no_new_privs = FALSE; *ret_unit = strdup (unit); if (!*ret_unit) { g_warning ("Failed to allocate memory for systemd unit ID"); goto out; } out: if (tmp_context) { g_main_context_pop_thread_default (tmp_context); g_main_context_unref (tmp_context); } if (connection != NULL) g_object_unref (connection); if (result != NULL) g_variant_unref (result); if (no_new_privs_result != NULL) g_variant_unref (no_new_privs_result); if (fd_list != NULL) g_object_unref (fd_list); if (error) g_error_free (error); } polkit-126/src/polkitbackend/polkitbackendcommon.h000066400000000000000000000157141474122443600225020ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_BACKEND_COMMON_H #define __POLKIT_BACKEND_COMMON_H #include #include #include #include #ifdef HAVE_NETGROUP_H #include #else #include #endif #include #include #include #include #include //here, all things glib via glib.h (including -> gspawn.h) #include #include "polkitbackendjsauthority.h" #include #ifdef HAVE_LIBSYSTEMD #include #endif /* HAVE_LIBSYSTEMD */ #define RUNAWAY_KILLER_TIMEOUT (15) #ifdef __cplusplus extern "C" { #endif enum { PROP_0, PROP_RULES_DIRS, }; typedef struct { GSimpleAsyncResult *simple; /* borrowed reference */ GMainContext *main_context; /* may be NULL */ GCancellable *cancellable; /* may be NULL */ gulong cancellable_handler_id; GPid child_pid; gint child_stdout_fd; gint child_stderr_fd; GIOChannel *child_stdout_channel; GIOChannel *child_stderr_channel; GSource *child_watch_source; GSource *child_stdout_source; GSource *child_stderr_source; guint timeout_seconds; gboolean timed_out; GSource *timeout_source; GString *child_stdout; GString *child_stderr; gint exit_status; } UtilsSpawnData; typedef struct { GMainLoop *loop; GAsyncResult *res; } SpawnData; void polkit_backend_common_spawn (const gchar *const *argv, guint timeout_seconds, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); void polkit_backend_common_spawn_cb (GObject *source_object, GAsyncResult *res, gpointer user_data); gboolean polkit_backend_common_spawn_finish (GAsyncResult *res, gint *out_exit_status, gchar **out_standard_output, gchar **out_standard_error, GError **error); void polkit_backend_common_on_dir_monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data); void polkit_backend_common_js_authority_class_init_common (PolkitBackendJsAuthorityClass *klass); gint polkit_backend_common_rules_file_name_cmp (const gchar *a, const gchar *b); const gchar *polkit_backend_common_get_signal_name (gint signal_number); /* To be provided by each JS backend, from here onwards ---------------------------------------------- */ void polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority); void polkit_backend_common_js_authority_finalize (GObject *object); void polkit_backend_common_js_authority_constructed (GObject *object); GList *polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details); void polkit_backend_common_js_authority_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); PolkitImplicitAuthorization polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details, PolkitImplicitAuthorization implicit); void polkit_backend_common_pidfd_to_systemd_unit (gint pid, gchar **ret_unit, gboolean *ret_no_new_privs); #ifdef __cplusplus } #endif #endif /* __POLKIT_BACKEND_COMMON_H */ polkit-126/src/polkitbackend/polkitbackendduktapeauthority.c000066400000000000000000001201151474122443600246030ustar00rootroot00000000000000/* * Copyright (C) 2008-2012 Red Hat, Inc. * Copyright (C) 2015 Tangent Space * Copyright (C) 2019 Wu Xiaotian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include "polkitbackendcommon.h" #include "duktape.h" /* Built source and not too big to worry about deduplication */ #include "initjs.h" /* init.js */ /** * SECTION:polkitbackendjsauthority * @title: PolkitBackendJsAuthority * @short_description: JS Authority * @stability: Unstable * * An (Duktape-based) implementation of #PolkitBackendAuthority that reads and * evaluates Javascript files and supports interaction with authentication * agents (virtue of being based on #PolkitBackendInteractiveAuthority). */ /* ---------------------------------------------------------------------------------------------------- */ struct _PolkitBackendJsAuthorityPrivate { gchar **rules_dirs; GFileMonitor **dir_monitors; /* NULL-terminated array of GFileMonitor instances */ duk_context *cx; pthread_t runaway_killer_thread; }; enum { RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS, RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE, }; static gboolean execute_script_with_runaway_killer(PolkitBackendJsAuthority *authority, const gchar *filename); /* ---------------------------------------------------------------------------------------------------- */ G_DEFINE_TYPE_WITH_PRIVATE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY); /* ---------------------------------------------------------------------------------------------------- */ static duk_ret_t js_polkit_log (duk_context *cx); static duk_ret_t js_polkit_spawn (duk_context *cx); static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx); static const duk_function_list_entry js_polkit_functions[] = { { "log", js_polkit_log, 1 }, { "spawn", js_polkit_spawn, 1 }, { "_userIsInNetGroup", js_polkit_user_is_in_netgroup, 2 }, { NULL, NULL, 0 }, }; static void report_error (void *udata, const char *msg) { PolkitBackendJsAuthority *authority = udata; polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "fatal Duktape JS backend error: %s", (msg ? msg : "no message")); } static void polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority) { authority->priv = polkit_backend_js_authority_get_instance_private (authority); } static void load_scripts (PolkitBackendJsAuthority *authority) { GList *files = NULL; GList *l; guint num_scripts = 0; GError *error = NULL; guint n; files = NULL; for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++) { const gchar *dir_name = authority->priv->rules_dirs[n]; GDir *dir = NULL; polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Loading rules from directory %s", dir_name); dir = g_dir_open (dir_name, 0, &error); if (dir != NULL) { const gchar *name; while ((name = g_dir_read_name (dir)) != NULL) { if (g_str_has_suffix (name, ".rules")) files = g_list_prepend (files, g_strdup_printf ("%s/%s", dir_name, name)); } g_dir_close (dir); } else { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Error opening rules directory: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_clear_error (&error); } } files = g_list_sort (files, (GCompareFunc) polkit_backend_common_rules_file_name_cmp); for (l = files; l != NULL; l = l->next) { const gchar *filename = (gchar *)l->data; if (!execute_script_with_runaway_killer(authority, filename)) continue; num_scripts++; polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_DEBUG, "Loaded and executed script in file %s", filename); } polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Finished loading, compiling and executing %d rules", num_scripts); g_list_free_full (files, g_free); } void polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority) { duk_context *cx = authority->priv->cx; duk_set_top (cx, 0); if (!duk_get_global_string (cx, "polkit")) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error deleting old rules, not loading new ones"); return; } duk_push_string (cx, "_deleteRules"); duk_call_prop (cx, 0, 0); polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Collecting garbage unconditionally..."); load_scripts (authority); /* Let applications know we have new rules... */ g_signal_emit_by_name (authority, "changed"); } static void setup_file_monitors (PolkitBackendJsAuthority *authority) { guint n; GPtrArray *p; p = g_ptr_array_new (); for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++) { GFile *file; GError *error; GFileMonitor *monitor; file = g_file_new_for_path (authority->priv->rules_dirs[n]); error = NULL; monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &error); g_object_unref (file); if (monitor == NULL) { g_warning ("Error monitoring directory %s: %s", authority->priv->rules_dirs[n], error->message); g_clear_error (&error); } else { g_signal_connect (monitor, "changed", G_CALLBACK (polkit_backend_common_on_dir_monitor_changed), authority); g_ptr_array_add (p, monitor); } } g_ptr_array_add (p, NULL); authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE); } void polkit_backend_common_js_authority_constructed (GObject *object) { PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); duk_context *cx; cx = duk_create_heap (NULL, NULL, NULL, authority, report_error); if (cx == NULL) goto fail; authority->priv->cx = cx; duk_push_global_object (cx); duk_push_object (cx); duk_put_function_list (cx, -1, js_polkit_functions); duk_put_prop_string (cx, -2, "polkit"); /* load polkit objects/functions into JS context (e.g. addRule(), * _deleteRules(), _runRules() et al) */ duk_eval_string (cx, init_js); if (authority->priv->rules_dirs == NULL) { authority->priv->rules_dirs = g_new0 (gchar *, 5); authority->priv->rules_dirs[0] = g_strdup (PACKAGE_SYSCONF_DIR "/polkit-1/rules.d"); authority->priv->rules_dirs[1] = g_strdup ("/run/polkit-1/rules.d"); authority->priv->rules_dirs[2] = g_strdup ("/usr/local/share/polkit-1/rules.d"); authority->priv->rules_dirs[3] = g_strdup (PACKAGE_DATA_DIR "/polkit-1/rules.d"); } setup_file_monitors (authority); load_scripts (authority); G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->constructed (object); return; fail: g_critical ("Error initializing JavaScript environment"); g_assert_not_reached (); } void polkit_backend_common_js_authority_finalize (GObject *object) { PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); guint n; for (n = 0; authority->priv->dir_monitors != NULL && authority->priv->dir_monitors[n] != NULL; n++) { GFileMonitor *monitor = authority->priv->dir_monitors[n]; g_signal_handlers_disconnect_by_func (monitor, G_CALLBACK (polkit_backend_common_on_dir_monitor_changed), authority); g_object_unref (monitor); } g_free (authority->priv->dir_monitors); g_strfreev (authority->priv->rules_dirs); duk_destroy_heap (authority->priv->cx); G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object); } void polkit_backend_common_js_authority_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); switch (property_id) { case PROP_RULES_DIRS: g_assert (authority->priv->rules_dirs == NULL); authority->priv->rules_dirs = (gchar **) g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass) { polkit_backend_common_js_authority_class_init_common (klass); } /* ---------------------------------------------------------------------------------------------------- */ static void set_property_str (duk_context *cx, const gchar *name, const gchar *value) { duk_push_string (cx, value); duk_put_prop_string (cx, -2, name); } static void set_property_strv (duk_context *cx, const gchar *name, GPtrArray *value) { guint n; duk_push_array (cx); for (n = 0; n < value->len; n++) { duk_push_string (cx, g_ptr_array_index (value, n)); duk_put_prop_index (cx, -2, n); } duk_put_prop_string (cx, -2, name); } static void set_property_int32 (duk_context *cx, const gchar *name, gint32 value) { duk_push_int (cx, value); duk_put_prop_string (cx, -2, name); } static void set_property_bool (duk_context *cx, const char *name, gboolean value) { duk_push_boolean (cx, value); duk_put_prop_string (cx, -2, name); } /* ---------------------------------------------------------------------------------------------------- */ static gboolean push_subject (duk_context *cx, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, GError **error) { gboolean ret = FALSE; gboolean no_new_privs = FALSE; gint pidfd = -1; pid_t pid_early, pid_late; uid_t uid; PolkitSubject *process = NULL; gchar *user_name = NULL; GPtrArray *groups = NULL; GArray *gids_from_dbus = NULL; struct passwd *passwd; char *seat_str = NULL; char *session_str = NULL; char *system_unit = NULL; if (!duk_get_global_string (cx, "Subject")) { return FALSE; } duk_new (cx, 0); if (POLKIT_IS_UNIX_PROCESS (subject)) { process = subject; } else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error); if (process == NULL) goto out; } else { g_assert_not_reached (); } pid_early = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process)); pidfd = polkit_unix_process_get_pidfd (POLKIT_UNIX_PROCESS (process)); #ifdef HAVE_LIBSYSTEMD #if HAVE_SD_PIDFD_GET_SESSION if (pidfd >= 0) sd_pidfd_get_session (pidfd, &session_str); else #endif /* HAVE_SD_PIDFD_GET_SESSION */ sd_pid_get_session (pid_early, &session_str); if (session_str) sd_session_get_seat (session_str, &seat_str); #endif /* HAVE_LIBSYSTEMD */ g_assert (POLKIT_IS_UNIX_USER (user_for_subject)); uid = polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_for_subject)); groups = g_ptr_array_new_with_free_func (g_free); gids_from_dbus = polkit_unix_process_get_gids (POLKIT_UNIX_PROCESS (process)); passwd = getpwuid (uid); if (passwd == NULL) { user_name = g_strdup_printf ("%d", (gint) uid); g_warning ("Error looking up info for uid %d: %m", (gint) uid); } else { user_name = g_strdup (passwd->pw_name); } /* D-Bus will give us supplementary groups too, so prefer that to looking up * the group from the uid. */ if (gids_from_dbus && gids_from_dbus->len > 0) { gint n; for (n = 0; n < gids_from_dbus->len; n++) { struct group *group; group = getgrgid (g_array_index (gids_from_dbus, gid_t, n)); if (group == NULL) { g_ptr_array_add (groups, g_strdup_printf ("%d", (gint) g_array_index (gids_from_dbus, gid_t, n))); } else { g_ptr_array_add (groups, g_strdup (group->gr_name)); } } } else { if (passwd != NULL) { gid_t gids[512]; int num_gids = 512; if (getgrouplist (passwd->pw_name, passwd->pw_gid, gids, &num_gids) < 0) { g_warning ("Error looking up groups for uid %d: %m", (gint) uid); } else { gint n; for (n = 0; n < num_gids; n++) { struct group *group; group = getgrgid (gids[n]); if (group == NULL) { g_ptr_array_add (groups, g_strdup_printf ("%d", (gint) gids[n])); } else { g_ptr_array_add (groups, g_strdup (group->gr_name)); } } } } } /* Query the unit, will work only if we got the pidfd from dbus-daemon/broker. * Best-effort operation, will log on failure, but we don't bail here. But * only do so if the pidfd was marked as safe, i.e.: we got it from D-Bus so * it can be trusted end-to-end, with no reuse attack window. */ if (polkit_unix_process_get_pidfd_is_safe (POLKIT_UNIX_PROCESS (process))) polkit_backend_common_pidfd_to_systemd_unit (pidfd, &system_unit, &no_new_privs); /* In case we are using PIDFDs, check that the PID still matches to avoid race * conditions and PID recycle attacks. */ pid_late = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process)); if (pid_late != pid_early) { if (pid_late <= 0) { g_warning ("Process %d terminated", (gint) pid_early); g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Process %d terminated", (gint) pid_early); } else { g_warning ("Process changed pid from %d to %d", (gint) pid_early, (gint) pid_late); g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Process changed pid from %d to %d", (gint) pid_early, (gint) pid_late); } goto out; } set_property_int32 (cx, "pid", pid_early); set_property_str (cx, "user", user_name); set_property_strv (cx, "groups", groups); set_property_str (cx, "seat", seat_str); set_property_str (cx, "session", session_str); set_property_str (cx, "system_unit", system_unit); /* If we have a unit, also record if it has the NoNewPrivileges setting enabled */ if (system_unit) set_property_bool (cx, "no_new_privileges", no_new_privs); set_property_bool (cx, "local", subject_is_local); set_property_bool (cx, "active", subject_is_active); ret = TRUE; out: if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) g_object_unref (process); free (session_str); free (seat_str); free (system_unit); g_free (user_name); if (groups != NULL) g_ptr_array_unref (groups); if (gids_from_dbus != NULL) g_array_unref (gids_from_dbus); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean push_action_and_details (duk_context *cx, const gchar *action_id, PolkitDetails *details) { gchar **keys; guint n; if (!duk_get_global_string (cx, "Action")) { return FALSE; } duk_new (cx, 0); set_property_str (cx, "id", action_id); keys = polkit_details_get_keys (details); for (n = 0; keys != NULL && keys[n] != NULL; n++) { gchar *key; const gchar *value; key = g_strdup_printf ("_detail_%s", keys[n]); value = polkit_details_lookup (details, keys[n]); set_property_str (cx, key, value); g_free (key); } g_strfreev (keys); return TRUE; } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { PolkitBackendJsAuthority *authority; const gchar *filename; pthread_cond_t cond; pthread_mutex_t mutex; gint ret; } RunawayKillerCtx; static gpointer runaway_killer_thread_execute_js (gpointer user_data) { RunawayKillerCtx *ctx = user_data; duk_context *cx = ctx->authority->priv->cx; int oldtype, pthread_err; if ((pthread_err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error setting thread cancel type: %s", strerror(pthread_err)); goto err; } GFile *file = g_file_new_for_path(ctx->filename); char *contents; gsize len; if (!g_file_load_contents(file, NULL, &contents, &len, NULL, NULL)) { polkit_backend_authority_log(POLKIT_BACKEND_AUTHORITY(ctx->authority), LOG_LEVEL_ERROR, "Error loading script %s", ctx->filename); g_object_unref(file); goto err; } g_object_unref(file); /* evaluate the script, trying to print context in any syntax errors found */ if (duk_peval_lstring(cx, contents, len) != 0) { polkit_backend_authority_log(POLKIT_BACKEND_AUTHORITY(ctx->authority), LOG_LEVEL_ERROR, "Error compiling script %s: %s", ctx->filename, duk_safe_to_string(cx, -1)); duk_pop(cx); goto free_err; } g_free(contents); if ((pthread_err = pthread_mutex_lock(&ctx->mutex))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error locking mutex: %s", strerror(pthread_err)); return NULL; } ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS; goto end; free_err: g_free(contents); err: if ((pthread_err = pthread_mutex_lock(&ctx->mutex))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error locking mutex: %s", strerror(pthread_err)); return NULL; } ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; end: if ((pthread_err = pthread_cond_signal(&ctx->cond))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error signaling on condition variable: %s", strerror(pthread_err)); ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; } if ((pthread_err = pthread_mutex_unlock(&ctx->mutex))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error unlocking mutex: %s", strerror(pthread_err)); ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; } return NULL; } static gpointer runaway_killer_thread_call_js (gpointer user_data) { RunawayKillerCtx *ctx = user_data; duk_context *cx = ctx->authority->priv->cx; int oldtype, pthread_err; if ((pthread_err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error setting thread cancel type: %s", strerror(pthread_err)); goto err; } if (duk_pcall_prop (cx, 0, 2) != DUK_EXEC_SUCCESS) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error evaluating admin rules: %s", duk_safe_to_string (cx, -1)); goto err; } if ((pthread_err = pthread_mutex_lock(&ctx->mutex))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error locking mutex: %s", strerror(pthread_err)); return NULL; } ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS; goto end; err: if ((pthread_err = pthread_mutex_lock(&ctx->mutex))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error locking mutex: %s", strerror(pthread_err)); return NULL; } ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; end: if ((pthread_err = pthread_cond_signal(&ctx->cond))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error signaling on condition variable: %s", strerror(pthread_err)); ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; } if ((pthread_err = pthread_mutex_unlock(&ctx->mutex))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), LOG_LEVEL_ERROR, "Error unlocking mutex: %s", strerror(pthread_err)); ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; } return NULL; } #if defined (HAVE_PTHREAD_CONDATTR_SETCLOCK) # if defined(CLOCK_MONOTONIC) # define PK_CLOCK CLOCK_MONOTONIC # elif defined(CLOCK_BOOTTIME) # define PK_CLOCK CLOCK_BOOTTIME # else /* No suitable clock */ # undef HAVE_PTHREAD_CONDATTR_SETCLOCK # define PK_CLOCK CLOCK_REALTIME # endif #else /* ! HAVE_PTHREAD_CONDATTR_SETCLOCK */ # define PK_CLOCK CLOCK_REALTIME #endif /* ! HAVE_PTHREAD_CONDATTR_SETCLOCK */ static gboolean runaway_killer_common(PolkitBackendJsAuthority *authority, RunawayKillerCtx *ctx, void *js_context_cb (void *user_data)) { int pthread_err; gboolean cancel = FALSE; #ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK pthread_condattr_t attr; #endif struct timespec abs_time; #ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK if ((pthread_err = pthread_condattr_init(&attr))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error initializing condition variable attributes: %s", strerror(pthread_err)); return FALSE; } if ((pthread_err = pthread_condattr_setclock(&attr, PK_CLOCK))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error setting condition variable attributes: %s", strerror(pthread_err)); goto err_clean_condattr; } /* Init again, with needed attr */ if ((pthread_err = pthread_cond_init(&ctx->cond, &attr))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error initializing condition variable: %s", strerror(pthread_err)); goto err_clean_condattr; } #endif if ((pthread_err = pthread_mutex_lock(&ctx->mutex))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error locking mutex: %s", strerror(pthread_err)); goto err_clean_cond; } if (clock_gettime(PK_CLOCK, &abs_time)) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error getting system's monotonic time: %s", strerror(errno)); goto err_clean_cond; } abs_time.tv_sec += RUNAWAY_KILLER_TIMEOUT; if ((pthread_err = pthread_create(&authority->priv->runaway_killer_thread, NULL, js_context_cb, ctx))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error creating runaway JS killer thread: %s", strerror(pthread_err)); goto err_clean_cond; } while (ctx->ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET) /* loop to treat spurious wakeups */ if (pthread_cond_timedwait(&ctx->cond, &ctx->mutex, &abs_time) == ETIMEDOUT) { cancel = TRUE; /* Log that we are terminating the script */ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_WARNING, "Terminating runaway script after %d seconds", RUNAWAY_KILLER_TIMEOUT); break; } if ((pthread_err = pthread_mutex_unlock(&ctx->mutex))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error unlocking mutex: %s", strerror(pthread_err)); goto err_clean_cond; } if (cancel) { if ((pthread_err = pthread_cancel (authority->priv->runaway_killer_thread))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error cancelling runaway JS killer thread: %s", strerror(pthread_err)); goto err_clean_cond; } } if ((pthread_err = pthread_join (authority->priv->runaway_killer_thread, NULL))) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error joining runaway JS killer thread: %s", strerror(pthread_err)); goto err_clean_cond; } return ctx->ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS; err_clean_cond: #ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK pthread_cond_destroy(&ctx->cond); err_clean_condattr: pthread_condattr_destroy(&attr); #endif return FALSE; } /* Blocking for at most RUNAWAY_KILLER_TIMEOUT */ static gboolean execute_script_with_runaway_killer(PolkitBackendJsAuthority *authority, const gchar *filename) { RunawayKillerCtx ctx = {.authority = authority, .filename = filename, .ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, .mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER}; return runaway_killer_common(authority, &ctx, &runaway_killer_thread_execute_js); } /* Calls already stacked function and args. Blocking for at most * RUNAWAY_KILLER_TIMEOUT. If timeout is the case, ctx.ret will be * RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, thus returning FALSE. */ static gboolean call_js_function_with_runaway_killer(PolkitBackendJsAuthority *authority) { RunawayKillerCtx ctx = {.authority = authority, .ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, .mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER}; return runaway_killer_common(authority, &ctx, &runaway_killer_thread_call_js); } /* ---------------------------------------------------------------------------------------------------- */ GList * polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details) { PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority); GList *ret = NULL; guint n; GError *error = NULL; const char *ret_str = NULL; gchar **ret_strs = NULL; duk_context *cx = authority->priv->cx; duk_set_top (cx, 0); if (!duk_get_global_string (cx, "polkit")) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error deleting old rules, not loading new ones"); goto out; } duk_push_string (cx, "_runAdminRules"); if (!push_action_and_details (cx, action_id, details)) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error converting action and details to JS object"); goto out; } if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error)) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error converting subject to JS object: %s", error->message); g_clear_error (&error); goto out; } if (!call_js_function_with_runaway_killer (authority)) goto out; ret_str = duk_require_string (cx, -1); ret_strs = g_strsplit (ret_str, ",", -1); for (n = 0; ret_strs != NULL && ret_strs[n] != NULL; n++) { const gchar *identity_str = ret_strs[n]; PolkitIdentity *identity; error = NULL; identity = polkit_identity_from_string (identity_str, &error); if (identity == NULL) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_WARNING, "Identity `%s' is not valid, ignoring: %s", identity_str, error->message); g_clear_error (&error); } else { ret = g_list_prepend (ret, identity); } } ret = g_list_reverse (ret); out: g_strfreev (ret_strs); /* fallback to root password auth */ if (ret == NULL) ret = g_list_prepend (ret, polkit_unix_user_new (0)); return ret; } /* ---------------------------------------------------------------------------------------------------- */ PolkitImplicitAuthorization polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details, PolkitImplicitAuthorization implicit) { PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority); PolkitImplicitAuthorization ret = implicit; GError *error = NULL; gchar *ret_str = NULL; gboolean good = FALSE; duk_context *cx = authority->priv->cx; duk_set_top (cx, 0); if (!duk_get_global_string (cx, "polkit")) { goto out; } duk_push_string (cx, "_runRules"); if (!push_action_and_details (cx, action_id, details)) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error converting action and details to JS object"); goto out; } if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error)) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error converting subject to JS object: %s", error->message); g_clear_error (&error); goto out; } // If any error is the js context happened (ctx.ret == // RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE) or it never properly returned // (runaway scripts or ctx.ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET), // unauthorize if (!call_js_function_with_runaway_killer (authority)) goto out; if (duk_is_null(cx, -1)) { /* this is fine, means there was no match, use implicit authorizations */ good = TRUE; goto out; } ret_str = g_strdup (duk_require_string (cx, -1)); if (!polkit_implicit_authorization_from_string (ret_str, &ret)) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_WARNING, "Returned result `%s' is not valid", ret_str); goto out; } good = TRUE; out: if (!good) ret = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; if (ret_str != NULL) g_free (ret_str); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static duk_ret_t js_polkit_log (duk_context *cx) { const char *str = duk_require_string (cx, 0); fprintf (stderr, "%s\n", str); return 0; } /* ---------------------------------------------------------------------------------------------------- */ static duk_ret_t js_polkit_spawn (duk_context *cx) { duk_ret_t ret = DUK_RET_ERROR; gchar *standard_output = NULL; gchar *standard_error = NULL; gint exit_status; GError *error = NULL; guint32 array_len; gchar **argv = NULL; GMainContext *context = NULL; GMainLoop *loop = NULL; SpawnData data = {0}; char *err_str = NULL; guint n; if (!duk_is_array (cx, 0)) goto out; array_len = duk_get_length (cx, 0); argv = g_new0 (gchar*, array_len + 1); for (n = 0; n < array_len; n++) { duk_get_prop_index (cx, 0, n); argv[n] = g_strdup (duk_to_string (cx, -1)); duk_pop (cx); } context = g_main_context_new (); loop = g_main_loop_new (context, FALSE); g_main_context_push_thread_default (context); data.loop = loop; polkit_backend_common_spawn ((const gchar *const *) argv, 10, /* timeout_seconds */ NULL, /* cancellable */ polkit_backend_common_spawn_cb, &data); g_main_loop_run (loop); g_main_context_pop_thread_default (context); if (!polkit_backend_common_spawn_finish (data.res, &exit_status, &standard_output, &standard_error, &error)) { err_str = g_strdup_printf ("Error spawning helper: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_clear_error (&error); goto out; } if (!(WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0)) { GString *gstr; gstr = g_string_new (NULL); if (WIFEXITED (exit_status)) { g_string_append_printf (gstr, "Helper exited with non-zero exit status %d", WEXITSTATUS (exit_status)); } else if (WIFSIGNALED (exit_status)) { g_string_append_printf (gstr, "Helper was signaled with signal %s (%d)", polkit_backend_common_get_signal_name (WTERMSIG (exit_status)), WTERMSIG (exit_status)); } g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'", standard_output, standard_error); err_str = g_string_free (gstr, FALSE); goto out; } duk_push_string (cx, standard_output); ret = 1; out: g_strfreev (argv); g_free (standard_output); g_free (standard_error); g_clear_object (&data.res); if (loop != NULL) g_main_loop_unref (loop); if (context != NULL) g_main_context_unref (context); if (err_str) { duk_push_error_object (cx, DUK_ERR_ERROR, err_str); free (err_str); duk_throw (cx); g_assert_not_reached (); } return ret; } /* ---------------------------------------------------------------------------------------------------- */ static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx) { const char *user; const char *netgroup; gboolean is_in_netgroup = FALSE; user = duk_require_string (cx, 0); netgroup = duk_require_string (cx, 1); #ifdef HAVE_SETNETGRENT if (innetgr (netgroup, NULL, /* host */ user, NULL)) /* domain */ { is_in_netgroup = TRUE; } #endif duk_push_boolean (cx, is_in_netgroup); return 1; } /* ---------------------------------------------------------------------------------------------------- */ polkit-126/src/polkitbackend/polkitbackendinteractiveauthority.c000066400000000000000000004175331474122443600255000ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #ifdef HAVE_NETGROUP_H #include #else #include #endif #include #include #include #include #include "polkitbackendinteractiveauthority.h" #include "polkitbackendactionpool.h" #include "polkitbackendcommon.h" #include "polkitbackendsessionmonitor.h" #include /** * SECTION:polkitbackendinteractiveauthority * @title: PolkitBackendInteractiveAuthority * @short_description: Interactive Authority * @stability: Unstable * * An subclass of #PolkitBackendAuthority that supports interaction * with authentication agents. */ /* ---------------------------------------------------------------------------------------------------- */ typedef struct TemporaryAuthorizationStore TemporaryAuthorizationStore; static TemporaryAuthorizationStore *temporary_authorization_store_new (PolkitBackendInteractiveAuthority *authority); static void temporary_authorization_store_free (TemporaryAuthorizationStore *store); static gboolean temporary_authorization_store_has_authorization (TemporaryAuthorizationStore *store, PolkitSubject *subject, const gchar *action_id, const gchar **out_tmp_authz_id); static const gchar *temporary_authorization_store_add_authorization (TemporaryAuthorizationStore *store, PolkitSubject *subject, PolkitSubject *session, const gchar *action_id); static void temporary_authorization_store_remove_authorizations_for_system_bus_name (TemporaryAuthorizationStore *store, const gchar *name); /* ---------------------------------------------------------------------------------------------------- */ struct AuthenticationAgent; typedef struct AuthenticationAgent AuthenticationAgent; struct AuthenticationSession; typedef struct AuthenticationSession AuthenticationSession; typedef void (*AuthenticationAgentCallback) (AuthenticationAgent *agent, PolkitSubject *subject, PolkitIdentity *user_of_subject, PolkitSubject *caller, PolkitBackendInteractiveAuthority *authority, const gchar *action_id, PolkitDetails *details, PolkitImplicitAuthorization implicit_authorization, gboolean authentication_success, gboolean was_dismissed, PolkitIdentity *authenticated_identity, gpointer user_data); static AuthenticationAgent *authentication_agent_ref (AuthenticationAgent *agent); static void authentication_agent_unref (AuthenticationAgent *agent); static void authentication_agent_initiate_challenge (AuthenticationAgent *agent, PolkitSubject *subject, PolkitIdentity *user_of_subject, PolkitBackendInteractiveAuthority *authority, const gchar *action_id, PolkitDetails *details, PolkitSubject *caller, PolkitImplicitAuthorization implicit_authorization, GCancellable *cancellable, AuthenticationAgentCallback callback, gpointer user_data); static PolkitSubject *authentication_agent_get_scope (AuthenticationAgent *agent); static AuthenticationAgent *get_authentication_agent_for_subject (PolkitBackendInteractiveAuthority *authority, PolkitSubject *subject); static AuthenticationSession *get_authentication_session_for_uid_and_cookie (PolkitBackendInteractiveAuthority *authority, uid_t uid, const gchar *cookie); static GList *get_authentication_sessions_initiated_by_system_bus_unique_name (PolkitBackendInteractiveAuthority *authority, const gchar *system_bus_unique_name); static void authentication_session_cancel (AuthenticationSession *session); /* ---------------------------------------------------------------------------------------------------- */ static void polkit_backend_interactive_authority_system_bus_name_owner_changed (PolkitBackendInteractiveAuthority *authority, const gchar *name, const gchar *old_owner, const gchar *new_owner); static GList *polkit_backend_interactive_authority_enumerate_actions (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *locale, GError **error); static void polkit_backend_interactive_authority_check_authorization (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static PolkitAuthorizationResult *polkit_backend_interactive_authority_check_authorization_finish ( PolkitBackendAuthority *authority, GAsyncResult *res, GError **error); static PolkitAuthorizationResult *check_authorization_sync (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, PolkitImplicitAuthorization *out_implicit_authorization, gboolean checking_imply, GError **error); static gboolean polkit_backend_interactive_authority_register_authentication_agent (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GError **error); static gboolean polkit_backend_interactive_authority_unregister_authentication_agent (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *object_path, GError **error); static gboolean polkit_backend_interactive_authority_authentication_agent_response (PolkitBackendAuthority *authority, PolkitSubject *caller, uid_t uid, const gchar *cookie, PolkitIdentity *identity, GError **error); static GList *polkit_backend_interactive_authority_enumerate_temporary_authorizations (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error); static gboolean polkit_backend_interactive_authority_revoke_temporary_authorizations (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error); static gboolean polkit_backend_interactive_authority_revoke_temporary_authorization_by_id (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *id, GError **error); /* ---------------------------------------------------------------------------------------------------- */ typedef struct { PolkitBackendActionPool *action_pool; PolkitBackendSessionMonitor *session_monitor; TemporaryAuthorizationStore *temporary_authorization_store; /* Maps from PolkitSubject* to AuthenticationAgent* - currently the * following PolkitSubject-derived types are used * * - PolkitSystemBusName - for authentication agents handling interaction for a single well-known name * - typically pkexec(1) launched via e.g. ssh(1) or login(1) * * - PolkitUnixSession - for authentication agents handling interaction for a whole login session * - typically a desktop environment session * */ GHashTable *hash_scope_to_authentication_agent; GDBusConnection *system_bus_connection; guint name_owner_changed_signal_id; guint64 agent_serial; } PolkitBackendInteractiveAuthorityPrivate; /* ---------------------------------------------------------------------------------------------------- */ G_DEFINE_TYPE_WITH_PRIVATE (PolkitBackendInteractiveAuthority, polkit_backend_interactive_authority, POLKIT_BACKEND_TYPE_AUTHORITY); static gboolean identity_is_root_user (PolkitIdentity *user) { if (!POLKIT_IS_UNIX_USER (user)) return FALSE; return polkit_unix_user_get_uid (POLKIT_UNIX_USER (user)) == 0; } /* ---------------------------------------------------------------------------------------------------- */ static void action_pool_changed (PolkitBackendActionPool *action_pool, PolkitBackendInteractiveAuthority *authority) { g_signal_emit_by_name (authority, "changed"); } /* ---------------------------------------------------------------------------------------------------- */ static void on_name_owner_changed_signal (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { PolkitBackendInteractiveAuthority *authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (user_data); const gchar *name; const gchar *old_owner; const gchar *new_owner; g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner); polkit_backend_interactive_authority_system_bus_name_owner_changed (authority, name, old_owner, new_owner); } /* ---------------------------------------------------------------------------------------------------- */ static void on_session_monitor_changed (PolkitBackendSessionMonitor *monitor, gpointer user_data) { PolkitBackendInteractiveAuthority *authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (user_data); g_signal_emit_by_name (authority, "sessions-changed"); } static void polkit_backend_interactive_authority_init (PolkitBackendInteractiveAuthority *authority) { PolkitBackendInteractiveAuthorityPrivate *priv; GError *error; const gchar* directories[] = { PACKAGE_SYSCONF_DIR "/polkit-1/actions", "/run/polkit-1/actions", "/usr/local/share/polkit-1/actions", PACKAGE_DATA_DIR "/polkit-1/actions", NULL }; /* Force registering error domain */ (void)POLKIT_ERROR; priv = polkit_backend_interactive_authority_get_instance_private (authority); priv->action_pool = polkit_backend_action_pool_new (directories); g_signal_connect (priv->action_pool, "changed", (GCallback) action_pool_changed, authority); priv->temporary_authorization_store = temporary_authorization_store_new (authority); priv->hash_scope_to_authentication_agent = g_hash_table_new_full ((GHashFunc) polkit_subject_hash, (GEqualFunc) polkit_subject_equal, (GDestroyNotify) g_object_unref, (GDestroyNotify) authentication_agent_unref); priv->session_monitor = polkit_backend_session_monitor_new (); g_signal_connect (priv->session_monitor, "changed", G_CALLBACK (on_session_monitor_changed), authority); error = NULL; priv->system_bus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (priv->system_bus_connection == NULL) { g_warning ("Error getting system bus: %s", error->message); g_error_free (error); } else { /* TODO: this is a bit inefficient */ priv->name_owner_changed_signal_id = g_dbus_connection_signal_subscribe (priv->system_bus_connection, "org.freedesktop.DBus", /* sender */ "org.freedesktop.DBus", /* interface */ "NameOwnerChanged", /* member */ "/org/freedesktop/DBus", /* path */ NULL, /* arg0 */ G_DBUS_SIGNAL_FLAGS_NONE, on_name_owner_changed_signal, authority, NULL); /* GDestroyNotify */ } } static void polkit_backend_interactive_authority_finalize (GObject *object) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (object); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); if (priv->name_owner_changed_signal_id > 0) g_dbus_connection_signal_unsubscribe (priv->system_bus_connection, priv->name_owner_changed_signal_id); if (priv->system_bus_connection != NULL) g_object_unref (priv->system_bus_connection); if (priv->action_pool != NULL) g_object_unref (priv->action_pool); if (priv->session_monitor != NULL) g_object_unref (priv->session_monitor); temporary_authorization_store_free (priv->temporary_authorization_store); g_hash_table_unref (priv->hash_scope_to_authentication_agent); G_OBJECT_CLASS (polkit_backend_interactive_authority_parent_class)->finalize (object); } static const gchar * polkit_backend_interactive_authority_get_name (PolkitBackendAuthority *authority) { return "interactive"; } static const gchar * polkit_backend_interactive_authority_get_version (PolkitBackendAuthority *authority) { return PACKAGE_VERSION; } static PolkitAuthorityFeatures polkit_backend_interactive_authority_get_features (PolkitBackendAuthority *authority) { return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION; } static void polkit_backend_interactive_authority_class_init (PolkitBackendInteractiveAuthorityClass *klass) { GObjectClass *gobject_class; PolkitBackendAuthorityClass *authority_class; gobject_class = G_OBJECT_CLASS (klass); authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass); gobject_class->finalize = polkit_backend_interactive_authority_finalize; authority_class->get_name = polkit_backend_interactive_authority_get_name; authority_class->get_version = polkit_backend_interactive_authority_get_version; authority_class->get_features = polkit_backend_interactive_authority_get_features; authority_class->enumerate_actions = polkit_backend_interactive_authority_enumerate_actions; authority_class->check_authorization = polkit_backend_interactive_authority_check_authorization; authority_class->check_authorization_finish = polkit_backend_interactive_authority_check_authorization_finish; authority_class->register_authentication_agent = polkit_backend_interactive_authority_register_authentication_agent; authority_class->unregister_authentication_agent = polkit_backend_interactive_authority_unregister_authentication_agent; authority_class->authentication_agent_response = polkit_backend_interactive_authority_authentication_agent_response; authority_class->enumerate_temporary_authorizations = polkit_backend_interactive_authority_enumerate_temporary_authorizations; authority_class->revoke_temporary_authorizations = polkit_backend_interactive_authority_revoke_temporary_authorizations; authority_class->revoke_temporary_authorization_by_id = polkit_backend_interactive_authority_revoke_temporary_authorization_by_id; } /* ---------------------------------------------------------------------------------------------------- */ static GList * polkit_backend_interactive_authority_enumerate_actions (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *interactivee, GError **error) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; GList *actions; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); actions = polkit_backend_action_pool_get_all_actions (priv->action_pool, interactivee); return actions; } /* ---------------------------------------------------------------------------------------------------- */ struct AuthenticationAgent { volatile gint ref_count; uid_t creator_uid; PolkitSubject *scope; guint64 serial; gchar *locale; GVariant *registration_options; gchar *object_path; gchar *unique_system_bus_name; GRand *cookie_pool; gchar *cookie_prefix; guint64 cookie_serial; GDBusProxy *proxy; GList *active_sessions; }; /* TODO: should probably move to PolkitSubject * (also see copy in src/programs/pkcheck.c) * * Also, can't really trust the cmdline... but might be useful in the logs anyway. */ static gchar * _polkit_subject_get_cmdline (PolkitSubject *subject) { PolkitSubject *process; gchar *ret; gint pid; gchar *filename; gchar *contents; gsize contents_len; GError *error; guint n; g_return_val_if_fail (subject != NULL, NULL); error = NULL; ret = NULL; process = NULL; filename = NULL; contents = NULL; if (POLKIT_IS_UNIX_PROCESS (subject)) { process = g_object_ref (subject); } else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, &error); if (process == NULL) { g_printerr ("Error getting process for system bus name `%s': %s\n", polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)), error->message); g_error_free (error); goto out; } } else { g_warning ("Unknown subject type passed to _polkit_subject_get_cmdline()"); goto out; } pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process)); if (pid <= 0) { g_debug ("Process is no longer active, cannot fetch cmdline."); goto out; } filename = g_strdup_printf ("/proc/%d/cmdline", pid); if (!g_file_get_contents (filename, &contents, &contents_len, &error)) { g_printerr ("Error opening `%s': %s\n", filename, error->message); g_error_free (error); goto out; } if (contents == NULL || contents_len == 0) { goto out; } else { /* The kernel uses '\0' to separate arguments - replace those with a space. */ for (n = 0; n < contents_len - 1; n++) { if (contents[n] == '\0') contents[n] = ' '; } ret = g_strdup (contents); g_strstrip (ret); } out: g_free (filename); g_free (contents); if (process != NULL) g_object_unref (process); return ret; } /* TODO: possibly remove this function altogether */ G_GNUC_UNUSED static void log_result (PolkitBackendInteractiveAuthority *authority, const gchar *action_id, PolkitSubject *subject, PolkitSubject *caller, PolkitAuthorizationResult *result) { PolkitBackendInteractiveAuthorityPrivate *priv; PolkitIdentity *user_of_subject; const gchar *log_result_str; gchar *subject_str; gchar *user_of_subject_str; gchar *caller_str; gchar *subject_cmdline; gchar *caller_cmdline; priv = polkit_backend_interactive_authority_get_instance_private (authority); log_result_str = "DENYING"; if (polkit_authorization_result_get_is_authorized (result)) log_result_str = "ALLOWING"; user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, subject, NULL, NULL); subject_str = polkit_subject_to_string (subject); if (user_of_subject != NULL) user_of_subject_str = polkit_identity_to_string (user_of_subject); else user_of_subject_str = g_strdup (""); caller_str = polkit_subject_to_string (caller); subject_cmdline = _polkit_subject_get_cmdline (subject); if (subject_cmdline == NULL) subject_cmdline = g_strdup (""); caller_cmdline = _polkit_subject_get_cmdline (caller); if (caller_cmdline == NULL) caller_cmdline = g_strdup (""); polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "%s action %s for %s [%s] owned by %s (check requested by %s [%s])", log_result_str, action_id, subject_str, subject_cmdline, user_of_subject_str, caller_str, caller_cmdline); if (user_of_subject != NULL) g_object_unref (user_of_subject); g_free (subject_str); g_free (user_of_subject_str); g_free (caller_str); g_free (subject_cmdline); g_free (caller_cmdline); } static void check_authorization_challenge_cb (AuthenticationAgent *agent, PolkitSubject *subject, PolkitIdentity *user_of_subject, PolkitSubject *caller, PolkitBackendInteractiveAuthority *authority, const gchar *action_id, PolkitDetails *details, PolkitImplicitAuthorization implicit_authorization, gboolean authentication_success, gboolean was_dismissed, PolkitIdentity *authenticated_identity, gpointer user_data) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); PolkitBackendInteractiveAuthorityPrivate *priv; PolkitAuthorizationResult *result; gchar *scope_str; gchar *subject_str; gchar *user_of_subject_str; gchar *authenticated_identity_str; gchar *subject_cmdline; gboolean is_temp; priv = polkit_backend_interactive_authority_get_instance_private (authority); result = NULL; scope_str = polkit_subject_to_string (agent->scope); subject_str = polkit_subject_to_string (subject); user_of_subject_str = polkit_identity_to_string (user_of_subject); authenticated_identity_str = NULL; if (authenticated_identity != NULL) authenticated_identity_str = polkit_identity_to_string (authenticated_identity); subject_cmdline = _polkit_subject_get_cmdline (subject); if (subject_cmdline == NULL) subject_cmdline = g_strdup (""); g_debug ("In check_authorization_challenge_cb\n" " subject %s\n" " action_id %s\n" " was_dismissed %d\n" " authentication_success %d\n", subject_str, action_id, was_dismissed, authentication_success); if (implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED_RETAINED || implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED) polkit_details_insert (details, "polkit.retains_authorization_after_challenge", "true"); is_temp = FALSE; if (authentication_success) { /* store temporary authorization depending on value of implicit_authorization */ if (implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED_RETAINED || implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED) { const gchar *id; is_temp = TRUE; id = temporary_authorization_store_add_authorization (priv->temporary_authorization_store, subject, authentication_agent_get_scope (agent), action_id); polkit_details_insert (details, "polkit.temporary_authorization_id", id); /* we've added a temporary authorization, let the user know */ g_signal_emit_by_name (authority, "changed"); } result = polkit_authorization_result_new (TRUE, FALSE, details); } else { /* TODO: maybe return set is_challenge? */ if (was_dismissed) polkit_details_insert (details, "polkit.dismissed", "true"); result = polkit_authorization_result_new (FALSE, FALSE, details); } /* Log the event */ if (authentication_success) { if (is_temp) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Operator of %s successfully authenticated as %s to gain " "TEMPORARY authorization for action %s for %s [%s] (owned by %s)", scope_str, authenticated_identity_str, action_id, subject_str, subject_cmdline, user_of_subject_str); } else { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Operator of %s successfully authenticated as %s to gain " "ONE-SHOT authorization for action %s for %s [%s] (owned by %s)", scope_str, authenticated_identity_str, action_id, subject_str, subject_cmdline, user_of_subject_str); } } else { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Operator of %s FAILED to authenticate to gain " "authorization for action %s for %s [%s] (owned by %s)", scope_str, action_id, subject_str, subject_cmdline, user_of_subject_str); } /* log_result (authority, action_id, subject, caller, result); */ g_simple_async_result_set_op_res_gpointer (simple, result, g_object_unref); g_simple_async_result_complete (simple); g_object_unref (simple); g_free (subject_cmdline); g_free (authenticated_identity_str); g_free (user_of_subject_str); g_free (subject_str); g_free (scope_str); } static PolkitAuthorizationResult * polkit_backend_interactive_authority_check_authorization_finish (PolkitBackendAuthority *authority, GAsyncResult *res, GError **error) { GSimpleAsyncResult *simple; PolkitAuthorizationResult *result; simple = G_SIMPLE_ASYNC_RESULT (res); g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_backend_interactive_authority_check_authorization); result = NULL; if (g_simple_async_result_propagate_error (simple, error)) goto out; result = g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); out: return result; } static gboolean may_identity_check_authorization (PolkitBackendInteractiveAuthority *interactive_authority, const gchar *action_id, PolkitIdentity *identity) { PolkitBackendInteractiveAuthorityPrivate *priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); gboolean ret = FALSE; PolkitActionDescription *action_desc = NULL; const gchar *owners = NULL; gchar **tokens = NULL; guint n; /* uid 0 may check anything */ if (identity_is_root_user (identity)) { ret = TRUE; goto out; } action_desc = polkit_backend_action_pool_get_action (priv->action_pool, action_id, NULL); if (action_desc == NULL) goto out; owners = polkit_action_description_get_annotation (action_desc, "org.freedesktop.policykit.owner"); if (owners == NULL) goto out; tokens = g_strsplit (owners, " ", 0); for (n = 0; tokens != NULL && tokens[n] != NULL; n++) { PolkitIdentity *owner_identity; GError *error = NULL; owner_identity = polkit_identity_from_string (tokens[n], &error); if (owner_identity == NULL) { g_warning ("Error parsing owner identity %d of action_id %s: %s (%s, %d)", n, action_id, error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); continue; } if (polkit_identity_equal (identity, owner_identity)) { ret = TRUE; g_object_unref (owner_identity); goto out; } g_object_unref (owner_identity); } out: g_clear_object (&action_desc); g_strfreev (tokens); return ret; } static void polkit_backend_interactive_authority_check_authorization (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; gchar *caller_str; gchar *subject_str; PolkitIdentity *user_of_caller; PolkitIdentity *user_of_subject; gboolean user_of_subject_matches; gchar *user_of_caller_str; gchar *user_of_subject_str; PolkitAuthorizationResult *result; PolkitImplicitAuthorization implicit_authorization; GError *error; GSimpleAsyncResult *simple; gboolean has_details; gchar **detail_keys; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); error = NULL; caller_str = NULL; subject_str = NULL; user_of_caller = NULL; user_of_subject = NULL; user_of_caller_str = NULL; user_of_subject_str = NULL; result = NULL; simple = g_simple_async_result_new (G_OBJECT (authority), callback, user_data, polkit_backend_interactive_authority_check_authorization); /* handle being called from ourselves */ if (caller == NULL) { /* TODO: this is kind of a hack */ GDBusConnection *system_bus; system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); caller = polkit_system_bus_name_new (g_dbus_connection_get_unique_name (system_bus)); g_object_unref (system_bus); } caller_str = polkit_subject_to_string (caller); subject_str = polkit_subject_to_string (subject); g_debug ("%s is inquiring whether %s is authorized for %s", caller_str, subject_str, action_id); user_of_caller = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, caller, NULL, &error); if (error != NULL) { g_simple_async_result_set_from_error (simple, error); g_simple_async_result_complete (simple); g_object_unref (simple); g_error_free (error); goto out; } user_of_caller_str = polkit_identity_to_string (user_of_caller); g_debug (" user of caller is %s", user_of_caller_str); user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, subject, &user_of_subject_matches, &error); if (error != NULL) { g_simple_async_result_set_from_error (simple, error); g_simple_async_result_complete (simple); g_object_unref (simple); g_error_free (error); goto out; } user_of_subject_str = polkit_identity_to_string (user_of_subject); g_debug (" user of subject is %s", user_of_subject_str); has_details = FALSE; if (details != NULL) { detail_keys = polkit_details_get_keys (details); if (detail_keys != NULL) { if (g_strv_length (detail_keys) > 0) has_details = TRUE; g_strfreev (detail_keys); } } /* Not anyone is allowed to check that process XYZ is allowed to do ABC. * We allow this if, and only if, * * - processes may check for another process owned by the *same* user but not * if details are passed (otherwise you'd be able to spoof the dialog); * the caller supplies the user_of_subject value, so we additionally * require it to match at least at one point in time (via * user_of_subject_matches). * * - processes running as uid 0 may check anything and pass any details * * - if the action_id has the "org.freedesktop.policykit.owner" annotation * then any uid referenced by that annotation is also allowed to check * anything and pass any details */ if (!user_of_subject_matches || !polkit_identity_equal (user_of_caller, user_of_subject) || has_details) { if (!may_identity_check_authorization (interactive_authority, action_id, user_of_caller)) { if (has_details) { g_simple_async_result_set_error (simple, POLKIT_ERROR, POLKIT_ERROR_NOT_AUTHORIZED, "Only trusted callers (e.g. uid 0 or an action owner) can use CheckAuthorization() and " "pass details"); } else { g_simple_async_result_set_error (simple, POLKIT_ERROR, POLKIT_ERROR_NOT_AUTHORIZED, "Only trusted callers (e.g. uid 0 or an action owner) can use CheckAuthorization() for " "subjects belonging to other identities"); } g_simple_async_result_complete (simple); g_object_unref (simple); goto out; } } implicit_authorization = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; result = check_authorization_sync (authority, caller, subject, action_id, details, flags, &implicit_authorization, FALSE, /* checking_imply */ &error); if (error != NULL) { g_simple_async_result_set_from_error (simple, error); g_simple_async_result_complete (simple); g_object_unref (simple); g_error_free (error); goto out; } /* Caller is up for a challenge! With light sabers! Use an authentication agent if one exists... */ if (polkit_authorization_result_get_is_challenge (result) && (flags & POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION)) { AuthenticationAgent *agent; agent = get_authentication_agent_for_subject (interactive_authority, subject); if (agent != NULL) { g_object_unref (result); result = NULL; g_debug (" using authentication agent for challenge"); authentication_agent_initiate_challenge (agent, subject, user_of_subject, interactive_authority, action_id, details, caller, implicit_authorization, cancellable, check_authorization_challenge_cb, simple); /* keep going */ goto out; } } /* log_result (interactive_authority, action_id, subject, caller, result); */ /* Otherwise just return the result */ g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (result), g_object_unref); g_simple_async_result_complete (simple); g_object_unref (simple); out: if (user_of_caller != NULL) g_object_unref (user_of_caller); if (user_of_subject != NULL) g_object_unref (user_of_subject); g_free (caller_str); g_free (subject_str); g_free (user_of_caller_str); g_free (user_of_subject_str); if (result != NULL) g_object_unref (result); } /* ---------------------------------------------------------------------------------------------------- */ static PolkitAuthorizationResult * check_authorization_sync (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *action_id, PolkitDetails *details, PolkitCheckAuthorizationFlags flags, PolkitImplicitAuthorization *out_implicit_authorization, gboolean checking_imply, GError **error) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; PolkitAuthorizationResult *result; PolkitIdentity *user_of_subject; PolkitSubject *session_for_subject; gchar *subject_str; GList *groups_of_user; PolkitActionDescription *action_desc; gboolean session_is_local; gboolean session_is_active; PolkitImplicitAuthorization implicit_authorization; const gchar *tmp_authz_id; GList *actions; GList *l; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); result = NULL; actions = NULL; user_of_subject = NULL; groups_of_user = NULL; subject_str = NULL; session_for_subject = NULL; session_is_local = FALSE; session_is_active = FALSE; subject_str = polkit_subject_to_string (subject); g_debug ("checking whether %s is authorized for %s", subject_str, action_id); /* get the action description */ action_desc = polkit_backend_action_pool_get_action (priv->action_pool, action_id, NULL); if (action_desc == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Action %s is not registered", action_id); goto out; } /* every subject has a user; this is supplied by the client, so we rely * on the caller to validate its acceptability. */ user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, subject, NULL, error); if (user_of_subject == NULL) goto out; /* special case: uid 0, root, is _always_ authorized for anything */ if (!(flags & POLKIT_CHECK_AUTHORIZATION_FLAGS_ALWAYS_CHECK) && identity_is_root_user (user_of_subject)) { result = polkit_authorization_result_new (TRUE, FALSE, NULL); goto out; } /* a subject *may* be in a session */ session_for_subject = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor, subject, NULL); g_debug (" %p", session_for_subject); if (session_for_subject != NULL) { session_is_local = polkit_backend_session_monitor_is_session_local (priv->session_monitor, session_for_subject); session_is_active = polkit_backend_session_monitor_is_session_active (priv->session_monitor, session_for_subject); g_debug (" subject is in session %s (local=%d active=%d)", polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (session_for_subject)), session_is_local, session_is_active); } /* find the implicit authorization to use; it depends on is_local and is_active */ if (session_is_local) { if (session_is_active) implicit_authorization = polkit_action_description_get_implicit_active (action_desc); else implicit_authorization = polkit_action_description_get_implicit_inactive (action_desc); } else { implicit_authorization = polkit_action_description_get_implicit_any (action_desc); } /* allow subclasses to rewrite implicit_authorization */ implicit_authorization = polkit_backend_interactive_authority_check_authorization_sync (interactive_authority, caller, subject, user_of_subject, session_is_local, session_is_active, action_id, details, implicit_authorization); /* first see if there's an implicit authorization for subject available */ if (implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED) { g_debug (" is authorized (has implicit authorization local=%d active=%d)", session_is_local, session_is_active); result = polkit_authorization_result_new (TRUE, FALSE, details); goto out; } /* then see if there's a temporary authorization for the subject */ if (temporary_authorization_store_has_authorization (priv->temporary_authorization_store, subject, action_id, &tmp_authz_id)) { g_debug (" is authorized (has temporary authorization)"); polkit_details_insert (details, "polkit.temporary_authorization_id", tmp_authz_id); result = polkit_authorization_result_new (TRUE, FALSE, details); goto out; } /* then see if implied by another action that the subject is authorized for * (but only one level deep to avoid infinite recursion) * * TODO: if this is slow, we can maintain a hash table for looking up what * actions implies a given action */ if (!checking_imply) { actions = polkit_backend_action_pool_get_all_actions (priv->action_pool, NULL); for (l = actions; l != NULL; l = l->next) { PolkitActionDescription *imply_ad = POLKIT_ACTION_DESCRIPTION (l->data); const gchar *imply; imply = polkit_action_description_get_annotation (imply_ad, "org.freedesktop.policykit.imply"); if (imply != NULL) { gchar **tokens; guint n; tokens = g_strsplit (imply, " ", 0); for (n = 0; tokens[n] != NULL; n++) { if (g_strcmp0 (tokens[n], action_id) == 0) { PolkitAuthorizationResult *implied_result = NULL; PolkitImplicitAuthorization implied_implicit_authorization; GError *implied_error = NULL; const gchar *imply_action_id; imply_action_id = polkit_action_description_get_action_id (imply_ad); /* g_debug ("%s is implied by %s, checking", action_id, imply_action_id); */ implied_result = check_authorization_sync (authority, caller, subject, imply_action_id, details, flags, &implied_implicit_authorization, TRUE, &implied_error); if (implied_result != NULL) { if (polkit_authorization_result_get_is_authorized (implied_result)) { g_debug (" is authorized (implied by %s)", imply_action_id); result = implied_result; /* cleanup */ g_strfreev (tokens); goto out; } g_object_unref (implied_result); } if (implied_error != NULL) g_error_free (implied_error); } } g_strfreev (tokens); } } } if (implicit_authorization != POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED) { if (implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED_RETAINED || implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED) { polkit_details_insert (details, "polkit.retains_authorization_after_challenge", "1"); } result = polkit_authorization_result_new (FALSE, TRUE, details); /* return implicit_authorization so the caller can use an authentication agent if applicable */ if (out_implicit_authorization != NULL) *out_implicit_authorization = implicit_authorization; g_debug (" challenge (implicit_authorization = %s)", polkit_implicit_authorization_to_string (implicit_authorization)); } else { result = polkit_authorization_result_new (FALSE, FALSE, details); g_debug (" not authorized"); } out: g_list_foreach (actions, (GFunc) g_object_unref, NULL); g_list_free (actions); g_free (subject_str); g_list_foreach (groups_of_user, (GFunc) g_object_unref, NULL); g_list_free (groups_of_user); if (user_of_subject != NULL) g_object_unref (user_of_subject); if (session_for_subject != NULL) g_object_unref (session_for_subject); if (action_desc != NULL) g_object_unref (action_desc); g_debug (" "); return result; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_backend_interactive_authority_get_admin_identities: * @authority: A #PolkitBackendInteractiveAuthority. * @caller: The subject that is inquiring whether @subject is authorized. * @subject: The subject we are about to authenticate for. * @user_for_subject: The user of the subject we are about to authenticate for. * @subject_is_local: %TRUE if the session for @subject is local. * @subject_is_active: %TRUE if the session for @subject is active. * @action_id: The action we are about to authenticate for. * @details: Details about the action. * * Gets a list of identities to use for administrator authentication. * * The default implementation returns a list with a single element for the super user. * * Returns: A list of #PolkitIdentity objects. Free each element * g_object_unref(), then free the list with g_list_free(). */ GList * polkit_backend_interactive_authority_get_admin_identities (PolkitBackendInteractiveAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details) { PolkitBackendInteractiveAuthorityClass *klass; GList *ret = NULL; klass = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_CLASS (authority); if (klass->get_admin_identities != NULL) { ret = klass->get_admin_identities (authority, caller, subject, user_for_subject, subject_is_local, subject_is_active, action_id, details); } return ret; } /** * polkit_backend_interactive_authority_check_authorization_sync: * @authority: A #PolkitBackendInteractiveAuthority. * @caller: The subject that is inquiring whether @subject is authorized. * @subject: The subject we are checking an authorization for. * @user_for_subject: The user of the subject we are checking an authorization for. * @subject_is_local: %TRUE if the session for @subject is local. * @subject_is_active: %TRUE if the session for @subject is active. * @action_id: The action we are checking an authorization for. * @details: Details about the action. * @implicit: A #PolkitImplicitAuthorization value computed from the policy file and @subject. * * Checks whether @subject is authorized to perform the action * specified by @action_id and @details. The implementation may append * key/value pairs to @details to return extra information to @caller. * * The default implementation of this method simply returns @implicit. * * Returns: A #PolkitImplicitAuthorization that specifies if the subject is authorized or whether * authentication is required. */ PolkitImplicitAuthorization polkit_backend_interactive_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details, PolkitImplicitAuthorization implicit) { PolkitBackendInteractiveAuthorityClass *klass; PolkitImplicitAuthorization ret; klass = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_CLASS (authority); if (klass->check_authorization_sync == NULL) { ret = implicit; } else { ret = klass->check_authorization_sync (authority, caller, subject, user_for_subject, subject_is_local, subject_is_active, action_id, details, implicit); } return ret; } /** * polkit_backend_interactive_authority_reload: * @authority: A #PolkitBackendInteractiveAuthority. * * Reload configuration files. */ void polkit_backend_interactive_authority_reload (PolkitBackendInteractiveAuthority *authority) { PolkitBackendInteractiveAuthorityPrivate *priv; PolkitBackendJsAuthority *jsauthority; if (!authority) return; priv = polkit_backend_interactive_authority_get_instance_private (authority); polkit_backend_action_pool_reload (priv->action_pool); jsauthority = POLKIT_BACKEND_JS_AUTHORITY (authority); polkit_backend_common_reload_scripts (jsauthority); } /* ---------------------------------------------------------------------------------------------------- */ struct AuthenticationSession { AuthenticationAgent *agent; gchar *cookie; PolkitSubject *subject; PolkitIdentity *user_of_subject; PolkitSubject *caller; PolkitBackendInteractiveAuthority *authority; GList *identities; gchar *action_id; PolkitDetails *details; gchar *initiated_by_system_bus_unique_name; PolkitImplicitAuthorization implicit_authorization; AuthenticationAgentCallback callback; gpointer user_data; guint call_id; gboolean is_authenticated; PolkitIdentity *authenticated_identity; GCancellable *cancellable; gulong cancellable_signal_handler_id; }; static void authentication_session_cancelled_cb (GCancellable *cancellable, AuthenticationSession *session) { authentication_session_cancel (session); } /* We're not calling this a UUID, but it's basically * the same thing, just not formatted that way because: * * - I'm too lazy to do it * - If we did, people might think it was actually * generated from /dev/random, which we're not doing * because this value doesn't actually need to be * globally unique. */ static void append_rand_u128_str (GString *buf, GRand *pool) { g_string_append_printf (buf, "%08x%08x%08x%08x", g_rand_int (pool), g_rand_int (pool), g_rand_int (pool), g_rand_int (pool)); } /* A value that should be unique to the (AuthenticationAgent, AuthenticationSession) * pair, and not guessable by other agents. * * - - - * * See http://lists.freedesktop.org/archives/polkit-devel/2015-June/000425.html * */ static gchar * authentication_agent_generate_cookie (AuthenticationAgent *agent) { GString *buf = g_string_new (""); g_string_append (buf, agent->cookie_prefix); g_string_append_c (buf, '-'); agent->cookie_serial++; g_string_append_printf (buf, "%" G_GUINT64_FORMAT, agent->cookie_serial); g_string_append_c (buf, '-'); append_rand_u128_str (buf, agent->cookie_pool); return g_string_free (buf, FALSE); } static AuthenticationSession * authentication_session_new (AuthenticationAgent *agent, PolkitSubject *subject, PolkitIdentity *user_of_subject, PolkitSubject *caller, PolkitBackendInteractiveAuthority *authority, GList *identities, const gchar *action_id, PolkitDetails *details, const gchar *initiated_by_system_bus_unique_name, PolkitImplicitAuthorization implicit_authorization, GCancellable *cancellable, AuthenticationAgentCallback callback, gpointer user_data) { AuthenticationSession *session; session = g_new0 (AuthenticationSession, 1); session->agent = authentication_agent_ref (agent); session->cookie = authentication_agent_generate_cookie (agent); session->subject = g_object_ref (subject); session->user_of_subject = g_object_ref (user_of_subject); session->caller = g_object_ref (caller); session->authority = g_object_ref (authority); session->identities = g_list_copy (identities); g_list_foreach (session->identities, (GFunc) g_object_ref, NULL); session->action_id = g_strdup (action_id); session->details = g_object_ref (details); session->initiated_by_system_bus_unique_name = g_strdup (initiated_by_system_bus_unique_name); session->implicit_authorization = implicit_authorization; session->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL; session->callback = callback; session->user_data = user_data; if (session->cancellable != NULL) { session->cancellable_signal_handler_id = g_signal_connect (session->cancellable, "cancelled", G_CALLBACK (authentication_session_cancelled_cb), session); } return session; } static void authentication_session_free (AuthenticationSession *session) { authentication_agent_unref (session->agent); g_free (session->cookie); g_list_foreach (session->identities, (GFunc) g_object_unref, NULL); g_list_free (session->identities); g_object_unref (session->subject); g_object_unref (session->user_of_subject); g_object_unref (session->caller); g_object_unref (session->authority); g_free (session->action_id); g_object_unref (session->details); g_free (session->initiated_by_system_bus_unique_name); if (session->cancellable_signal_handler_id > 0) g_signal_handler_disconnect (session->cancellable, session->cancellable_signal_handler_id); if (session->authenticated_identity != NULL) g_object_unref (session->authenticated_identity); if (session->cancellable != NULL) g_object_unref (session->cancellable); g_free (session); } static PolkitSubject * authentication_agent_get_scope (AuthenticationAgent *agent) { return agent->scope; } static void authentication_agent_cancel_all_sessions (AuthenticationAgent *agent) { /* cancel all active authentication sessions; use a copy of the list since * callbacks will modify the list */ if (agent->active_sessions != NULL) { GList *l; GList *active_sessions; active_sessions = g_list_copy (agent->active_sessions); for (l = active_sessions; l != NULL; l = l->next) { AuthenticationSession *session = l->data; authentication_session_cancel (session); } g_list_free (active_sessions); } } static AuthenticationAgent * authentication_agent_ref (AuthenticationAgent *agent) { g_atomic_int_inc (&agent->ref_count); return agent; } static void authentication_agent_unref (AuthenticationAgent *agent) { if (g_atomic_int_dec_and_test (&agent->ref_count)) { if (agent->proxy != NULL) g_object_unref (agent->proxy); g_object_unref (agent->scope); g_free (agent->locale); g_free (agent->object_path); g_free (agent->unique_system_bus_name); if (agent->registration_options != NULL) g_variant_unref (agent->registration_options); g_rand_free (agent->cookie_pool); g_free (agent->cookie_prefix); g_free (agent); } } static AuthenticationAgent * authentication_agent_new (guint64 serial, PolkitSubject *scope, PolkitIdentity *creator, const gchar *unique_system_bus_name, const gchar *locale, const gchar *object_path, GVariant *registration_options, GError **error) { AuthenticationAgent *agent; GDBusProxy *proxy; PolkitUnixUser *creator_user; g_assert (POLKIT_IS_UNIX_USER (creator)); creator_user = POLKIT_UNIX_USER (creator); if (!g_variant_is_object_path (object_path)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Invalid object path '%s'", object_path); return NULL; } proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, /* GDBusInterfaceInfo* */ unique_system_bus_name, object_path, "org.freedesktop.PolicyKit1.AuthenticationAgent", NULL, /* GCancellable* */ error); if (proxy == NULL) { g_prefix_error (error, "Failed to construct proxy for agent: " ); return NULL; } agent = g_new0 (AuthenticationAgent, 1); agent->ref_count = 1; agent->serial = serial; agent->scope = g_object_ref (scope); agent->creator_uid = (uid_t)polkit_unix_user_get_uid (creator_user); agent->object_path = g_strdup (object_path); agent->unique_system_bus_name = g_strdup (unique_system_bus_name); agent->locale = g_strdup (locale); agent->registration_options = registration_options != NULL ? g_variant_ref (registration_options) : NULL; agent->proxy = proxy; { GString *cookie_prefix = g_string_new (""); GRand *agent_private_rand = g_rand_new (); g_string_append_printf (cookie_prefix, "%" G_GUINT64_FORMAT "-", agent->serial); /* Use a uniquely seeded PRNG to get a prefix cookie for this agent, * whose sequence will not correlate with the per-authentication session * cookies. */ append_rand_u128_str (cookie_prefix, agent_private_rand); g_rand_free (agent_private_rand); agent->cookie_prefix = g_string_free (cookie_prefix, FALSE); /* And a newly seeded pool for per-session cookies */ agent->cookie_pool = g_rand_new (); } return agent; } static AuthenticationAgent * get_authentication_agent_for_subject (PolkitBackendInteractiveAuthority *authority, PolkitSubject *subject) { PolkitBackendInteractiveAuthorityPrivate *priv; PolkitSubject *session_for_subject = NULL; AuthenticationAgent *agent = NULL; AuthenticationAgent *agent_fallback = NULL; gboolean fallback = FALSE; priv = polkit_backend_interactive_authority_get_instance_private (authority); agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, subject); if (agent == NULL && POLKIT_IS_SYSTEM_BUS_NAME (subject)) { PolkitSubject *process; process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, NULL); if (process != NULL) { agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, process); g_object_unref (process); } } if (agent != NULL) { /* We have an agent! Now see if we should use this as a fallback only */ if (agent->registration_options != NULL && g_variant_lookup (agent->registration_options, "fallback", "b", &fallback) && fallback) { agent_fallback = agent; agent = NULL; } else { /* Nope, use it */ goto out; } } /* Now, we should also cover the case where @subject is a * UnixProcess but the agent is a SystemBusName. However, this can't * happen because we only allow registering agents for UnixProcess * and UnixSession subjects! */ session_for_subject = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor, subject, NULL); if (session_for_subject == NULL) goto out; agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, session_for_subject); /* use fallback, if available */ if (agent == NULL && agent_fallback != NULL) agent = agent_fallback; out: if (session_for_subject != NULL) g_object_unref (session_for_subject); return agent; } static AuthenticationSession * get_authentication_session_for_uid_and_cookie (PolkitBackendInteractiveAuthority *authority, uid_t uid, const gchar *cookie) { PolkitBackendInteractiveAuthorityPrivate *priv; GHashTableIter hash_iter; AuthenticationAgent *agent; AuthenticationSession *result; result = NULL; /* TODO: perhaps use a hash on the cookie to speed this up */ priv = polkit_backend_interactive_authority_get_instance_private (authority); g_hash_table_iter_init (&hash_iter, priv->hash_scope_to_authentication_agent); while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &agent)) { GList *l; /* We need to ensure that if somehow we have duplicate cookies * due to wrapping, that the cookie used is matched to the user * who called AuthenticationAgentResponse2. See * http://lists.freedesktop.org/archives/polkit-devel/2015-June/000425.html * * Except if the legacy AuthenticationAgentResponse is invoked, * we don't know the uid and hence use -1. Continue to support * the old behavior for backwards compatibility, although everyone * who is using our own setuid helper will automatically be updated * to the new API. */ if (uid != (uid_t)-1) { if (agent->creator_uid != uid) continue; } for (l = agent->active_sessions; l != NULL; l = l->next) { AuthenticationSession *session = l->data; if (strcmp (session->cookie, cookie) == 0) { result = session; goto out; } } } out: return result; } static GList * get_authentication_sessions_initiated_by_system_bus_unique_name (PolkitBackendInteractiveAuthority *authority, const gchar *system_bus_unique_name) { PolkitBackendInteractiveAuthorityPrivate *priv; GHashTableIter hash_iter; AuthenticationAgent *agent; GList *result; result = NULL; /* TODO: perhaps use a hash on the cookie to speed this up */ priv = polkit_backend_interactive_authority_get_instance_private (authority); g_hash_table_iter_init (&hash_iter, priv->hash_scope_to_authentication_agent); while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &agent)) { GList *l; for (l = agent->active_sessions; l != NULL; l = l->next) { AuthenticationSession *session = l->data; if (strcmp (session->initiated_by_system_bus_unique_name, system_bus_unique_name) == 0) { result = g_list_prepend (result, session); } } } return result; } static GList * get_authentication_sessions_for_system_bus_unique_name_subject (PolkitBackendInteractiveAuthority *authority, const gchar *system_bus_unique_name) { PolkitBackendInteractiveAuthorityPrivate *priv; GHashTableIter hash_iter; AuthenticationAgent *agent; GList *result; result = NULL; /* TODO: perhaps use a hash on the cookie to speed this up */ priv = polkit_backend_interactive_authority_get_instance_private (authority); g_hash_table_iter_init (&hash_iter, priv->hash_scope_to_authentication_agent); while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &agent)) { GList *l; for (l = agent->active_sessions; l != NULL; l = l->next) { AuthenticationSession *session = l->data; if (POLKIT_IS_SYSTEM_BUS_NAME (session->subject) && strcmp (polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (session->subject)), system_bus_unique_name) == 0) { result = g_list_prepend (result, session); } } } return result; } static AuthenticationAgent * get_authentication_agent_by_unique_system_bus_name (PolkitBackendInteractiveAuthority *authority, const gchar *unique_system_bus_name) { PolkitBackendInteractiveAuthorityPrivate *priv; GHashTableIter hash_iter; AuthenticationAgent *agent; priv = polkit_backend_interactive_authority_get_instance_private (authority); g_hash_table_iter_init (&hash_iter, priv->hash_scope_to_authentication_agent); while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &agent)) { if (strcmp (agent->unique_system_bus_name, unique_system_bus_name) == 0) goto out; } agent = NULL; out: return agent; } static void authentication_agent_begin_cb (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { AuthenticationSession *session = user_data; gboolean gained_authorization; gboolean was_dismissed; GVariant *result; GError *error; was_dismissed = FALSE; gained_authorization = FALSE; error = NULL; result = g_dbus_proxy_call_finish (proxy, res, &error); if (result == NULL) { g_printerr ("Error performing authentication: %s (%s %d)\n", error->message, g_quark_to_string (error->domain), error->code); if (error->domain == POLKIT_ERROR && error->code == POLKIT_ERROR_CANCELLED) was_dismissed = TRUE; g_error_free (error); } else { g_variant_unref (result); gained_authorization = session->is_authenticated; g_debug ("Authentication complete, is_authenticated = %d", session->is_authenticated); } session->agent->active_sessions = g_list_remove (session->agent->active_sessions, session); session->callback (session->agent, session->subject, session->user_of_subject, session->caller, session->authority, session->action_id, session->details, session->implicit_authorization, gained_authorization, was_dismissed, session->authenticated_identity, session->user_data); authentication_session_free (session); } static void append_property (GString *dest, PolkitDetails *details, const gchar *key, PolkitBackendInteractiveAuthority *authority, const gchar *message, const gchar *action_id) { const gchar *value; value = polkit_details_lookup (details, key); if (value != NULL) { g_string_append (dest, value); } else { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_ERROR, "Error substituting value for property $(%s) when preparing message `%s' for action-id %s", key, message, action_id); g_string_append (dest, "$("); g_string_append (dest, key); g_string_append (dest, ")"); } } static gchar * expand_properties (const gchar *message, PolkitDetails *details, PolkitBackendInteractiveAuthority *authority, const gchar *action_id) { GString *ret; GString *var; guint n; gboolean in_resolve; ret = g_string_new (NULL); var = g_string_new (NULL); in_resolve = FALSE; for (n = 0; message[n] != '\0'; n++) { gint c = message[n]; if (c == '$' && message[n+1] == '(') { in_resolve = TRUE; n += 1; } else { if (in_resolve) { if (c == ')') { append_property (ret, details, var->str, authority, message, action_id); g_string_set_size (var, 0); in_resolve = FALSE; } else { g_string_append_c (var, c); } } else { g_string_append_c (ret, c); } } } g_string_free (var, TRUE); return g_string_free (ret, FALSE); } static void get_localized_data_for_challenge (PolkitBackendInteractiveAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_of_subject, const gchar *action_id, PolkitDetails *details, const gchar *locale, gchar **out_localized_message, gchar **out_localized_icon_name, PolkitDetails **out_localized_details) { PolkitBackendInteractiveAuthorityPrivate *priv; PolkitActionDescription *action_desc; gchar *message; gchar *icon_name; PolkitDetails *localized_details; const gchar *message_to_use; const gchar *gettext_domain; gchar *s; priv = polkit_backend_interactive_authority_get_instance_private (authority); message = NULL; icon_name = NULL; localized_details = NULL; action_desc = NULL; *out_localized_message = NULL; *out_localized_icon_name = NULL; *out_localized_details = NULL; action_desc = polkit_backend_action_pool_get_action (priv->action_pool, action_id, locale); if (action_desc == NULL) goto out; /* Set LANG and locale so g_dgettext() + friends work below */ if (setlocale (LC_ALL, locale) == NULL) { g_printerr ("Invalid locale '%s'\n", locale); } /* if LANGUAGE have been set in /etc/default, set LANG is invalid. */ g_setenv ("LANGUAGE", locale, TRUE); gettext_domain = polkit_details_lookup (details, "polkit.gettext_domain"); message_to_use = polkit_details_lookup (details, "polkit.message"); if (message_to_use != NULL) { message = g_strdup (g_dgettext (gettext_domain, message_to_use)); /* g_print ("locale=%s, domain=%s, msg=`%s' -> `%s'\n", locale, gettext_domain, message_to_use, message); */ } icon_name = g_strdup (polkit_details_lookup (details, "polkit.icon_name")); /* fall back to action description */ if (message == NULL) { message = g_strdup (polkit_action_description_get_message (action_desc)); } if (icon_name == NULL) { icon_name = g_strdup (polkit_action_description_get_icon_name (action_desc)); } /* replace $(property) with values */ if (message != NULL) { s = message; message = expand_properties (message, details, authority, action_id); g_free (s); } /* Back to C! */ setlocale (LC_ALL, "C"); g_setenv ("LANGUAGE", "C", TRUE); out: if (message == NULL) message = g_strdup (""); if (icon_name == NULL) icon_name = g_strdup (""); *out_localized_message = message; *out_localized_icon_name = icon_name; *out_localized_details = localized_details; if (action_desc != NULL) g_object_unref (action_desc); } static void add_pid (PolkitDetails *details, PolkitSubject *subject, const gchar *key) { gchar buf[32]; gint pid; if (POLKIT_IS_UNIX_PROCESS (subject)) { pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject)); } else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { PolkitSubject *process; GError *error; error = NULL; process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, &error); if (process == NULL) { g_printerr ("Error getting process for system bus name `%s': %s\n", polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)), error->message); g_error_free (error); goto out; } pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process)); g_object_unref (process); } else if (POLKIT_IS_UNIX_SESSION (subject)) { goto out; } else { gchar *s; s = polkit_subject_to_string (subject); g_printerr ("Don't know how to get pid from subject of type %s: %s\n", g_type_name (G_TYPE_FROM_INSTANCE (subject)), s); g_free (s); goto out; } g_snprintf (buf, sizeof (buf), "%d", pid); polkit_details_insert (details, key, buf); out: ; } /* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */ static GList * get_users_in_group (PolkitIdentity *group, PolkitIdentity *user_of_subject, gboolean include_root) { gid_t gid; uid_t uid_of_subject; struct group *grp; GList *ret; guint n; ret = NULL; gid = polkit_unix_group_get_gid (POLKIT_UNIX_GROUP (group)); /* Check if group is subject's primary group. */ uid_of_subject = polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_of_subject)); if (uid_of_subject != 0 || include_root) { struct passwd *pwd; pwd = getpwuid (uid_of_subject); if (pwd != NULL && pwd->pw_gid == gid) ret = g_list_prepend (ret, g_object_ref (user_of_subject)); } /* Add supplemental group members. */ grp = getgrgid (gid); if (grp == NULL) { g_warning ("Error looking up group with gid %d: %s", gid, g_strerror (errno)); goto out; } for (n = 0; grp->gr_mem != NULL && grp->gr_mem[n] != NULL; n++) { PolkitIdentity *user; GError *error; if (!include_root && g_strcmp0 (grp->gr_mem[n], "root") == 0) continue; error = NULL; user = polkit_unix_user_new_for_name (grp->gr_mem[n], &error); if (user == NULL) { g_warning ("Unknown username '%s' in group: %s", grp->gr_mem[n], error->message); g_error_free (error); } else { ret = g_list_prepend (ret, user); } } ret = g_list_reverse (ret); out: return ret; } static GList * get_users_in_net_group (PolkitIdentity *group, gboolean include_root) { const gchar *name; GList *ret; ret = NULL; #ifdef HAVE_SETNETGRENT name = polkit_unix_netgroup_get_name (POLKIT_UNIX_NETGROUP (group)); # ifdef HAVE_SETNETGRENT_RETURN if (setnetgrent (name) == 0) { g_warning ("Error looking up net group with name %s: %s", name, g_strerror (errno)); goto out; } # else setnetgrent (name); # endif /* HAVE_SETNETGRENT_RETURN */ for (;;) { # if defined(HAVE_NETBSD) || defined(HAVE_OPENBSD) const char *hostname, *username, *domainname; # else char *hostname, *username, *domainname; # endif /* defined(HAVE_NETBSD) || defined(HAVE_OPENBSD) */ PolkitIdentity *user; GError *error = NULL; if (getnetgrent (&hostname, &username, &domainname) == 0) break; /* Skip NULL entries since we never want to make everyone an admin * Skip "-" entries which mean "no match ever" in netgroup land */ if (username == NULL || g_strcmp0 (username, "-") == 0) continue; /* TODO: Should we match on hostname? Maybe only allow "-" as a hostname * for safety. */ user = polkit_unix_user_new_for_name (username, &error); if (user == NULL) { g_warning ("Unknown username '%s' in unix-netgroup: %s", username, error->message); g_error_free (error); } else { ret = g_list_prepend (ret, user); } } ret = g_list_reverse (ret); out: endnetgrent (); #endif /* HAVE_SETNETGRENT */ return ret; } /* ---------------------------------------------------------------------------------------------------- */ static void authentication_agent_initiate_challenge (AuthenticationAgent *agent, PolkitSubject *subject, PolkitIdentity *user_of_subject, PolkitBackendInteractiveAuthority *authority, const gchar *action_id, PolkitDetails *details, PolkitSubject *caller, PolkitImplicitAuthorization implicit_authorization, GCancellable *cancellable, AuthenticationAgentCallback callback, gpointer user_data) { PolkitBackendInteractiveAuthorityPrivate *priv = polkit_backend_interactive_authority_get_instance_private (authority); AuthenticationSession *session; GList *l; GList *identities; gchar *localized_message; gchar *localized_icon_name; PolkitDetails *localized_details; GList *user_identities = NULL; GVariantBuilder identities_builder; GVariant *parameters; get_localized_data_for_challenge (authority, caller, subject, user_of_subject, action_id, details, agent->locale, &localized_message, &localized_icon_name, &localized_details); identities = NULL; /* select admin user if required by the implicit authorization */ if (implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED || implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED) { gboolean is_local = FALSE; gboolean is_active = FALSE; PolkitSubject *session_for_subject = NULL; session_for_subject = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor, subject, NULL); if (session_for_subject != NULL) { is_local = polkit_backend_session_monitor_is_session_local (priv->session_monitor, session_for_subject); is_active = polkit_backend_session_monitor_is_session_active (priv->session_monitor, session_for_subject); } identities = polkit_backend_interactive_authority_get_admin_identities (authority, caller, subject, user_of_subject, is_local, is_active, action_id, details); g_clear_object (&session_for_subject); } else { identities = g_list_prepend (identities, g_object_ref (user_of_subject)); } /* expand groups/netgroups to users */ user_identities = NULL; for (l = identities; l != NULL; l = l->next) { PolkitIdentity *identity = POLKIT_IDENTITY (l->data); if (POLKIT_IS_UNIX_USER (identity)) { user_identities = g_list_append (user_identities, g_object_ref (identity)); } else if (POLKIT_IS_UNIX_GROUP (identity)) { user_identities = g_list_concat (user_identities, get_users_in_group (identity, user_of_subject, FALSE)); } else if (POLKIT_IS_UNIX_NETGROUP (identity)) { user_identities = g_list_concat (user_identities, get_users_in_net_group (identity, FALSE)); } else { g_warning ("Unsupported identity"); } } /* Fall back to uid 0 if no users are available (rhbz #834494) */ if (user_identities == NULL) user_identities = g_list_prepend (NULL, polkit_unix_user_new (0)); session = authentication_session_new (agent, subject, user_of_subject, caller, authority, user_identities, action_id, details, polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller)), implicit_authorization, cancellable, callback, user_data); agent->active_sessions = g_list_prepend (agent->active_sessions, session); if (localized_details == NULL) localized_details = polkit_details_new (); add_pid (localized_details, caller, "polkit.caller-pid"); add_pid (localized_details, subject, "polkit.subject-pid"); g_variant_builder_init (&identities_builder, G_VARIANT_TYPE ("a(sa{sv})")); for (l = user_identities; l != NULL; l = l->next) { PolkitIdentity *identity = POLKIT_IDENTITY (l->data); g_variant_builder_add_value (&identities_builder, polkit_identity_to_gvariant (identity)); /* A floating value */ } parameters = g_variant_new ("(sss@a{ss}sa(sa{sv}))", action_id, localized_message, localized_icon_name, polkit_details_to_gvariant (localized_details), /* A floating value */ session->cookie, &identities_builder); g_dbus_proxy_call (agent->proxy, "BeginAuthentication", parameters, /* consumes the floating GVariant */ G_DBUS_CALL_FLAGS_NONE, G_MAXINT, /* timeout_msec - no timeout */ session->cancellable, (GAsyncReadyCallback) authentication_agent_begin_cb, session); g_list_free_full (user_identities, g_object_unref); g_list_foreach (identities, (GFunc) g_object_unref, NULL); g_list_free (identities); g_free (localized_message); g_free (localized_icon_name); if (localized_details != NULL) g_object_unref (localized_details); } static void authentication_agent_cancel_cb (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { GVariant *result; GError *error; error = NULL; result = g_dbus_proxy_call_finish (proxy, res, &error); if (result == NULL) { g_printerr ("Error cancelling authentication: %s\n", error->message); g_error_free (error); } else g_variant_unref (result); } static void authentication_session_cancel (AuthenticationSession *session) { g_dbus_proxy_call (session->agent->proxy, "CancelAuthentication", g_variant_new ("(s)", session->cookie), G_DBUS_CALL_FLAGS_NONE, -1, /* timeout_msec */ NULL, /* GCancellable* */ (GAsyncReadyCallback) authentication_agent_cancel_cb, NULL); } /* ---------------------------------------------------------------------------------------------------- */ static gboolean polkit_backend_interactive_authority_register_authentication_agent (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *locale, const gchar *object_path, GVariant *options, GError **error) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; PolkitSubject *session_for_caller; PolkitIdentity *user_of_caller; PolkitIdentity *user_of_subject; gboolean user_of_subject_matches; AuthenticationAgent *agent; gboolean ret; gchar *caller_cmdline; gchar *subject_as_string; ret = FALSE; session_for_caller = NULL; user_of_caller = NULL; user_of_subject = NULL; subject_as_string = NULL; caller_cmdline = NULL; agent = NULL; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); if (POLKIT_IS_UNIX_SESSION (subject)) { session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor, caller, NULL); if (session_for_caller == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine session the caller is in"); goto out; } if (!polkit_subject_equal (session_for_caller, subject)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Passed session and the session the caller is in differs. They must be equal for now."); goto out; } } else if (POLKIT_IS_UNIX_PROCESS (subject)) { /* explicitly OK */ } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Only unix-process and unix-session subjects can be used for authentication agents."); goto out; } user_of_caller = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, caller, NULL, NULL); if (user_of_caller == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine user of caller"); goto out; } user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, subject, &user_of_subject_matches, NULL); if (user_of_subject == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine user of subject"); goto out; } if (!user_of_subject_matches || !polkit_identity_equal (user_of_caller, user_of_subject)) { if (identity_is_root_user (user_of_caller)) { /* explicitly allow uid 0 to register for other users */ } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "User of caller and user of subject differs."); goto out; } } agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, subject); if (agent != NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "An authentication agent already exists for the given subject"); goto out; } priv->agent_serial++; agent = authentication_agent_new (priv->agent_serial, subject, user_of_subject,/*fix pkexec fails with No session for cookie, upstream issue:https://gitlab.freedesktop.org/polkit/polkit/issues/17*/ polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller)), locale, object_path, options, error); if (!agent) goto out; g_hash_table_insert (priv->hash_scope_to_authentication_agent, g_object_ref (subject), agent); caller_cmdline = _polkit_subject_get_cmdline (caller); if (caller_cmdline == NULL) caller_cmdline = g_strdup (""); subject_as_string = polkit_subject_to_string (subject); g_debug ("Added authentication agent for %s at name %s [%s], object path %s, locale %s", subject_as_string, polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller)), caller_cmdline, object_path, locale); polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Registered Authentication Agent for %s " "(system bus name %s [%s], object path %s, locale %s)", subject_as_string, polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller)), caller_cmdline, object_path, locale); g_signal_emit_by_name (authority, "changed"); ret = TRUE; out: g_free (caller_cmdline); g_free (subject_as_string); if (user_of_caller != NULL) g_object_unref (user_of_caller); if (user_of_subject != NULL) g_object_unref (user_of_subject); if (session_for_caller != NULL) g_object_unref (session_for_caller); return ret; } static gboolean polkit_backend_interactive_authority_unregister_authentication_agent (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, const gchar *object_path, GError **error) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; PolkitSubject *session_for_caller; PolkitIdentity *user_of_caller; PolkitIdentity *user_of_subject; gboolean user_of_subject_matches; AuthenticationAgent *agent; gboolean ret; gchar *scope_str; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); ret = FALSE; session_for_caller = NULL; user_of_caller = NULL; user_of_subject = NULL; if (POLKIT_IS_UNIX_SESSION (subject)) { session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor, caller, NULL); if (session_for_caller == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine session the caller is in"); goto out; } if (!polkit_subject_equal (session_for_caller, subject)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Passed session and the session the caller is in differs. They must be equal for now."); goto out; } } else if (POLKIT_IS_UNIX_PROCESS (subject)) { /* explicitly OK */ } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Only unix-process and unix-session subjects can be used for authentication agents."); goto out; } user_of_caller = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, caller, NULL, NULL); if (user_of_caller == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine user of caller"); goto out; } user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, subject, &user_of_subject_matches, NULL); if (user_of_subject == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine user of subject"); goto out; } if (!user_of_subject_matches || !polkit_identity_equal (user_of_caller, user_of_subject)) { if (identity_is_root_user (user_of_caller)) { /* explicitly allow uid 0 to register for other users */ } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "User of caller and user of subject differs."); goto out; } } agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, subject); if (agent == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "No such agent registered"); goto out; } if (g_strcmp0 (agent->unique_system_bus_name, polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller))) != 0) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "System bus names do not match"); goto out; } if (g_strcmp0 (agent->object_path, object_path) != 0) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Object paths do not match"); goto out; } scope_str = polkit_subject_to_string (agent->scope); g_debug ("Removing authentication agent for %s at name %s, object path %s, locale %s", scope_str, agent->unique_system_bus_name, agent->object_path, agent->locale); polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Unregistered Authentication Agent for %s " "(system bus name %s, object path %s, locale %s)", scope_str, agent->unique_system_bus_name, agent->object_path, agent->locale); g_free (scope_str); authentication_agent_cancel_all_sessions (agent); /* this works because we have exactly one agent per session */ /* this frees agent... */ g_hash_table_remove (priv->hash_scope_to_authentication_agent, agent->scope); g_signal_emit_by_name (authority, "changed"); ret = TRUE; out: if (user_of_caller != NULL) g_object_unref (user_of_caller); if (user_of_subject != NULL) g_object_unref (user_of_subject); if (session_for_caller != NULL) g_object_unref (session_for_caller); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean polkit_backend_interactive_authority_authentication_agent_response (PolkitBackendAuthority *authority, PolkitSubject *caller, uid_t uid, const gchar *cookie, PolkitIdentity *identity, GError **error) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; PolkitIdentity *user_of_caller; gchar *identity_str; AuthenticationSession *session; GList *l; gboolean ret; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); ret = FALSE; user_of_caller = NULL; identity_str = polkit_identity_to_string (identity); g_debug ("In authentication_agent_response for cookie '%s' and identity %s", cookie, identity_str); user_of_caller = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, caller, NULL, error); if (user_of_caller == NULL) goto out; /* only uid 0 is allowed to invoke this method */ if (!identity_is_root_user (user_of_caller)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Only uid 0 may invoke this method. This incident has been logged."); /* TODO: actually log this */ goto out; } /* find the authentication session */ session = get_authentication_session_for_uid_and_cookie (interactive_authority, uid, cookie); if (session == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "No session for cookie"); goto out; } /* check that the authentication identity was one of the possibilities we allowed */ for (l = session->identities; l != NULL; l = l->next) { PolkitIdentity *i = POLKIT_IDENTITY (l->data); if (polkit_identity_equal (i, identity)) break; } if (l == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "The authenticated identity is wrong"); goto out; } /* checks out, mark the session as authenticated */ session->is_authenticated = TRUE; session->authenticated_identity = g_object_ref (identity); ret = TRUE; out: g_free (identity_str); if (user_of_caller != NULL) g_object_unref (user_of_caller); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static void polkit_backend_interactive_authority_system_bus_name_owner_changed (PolkitBackendInteractiveAuthority *authority, const gchar *name, const gchar *old_owner, const gchar *new_owner) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); //g_debug ("name-owner-changed: '%s' '%s' '%s'", name, old_owner, new_owner); if (name[0] == ':' && strlen (new_owner) == 0) { AuthenticationAgent *agent; GList *sessions; GList *l; agent = get_authentication_agent_by_unique_system_bus_name (interactive_authority, name); if (agent != NULL) { gchar *scope_str; scope_str = polkit_subject_to_string (agent->scope); g_debug ("Removing authentication agent for %s at name %s, object path %s (disconnected from bus)", scope_str, agent->unique_system_bus_name, agent->object_path); polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Unregistered Authentication Agent for %s " "(system bus name %s, object path %s, locale %s) (disconnected from bus)", scope_str, agent->unique_system_bus_name, agent->object_path, agent->locale); g_free (scope_str); authentication_agent_cancel_all_sessions (agent); /* this works because we have exactly one agent per session */ /* this frees agent... */ g_hash_table_remove (priv->hash_scope_to_authentication_agent, agent->scope); g_signal_emit_by_name (authority, "changed"); } /* cancel all authentication sessions initiated by the process owning the vanished name */ sessions = get_authentication_sessions_initiated_by_system_bus_unique_name (interactive_authority, name); for (l = sessions; l != NULL; l = l->next) { AuthenticationSession *session = l->data; authentication_session_cancel (session); } g_list_free (sessions); /* cancel all authentication sessions that is about the vanished name */ sessions = get_authentication_sessions_for_system_bus_unique_name_subject (interactive_authority, name); for (l = sessions; l != NULL; l = l->next) { AuthenticationSession *session = l->data; authentication_session_cancel (session); } g_list_free (sessions); /* remove all temporary authorizations that applies to the vanished name * (temporary_authorization_store_add_authorization for the code path for handling processes) */ temporary_authorization_store_remove_authorizations_for_system_bus_name (priv->temporary_authorization_store, name); } } /* ---------------------------------------------------------------------------------------------------- */ typedef struct TemporaryAuthorization TemporaryAuthorization; struct TemporaryAuthorizationStore { GList *authorizations; PolkitBackendInteractiveAuthority *authority; guint64 serial; }; struct TemporaryAuthorization { TemporaryAuthorizationStore *store; PolkitSubject *subject; PolkitSubject *scope; gchar *id; gchar *action_id; /* both of these are obtained using g_get_monotonic_time(), * so the resolution is usec */ gint64 time_granted; gint64 time_expires; guint expiration_timeout_id; guint check_vanished_timeout_id; }; static void temporary_authorization_free (TemporaryAuthorization *authorization) { g_free (authorization->id); g_object_unref (authorization->subject); g_object_unref (authorization->scope); g_free (authorization->action_id); if (authorization->expiration_timeout_id > 0) g_source_remove (authorization->expiration_timeout_id); if (authorization->check_vanished_timeout_id > 0) g_source_remove (authorization->check_vanished_timeout_id); g_free (authorization); } static TemporaryAuthorizationStore * temporary_authorization_store_new (PolkitBackendInteractiveAuthority *authority) { TemporaryAuthorizationStore *store; store = g_new0 (TemporaryAuthorizationStore, 1); store->authority = authority; store->authorizations = NULL; return store; } static void temporary_authorization_store_free (TemporaryAuthorizationStore *store) { g_list_foreach (store->authorizations, (GFunc) temporary_authorization_free, NULL); g_list_free (store->authorizations); g_free (store); } /* XXX: for now, prefer to store the process; see * https://bugs.freedesktop.org/show_bug.cgi?id=23867 */ static PolkitSubject * convert_temporary_authorization_subject (PolkitSubject *subject) { PolkitSubject *ret; if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { GError *error = NULL; ret = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, &error); if (ret == NULL) { g_printerr ("Error getting process for system bus name `%s': %s\n", polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)), error->message); g_error_free (error); return g_object_ref (subject); } else { return ret; } } else { return g_object_ref (subject); } } /* See the comment at the top of polkitunixprocess.c */ static gboolean subject_equal_for_authz (PolkitSubject *a, PolkitSubject *b) { if (!polkit_subject_equal (a, b)) return FALSE; /* Now special case unix processes, as we want to protect against * pid reuse by including the PID FDs or UIDs as a fallback. */ if (POLKIT_IS_UNIX_PROCESS (a) && POLKIT_IS_UNIX_PROCESS (b)) { /* If both objects are tracking via PID FD then we can rely on that, * as the PID is resolved on-the-fly via the pinned file descriptor, * and it will be -1 if the process exited in the meanwhile. */ if (polkit_unix_process_get_pidfd ((PolkitUnixProcess*)a) >= 0 && polkit_unix_process_get_pidfd ((PolkitUnixProcess*)b) >= 0) { int pid_a = polkit_unix_process_get_pid ((PolkitUnixProcess*)a); int pid_b = polkit_unix_process_get_pid ((PolkitUnixProcess*)b); return pid_a > 0 && pid_b > 0 && pid_a == pid_b; } int uid_a = polkit_unix_process_get_uid ((PolkitUnixProcess*)a); int uid_b = polkit_unix_process_get_uid ((PolkitUnixProcess*)b); if (uid_a != -1 && uid_b != -1) { if (uid_a == uid_b) { return TRUE; } else { g_printerr ("denying slowfork; pid %d uid %d != %d!\n", polkit_unix_process_get_pid ((PolkitUnixProcess*)a), uid_a, uid_b); return FALSE; } } /* Fall through; one of the uids is unset so we can't reliably compare */ } return TRUE; } static gboolean temporary_authorization_store_has_authorization (TemporaryAuthorizationStore *store, PolkitSubject *subject, const gchar *action_id, const gchar **out_tmp_authz_id) { GList *l; gboolean ret; PolkitSubject *subject_to_use; g_return_val_if_fail (store != NULL, FALSE); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE); g_return_val_if_fail (action_id != NULL, FALSE); subject_to_use = convert_temporary_authorization_subject (subject); ret = FALSE; for (l = store->authorizations; l != NULL; l = l->next) { TemporaryAuthorization *authorization = l->data; if (strcmp (action_id, authorization->action_id) == 0 && subject_equal_for_authz (subject_to_use, authorization->subject)) { ret = TRUE; if (out_tmp_authz_id != NULL) *out_tmp_authz_id = authorization->id; goto out; } } out: g_object_unref (subject_to_use); return ret; } static gboolean on_expiration_timeout (gpointer user_data) { TemporaryAuthorization *authorization = user_data; gchar *s; s = polkit_subject_to_string (authorization->subject); g_debug ("Removing tempoary authorization with id `%s' for action-id `%s' for subject `%s': " "authorization has expired", authorization->id, authorization->action_id, s); g_free (s); authorization->store->authorizations = g_list_remove (authorization->store->authorizations, authorization); authorization->expiration_timeout_id = 0; g_signal_emit_by_name (authorization->store->authority, "changed"); temporary_authorization_free (authorization); /* remove source */ return FALSE; } static gboolean on_unix_process_check_vanished_timeout (gpointer user_data) { TemporaryAuthorization *authorization = user_data; GError *error; /* we know that this is a PolkitUnixProcess so the check is fast (no IPC involved) */ error = NULL; if (!polkit_subject_exists_sync (authorization->subject, NULL, &error)) { if (error != NULL) { g_printerr ("Error checking if process exists: %s\n", error->message); g_error_free (error); } else { gchar *s; s = polkit_subject_to_string (authorization->subject); g_debug ("Removing tempoary authorization with id `%s' for action-id `%s' for subject `%s': " "subject has vanished", authorization->id, authorization->action_id, s); g_free (s); authorization->store->authorizations = g_list_remove (authorization->store->authorizations, authorization); g_signal_emit_by_name (authorization->store->authority, "changed"); temporary_authorization_free (authorization); } } /* keep source around */ return TRUE; } static void temporary_authorization_store_remove_authorizations_for_system_bus_name (TemporaryAuthorizationStore *store, const gchar *name) { guint num_removed; GList *l, *ll; num_removed = 0; for (l = store->authorizations; l != NULL; l = ll) { TemporaryAuthorization *ta = l->data; gchar *s; ll = l->next; if (!POLKIT_IS_SYSTEM_BUS_NAME (ta->subject)) continue; if (g_strcmp0 (name, polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (ta->subject))) != 0) continue; s = polkit_subject_to_string (ta->subject); g_debug ("Removing tempoary authorization with id `%s' for action-id `%s' for subject `%s': " "subject has vanished", ta->id, ta->action_id, s); g_free (s); store->authorizations = g_list_remove (store->authorizations, ta); temporary_authorization_free (ta); num_removed++; } if (num_removed > 0) g_signal_emit_by_name (store->authority, "changed"); } static const gchar * temporary_authorization_store_add_authorization (TemporaryAuthorizationStore *store, PolkitSubject *subject, PolkitSubject *scope, const gchar *action_id) { TemporaryAuthorization *authorization; guint expiration_seconds; PolkitSubject *subject_to_use; g_return_val_if_fail (store != NULL, NULL); g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), NULL); g_return_val_if_fail (action_id != NULL, NULL); g_return_val_if_fail (!temporary_authorization_store_has_authorization (store, subject, action_id, NULL), NULL); subject_to_use = convert_temporary_authorization_subject (subject); /* TODO: right now the time the temporary authorization is kept is hard-coded - we * could make it a propery on the PolkitBackendInteractiveAuthority class (so * the local authority could read it from a config file) or a vfunc * (so the local authority could read it from an annotation on the action). */ expiration_seconds = 5 * 60; authorization = g_new0 (TemporaryAuthorization, 1); authorization->id = g_strdup_printf ("tmpauthz%" G_GUINT64_FORMAT, store->serial++); authorization->store = store; authorization->subject = g_object_ref (subject_to_use); authorization->scope = g_object_ref (scope); authorization->action_id = g_strdup (action_id); /* store monotonic time and convert to secs-since-epoch when returning TemporaryAuthorization structs */ authorization->time_granted = g_get_monotonic_time (); authorization->time_expires = authorization->time_granted + expiration_seconds * G_USEC_PER_SEC; /* g_timeout_add() is using monotonic time since 2.28 */ authorization->expiration_timeout_id = g_timeout_add (expiration_seconds * 1000, on_expiration_timeout, authorization); if (POLKIT_IS_UNIX_PROCESS (authorization->subject)) { /* For now, set up a timer to poll every two seconds - this is used to determine * when the process vanishes. We want to do this so we can remove the temporary * authorization - this is because we want agents to update e.g. a notification * area icon saying the user has temporary authorizations (e.g. remove the icon). * * Ideally we'd just do * * g_signal_connect (kernel, "process-exited", G_CALLBACK (on_process_exited), user_data); * * but that is not how things work right now (and, hey, it's not like the kernel * is a GObject either!) - so we poll. * * TODO: On Linux, it might be possible to obtain notifications by connecting * to the netlink socket. Needs looking into. */ authorization->check_vanished_timeout_id = g_timeout_add_seconds (2, on_unix_process_check_vanished_timeout, authorization); } #if 0 else if (POLKIT_IS_SYSTEM_BUS_NAME (authorization->subject)) { /* This is currently handled in polkit_backend_interactive_authority_system_bus_name_owner_changed() */ } #endif store->authorizations = g_list_prepend (store->authorizations, authorization); g_object_unref (subject_to_use); return authorization->id; } /* ---------------------------------------------------------------------------------------------------- */ static GList * polkit_backend_interactive_authority_enumerate_temporary_authorizations (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; PolkitSubject *session_for_caller; GList *ret; GList *l; gint64 monotonic_now; gint64 real_now; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); ret = NULL; session_for_caller = NULL; if (!POLKIT_IS_UNIX_SESSION (subject)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Can only handle PolkitUnixSession objects for now."); goto out; } session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor, caller, NULL); if (session_for_caller == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine session the caller is in"); goto out; } if (!polkit_subject_equal (session_for_caller, subject)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Passed session and the session the caller is in differs. They must be equal for now."); goto out; } monotonic_now = g_get_monotonic_time (); real_now = g_get_real_time () / G_TIME_SPAN_SECOND; for (l = priv->temporary_authorization_store->authorizations; l != NULL; l = l->next) { TemporaryAuthorization *ta = l->data; PolkitTemporaryAuthorization *tmp_authz; guint64 real_granted; guint64 real_expires; if (!polkit_subject_equal (ta->scope, subject)) continue; real_granted = (ta->time_granted - monotonic_now) / G_USEC_PER_SEC + real_now; real_expires = (ta->time_expires - monotonic_now) / G_USEC_PER_SEC + real_now; tmp_authz = polkit_temporary_authorization_new (ta->id, ta->action_id, ta->subject, real_granted, real_expires); ret = g_list_prepend (ret, tmp_authz); } out: if (session_for_caller != NULL) g_object_unref (session_for_caller); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean polkit_backend_interactive_authority_revoke_temporary_authorizations (PolkitBackendAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, GError **error) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; PolkitSubject *session_for_caller; gboolean ret; GList *l; GList *ll; guint num_removed; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); ret = FALSE; session_for_caller = NULL; if (!POLKIT_IS_UNIX_SESSION (subject)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Can only handle PolkitUnixSession objects for now."); goto out; } session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor, caller, NULL); if (session_for_caller == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine session the caller is in"); goto out; } if (!polkit_subject_equal (session_for_caller, subject)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Passed session and the session the caller is in differs. They must be equal for now."); goto out; } num_removed = 0; for (l = priv->temporary_authorization_store->authorizations; l != NULL; l = ll) { TemporaryAuthorization *ta = l->data; ll = l->next; if (!polkit_subject_equal (ta->scope, subject)) continue; priv->temporary_authorization_store->authorizations = g_list_remove (priv->temporary_authorization_store->authorizations, ta); temporary_authorization_free (ta); num_removed++; } if (num_removed > 0) g_signal_emit_by_name (authority, "changed"); ret = TRUE; out: if (session_for_caller != NULL) g_object_unref (session_for_caller); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean polkit_backend_interactive_authority_revoke_temporary_authorization_by_id (PolkitBackendAuthority *authority, PolkitSubject *caller, const gchar *id, GError **error) { PolkitBackendInteractiveAuthority *interactive_authority; PolkitBackendInteractiveAuthorityPrivate *priv; PolkitSubject *session_for_caller; gboolean ret; GList *l; GList *ll; guint num_removed; interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); priv = polkit_backend_interactive_authority_get_instance_private (interactive_authority); ret = FALSE; session_for_caller = NULL; session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor, caller, NULL); if (session_for_caller == NULL) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot determine session the caller is in"); goto out; } num_removed = 0; for (l = priv->temporary_authorization_store->authorizations; l != NULL; l = ll) { TemporaryAuthorization *ta = l->data; ll = l->next; if (strcmp (ta->id, id) != 0) continue; if (!polkit_subject_equal (session_for_caller, ta->scope)) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Cannot remove a temporary authorization belonging to another subject."); goto out; } priv->temporary_authorization_store->authorizations = g_list_remove (priv->temporary_authorization_store->authorizations, ta); temporary_authorization_free (ta); num_removed++; } if (num_removed > 0) { g_signal_emit_by_name (authority, "changed"); } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "No such authorization with id `%s'", id); goto out; } ret = TRUE; out: if (session_for_caller != NULL) g_object_unref (session_for_caller); return ret; } /* ---------------------------------------------------------------------------------------------------- */ polkit-126/src/polkitbackend/polkitbackendinteractiveauthority.h000066400000000000000000000204711474122443600254740ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_BACKEND_INTERACTIVE_AUTHORITY_H #define __POLKIT_BACKEND_INTERACTIVE_AUTHORITY_H #include #include #include G_BEGIN_DECLS #define POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY (polkit_backend_interactive_authority_get_type ()) #define POLKIT_BACKEND_INTERACTIVE_AUTHORITY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY, PolkitBackendInteractiveAuthority)) #define POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY, PolkitBackendInteractiveAuthorityClass)) #define POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY,PolkitBackendInteractiveAuthorityClass)) #define POLKIT_BACKEND_IS_INTERACTIVE_AUTHORITY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY)) #define POLKIT_BACKEND_IS_INTERACTIVE_AUTHORITY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY)) typedef struct _PolkitBackendInteractiveAuthorityClass PolkitBackendInteractiveAuthorityClass; /** * PolkitBackendInteractiveAuthority: * * The #PolkitBackendInteractiveAuthority struct should not be accessed directly. */ struct _PolkitBackendInteractiveAuthority { /*< private >*/ PolkitBackendAuthority parent_instance; }; /** * PolkitBackendInteractiveAuthorityClass: * @parent_class: The parent class. * @get_admin_identities: Returns list of identities for administrator authentication or %NULL to use the default * implementation. See polkit_backend_interactive_authority_get_admin_identities() for details. * @check_authorization_sync: Checks for an authorization or %NULL to use the default implementation. * See polkit_backend_interactive_authority_check_authorization_sync() for details. * * Class structure for #PolkitBackendInteractiveAuthority. */ struct _PolkitBackendInteractiveAuthorityClass { /*< public >*/ PolkitBackendAuthorityClass parent_class; /* VTable */ GList * (*get_admin_identities) (PolkitBackendInteractiveAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details); PolkitImplicitAuthorization (*check_authorization_sync) (PolkitBackendInteractiveAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details, PolkitImplicitAuthorization implicit); /*< private >*/ /* Padding for future expansion */ void (*_polkit_reserved1) (void); void (*_polkit_reserved2) (void); void (*_polkit_reserved3) (void); void (*_polkit_reserved4) (void); void (*_polkit_reserved5) (void); void (*_polkit_reserved6) (void); void (*_polkit_reserved7) (void); void (*_polkit_reserved8) (void); void (*_polkit_reserved9) (void); void (*_polkit_reserved10) (void); void (*_polkit_reserved11) (void); void (*_polkit_reserved12) (void); void (*_polkit_reserved13) (void); void (*_polkit_reserved14) (void); void (*_polkit_reserved15) (void); void (*_polkit_reserved16) (void); void (*_polkit_reserved17) (void); void (*_polkit_reserved18) (void); void (*_polkit_reserved19) (void); void (*_polkit_reserved20) (void); void (*_polkit_reserved21) (void); void (*_polkit_reserved22) (void); void (*_polkit_reserved23) (void); void (*_polkit_reserved24) (void); void (*_polkit_reserved25) (void); void (*_polkit_reserved26) (void); void (*_polkit_reserved27) (void); void (*_polkit_reserved28) (void); void (*_polkit_reserved29) (void); void (*_polkit_reserved30) (void); void (*_polkit_reserved31) (void); void (*_polkit_reserved32) (void); }; GType polkit_backend_interactive_authority_get_type (void) G_GNUC_CONST; GList *polkit_backend_interactive_authority_get_admin_identities (PolkitBackendInteractiveAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details); PolkitImplicitAuthorization polkit_backend_interactive_authority_check_authorization_sync ( PolkitBackendInteractiveAuthority *authority, PolkitSubject *caller, PolkitSubject *subject, PolkitIdentity *user_for_subject, gboolean subject_is_local, gboolean subject_is_active, const gchar *action_id, PolkitDetails *details, PolkitImplicitAuthorization implicit); void polkit_backend_interactive_authority_reload (PolkitBackendInteractiveAuthority *authority); G_END_DECLS #endif /* __POLKIT_BACKEND_INTERACTIVE_AUTHORITY_H */ polkit-126/src/polkitbackend/polkitbackendjsauthority.h000066400000000000000000000054611474122443600235750ustar00rootroot00000000000000/* * Copyright (C) 2008-2012 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef __POLKIT_BACKEND_JS_AUTHORITY_H #define __POLKIT_BACKEND_JS_AUTHORITY_H #include #include #include G_BEGIN_DECLS #define POLKIT_BACKEND_TYPE_JS_AUTHORITY (polkit_backend_js_authority_get_type ()) #define POLKIT_BACKEND_JS_AUTHORITY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_BACKEND_TYPE_JS_AUTHORITY, PolkitBackendJsAuthority)) #define POLKIT_BACKEND_JS_AUTHORITY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), POLKIT_BACKEND_TYPE_JS_AUTHORITY, PolkitBackendJsAuthorityClass)) #define POLKIT_BACKEND_JS_AUTHORITY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_BACKEND_TYPE_JS_AUTHORITY,PolkitBackendJsAuthorityClass)) #define POLKIT_BACKEND_IS_JS_AUTHORITY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_BACKEND_TYPE_JS_AUTHORITY)) #define POLKIT_BACKEND_IS_JS_AUTHORITY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_BACKEND_TYPE_JS_AUTHORITY)) typedef struct _PolkitBackendJsAuthorityClass PolkitBackendJsAuthorityClass; typedef struct _PolkitBackendJsAuthorityPrivate PolkitBackendJsAuthorityPrivate; /** * PolkitBackendJsAuthority: * * The #PolkitBackendJsAuthority struct should not be accessed directly. */ struct _PolkitBackendJsAuthority { /*< private >*/ PolkitBackendInteractiveAuthority parent_instance; PolkitBackendJsAuthorityPrivate *priv; }; /** * PolkitBackendJsAuthorityClass: * @parent_class: The parent class. * * Class structure for #PolkitBackendJsAuthority. */ struct _PolkitBackendJsAuthorityClass { /*< public >*/ PolkitBackendInteractiveAuthorityClass parent_class; }; GType polkit_backend_js_authority_get_type (void) G_GNUC_CONST; G_END_DECLS #endif /* __POLKIT_BACKEND_JS_AUTHORITY_H */ polkit-126/src/polkitbackend/polkitbackendprivate.h000066400000000000000000000020761474122443600226610ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_BACKEND_COMPILATION) || defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) #error "This is a private header file." #endif #ifndef __POLKIT_BACKEND_PRIVATE_H #define __POLKIT_BACKEND_PRIVATE_H #endif /* __POLKIT_BACKEND_PRIVATE_H */ polkit-126/src/polkitbackend/polkitbackendsessionmonitor-systemd.c000066400000000000000000000326611474122443600257660ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Matthias Clasen */ #include #include #include #include #include #include #include #include #include #include "polkitbackendsessionmonitor.h" /* * SECTION:polkitbackendsessionmonitor * @title: PolkitBackendSessionMonitor * @short_description: Monitor sessions * * The #PolkitBackendSessionMonitor class is a utility class to track and monitor sessions. */ typedef struct { GSource source; GPollFD pollfd; sd_login_monitor *monitor; } SdSource; static gboolean sd_source_prepare (GSource *source, gint *timeout) { *timeout = -1; return FALSE; } static gboolean sd_source_check (GSource *source) { SdSource *sd_source = (SdSource *)source; return sd_source->pollfd.revents != 0; } static gboolean sd_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { SdSource *sd_source = (SdSource *)source; gboolean ret; g_warn_if_fail (callback != NULL); if (callback == NULL) return G_SOURCE_CONTINUE; ret = (*callback) (user_data); sd_login_monitor_flush (sd_source->monitor); return ret; } static void sd_source_finalize (GSource *source) { SdSource *sd_source = (SdSource*)source; sd_login_monitor_unref (sd_source->monitor); } static GSourceFuncs sd_source_funcs = { sd_source_prepare, sd_source_check, sd_source_dispatch, sd_source_finalize }; static GSource * sd_source_new (void) { GSource *source; SdSource *sd_source; int ret; source = g_source_new (&sd_source_funcs, sizeof (SdSource)); sd_source = (SdSource *)source; if ((ret = sd_login_monitor_new ("session", &sd_source->monitor)) < 0) { g_printerr ("Error getting login monitor: %d\n", ret); } else { sd_source->pollfd.fd = sd_login_monitor_get_fd (sd_source->monitor); sd_source->pollfd.events = G_IO_IN; g_source_add_poll (source, &sd_source->pollfd); } return source; } struct _PolkitBackendSessionMonitor { GObject parent_instance; GDBusConnection *system_bus; GSource *sd_source; }; struct _PolkitBackendSessionMonitorClass { GObjectClass parent_class; void (*changed) (PolkitBackendSessionMonitor *monitor); }; enum { CHANGED_SIGNAL, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; G_DEFINE_TYPE (PolkitBackendSessionMonitor, polkit_backend_session_monitor, G_TYPE_OBJECT); /* ---------------------------------------------------------------------------------------------------- */ static gboolean sessions_changed (gpointer user_data) { PolkitBackendSessionMonitor *monitor = POLKIT_BACKEND_SESSION_MONITOR (user_data); g_signal_emit (monitor, signals[CHANGED_SIGNAL], 0); return TRUE; } static void polkit_backend_session_monitor_init (PolkitBackendSessionMonitor *monitor) { GError *error; error = NULL; monitor->system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (monitor->system_bus == NULL) { g_printerr ("Error getting system bus: %s", error->message); g_error_free (error); } monitor->sd_source = sd_source_new (); g_source_set_callback (monitor->sd_source, sessions_changed, monitor, NULL); g_source_attach (monitor->sd_source, NULL); } static void polkit_backend_session_monitor_finalize (GObject *object) { PolkitBackendSessionMonitor *monitor = POLKIT_BACKEND_SESSION_MONITOR (object); if (monitor->system_bus != NULL) g_object_unref (monitor->system_bus); if (monitor->sd_source != NULL) { g_source_destroy (monitor->sd_source); g_source_unref (monitor->sd_source); } if (G_OBJECT_CLASS (polkit_backend_session_monitor_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_backend_session_monitor_parent_class)->finalize (object); } static void polkit_backend_session_monitor_class_init (PolkitBackendSessionMonitorClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_backend_session_monitor_finalize; /** * PolkitBackendSessionMonitor::changed: * @monitor: A #PolkitBackendSessionMonitor * * Emitted when something changes. */ signals[CHANGED_SIGNAL] = g_signal_new ("changed", POLKIT_BACKEND_TYPE_SESSION_MONITOR, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (PolkitBackendSessionMonitorClass, changed), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } PolkitBackendSessionMonitor * polkit_backend_session_monitor_new (void) { PolkitBackendSessionMonitor *monitor; monitor = POLKIT_BACKEND_SESSION_MONITOR (g_object_new (POLKIT_BACKEND_TYPE_SESSION_MONITOR, NULL)); return monitor; } /* ---------------------------------------------------------------------------------------------------- */ GList * polkit_backend_session_monitor_get_sessions (PolkitBackendSessionMonitor *monitor) { /* TODO */ return NULL; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_backend_session_monitor_get_user: * @monitor: A #PolkitBackendSessionMonitor. * @subject: A #PolkitSubject. * @result_matches: If not %NULL, set to indicate whether the return value matches current (RACY) state. * @error: Return location for error. * * Gets the user corresponding to @subject or %NULL if no user exists. * * NOTE: For a #PolkitUnixProcess, the UID is read from @subject (which may * come from e.g. a D-Bus client), so it may not correspond to the actual UID * of the referenced process (at any point in time). This is indicated by * setting @result_matches to %FALSE; the caller may reject such subjects or * require additional privileges. @result_matches == %TRUE only indicates that * the UID matched the underlying process at ONE point in time, it may not match * later. * * Returns: %NULL if @error is set otherwise a #PolkitUnixUser that should be freed with g_object_unref(). */ PolkitIdentity * polkit_backend_session_monitor_get_user_for_subject (PolkitBackendSessionMonitor *monitor, PolkitSubject *subject, gboolean *result_matches, GError **error) { PolkitIdentity *ret; gboolean matches; ret = NULL; matches = FALSE; if (POLKIT_IS_UNIX_PROCESS (subject)) { gint subject_uid, current_uid; GError *local_error; subject_uid = polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)); if (subject_uid == -1) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Unix process subject does not have uid set"); goto out; } local_error = NULL; current_uid = polkit_unix_process_get_racy_uid__ (POLKIT_UNIX_PROCESS (subject), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); goto out; } ret = polkit_unix_user_new (subject_uid); matches = (subject_uid == current_uid); } else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { ret = (PolkitIdentity*)polkit_system_bus_name_get_user_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error); matches = TRUE; } else if (POLKIT_IS_UNIX_SESSION (subject)) { uid_t uid; if (sd_session_get_uid (polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (subject)), &uid) < 0) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Error getting uid for session"); goto out; } ret = polkit_unix_user_new (uid); matches = TRUE; } out: if (result_matches != NULL) { *result_matches = matches; } return ret; } /** * polkit_backend_session_monitor_get_session_for_subject: * @monitor: A #PolkitBackendSessionMonitor. * @subject: A #PolkitSubject. * @error: Return location for error. * * Gets the session corresponding to @subject or %NULL if no session exists. * * Returns: %NULL if @error is set otherwise a #PolkitUnixSession that should be freed with g_object_unref(). */ PolkitSubject * polkit_backend_session_monitor_get_session_for_subject (PolkitBackendSessionMonitor *monitor, PolkitSubject *subject, GError **error) { PolkitUnixProcess *tmp_process = NULL; PolkitUnixProcess *process = NULL; PolkitSubject *session = NULL; char *session_id = NULL; pid_t pid; #if HAVE_SD_UID_GET_DISPLAY uid_t uid; #endif #if HAVE_SD_PIDFD_GET_SESSION int pidfd; #endif if (POLKIT_IS_UNIX_PROCESS (subject)) process = POLKIT_UNIX_PROCESS (subject); /* We already have a process */ else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { /* Convert bus name to process */ tmp_process = (PolkitUnixProcess*)polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error); if (!tmp_process) goto out; process = tmp_process; } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Cannot get session for subject of type %s", g_type_name (G_TYPE_FROM_INSTANCE (subject))); goto out; } #if HAVE_SD_PIDFD_GET_SESSION /* First try to get the session from the pidfd (systemd version 253) */ pidfd = polkit_unix_process_get_pidfd (process); if (pidfd >= 0) { if (sd_pidfd_get_session (pidfd, &session_id) >= 0) { session = polkit_unix_session_new (session_id); goto out; } } #endif /* Now do process -> pid -> same session */ g_assert (process != NULL); pid = polkit_unix_process_get_pid (process); if (sd_pid_get_session (pid, &session_id) >= 0) { session = polkit_unix_session_new (session_id); goto out; } #if HAVE_SD_PIDFD_GET_SESSION /* Now do process fd -> uid -> graphical session (systemd version 253) */ pidfd = polkit_unix_process_get_pidfd (process); if (pidfd >= 0) { if (sd_pidfd_get_owner_uid (pidfd, &uid) < 0) goto out; if (sd_uid_get_display (uid, &session_id) >= 0) { session = polkit_unix_session_new (session_id); goto out; } } #endif #if HAVE_SD_UID_GET_DISPLAY /* Now do process -> uid -> graphical session (systemd version 213)*/ if (sd_pid_get_owner_uid (pid, &uid) < 0) goto out; if (sd_uid_get_display (uid, &session_id) >= 0) { session = polkit_unix_session_new (session_id); goto out; } #endif out: free (session_id); if (tmp_process) g_object_unref (tmp_process); return session; } gboolean polkit_backend_session_monitor_is_session_local (PolkitBackendSessionMonitor *monitor, PolkitSubject *session) { char *seat; if (!sd_session_get_seat (polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (session)), &seat)) { free (seat); return TRUE; } return FALSE; } gboolean polkit_backend_session_monitor_is_session_active (PolkitBackendSessionMonitor *monitor, PolkitSubject *session) { const char *session_id; char *state; uid_t uid; gboolean is_active = FALSE; session_id = polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (session)); g_debug ("Checking whether session %s is active.", session_id); /* Check whether *any* of the user's current sessions are active. */ if (sd_session_get_uid (session_id, &uid) < 0) goto fallback; g_debug ("Session %s has UID %u.", session_id, uid); if (sd_uid_get_state (uid, &state) < 0) goto fallback; g_debug ("UID %u has state %s.", uid, state); is_active = (g_strcmp0 (state, "active") == 0); free (state); return is_active; fallback: /* Fall back to checking the session. This is not ideal, since the user * might have multiple sessions, and we cannot guarantee to have chosen * the active one. * * See: https://bugs.freedesktop.org/show_bug.cgi?id=76358. */ return sd_session_is_active (session_id); } polkit-126/src/polkitbackend/polkitbackendsessionmonitor.c000066400000000000000000000401221474122443600242670ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #include #include #include #include "polkitbackendsessionmonitor.h" #define CKDB_PATH "/var/run/ConsoleKit/database" /* * SECTION:polkitbackendsessionmonitor * @title: PolkitBackendSessionMonitor * @short_description: Monitor sessions * * The #PolkitBackendSessionMonitor class is a utility class to track and monitor sessions. */ struct _PolkitBackendSessionMonitor { GObject parent_instance; GDBusConnection *system_bus; GKeyFile *database; GFileMonitor *database_monitor; time_t database_mtime; }; struct _PolkitBackendSessionMonitorClass { GObjectClass parent_class; void (*changed) (PolkitBackendSessionMonitor *monitor); }; enum { CHANGED_SIGNAL, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; G_DEFINE_TYPE (PolkitBackendSessionMonitor, polkit_backend_session_monitor, G_TYPE_OBJECT); /* ---------------------------------------------------------------------------------------------------- */ static gboolean reload_database (PolkitBackendSessionMonitor *monitor, GError **error) { gboolean ret; struct stat statbuf; ret = FALSE; if (monitor->database != NULL) { g_key_file_free (monitor->database); monitor->database = NULL; } if (stat (CKDB_PATH, &statbuf) != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Error statting file " CKDB_PATH ": %s", strerror (errno)); goto out; } monitor->database_mtime = statbuf.st_mtime; monitor->database = g_key_file_new (); if (!g_key_file_load_from_file (monitor->database, CKDB_PATH, G_KEY_FILE_NONE, error)) { goto out; } ret = TRUE; out: return ret; } static gboolean ensure_database (PolkitBackendSessionMonitor *monitor, GError **error) { gboolean ret = FALSE; if (monitor->database != NULL) { struct stat statbuf; if (stat (CKDB_PATH, &statbuf) != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Error statting file " CKDB_PATH " to check timestamp: %s", strerror (errno)); goto out; } if (statbuf.st_mtime == monitor->database_mtime) { ret = TRUE; goto out; } } ret = reload_database (monitor, error); out: return ret; } static void on_file_monitor_changed (GFileMonitor *file_monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data) { PolkitBackendSessionMonitor *monitor = POLKIT_BACKEND_SESSION_MONITOR (user_data); /* throw away cache */ if (monitor->database != NULL) { g_key_file_free (monitor->database); monitor->database = NULL; } g_signal_emit (monitor, signals[CHANGED_SIGNAL], 0); } static void polkit_backend_session_monitor_init (PolkitBackendSessionMonitor *monitor) { GError *error; GFile *file; error = NULL; monitor->system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (monitor->system_bus == NULL) { g_printerr ("Error getting system bus: %s", error->message); g_error_free (error); } error = NULL; if (!ensure_database (monitor, &error)) { g_printerr ("Error loading " CKDB_PATH ": %s", error->message); g_error_free (error); } error = NULL; file = g_file_new_for_path (CKDB_PATH); monitor->database_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error); g_object_unref (file); if (monitor->database_monitor == NULL) { g_printerr ("Error monitoring " CKDB_PATH ": %s", error->message); g_error_free (error); } else { g_signal_connect (monitor->database_monitor, "changed", G_CALLBACK (on_file_monitor_changed), monitor); } } static void polkit_backend_session_monitor_finalize (GObject *object) { PolkitBackendSessionMonitor *monitor = POLKIT_BACKEND_SESSION_MONITOR (object); if (monitor->system_bus != NULL) g_object_unref (monitor->system_bus); if (monitor->database_monitor != NULL) g_object_unref (monitor->database_monitor); if (monitor->database != NULL) g_key_file_free (monitor->database); if (G_OBJECT_CLASS (polkit_backend_session_monitor_parent_class)->finalize != NULL) G_OBJECT_CLASS (polkit_backend_session_monitor_parent_class)->finalize (object); } static void polkit_backend_session_monitor_class_init (PolkitBackendSessionMonitorClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = polkit_backend_session_monitor_finalize; /** * PolkitBackendSessionMonitor::changed: * @monitor: A #PolkitBackendSessionMonitor * * Emitted when something changes. */ signals[CHANGED_SIGNAL] = g_signal_new ("changed", POLKIT_BACKEND_TYPE_SESSION_MONITOR, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (PolkitBackendSessionMonitorClass, changed), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } PolkitBackendSessionMonitor * polkit_backend_session_monitor_new (void) { PolkitBackendSessionMonitor *monitor; monitor = POLKIT_BACKEND_SESSION_MONITOR (g_object_new (POLKIT_BACKEND_TYPE_SESSION_MONITOR, NULL)); return monitor; } /* ---------------------------------------------------------------------------------------------------- */ GList * polkit_backend_session_monitor_get_sessions (PolkitBackendSessionMonitor *monitor) { /* TODO */ return NULL; } /* ---------------------------------------------------------------------------------------------------- */ /** * polkit_backend_session_monitor_get_user: * @monitor: A #PolkitBackendSessionMonitor. * @subject: A #PolkitSubject. * @result_matches: If not %NULL, set to indicate whether the return value matches current (RACY) state. * @error: Return location for error. * * Gets the user corresponding to @subject or %NULL if no user exists. * * NOTE: For a #PolkitUnixProcess, the UID is read from @subject (which may * come from e.g. a D-Bus client), so it may not correspond to the actual UID * of the referenced process (at any point in time). This is indicated by * setting @result_matches to %FALSE; the caller may reject such subjects or * require additional privileges. @result_matches == %TRUE only indicates that * the UID matched the underlying process at ONE point in time, it may not match * later. * * Returns: %NULL if @error is set otherwise a #PolkitUnixUser that should be freed with g_object_unref(). */ PolkitIdentity * polkit_backend_session_monitor_get_user_for_subject (PolkitBackendSessionMonitor *monitor, PolkitSubject *subject, gboolean *result_matches, GError **error) { PolkitIdentity *ret; gboolean matches; GError *local_error; ret = NULL; matches = FALSE; if (POLKIT_IS_UNIX_PROCESS (subject)) { gint subject_uid, current_uid; subject_uid = polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)); if (subject_uid == -1) { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Unix process subject does not have uid set"); goto out; } local_error = NULL; current_uid = polkit_unix_process_get_racy_uid__ (POLKIT_UNIX_PROCESS (subject), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); goto out; } ret = polkit_unix_user_new (subject_uid); matches = (subject_uid == current_uid); } else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { ret = (PolkitIdentity*)polkit_system_bus_name_get_user_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error); matches = TRUE; } else if (POLKIT_IS_UNIX_SESSION (subject)) { gint uid; gchar *group; if (!ensure_database (monitor, error)) { g_prefix_error (error, "Error getting user for session: Error ensuring CK database at " CKDB_PATH ": "); goto out; } group = g_strdup_printf ("Session %s", polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (subject))); local_error = NULL; uid = g_key_file_get_integer (monitor->database, group, "uid", &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "Error getting uid using " CKDB_PATH ": "); g_free (group); goto out; } g_free (group); ret = polkit_unix_user_new (uid); matches = TRUE; } out: if (result_matches != NULL) { *result_matches = matches; } return ret; } /** * polkit_backend_session_monitor_get_session_for_subject: * @monitor: A #PolkitBackendSessionMonitor. * @subject: A #PolkitSubject. * @error: Return location for error. * * Gets the session corresponding to @subject or %NULL if no session exists. * * Returns: %NULL if @error is set otherwise a #PolkitUnixSession that should be freed with g_object_unref(). */ PolkitSubject * polkit_backend_session_monitor_get_session_for_subject (PolkitBackendSessionMonitor *monitor, PolkitSubject *subject, GError **error) { PolkitSubject *session; session = NULL; if (POLKIT_IS_UNIX_PROCESS (subject)) { const gchar *session_id; GVariant *result; result = g_dbus_connection_call_sync (monitor->system_bus, "org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "GetSessionForUnixProcess", g_variant_new ("(u)", polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject))), G_VARIANT_TYPE ("(o)"), G_DBUS_CALL_FLAGS_NONE, -1, /* timeout_msec */ NULL, /* GCancellable */ error); if (result == NULL) goto out; g_variant_get (result, "(&o)", &session_id); session = polkit_unix_session_new (session_id); g_variant_unref (result); } else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { guint32 pid; const gchar *session_id; GVariant *result; result = g_dbus_connection_call_sync (monitor->system_bus, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetConnectionUnixProcessID", g_variant_new ("(s)", polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject))), G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, /* timeout_msec */ NULL, /* GCancellable */ error); if (result == NULL) goto out; g_variant_get (result, "(u)", &pid); g_variant_unref (result); result = g_dbus_connection_call_sync (monitor->system_bus, "org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "GetSessionForUnixProcess", g_variant_new ("(u)", pid), G_VARIANT_TYPE ("(o)"), G_DBUS_CALL_FLAGS_NONE, -1, /* timeout_msec */ NULL, /* GCancellable */ error); if (result == NULL) goto out; g_variant_get (result, "(&o)", &session_id); session = polkit_unix_session_new (session_id); g_variant_unref (result); } else { g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_NOT_SUPPORTED, "Cannot get user for subject of type %s", g_type_name (G_TYPE_FROM_INSTANCE (subject))); } out: return session; } static gboolean get_boolean (PolkitBackendSessionMonitor *monitor, PolkitSubject *session, const gchar *key_name) { gboolean ret; gchar *group; GError *error; ret = FALSE; group = g_strdup_printf ("Session %s", polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (session))); error = NULL; if (!ensure_database (monitor, &error)) { g_printerr ("Error getting boolean `%s' in group `%s': Error ensuring CK database at " CKDB_PATH ": %s", key_name, group, error->message); g_error_free (error); goto out; } error = NULL; ret = g_key_file_get_boolean (monitor->database, group, key_name, &error); if (error != NULL) { g_printerr ("Error looking %s using " CKDB_PATH " for %s: %s\n", key_name, group, error->message); g_error_free (error); goto out; } out: g_free (group); return ret; } gboolean polkit_backend_session_monitor_is_session_local (PolkitBackendSessionMonitor *monitor, PolkitSubject *session) { return get_boolean (monitor, session, "is_local"); } gboolean polkit_backend_session_monitor_is_session_active (PolkitBackendSessionMonitor *monitor, PolkitSubject *session) { return get_boolean (monitor, session, "is_active"); } polkit-126/src/polkitbackend/polkitbackendsessionmonitor.h000066400000000000000000000073331474122443600243030ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #if !defined (_POLKIT_BACKEND_COMPILATION) || defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) #error "This is a private header file." #endif #ifndef __POLKIT_BACKEND_SESSION_MONITOR_H #define __POLKIT_BACKEND_SESSION_MONITOR_H #include #include G_BEGIN_DECLS #define POLKIT_BACKEND_TYPE_SESSION_MONITOR (polkit_backend_session_monitor_get_type ()) #define POLKIT_BACKEND_SESSION_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_BACKEND_TYPE_SESSION_MONITOR, PolkitBackendSessionMonitor)) #define POLKIT_BACKEND_SESSION_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), POLKIT_BACKEND_TYPE_SESSION_MONITOR, PolkitBackendSessionMonitorClass)) #define POLKIT_BACKEND_SESSION_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_BACKEND_TYPE_SESSION_MONITOR,PolkitBackendSessionMonitorClass)) #define POLKIT_BACKEND_IS_SESSION_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_BACKEND_TYPE_SESSION_MONITOR)) #define POLKIT_BACKEND_IS_SESSION_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_BACKEND_TYPE_SESSION_MONITOR)) typedef struct _PolkitBackendSessionMonitor PolkitBackendSessionMonitor; typedef struct _PolkitBackendSessionMonitorClass PolkitBackendSessionMonitorClass; GType polkit_backend_session_monitor_get_type (void) G_GNUC_CONST; PolkitBackendSessionMonitor *polkit_backend_session_monitor_new (void); GList *polkit_backend_session_monitor_get_sessions (PolkitBackendSessionMonitor *monitor); PolkitIdentity *polkit_backend_session_monitor_get_user_for_subject (PolkitBackendSessionMonitor *monitor, PolkitSubject *subject, gboolean *result_matches, GError **error); PolkitSubject *polkit_backend_session_monitor_get_session_for_subject (PolkitBackendSessionMonitor *monitor, PolkitSubject *subject, GError **error); gboolean polkit_backend_session_monitor_is_session_local (PolkitBackendSessionMonitor *monitor, PolkitSubject *session); gboolean polkit_backend_session_monitor_is_session_active (PolkitBackendSessionMonitor *monitor, PolkitSubject *session); G_END_DECLS #endif /* __POLKIT_BACKEND_SESSION_MONITOR_H */ polkit-126/src/polkitbackend/polkitbackendtypes.h000066400000000000000000000026101474122443600223450ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #ifndef __POLKIT_BACKEND_TYPES_H #define __POLKIT_BACKEND_TYPES_H #include struct _PolkitBackendActionLookup; typedef struct _PolkitBackendActionLookup PolkitBackendActionLookup; /* Dummy typedef */ struct _PolkitBackendAuthority; typedef struct _PolkitBackendAuthority PolkitBackendAuthority; struct _PolkitBackendInteractiveAuthority; typedef struct _PolkitBackendInteractiveAuthority PolkitBackendInteractiveAuthority; struct _PolkitBackendJsAuthority; typedef struct _PolkitBackendJsAuthority PolkitBackendJsAuthority; #endif /* __POLKIT_BACKEND_TYPES_H */ polkit-126/src/polkitbackend/polkitd.c000066400000000000000000000225461474122443600201210ustar00rootroot00000000000000/* * Copyright (C) 2008-2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #include #include #include #ifdef HAVE_LIBSYSTEMD # include #endif /* ---------------------------------------------------------------------------------------------------- */ static PolkitBackendAuthority *authority = NULL; static gpointer registration_id = NULL; static GMainLoop *loop = NULL; static gint exit_status = EXIT_FAILURE; static gboolean opt_replace = FALSE; static gboolean opt_no_debug = FALSE; static gchar *opt_log_level = "notice"; static GOptionEntry opt_entries[] = { {"replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace, "Replace existing daemon", NULL}, {"no-debug", 'n', 0, G_OPTION_ARG_NONE, &opt_no_debug, "Don't print debug information to stderr and stdout", NULL}, {"log-level", 'l', 0, G_OPTION_ARG_STRING, &opt_log_level, "Set a level of logging (syslog style). Defaults to 'notice'.", "[emerg|alert|crit|err|warning|notice|info|debug]"}, {NULL } }; static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { GError *error; g_print ("Connected to the system bus\n"); g_assert (registration_id == NULL); error = NULL; registration_id = polkit_backend_authority_register (authority, connection, "/org/freedesktop/PolicyKit1/Authority", &error); if (registration_id == NULL) { g_printerr ("Error registering authority: %s\n", error->message); g_error_free (error); g_main_loop_quit (loop); /* exit */ } } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_WARNING, "Lost the name org.freedesktop.PolicyKit1 - exiting"); g_main_loop_quit (loop); } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { exit_status = EXIT_SUCCESS; polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), LOG_LEVEL_NOTICE, "Acquired the name org.freedesktop.PolicyKit1 on the system bus"); } static gboolean on_sigint_sigterm (gpointer user_data) { g_print ("Handling %s\n", (const char *) user_data); g_main_loop_quit (loop); return TRUE; } static gboolean on_sighup (gpointer user_data) { #ifdef HAVE_LIBSYSTEMD gchar reload_message[sizeof("RELOADING=1\nMONOTONIC_USEC=18446744073709551615")]; gint64 monotonic_now; /* Notify systemd that we are reloading, including a CLOCK_MONOTONIC timestamp in usec * so that the program is compatible with a Type=notify-reload service. */ monotonic_now = g_get_monotonic_time (); g_snprintf (reload_message, sizeof(reload_message), "RELOADING=1\nMONOTONIC_USEC=%" G_GINT64_FORMAT, monotonic_now); sd_notify (0, reload_message); #endif g_print ("Handling SIGHUP\n"); polkit_backend_interactive_authority_reload (POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority)); #ifdef HAVE_LIBSYSTEMD /* Notify systemd that we have finished reloading. */ sd_notify (0, "READY=1\nSTATUS=Processing requests..."); #endif return TRUE; } static gboolean become_user (const gchar *user, GError **error) { gboolean ret = FALSE; struct passwd *pw; g_return_val_if_fail (user != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); pw = getpwnam (user); if (pw == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error calling getpwnam(): %m"); goto out; } if ((geteuid () == pw->pw_uid) && (getuid () == pw->pw_uid) && (getegid () == pw->pw_gid) && (getgid () == pw->pw_gid)) { /* already running as user */ ret = TRUE; goto out; } if (setgroups (0, NULL) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error clearing groups: %m"); goto out; } if (initgroups (pw->pw_name, pw->pw_gid) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error initializing groups: %m"); goto out; } (void) setregid (pw->pw_gid, pw->pw_gid); (void) setreuid (pw->pw_uid, pw->pw_uid); if ((geteuid () != pw->pw_uid) || (getuid () != pw->pw_uid) || (getegid () != pw->pw_gid) || (getgid () != pw->pw_gid)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error becoming real+effective uid %d and gid %d: %m", (int) pw->pw_uid, (int) pw->pw_gid); goto out; } if (chdir ("/") != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error changing to root directory %s: %m", pw->pw_dir); goto out; } ret = TRUE; g_print ("Successfully changed to user %s\n", user); out: return ret; } int main (int argc, char **argv) { GError *error; GOptionContext *opt_context; guint name_owner_id; guint sigint_id; guint sighup_id; loop = NULL; opt_context = NULL; name_owner_id = 0; sigint_id = 0; sighup_id = 0; registration_id = NULL; /* Disable remote file access from GIO. */ setenv ("GIO_USE_VFS", "local", 1); opt_context = g_option_context_new ("polkit system daemon"); g_option_context_add_main_entries (opt_context, opt_entries, NULL); error = NULL; if (!g_option_context_parse (opt_context, &argc, &argv, &error)) { g_printerr ("Error parsing options: %s\n", error->message); g_error_free (error); goto out; } /* If --no-debug is requested don't clutter stdout/stderr etc. */ if (opt_no_debug) { gint dev_null_fd; dev_null_fd = open ("/dev/null", O_RDWR); if (dev_null_fd >= 0) { dup2 (dev_null_fd, STDIN_FILENO); dup2 (dev_null_fd, STDOUT_FILENO); dup2 (dev_null_fd, STDERR_FILENO); close (dev_null_fd); } else { g_warning ("Error opening /dev/null: %m"); } } error = NULL; if (!become_user (POLKITD_USER, &error)) { g_printerr ("Error switching to user %s: %s\n", POLKITD_USER, error->message); g_clear_error (&error); goto out; } if (g_getenv ("PATH") == NULL) g_setenv ("PATH", "/usr/bin:/bin:/usr/sbin:/sbin", TRUE); polkit_backend_authority_set_log_level (opt_log_level); authority = polkit_backend_authority_get (); loop = g_main_loop_new (NULL, FALSE); sigint_id = g_unix_signal_add (SIGINT, on_sigint_sigterm, "SIGINT"); sigint_id = g_unix_signal_add (SIGTERM, on_sigint_sigterm, "SIGTERM"); sighup_id = g_unix_signal_add (SIGHUP, on_sighup, NULL); name_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, "org.freedesktop.PolicyKit1", G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0), on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); g_print ("Entering main event loop\n"); #ifdef HAVE_LIBSYSTEMD sd_notify (0, "READY=1\nSTATUS=Processing requests..."); #endif g_main_loop_run (loop); #ifdef HAVE_LIBSYSTEMD sd_notify (0, "STOPPING=1"); #endif g_print ("Shutting down\n"); out: if (sigint_id > 0) g_source_remove (sigint_id); if (sighup_id > 0) g_source_remove (sighup_id); if (name_owner_id != 0) g_bus_unown_name (name_owner_id); if (registration_id != NULL) polkit_backend_authority_unregister (registration_id); if (authority != NULL) g_object_unref (authority); if (loop != NULL) g_main_loop_unref (loop); if (opt_context != NULL) g_option_context_free (opt_context); g_print ("Exiting with code %d\n", exit_status); return exit_status; } polkit-126/src/polkitbackend/toarray.pl000077500000000000000000000004251474122443600203200ustar00rootroot00000000000000#!/usr/bin/perl -w my $FILENAME = $ARGV[0]; open FILE, $FILENAME or die "Cannot open $FILENAME"; my $ARRAYNAME = $ARGV[1]; print "static const char $ARRAYNAME\[\] ="; while () { s@\\@\\\\@g; s@"@\\"@g; chomp ($_); print "\n \"$_\\n\""; } print ";\n"; polkit-126/src/programs/000077500000000000000000000000001474122443600153165ustar00rootroot00000000000000polkit-126/src/programs/meson.build000066400000000000000000000005521474122443600174620ustar00rootroot00000000000000programs = [ ['pkexec', auth_deps + [libpolkit_agent_dep]], ['pkcheck', [libpolkit_agent_dep]], ['pkaction', [libpolkit_gobject_dep]], ['pkttyagent', [libpolkit_agent_dep]], ] foreach program: programs executable( program[0], program[0] + '.c', include_directories: top_inc, dependencies: program[1], install: true, ) endforeach polkit-126/src/programs/pkaction.c000066400000000000000000000147051474122443600173010ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include static void print_action (PolkitActionDescription *action, gboolean opt_verbose) { if (!opt_verbose) { g_print ("%s\n", polkit_action_description_get_action_id (action)); } else { const gchar *vendor; const gchar *vendor_url; const gchar *icon_name; const gchar* const *annotation_keys; guint n; vendor = polkit_action_description_get_vendor_name (action); vendor_url = polkit_action_description_get_vendor_url (action); icon_name = polkit_action_description_get_icon_name (action); g_print ("%s:\n", polkit_action_description_get_action_id (action)); g_print (" description: %s\n", polkit_action_description_get_description (action)); g_print (" message: %s\n", polkit_action_description_get_message (action)); if (vendor != NULL) g_print (" vendor: %s\n", vendor); if (vendor_url != NULL) g_print (" vendor_url: %s\n", vendor_url); if (icon_name != NULL) g_print (" icon: %s\n", icon_name); g_print (" implicit any: %s\n", polkit_implicit_authorization_to_string (polkit_action_description_get_implicit_any (action))); g_print (" implicit inactive: %s\n", polkit_implicit_authorization_to_string (polkit_action_description_get_implicit_inactive (action))); g_print (" implicit active: %s\n", polkit_implicit_authorization_to_string (polkit_action_description_get_implicit_active (action))); annotation_keys = polkit_action_description_get_annotation_keys (action); for (n = 0; annotation_keys[n] != NULL; n++) { const gchar *key; const gchar *value; key = annotation_keys[n]; value = polkit_action_description_get_annotation (action, key); g_print (" annotation: %s -> %s\n", key, value); } g_print ("\n"); } } static gint action_desc_compare_by_action_id_func (PolkitActionDescription *a, PolkitActionDescription *b) { return g_strcmp0 (polkit_action_description_get_action_id (a), polkit_action_description_get_action_id (b)); } int main (int argc, char *argv[]) { guint ret; gchar *opt_action_id; gchar *s; gboolean opt_show_version; gboolean opt_verbose; GOptionEntry options[] = { { "action-id", 'a', 0, G_OPTION_ARG_STRING, &opt_action_id, N_("Only output information about ACTION"), N_("ACTION") }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, N_("Output detailed action information"), NULL }, { "version", 0, 0, G_OPTION_ARG_NONE, &opt_show_version, N_("Show version"), NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; GOptionContext *context; PolkitAuthority *authority; GList *l; GList *actions; GError *error; opt_action_id = NULL; context = NULL; authority = NULL; actions = NULL; ret = 1; /* Disable remote file access from GIO. */ setenv ("GIO_USE_VFS", "local", 1); opt_show_version = FALSE; opt_verbose = FALSE; error = NULL; context = g_option_context_new (N_("[--action-id ACTION]")); s = g_strdup_printf (_("Report bugs to: %s\n" "%s home page: <%s>"), PACKAGE_BUGREPORT, PACKAGE_NAME, PACKAGE_URL); g_option_context_set_description (context, s); g_free (s); g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr ("%s: %s\n", g_get_prgname (), error->message); g_error_free (error); goto out; } if (argc > 1) { g_printerr (_("%s: Unexpected argument `%s'\n"), g_get_prgname (), argv[1]); goto out; } if (opt_show_version) { g_print ("pkaction version %s\n", PACKAGE_VERSION); ret = 0; goto out; } authority = polkit_authority_get_sync (NULL /* GCancellable* */, &error); if (authority == NULL) { g_printerr ("Error getting authority: %s\n", error->message); g_error_free (error); goto out; } error = NULL; actions = polkit_authority_enumerate_actions_sync (authority, NULL, /* GCancellable */ &error); if (error != NULL) { g_printerr ("Error enumerating actions: %s\n", error->message); g_error_free (error); goto out; } if (opt_action_id != NULL) { for (l = actions; l != NULL; l = l->next) { PolkitActionDescription *action = POLKIT_ACTION_DESCRIPTION (l->data); const gchar *id; id = polkit_action_description_get_action_id (action); if (g_strcmp0 (id, opt_action_id) == 0) { print_action (action, opt_verbose); break; } } if (l == NULL) { g_printerr ("No action with action id %s\n", opt_action_id); goto out; } } else { actions = g_list_sort (actions, (GCompareFunc) action_desc_compare_by_action_id_func); for (l = actions; l != NULL; l = l->next) { PolkitActionDescription *action = POLKIT_ACTION_DESCRIPTION (l->data); print_action (action, opt_verbose); } } ret = 0; out: g_list_foreach (actions, (GFunc) g_object_unref, NULL); g_list_free (actions); g_free (opt_action_id); if (authority != NULL) g_object_unref (authority); g_option_context_free (context); return ret; } polkit-126/src/programs/pkcheck.c000066400000000000000000000453671474122443600171110ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE #include static void help (void) { g_print (_("Usage:\n" " pkcheck [OPTION...]\n" "\n" "Help Options:\n" " -h, --help Show help options\n" "\n" "Application Options:\n" " -a, --action-id=ACTION Check authorization to perform ACTION\n" " -u, --allow-user-interaction Interact with the user if necessary\n" " -d, --details=KEY VALUE Add (KEY, VALUE) to information about the action\n" " --enable-internal-agent Use an internal authentication agent if necessary\n" " --list-temp List temporary authorizations for current session\n" " -p, --process=PID[,START_TIME,UID] Check authorization of specified process\n" " --revoke-temp Revoke all temporary authorizations for current session\n" " -s, --system-bus-name=BUS_NAME Check authorization of owner of BUS_NAME\n" " --version Show version\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n"), PACKAGE_BUGREPORT, PACKAGE_NAME, PACKAGE_URL); } static gchar * escape_str (const gchar *str) { GString *s; guint n; s = g_string_new (NULL); if (str == NULL) goto out; for (n = 0; str[n] != '\0'; n++) { guint c = str[n] & 0xff; if (g_ascii_isalnum (c) || c=='_') g_string_append_c (s, c); else g_string_append_printf (s, "\\%o", c); } out: return g_string_free (s, FALSE); } static gchar * format_reltime (gint seconds) { gint magnitude; const gchar *ending; gchar *ret; if (seconds >= 0) { magnitude = seconds; ending = "from now"; } else { magnitude = -seconds; ending = "ago"; } if (magnitude >= 60) { ret = g_strdup_printf ("%d min %d sec %s", magnitude/60, magnitude%60, ending); } else { ret = g_strdup_printf ("%d sec %s", magnitude, ending); } return ret; } /* TODO: should probably move to PolkitSubject * (also see copy in src/polkitbackend/polkitbackendinteractiveauthority.c) * * Also, can't really trust the cmdline... but might be useful in the logs anyway. */ static gchar * _polkit_subject_get_cmdline (PolkitSubject *subject) { PolkitSubject *process; gchar *ret; gint pid; gchar *filename; gchar *contents; gsize contents_len; GError *error; guint n; g_return_val_if_fail (subject != NULL, NULL); error = NULL; ret = NULL; process = NULL; filename = NULL; contents = NULL; if (POLKIT_IS_UNIX_PROCESS (subject)) { process = g_object_ref (subject); } else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) { process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, &error); if (process == NULL) { g_printerr ("Error getting process for system bus name `%s': %s\n", polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)), error->message); g_error_free (error); goto out; } } else { g_warning ("Unknown subject type passed to _polkit_subject_get_cmdline()"); goto out; } pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process)); if (pid <= 0) goto out; filename = g_strdup_printf ("/proc/%d/cmdline", pid); if (!g_file_get_contents (filename, &contents, &contents_len, &error)) { g_printerr ("Error opening `%s': %s\n", filename, error->message); g_error_free (error); goto out; } if (contents == NULL || contents_len == 0) { goto out; } else { /* The kernel uses '\0' to separate arguments - replace those with a space. */ for (n = 0; n < contents_len - 1; n++) { if (contents[n] == '\0') contents[n] = ' '; } ret = g_strdup (contents); g_strstrip (ret); } out: g_free (filename); g_free (contents); if (process != NULL) g_object_unref (process); return ret; } static gint do_list_or_revoke_temp_authz (gboolean revoke) { gint ret; PolkitAuthority *authority; PolkitSubject *session; GError *error; ret = 1; authority = NULL; session = NULL; error = NULL; authority = polkit_authority_get_sync (NULL /* GCancellable* */, &error); if (authority == NULL) { g_printerr ("Error getting authority: %s\n", error->message); g_error_free (error); goto out; } error = NULL; session = polkit_unix_session_new_for_process_sync (getpid (), NULL, /* GCancellable */ &error); if (session == NULL) { g_printerr ("Error getting session: %s\n", error->message); g_error_free (error); goto out; } if (revoke) { if (!polkit_authority_revoke_temporary_authorizations_sync (authority, session, NULL, /* GCancellable */ &error)) { g_printerr ("Error revoking temporary authorizations: %s\n", error->message); g_error_free (error); goto out; } ret = 0; } else { GList *authorizations; GList *l; error = NULL; authorizations = polkit_authority_enumerate_temporary_authorizations_sync (authority, session, NULL, /* GCancellable */ &error); if (error != NULL) { g_printerr ("Error getting temporary authorizations: %s\n", error->message); g_error_free (error); goto out; } for (l = authorizations; l != NULL; l = l->next) { PolkitTemporaryAuthorization *a = POLKIT_TEMPORARY_AUTHORIZATION (l->data); const gchar *id; const gchar *action_id; PolkitSubject *subject; gchar *subject_cmdline; time_t obtained; time_t expires; gint64 now; gchar *subject_str; gchar obtained_str[64]; gchar expires_str[64]; gchar *obtained_rel_str; gchar *expires_rel_str; struct tm *broken_down; id = polkit_temporary_authorization_get_id (a); action_id = polkit_temporary_authorization_get_action_id (a); subject = polkit_temporary_authorization_get_subject (a); subject_str = polkit_subject_to_string (subject); subject_cmdline = _polkit_subject_get_cmdline (subject); obtained = polkit_temporary_authorization_get_time_obtained (a); expires = polkit_temporary_authorization_get_time_expires (a); now = g_get_real_time () / G_TIME_SPAN_SECOND; broken_down = localtime (&obtained); strftime (obtained_str, sizeof (obtained_str), "%c", broken_down); broken_down = localtime (&expires); strftime (expires_str, sizeof (expires_str), "%c", broken_down); obtained_rel_str = format_reltime (obtained - now); expires_rel_str = format_reltime (expires - now); g_print ("authorization id: %s\n" "action: %s\n" "subject: %s (%s)\n" "obtained: %s (%s)\n" "expires: %s (%s)\n" "\n", id, action_id, subject_str, subject_cmdline != NULL ? subject_cmdline : "cannot read cmdline", obtained_rel_str, obtained_str, expires_rel_str, expires_str); g_object_unref (subject); g_free (subject_str); g_free (subject_cmdline); g_free (obtained_rel_str); g_free (expires_rel_str); } g_list_foreach (authorizations, (GFunc) g_object_unref, NULL); g_list_free (authorizations); ret = 0; } out: if (authority != NULL) g_object_unref (authority); if (session != NULL) g_object_unref (session); return ret; } int main (int argc, char *argv[]) { guint n; guint ret; gchar *action_id; gboolean opt_show_help; gboolean opt_show_version; gboolean allow_user_interaction; gboolean enable_internal_agent; gboolean list_temp; gboolean revoke_temp; PolkitAuthority *authority; PolkitAuthorizationResult *result; PolkitSubject *subject; PolkitDetails *details; PolkitCheckAuthorizationFlags flags; PolkitDetails *result_details; GError *error; gpointer local_agent_handle; subject = NULL; action_id = NULL; details = NULL; authority = NULL; result = NULL; allow_user_interaction = FALSE; enable_internal_agent = FALSE; list_temp = FALSE; revoke_temp = FALSE; local_agent_handle = NULL; ret = 126; if (argc < 1) { exit(126); } /* Disable remote file access from GIO. */ setenv ("GIO_USE_VFS", "local", 1); details = polkit_details_new (); opt_show_help = FALSE; opt_show_version = FALSE; g_set_prgname ("pkcheck"); for (n = 1; n < (guint) argc; n++) { if (g_strcmp0 (argv[n], "--help") == 0 || g_strcmp0 (argv[n], "-h") == 0) { opt_show_help = TRUE; } else if (g_strcmp0 (argv[n], "--version") == 0) { opt_show_version = TRUE; } else if (g_strcmp0 (argv[n], "--process") == 0 || g_strcmp0 (argv[n], "-p") == 0) { gint pid; guint uid; guint64 pid_start_time; n++; if (n >= (guint) argc) { g_printerr (_("%s: Argument expected after `%s'\n"), g_get_prgname (), "--process, -p"); goto out; } if (sscanf (argv[n], "%i,%" G_GUINT64_FORMAT ",%u", &pid, &pid_start_time, &uid) == 3) { subject = polkit_unix_process_new_for_owner (pid, pid_start_time, uid); } else if (sscanf (argv[n], "%i,%" G_GUINT64_FORMAT, &pid, &pid_start_time) == 2) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS subject = polkit_unix_process_new_full (pid, pid_start_time); G_GNUC_END_IGNORE_DEPRECATIONS } else if (sscanf (argv[n], "%i", &pid) == 1) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS subject = polkit_unix_process_new (pid); G_GNUC_END_IGNORE_DEPRECATIONS } else { g_printerr (_("%s: Invalid --process value `%s'\n"), g_get_prgname (), argv[n]); goto out; } } else if (g_strcmp0 (argv[n], "--system-bus-name") == 0 || g_strcmp0 (argv[n], "-s") == 0) { n++; if (n >= (guint) argc) { g_printerr (_("%s: Argument expected after `%s'\n"), g_get_prgname (), "--system-bus-name, -s"); goto out; } subject = polkit_system_bus_name_new (argv[n]); } else if (g_strcmp0 (argv[n], "--action-id") == 0 || g_strcmp0 (argv[n], "-a") == 0) { n++; if (n >= (guint) argc) { g_printerr (_("%s: Argument expected after `%s'\n"), g_get_prgname (), "--action-id, -a"); goto out; } action_id = g_strdup (argv[n]); } else if (g_strcmp0 (argv[n], "--detail") == 0 || g_strcmp0 (argv[n], "-d") == 0) { const gchar *key; const gchar *value; n++; if (n >= (guint) argc) { g_printerr (_("%s: Two arguments expected after `--detail, -d'\n"), g_get_prgname ()); goto out; } key = argv[n]; n++; if (n >= (guint) argc) { g_printerr (_("%s: Two arguments expected after `--detail, -d'\n"), g_get_prgname ()); goto out; } value = argv[n]; polkit_details_insert (details, key, value); } else if (g_strcmp0 (argv[n], "--allow-user-interaction") == 0 || g_strcmp0 (argv[n], "-u") == 0) { allow_user_interaction = TRUE; } else if (g_strcmp0 (argv[n], "--enable-internal-agent") == 0) { enable_internal_agent = TRUE; } else if (g_strcmp0 (argv[n], "--list-temp") == 0) { list_temp = TRUE; } else if (g_strcmp0 (argv[n], "--revoke-temp") == 0) { revoke_temp = TRUE; } else { break; } } if (argv[n] != NULL) { g_printerr (_("%s: Unexpected argument `%s'\n"), g_get_prgname (), argv[n]); goto out; } if (opt_show_help) { help (); ret = 0; goto out; } else if (opt_show_version) { g_print ("pkcheck version %s\n", PACKAGE_VERSION); ret = 0; goto out; } if (list_temp) { ret = do_list_or_revoke_temp_authz (FALSE); goto out; } else if (revoke_temp) { ret = do_list_or_revoke_temp_authz (TRUE); goto out; } else if (subject == NULL) { g_printerr (_("%s: Subject not specified\n"), g_get_prgname ()); goto out; } error = NULL; authority = polkit_authority_get_sync (NULL /* GCancellable* */, &error); if (authority == NULL) { g_printerr ("Error getting authority: %s\n", error->message); g_error_free (error); goto out; } try_again: error = NULL; flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE; if (allow_user_interaction) flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; result = polkit_authority_check_authorization_sync (authority, subject, action_id, details, flags, NULL, &error); if (result == NULL) { g_printerr ("Error checking for authorization %s: %s\n", action_id, error ? error->message : "Could not verify; error object not present."); ret = 127; goto out; } result_details = polkit_authorization_result_get_details (result); if (result_details != NULL) { gchar **keys; keys = polkit_details_get_keys (result_details); for (n = 0; keys != NULL && keys[n] != NULL; n++) { const gchar *key; const gchar *value; gchar *s; key = keys[n]; value = polkit_details_lookup (result_details, key); s = escape_str (key); g_print ("%s", s); g_free (s); g_print ("="); s = escape_str (value); g_print ("%s", s); g_free (s); g_print ("\n"); } g_strfreev (keys); } if (polkit_authorization_result_get_is_authorized (result)) { ret = 0; } else if (polkit_authorization_result_get_is_challenge (result)) { if (allow_user_interaction) { if (local_agent_handle == NULL && enable_internal_agent) { PolkitAgentListener *listener; error = NULL; /* this will fail if we can't find a controlling terminal */ listener = polkit_agent_text_listener_new (NULL, &error); if (listener == NULL) { g_printerr ("Error creating textual authentication agent: %s\n", error->message); g_error_free (error); goto out; } local_agent_handle = polkit_agent_listener_register (listener, POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD, subject, NULL, /* object_path */ NULL, /* GCancellable */ &error); g_object_unref (listener); if (local_agent_handle == NULL) { g_printerr ("Error registering local authentication agent: %s\n", error->message); g_error_free (error); goto out; } g_object_unref (result); result = NULL; goto try_again; } else { g_printerr ("Authorization requires authentication but no agent is available.\n"); } } else { g_printerr ("Authorization requires authentication and -u wasn't passed.\n"); } ret = 2; } else if (polkit_authorization_result_get_dismissed (result)) { g_printerr ("Authentication request was dismissed.\n"); ret = 3; } else { g_printerr ("Not authorized.\n"); ret = 1; } out: /* if applicable, nuke the local authentication agent */ if (local_agent_handle != NULL) polkit_agent_listener_unregister (local_agent_handle); if (result != NULL) g_object_unref (result); g_free (action_id); if (details != NULL) g_object_unref (details); if (subject != NULL) g_object_unref (subject); if (authority != NULL) g_object_unref (authority); return ret; } polkit-126/src/programs/pkexec.c000066400000000000000000000747141474122443600167560ustar00rootroot00000000000000/* * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #ifdef __FreeBSD__ #include #include #endif #include #ifdef POLKIT_AUTHFW_PAM #include #endif /* POLKIT_AUTHFW_PAM */ #include #include #include #define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE #include static gchar *original_user_name = NULL; static gchar *original_cwd; static gchar *command_line = NULL; static gchar *cmdline_short = NULL; static struct passwd *pw; #ifndef HAVE_CLEARENV extern char **environ; static int clearenv (void) { if (environ != NULL) environ[0] = NULL; return 0; } #endif static void usage (int argc, char *argv[]) { g_printerr ("pkexec --version |\n" " --help |\n" " --disable-internal-agent |\n" " [--keep-cwd] [--user username] [PROGRAM] [ARGUMENTS...]\n" "\n" "See the pkexec manual page for more details.\n" "\n" "Report bugs to: %s\n" "%s home page: <%s>\n", PACKAGE_BUGREPORT, PACKAGE_NAME, PACKAGE_URL); } /* ---------------------------------------------------------------------------------------------------- */ G_GNUC_PRINTF(3, 4) static void log_message (gint level, gboolean print_to_stderr, const gchar *format, ...) { static gboolean is_log_open = FALSE; va_list var_args; gchar *s; const gchar *tty; if (!is_log_open) { openlog ("pkexec", LOG_PID, LOG_AUTHPRIV); /* security/authorization messages (private) */ is_log_open = TRUE; } va_start (var_args, format); s = g_strdup_vprintf (format, var_args); va_end (var_args); tty = ttyname (0); if (tty == NULL) tty = "unknown"; /* first complain to syslog */ syslog (level, "%s: %s [USER=%s] [TTY=%s] [CWD=%s] [COMMAND=%s]", original_user_name, s, pw->pw_name, tty, original_cwd, command_line); /* and then on stderr */ if (print_to_stderr) g_printerr ("%s\n", s); g_free (s); } /* ---------------------------------------------------------------------------------------------------- */ #ifdef POLKIT_AUTHFW_PAM static int pam_conversation_function (int n, const struct pam_message **msg, struct pam_response **resp, void *data) { g_assert_not_reached (); return PAM_CONV_ERR; } /* A work around for: * https://bugzilla.redhat.com/show_bug.cgi?id=753882 */ static gboolean xdg_runtime_dir_is_owned_by (const char *path, uid_t target_uid) { struct stat stbuf; return stat (path, &stbuf) == 0 && stbuf.st_uid == target_uid; } static gboolean open_session (const gchar *user_to_auth, uid_t target_uid) { gboolean ret; gint rc; pam_handle_t *pam_h; char **envlist; struct pam_conv conversation; ret = FALSE; pam_h = NULL; conversation.conv = pam_conversation_function; conversation.appdata_ptr = NULL; /* start the pam stack */ rc = pam_start ("polkit-1", user_to_auth, &conversation, &pam_h); if (rc != PAM_SUCCESS) { g_printerr ("pam_start() failed: %s\n", pam_strerror (pam_h, rc)); goto out; } /* open a session */ rc = pam_open_session (pam_h, 0); /* flags */ if (rc != PAM_SUCCESS) { g_printerr ("pam_open_session() failed: %s\n", pam_strerror (pam_h, rc)); goto out; } ret = TRUE; envlist = pam_getenvlist (pam_h); if (envlist != NULL) { guint n; for (n = 0; envlist[n]; n++) { const char *envitem = envlist[n]; if (g_str_has_prefix (envitem, "XDG_RUNTIME_DIR=")) { const char *eq = strchr (envitem, '='); g_assert (eq); if (!xdg_runtime_dir_is_owned_by (eq + 1, target_uid)) continue; } putenv (envlist[n]); } free (envlist); } out: if (pam_h != NULL) pam_end (pam_h, rc); return ret; } #endif /* POLKIT_AUTHFW_PAM */ /* ---------------------------------------------------------------------------------------------------- */ typedef gboolean (*FdCallback) (gint fd, gpointer user_data); static gboolean set_close_on_exec (gint fd, gpointer user_data) { gint fd_bottom; fd_bottom = GPOINTER_TO_INT (user_data); if (fd >= fd_bottom) { if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0 && errno != EBADF) { return FALSE; } } return TRUE; } static gboolean fdwalk (FdCallback callback, gpointer user_data) { gint fd; gint max_fd; g_return_val_if_fail (callback != NULL, FALSE); max_fd = sysconf (_SC_OPEN_MAX); for (fd = 0; fd < max_fd; fd++) { if (!callback (fd, user_data)) return FALSE; } return TRUE; } /* ---------------------------------------------------------------------------------------------------- */ static gchar * find_action_for_path (PolkitAuthority *authority, const gchar *path, const gchar *argv1, gboolean *allow_gui) { GList *l; GList *actions; gchar *action_id; GError *error; actions = NULL; action_id = NULL; error = NULL; *allow_gui = FALSE; actions = polkit_authority_enumerate_actions_sync (authority, NULL, &error); if (actions == NULL) { g_warning ("Error enumerating actions: %s", error->message); g_error_free (error); goto out; } for (l = actions; l != NULL; l = l->next) { PolkitActionDescription *action_desc = POLKIT_ACTION_DESCRIPTION (l->data); const gchar *argv1_for_action; const gchar *path_for_action; const gchar *allow_gui_annotation; path_for_action = polkit_action_description_get_annotation (action_desc, "org.freedesktop.policykit.exec.path"); if (path_for_action == NULL) continue; argv1_for_action = polkit_action_description_get_annotation (action_desc, "org.freedesktop.policykit.exec.argv1"); if (g_strcmp0 (path_for_action, path) == 0) { /* check against org.freedesktop.policykit.exec.argv1 but only if set */ if (argv1_for_action != NULL) { if (g_strcmp0 (argv1, argv1_for_action) != 0) continue; } action_id = g_strdup (polkit_action_description_get_action_id (action_desc)); allow_gui_annotation = polkit_action_description_get_annotation (action_desc, "org.freedesktop.policykit.exec.allow_gui"); if (allow_gui_annotation != NULL && strlen (allow_gui_annotation) > 0) *allow_gui = TRUE; goto out; } } out: g_list_foreach (actions, (GFunc) g_object_unref, NULL); g_list_free (actions); /* Fall back to org.freedesktop.policykit.exec */ if (action_id == NULL) action_id = g_strdup ("org.freedesktop.policykit.exec"); return action_id; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean is_valid_shell (const gchar *shell) { gboolean ret; gchar *contents; gchar **shells; GError *error; guint n; ret = FALSE; contents = NULL; shells = NULL; error = NULL; if (!g_file_get_contents ("/etc/shells", &contents, NULL, /* gsize *length */ &error)) { g_printerr ("Error getting contents of /etc/shells: %s\n", error->message); g_error_free (error); goto out; } shells = g_strsplit (contents, "\n", 0); for (n = 0; shells != NULL && shells[n] != NULL; n++) { if (shells[n][0] == '/' && g_strcmp0 (shell, shells[n]) == 0) { ret = TRUE; goto out; } } out: g_free (contents); g_strfreev (shells); return ret; } static gboolean validate_environment_variable (const gchar *key, const gchar *value) { gboolean ret; /* Generally we bail if any environment variable value contains * * - '/' characters * - '%' characters * - '..' substrings */ g_return_val_if_fail (key != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); ret = FALSE; /* special case $SHELL */ if (g_strcmp0 (key, "SHELL") == 0) { /* check if it's in /etc/shells */ if (!is_valid_shell (value)) { log_message (LOG_CRIT, TRUE, "The value for the SHELL variable was not found in the /etc/shells file"); g_printerr ("\n" "This incident has been reported.\n"); goto out; } } else if ((g_strcmp0 (key, "XAUTHORITY") != 0 && strstr (value, "/") != NULL) || strstr (value, "%") != NULL || strstr (value, "..") != NULL) { log_message (LOG_CRIT, TRUE, "The value for environment variable %s contains suspicious content", key); g_printerr ("\n" "This incident has been reported.\n"); goto out; } ret = TRUE; out: return ret; } /* ---------------------------------------------------------------------------------------------------- */ int main (int argc, char *argv[]) { guint n; guint ret; gint rc; gboolean opt_show_help; gboolean opt_show_version; gboolean opt_disable_internal_agent; gboolean opt_keep_cwd; PolkitAuthority *authority; PolkitAuthorizationResult *result; PolkitSubject *subject; PolkitDetails *details; GError *error; gchar *action_id; gboolean allow_gui; gchar **exec_argv; gchar *path; struct passwd pwstruct; gchar pwbuf[8192]; gchar *s; const gchar *environment_variables_to_save[] = { "SHELL", "LANG", "LINGUAS", "LANGUAGE", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LC_ALL", "TERM", "COLORTERM", /* By default we don't allow running X11 apps, as it does not work in the * general case. See * * https://bugs.freedesktop.org/show_bug.cgi?id=17970#c26 * * and surrounding comments for a lot of discussion about this. * * However, it can be enabled for some selected and tested legacy programs * which previously used e. g. gksu, by setting the * org.freedesktop.policykit.exec.allow_gui annotation to a nonempty value. * See https://bugs.freedesktop.org/show_bug.cgi?id=38769 for details. */ "DISPLAY", "XAUTHORITY", NULL }; GPtrArray *saved_env; gchar *opt_user; pid_t pid_of_caller; gpointer local_agent_handle; /* * If 'pkexec' is called THIS wrong, someone's probably evil-doing. Don't be nice, just bail out. */ if (argc<1) { exit(127); } ret = 127; authority = NULL; subject = NULL; details = NULL; result = NULL; action_id = NULL; saved_env = NULL; path = NULL; exec_argv = NULL; command_line = NULL; cmdline_short = NULL; opt_user = NULL; local_agent_handle = NULL; /* Disable remote file access from GIO. */ setenv ("GIO_USE_VFS", "local", 1); /* First process options and find the command-line to invoke. Avoid using fancy library routines * that depend on environment variables since we haven't cleared the environment just yet. */ opt_show_help = FALSE; opt_show_version = FALSE; opt_disable_internal_agent = FALSE; opt_keep_cwd = FALSE; for (n = 1; n < (guint) argc; n++) { if (strcmp (argv[n], "--help") == 0) { opt_show_help = TRUE; } else if (strcmp (argv[n], "--version") == 0) { opt_show_version = TRUE; } else if (strcmp (argv[n], "--user") == 0 || strcmp (argv[n], "-u") == 0) { n++; if (n >= (guint) argc) { usage (argc, argv); goto out; } if (opt_user != NULL) { g_printerr ("--user specified twice\n"); goto out; } opt_user = g_strdup (argv[n]); } else if (strcmp (argv[n], "--disable-internal-agent") == 0) { opt_disable_internal_agent = TRUE; } else if (strcmp (argv[n], "--keep-cwd") == 0) { opt_keep_cwd = TRUE; } else { break; } } if (opt_show_help) { usage (argc, argv); ret = 0; goto out; } else if (opt_show_version) { g_print ("pkexec version %s\n", PACKAGE_VERSION); ret = 0; goto out; } /* check for correct invocation */ if (geteuid () != 0) { g_printerr ("pkexec must be setuid root\n"); goto out; } original_user_name = g_strdup (g_get_user_name ()); if (original_user_name == NULL) { g_printerr ("Error getting user name.\n"); goto out; } if ((original_cwd = g_get_current_dir ()) == NULL) { g_printerr ("Error getting cwd: %s\n", g_strerror (errno)); goto out; } if (opt_user == NULL) opt_user = g_strdup ("root"); /* Look up information about the user we care about - yes, the return * value of this function is a bit funky */ rc = getpwnam_r (opt_user, &pwstruct, pwbuf, sizeof pwbuf, &pw); if (rc == 0 && pw == NULL) { g_printerr ("User `%s' does not exist.\n", opt_user); goto out; } else if (pw == NULL) { g_printerr ("Error getting information for user `%s': %s\n", opt_user, g_strerror (rc)); goto out; } /* Now figure out the command-line to run - argv is guaranteed to be NULL-terminated, see * * http://lkml.indiana.edu/hypermail/linux/kernel/0409.2/0287.html * * but do check this is the case. * * We also try to locate the program in the path if a non-absolute path is given. */ g_assert (argv[argc] == NULL); path = g_strdup (argv[n]); if (path == NULL) { GPtrArray *shell_argv; path = g_strdup (pwstruct.pw_shell); if (!path) { g_printerr ("No shell configured or error retrieving pw_shell\n"); goto out; } /* If you change this, be sure to change the if (!command_line) case below too */ command_line = g_strdup (path); shell_argv = g_ptr_array_new (); g_ptr_array_add (shell_argv, path); g_ptr_array_add (shell_argv, NULL); exec_argv = (char**)g_ptr_array_free (shell_argv, FALSE); } if (path[0] != '/') { /* g_find_program_in_path() is not suspectible to attacks via the environment */ s = g_find_program_in_path (path); if (s == NULL) { g_printerr ("Cannot run program %s: %s\n", path, strerror (ENOENT)); goto out; } g_free (path); path = s; /* argc<2 and pkexec runs just shell, argv is guaranteed to be null-terminated. * /-less shell shouldn't happen, but let's be defensive and don't write to null-termination */ if (argv[n] != NULL) { argv[n] = path; } } if (access (path, F_OK) != 0) { g_printerr ("Error accessing %s: %s\n", path, g_strerror (errno)); goto out; } if (!command_line) { /* If you change this, be sure to change the path == NULL case above too */ command_line = g_strjoinv (" ", argv + n); exec_argv = argv + n; } /* now save the environment variables we care about */ saved_env = g_ptr_array_new (); for (n = 0; environment_variables_to_save[n] != NULL; n++) { const gchar *key = environment_variables_to_save[n]; const gchar *value; value = g_getenv (key); if (value == NULL) continue; /* To qualify for the paranoia goldstar - we validate the value of each * environment variable passed through - this is to attempt to avoid * exploits in (potentially broken) programs launched via pkexec(1). */ if (!validate_environment_variable (key, value)) goto out; g_ptr_array_add (saved_env, g_strdup (key)); g_ptr_array_add (saved_env, g_strdup (value)); } /* $XAUTHORITY is "special" - if unset, we need to set it to ~/.Xauthority. Yes, * this is broken but it's unfortunately how things work (see fdo #51623 for * details) */ if (g_getenv ("XAUTHORITY") == NULL) { const gchar *home; /* pre-2.36 GLib does not examine $HOME (it always looks in /etc/passwd) and * this is not what we want */ home = g_getenv ("HOME"); if (home == NULL) home = g_get_home_dir (); if (home != NULL) { g_ptr_array_add (saved_env, g_strdup ("XAUTHORITY")); g_ptr_array_add (saved_env, g_build_filename (home, ".Xauthority", NULL)); } } /* Nuke the environment to get a well-known and sanitized environment to avoid attacks * via e.g. the DBUS_SYSTEM_BUS_ADDRESS environment variable and similar. */ if (clearenv () != 0) { g_printerr ("Error clearing environment: %s\n", g_strerror (errno)); goto out; } /* make sure we are nuked if the parent process dies */ #if defined(__linux__) if (prctl (PR_SET_PDEATHSIG, SIGTERM) != 0) { g_printerr ("prctl(PR_SET_PDEATHSIG, SIGTERM) failed: %s\n", g_strerror (errno)); goto out; } #elif defined(__FreeBSD__) int _sig = SIGTERM; if (procctl (P_PID, 0, PROC_PDEATHSIG_CTL, &_sig) != 0) { g_printerr ("procctl(2) failed: %s\n", g_strerror (errno)); goto out; } #else #warning "Please add OS specific code to catch when the parent dies" #endif /* Figure out the parent process */ pid_of_caller = getppid (); if (pid_of_caller == 1) { /* getppid() can return 1 if the parent died (meaning that we are reaped * by /sbin/init); In that case we simpy bail. */ g_printerr ("Refusing to render service to dead parents.\n"); goto out; } /* This process we want to check an authorization for is the process * that launched us - our parent process. * * At the time the parent process fork()'ed and exec()'ed us, the * process had the same real-uid that we have now. So we use this * real-uid instead of of looking it up to avoid TOCTTOU issues * (consider the parent process exec()'ing a setuid helper). * * On the other hand, the monotonic process start-time is guaranteed * to never change so it's safe to look that up given only the PID * since we are guaranteed to be nuked if the parent goes away * (cf. the prctl(2) call above). */ subject = polkit_unix_process_new_for_owner (pid_of_caller, 0, /* 0 means "look up start-time in /proc" */ getuid ()); /* really double-check the invariants guaranteed by the PolkitUnixProcess class */ g_assert (subject != NULL); g_assert (polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject)) == pid_of_caller); g_assert (polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)) >= 0); g_assert (polkit_unix_process_get_start_time (POLKIT_UNIX_PROCESS (subject)) > 0); error = NULL; authority = polkit_authority_get_sync (NULL /* GCancellable* */, &error); if (authority == NULL) { g_printerr ("Error getting authority: %s\n", error->message); g_error_free (error); goto out; } g_assert (path != NULL); g_assert (exec_argv != NULL); action_id = find_action_for_path (authority, path, exec_argv[1], &allow_gui); g_assert (action_id != NULL); details = polkit_details_new (); polkit_details_insert (details, "user", pw->pw_name); if (pw->pw_gecos != NULL) polkit_details_insert (details, "user.gecos", pw->pw_gecos); if (pw->pw_gecos != NULL && strlen (pw->pw_gecos) > 0) s = g_strdup_printf ("%s (%s)", pw->pw_gecos, pw->pw_name); else s = g_strdup_printf ("%s", pw->pw_name); polkit_details_insert (details, "user.display", s); g_free (s); polkit_details_insert (details, "program", path); polkit_details_insert (details, "command_line", command_line); cmdline_short = g_strdup(command_line); if (cmdline_short == NULL) { g_printerr ("Failed to allocate memory for shortened command line.\n"); goto out; } if (strlen(command_line) > 80) g_stpcpy(g_stpcpy( cmdline_short + 38, " ... " ), command_line + strlen(command_line) - 37 ); polkit_details_insert (details, "cmdline_short", cmdline_short); if (g_strcmp0 (action_id, "org.freedesktop.policykit.exec") == 0) { if (pw->pw_uid == 0) { polkit_details_insert (details, "polkit.message", /* Translators: message shown when trying to run a program as root. Do not * translate the $(program) fragment - it will be expanded to the path * of the program e.g. /bin/bash. */ N_("Authentication is needed to run `$(cmdline_short)' as the super user")); } else { polkit_details_insert (details, "polkit.message", /* Translators: message shown when trying to run a program as another user. * Do not translate the $(program) or $(user) fragments - the former will * be expanded to the path of the program e.g. "/bin/bash" and the latter * to the user e.g. "John Doe (johndoe)" or "johndoe". */ N_("Authentication is needed to run `$(cmdline_short)' as user $(user.display)")); } } polkit_details_insert (details, "polkit.gettext_domain", GETTEXT_PACKAGE); try_again: error = NULL; result = polkit_authority_check_authorization_sync (authority, subject, action_id, details, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, &error); if (result == NULL) { g_printerr ("Error checking for authorization %s: %s\n", action_id, error ? error->message : "Could not verify; error object not present."); goto out; } if (polkit_authorization_result_get_is_authorized (result)) { /* do nothing */ } else if (polkit_authorization_result_get_is_challenge (result)) { if (local_agent_handle == NULL && !opt_disable_internal_agent) { PolkitAgentListener *listener; error = NULL; /* this will fail if we can't find a controlling terminal */ listener = polkit_agent_text_listener_new (NULL, &error); if (listener == NULL) { g_printerr ("Error creating textual authentication agent: %s\n", error->message); g_error_free (error); goto out; } local_agent_handle = polkit_agent_listener_register (listener, POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD, subject, NULL, /* object_path */ NULL, /* GCancellable */ &error); g_object_unref (listener); if (local_agent_handle == NULL) { g_printerr ("Error registering local authentication agent: %s\n", error->message); g_error_free (error); goto out; } g_object_unref (result); result = NULL; goto try_again; } else { g_printerr ("Error executing command as another user: No authentication agent found.\n"); goto out; } } else { if (polkit_authorization_result_get_dismissed (result)) { log_message (LOG_WARNING, TRUE, "Error executing command as another user: Request dismissed"); ret = 126; } else { log_message (LOG_WARNING, TRUE, "Error executing command as another user: Not authorized"); g_printerr ("\n" "This incident has been reported.\n"); } goto out; } /* Set PATH to a safe list */ g_ptr_array_add (saved_env, g_strdup ("PATH")); if (pw->pw_uid != 0) s = g_strdup_printf ("/usr/bin:/bin:/usr/sbin:/sbin:%s/bin", pw->pw_dir); else s = g_strdup_printf ("/usr/sbin:/usr/bin:/sbin:/bin:%s/bin", pw->pw_dir); g_ptr_array_add (saved_env, s); g_ptr_array_add (saved_env, g_strdup ("LOGNAME")); g_ptr_array_add (saved_env, g_strdup (pw->pw_name)); g_ptr_array_add (saved_env, g_strdup ("USER")); g_ptr_array_add (saved_env, g_strdup (pw->pw_name)); g_ptr_array_add (saved_env, g_strdup ("HOME")); g_ptr_array_add (saved_env, g_strdup (pw->pw_dir)); s = g_strdup_printf ("%d", getuid ()); g_ptr_array_add (saved_env, g_strdup ("PKEXEC_UID")); g_ptr_array_add (saved_env, s); /* set the environment */ for (n = 0; n < saved_env->len - 1; n += 2) { const gchar *key = saved_env->pdata[n]; const gchar *value = saved_env->pdata[n + 1]; /* Only set $DISPLAY and $XAUTHORITY when explicitly allowed in the .policy */ if (!allow_gui && (strcmp (key, "DISPLAY") == 0 || strcmp (key, "XAUTHORITY") == 0)) continue; if (!g_setenv (key, value, TRUE)) { g_printerr ("Error setting environment variable %s to '%s': %s\n", key, value, g_strerror (errno)); goto out; } } /* set close_on_exec on all file descriptors except stdin, stdout, stderr */ if (!fdwalk (set_close_on_exec, GINT_TO_POINTER (3))) { g_printerr ("Error setting close-on-exec for file descriptors\n"); goto out; } /* if not changing to uid 0, become uid 0 before changing to the user */ if (pw->pw_uid != 0) { (void) setreuid (0, 0); if ((geteuid () != 0) || (getuid () != 0)) { g_printerr ("Error becoming uid 0: %s\n", g_strerror (errno)); goto out; } } /* open session - with PAM enabled, this runs the open_session() part of the PAM * stack - this includes applying limits via pam_limits.so but also other things * requested via the current PAM configuration. * * NOTE NOTE NOTE: pam_limits.so doesn't seem to clear existing limits - e.g. * * $ ulimit -t * unlimited * * $ su - * Password: * # ulimit -t * unlimited * # logout * * $ ulimit -t 1000 * $ ulimit -t * 1000 * $ su - * Password: * # ulimit -t * 1000 * * TODO: The question here is whether we should clear the limits before applying them? * As evident above, neither su(1) (and, for that matter, nor sudo(8)) does this. */ #ifdef POLKIT_AUTHFW_PAM if (!open_session (pw->pw_name, pw->pw_uid)) { goto out; } #endif /* POLKIT_AUTHFW_PAM */ /* become the user */ if (setgroups (0, NULL) != 0) { g_printerr ("Error setting groups: %s\n", g_strerror (errno)); goto out; } if (initgroups (pw->pw_name, pw->pw_gid) != 0) { g_printerr ("Error initializing groups for %s: %s\n", pw->pw_name, g_strerror (errno)); goto out; } (void) setregid (pw->pw_gid, pw->pw_gid); (void) setreuid (pw->pw_uid, pw->pw_uid); if ((geteuid () != pw->pw_uid) || (getuid () != pw->pw_uid) || (getegid () != pw->pw_gid) || (getgid () != pw->pw_gid)) { g_printerr ("Error becoming real+effective uid %d and gid %d: %s\n", pw->pw_uid, pw->pw_gid, g_strerror (errno)); goto out; } /* change to home directory */ if (!opt_keep_cwd) { if (chdir (pw->pw_dir) != 0) { g_printerr ("Error changing to home directory %s: %s\n", pw->pw_dir, g_strerror (errno)); goto out; } } /* Log the fact that we're executing a command */ log_message (LOG_NOTICE, FALSE, "Executing command"); /* exec the program */ if (execv (path, exec_argv) != 0) { g_printerr ("Error executing %s: %s\n", path, g_strerror (errno)); goto out; } /* if exec doesn't fail, it never returns... */ g_assert_not_reached (); out: /* if applicable, nuke the local authentication agent */ if (local_agent_handle != NULL) polkit_agent_listener_unregister (local_agent_handle); if (result != NULL) g_object_unref (result); g_free (action_id); if (details != NULL) g_object_unref (details); if (subject != NULL) g_object_unref (subject); if (authority != NULL) g_object_unref (authority); if (saved_env != NULL) { g_ptr_array_foreach (saved_env, (GFunc) g_free, NULL); g_ptr_array_free (saved_env, TRUE); } g_free (original_cwd); g_free (path); g_free (command_line); g_free (cmdline_short); g_free (opt_user); g_free (original_user_name); return ret; } polkit-126/src/programs/pkttyagent.c000066400000000000000000000231421474122443600176560ustar00rootroot00000000000000/* * Copyright (C) 2009-2012 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Zeuthen */ #include #include #include #include #include #include #define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE #include static volatile sig_atomic_t tty_flags_saved; static volatile sig_atomic_t tty_flags_changed; struct termios ts; FILE *tty = NULL; struct sigaction savesigterm, savesigint, savesigtstp; static void tty_handler(int signal) { switch (signal) { case SIGTERM: sigaction (SIGTERM, &savesigterm, NULL); break; case SIGINT: sigaction (SIGINT, &savesigint, NULL); break; case SIGTSTP: sigaction (SIGTSTP, &savesigtstp, NULL); break; } if (tty_flags_saved && tty_flags_changed) { tcsetattr (fileno (tty), TCSADRAIN, &ts); } kill(getpid(), signal); } static void tty_attrs_changed(PolkitAgentListener *listener G_GNUC_UNUSED, gboolean changed, gpointer user_data G_GNUC_UNUSED) { tty_flags_changed = changed; } int main (int argc, char *argv[]) { gboolean opt_show_version = FALSE; gboolean opt_fallback = FALSE; gchar *opt_process = NULL; gchar *opt_system_bus_name = NULL; gint opt_notify_fd = -1; GOptionEntry options[] = { { "fallback", 0, 0, G_OPTION_ARG_NONE, &opt_fallback, N_("Don't replace existing agent if any"), NULL }, { "notify-fd", 0, 0, G_OPTION_ARG_INT, &opt_notify_fd, N_("Close FD when the agent is registered"), N_("FD") }, { "process", 'p', 0, G_OPTION_ARG_STRING, &opt_process, N_("Register the agent for the specified process"), N_("PID[,START_TIME]") }, { "system-bus-name", 's', 0, G_OPTION_ARG_STRING, &opt_system_bus_name, N_("Register the agent for the owner of BUS_NAME"), N_("BUS_NAME") }, { "version", 0, 0, G_OPTION_ARG_NONE, &opt_show_version, N_("Show version"), NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; GOptionContext *context; gchar *s; PolkitAuthority *authority = NULL; PolkitSubject *subject = NULL; gpointer local_agent_handle = NULL; PolkitAgentListener *listener = NULL; GVariant *listener_options = NULL; GError *error; GMainLoop *loop = NULL; guint ret = 126; GVariantBuilder builder; struct sigaction sa; const char *tty_name = NULL; /* Disable remote file access from GIO. */ setenv ("GIO_USE_VFS", "local", 1); error = NULL; context = g_option_context_new (""); s = g_strdup_printf (_("Report bugs to: %s\n" "%s home page: <%s>"), PACKAGE_BUGREPORT, PACKAGE_NAME, PACKAGE_URL); g_option_context_set_description (context, s); g_free (s); g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr ("%s: %s\n", g_get_prgname (), error->message); g_error_free (error); goto out; } if (argc > 1) { g_printerr (_("%s: Unexpected argument `%s'\n"), g_get_prgname (), argv[1]); goto out; } if (opt_show_version) { g_print ("pkttyagent version %s\n", PACKAGE_VERSION); ret = 0; goto out; } if (opt_process != NULL && opt_system_bus_name != NULL) { g_printerr (_("%s: Options --process and --system-bus-name are mutually exclusive\n"), g_get_prgname()); goto out; } if (opt_process != NULL) { gint pid; guint64 pid_start_time; if (sscanf (opt_process, "%i,%" G_GUINT64_FORMAT, &pid, &pid_start_time) == 2) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS subject = polkit_unix_process_new_full (pid, pid_start_time); G_GNUC_END_IGNORE_DEPRECATIONS } else if (sscanf (opt_process, "%i", &pid) == 1) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS subject = polkit_unix_process_new (pid); G_GNUC_END_IGNORE_DEPRECATIONS } else { g_printerr (_("%s: Invalid process specifier `%s'\n"), g_get_prgname (), opt_process); goto out; } } if (opt_system_bus_name != NULL) subject = polkit_system_bus_name_new (opt_system_bus_name); /* Use parent process, if no subject has been specified */ if (subject == NULL) { pid_t pid_of_caller; pid_of_caller = getppid (); if (pid_of_caller == 1) { /* getppid() can return 1 if the parent died (meaning that we are reaped * by /sbin/init); In that case we simpy bail. */ g_printerr ("Refusing to render service to dead parents.\n"); goto out; } subject = polkit_unix_process_new_for_owner (pid_of_caller, 0, /* 0 means "look up start-time in /proc" */ getuid ()); /* really double-check the invariants guaranteed by the PolkitUnixProcess class */ g_assert (subject != NULL); g_assert (polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject)) == pid_of_caller); g_assert (polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)) >= 0); g_assert (polkit_unix_process_get_start_time (POLKIT_UNIX_PROCESS (subject)) > 0); } authority = polkit_authority_get_sync (NULL /* GCancellable* */, &error); if (authority == NULL) { g_printerr ("Authorization not available. Check if polkit service is running or see debug message for more information.\n"); g_debug ("Error getting authority: %s (%s, %d)\n", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); ret = 127; goto out; } if (opt_fallback) { g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&builder, "{sv}", "fallback", g_variant_new_boolean (TRUE)); listener_options = g_variant_builder_end (&builder); } error = NULL; /* this will fail if we can't find a controlling terminal */ listener = polkit_agent_text_listener_new (NULL, &error); if (listener == NULL) { g_printerr ("Error creating textual authentication agent: %s (%s, %d)\n", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); ret = 127; goto out; } g_signal_connect(G_OBJECT(listener), "tty_attrs_changed", G_CALLBACK(tty_attrs_changed), NULL); local_agent_handle = polkit_agent_listener_register_with_options (listener, POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD, subject, NULL, /* object_path */ listener_options, NULL, /* GCancellable */ &error); listener_options = NULL; /* consumed */ g_object_unref (listener); if (local_agent_handle == NULL) { g_printerr ("Error registering authentication agent: %s (%s, %d)\n", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); goto out; } if (opt_notify_fd != -1) { if (close (opt_notify_fd) != 0) { g_printerr ("Error closing notify-fd %d: %m\n", opt_notify_fd); goto out; } } /* Bash leaves tty echo disabled if SIGINT/SIGTERM comes to polkitagenttextlistener.c::on_request(), but due to threading the handlers cannot take care of the signal there. Though if controlling terminal cannot be found, the world won't stop spinning. */ tty_name = ctermid(NULL); if (tty_name != NULL) { tty = fopen(tty_name, "r+"); } if (tty != NULL && !tcgetattr (fileno (tty), &ts)) { tty_flags_saved = TRUE; } memset (&sa, 0, sizeof (sa)); sa.sa_handler = &tty_handler; /* If tty_handler() resets terminal while pkttyagent is run in background job, the process gets stopped by SIGTTOU. This impacts systemctl, hence it must be blocked for a while and then the process gets killed anyway. */ sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGTTOU); sigaction (SIGTERM, &sa, &savesigterm); sigaction (SIGINT, &sa, &savesigint); sigaction (SIGTSTP, &sa, &savesigtstp); loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); out: if (loop != NULL) g_main_loop_unref (loop); if (local_agent_handle != NULL) polkit_agent_listener_unregister (local_agent_handle); if (listener_options != NULL) g_variant_unref (listener_options); if (subject != NULL) g_object_unref (subject); if (authority != NULL) g_object_unref (authority); g_free (opt_process); g_free (opt_system_bus_name); g_option_context_free (context); return ret; } polkit-126/src/symbol.map000066400000000000000000000000451474122443600154670ustar00rootroot00000000000000{ global: polkit_*; local: *; }; polkit-126/test/000077500000000000000000000000001474122443600136545ustar00rootroot00000000000000polkit-126/test/data/000077500000000000000000000000001474122443600145655ustar00rootroot00000000000000polkit-126/test/data/etc/000077500000000000000000000000001474122443600153405ustar00rootroot00000000000000polkit-126/test/data/etc/group000066400000000000000000000002011474122443600164100ustar00rootroot00000000000000root:x:0: users:x:100:john,jane admin:x:101:sally,henry john:x:500: jane:x:501: sally:x:502: henry:x:503: highuid2:x:4000000000: polkit-126/test/data/etc/netgroup000066400000000000000000000000661474122443600171300ustar00rootroot00000000000000foo (-,john,) bar (-,jane,) baz foo bar all (,,) none polkit-126/test/data/etc/passwd000066400000000000000000000006011474122443600165610ustar00rootroot00000000000000root:x:0:0:root:/root:/bin/bash john:x:500:500:John Done:/home/john:/bin/bash jane:x:501:501:Jane Smith:/home/jane:/bin/bash sally:x:502:502:Sally Derp:/home/sally:/bin/bash henry:x:503:503:Henry Herp:/home/henry:/bin/bash highuid1:x:2147483648:2147483648:The first high uid:/home/highuid1:/sbin/nologin highuid2:x:4000000000:4000000000:An example high uid:/home/example:/sbin/nologin polkit-126/test/data/etc/polkit-1/000077500000000000000000000000001474122443600170005ustar00rootroot00000000000000polkit-126/test/data/etc/polkit-1/localauthority.conf.d/000077500000000000000000000000001474122443600232115ustar00rootroot00000000000000polkit-126/test/data/etc/polkit-1/localauthority.conf.d/10-test.conf000066400000000000000000000001221474122443600252500ustar00rootroot00000000000000[Configuration] AdminIdentities=unix-user:root;unix-netgroup:bar;unix-group:admin polkit-126/test/data/etc/polkit-1/localauthority/000077500000000000000000000000001474122443600220435ustar00rootroot00000000000000polkit-126/test/data/etc/polkit-1/localauthority/10-test/000077500000000000000000000000001474122443600232405ustar00rootroot00000000000000polkit-126/test/data/etc/polkit-1/localauthority/10-test/com.example.pkla000066400000000000000000000004771474122443600263310ustar00rootroot00000000000000[Users and Root can do Foo] Identity=unix-group:users;unix-user:root Action=com.example.awesomeproduct.foo ResultAny=no ResultInactive=auth_self ResultActive=yes [Users in netgroup baz can do Bar] Identity=unix-netgroup:baz Action=com.example.awesomeproduct.bar ResultAny=no ResultInactive=auth_self ResultActive=yes polkit-126/test/data/etc/polkit-1/rules.d/000077500000000000000000000000001474122443600203545ustar00rootroot00000000000000polkit-126/test/data/etc/polkit-1/rules.d/10-testing.rules000066400000000000000000000126131474122443600233260ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- */ /* see test/polkitbackend/test-polkitbackendjsauthority.c */ /* NOTE: this is the /etc/polkit-1/rules.d version of 10-testing.rules */ // --------------------------------------------------------------------- // admin rules polkit.addAdminRule(function(action, subject) { if (action.id == "net.company.action1") { return ["unix-group:admin"]; } }); polkit.addAdminRule(function(action, subject) { if (action.id == "net.company.action2") { return ["unix-group:users"]; } }); polkit.addAdminRule(function(action, subject) { if (action.id == "net.company.action3") { return ["unix-netgroup:foo"]; } }); // Fallback polkit.addAdminRule(function(action, subject) { return ["unix-group:admin", "unix-user:root"]; }); // ----- // --------------------------------------------------------------------- // basics polkit.addRule(function(action, subject) { if (action.id == "net.company.productA.action0") { return polkit.Result.AUTH_ADMIN; } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.productA.action1") { return polkit.Result.AUTH_SELF; } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.order0") { return polkit.Result.YES; } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.john_action") { if (subject.user == "john") { return polkit.Result.YES; } else { return polkit.Result.NO; } } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.highuid2_action") { if (subject.user == "highuid2") { return polkit.Result.YES; } else { return polkit.Result.NO; } } }); // --------------------------------------------------------------------- // variables polkit.addRule(function(action, subject) { if (action.id == "net.company.group.variables") { if (action.lookup("foo") == "1") return polkit.Result.YES; else if (action.lookup("foo") == "2") return polkit.Result.AUTH_SELF; else return polkit.Result.AUTH_ADMIN; } }); // --------------------------------------------------------------------- // group membership polkit.addRule(function(action, subject) { if (action.id == "net.company.group.only_group_users") { if (subject.isInGroup("users")) return polkit.Result.YES; else return polkit.Result.NO; } }); // --------------------------------------------------------------------- // netgroup membership polkit.addRule(function(action, subject) { if (action.id == "net.company.group.only_netgroup_users") { if (subject.isInNetGroup("foo")) return polkit.Result.YES; else return polkit.Result.NO; } }); // --------------------------------------------------------------------- // spawning polkit.addRule(function(action, subject) { if (action.id == "net.company.spawning.non_existing_helper") { try { polkit.spawn(["/path/to/non/existing/helper"]); return polkit.Result.NO; } catch (error) { return polkit.Result.YES; } } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.spawning.successful_helper") { try { polkit.spawn(["/bin/true"]); return polkit.Result.YES; } catch (error) { return polkit.Result.NO; } } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.spawning.failing_helper") { try { polkit.spawn(["/bin/false"]); return polkit.Result.NO; } catch (error) { return polkit.Result.YES; } } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.spawning.helper_with_output") { try { var out = polkit.spawn(["echo", "-n", "-e", "Hello\nWorld"]); if (out == "Hello\nWorld") return polkit.Result.YES; else return polkit.Result.NO; } catch (error) { return polkit.Result.NO; } } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.spawning.helper_timeout") { try { polkit.spawn(["sleep", "20"]); return polkit.Result.NO; } catch (error) { if (error == "Error: Error spawning helper: Timed out after 10 seconds (g-io-error-quark, 24)") return polkit.Result.YES; return polkit.Result.NO; } } }); // --------------------------------------------------------------------- // runaway scripts polkit.addRule(function(action, subject) { if (action.id == "net.company.run_away_script") { try { // The following code will never terminate so the runaway // script killer will step in after 15 seconds and throw // an exception... while (true) ; } catch (error) { if (error == "Terminating runaway script") // Inverted logic to accomodate Duktape's model as well, which // will always fail with negation, on timeouts return polkit.Result.NO; return polkit.Result.YES; } } }); polkit-126/test/data/etc/polkit-1/rules.d/15-testing.rules000066400000000000000000000011031474122443600233230ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- */ /* see test/polkitbackend/test-polkitbackendjsauthority.c */ polkit.addRule(function(action, subject) { if (action.id == "net.company.order0") { return polkit.Result.NO; // earlier rule should win } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.order1") { return polkit.Result.NO; // earlier rule should win } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.order2") { return polkit.Result.YES; } }); polkit-126/test/data/usr/000077500000000000000000000000001474122443600153765ustar00rootroot00000000000000polkit-126/test/data/usr/share/000077500000000000000000000000001474122443600165005ustar00rootroot00000000000000polkit-126/test/data/usr/share/polkit-1/000077500000000000000000000000001474122443600201405ustar00rootroot00000000000000polkit-126/test/data/usr/share/polkit-1/rules.d/000077500000000000000000000000001474122443600215145ustar00rootroot00000000000000polkit-126/test/data/usr/share/polkit-1/rules.d/10-testing.rules000066400000000000000000000007651474122443600244730ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- */ /* see test/polkitbackend/test-polkitbackendjsauthority.c */ /* NOTE: this is the /usr/share/polkit-1/rules.d version of 10-testing.rules */ polkit.addRule(function(action, subject) { if (action.id == "net.company.order0") { return polkit.Result.NO; // earlier rule should win } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.order1") { return polkit.Result.YES; } }); polkit-126/test/data/usr/share/polkit-1/rules.d/20-testing.rules000066400000000000000000000011351474122443600244640ustar00rootroot00000000000000/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- */ /* see test/polkitbackend/test-polkitbackendjsauthority.c */ polkit.addRule(function(action, subject) { if (action.id == "net.company.order0") { return polkit.Result.NO; // earlier rule should win } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.order1") { return polkit.Result.NO; // earlier rule should win } }); polkit.addRule(function(action, subject) { if (action.id == "net.company.order2") { return polkit.Result.NO; // earlier rule should win } }); polkit-126/test/data/var/000077500000000000000000000000001474122443600153555ustar00rootroot00000000000000polkit-126/test/data/var/lib/000077500000000000000000000000001474122443600161235ustar00rootroot00000000000000polkit-126/test/data/var/lib/polkit-1/000077500000000000000000000000001474122443600175635ustar00rootroot00000000000000polkit-126/test/data/var/lib/polkit-1/localauthority/000077500000000000000000000000001474122443600226265ustar00rootroot00000000000000polkit-126/test/data/var/lib/polkit-1/localauthority/10-test/000077500000000000000000000000001474122443600240235ustar00rootroot00000000000000polkit-126/test/data/var/lib/polkit-1/localauthority/10-test/com.example.pkla000066400000000000000000000002301474122443600270770ustar00rootroot00000000000000[Super Secret Project Permissions] Identity=unix-user:root Action=com.example.restrictedproduct.* ResultAny=no ResultInactive=no ResultActive=auth_self polkit-126/test/integration/000077500000000000000000000000001474122443600161775ustar00rootroot00000000000000polkit-126/test/integration/.fmf/000077500000000000000000000000001474122443600170255ustar00rootroot00000000000000polkit-126/test/integration/.fmf/version000066400000000000000000000000021474122443600204250ustar00rootroot000000000000001 polkit-126/test/integration/README000066400000000000000000000034601474122443600170620ustar00rootroot00000000000000# polkit integration test suite polkit's integration test suite uses TMT (Test Management Tool [0]) to organize and run tests. Since TMT offers a _lot_ of features, this document pinpoints the most _interesting_ ones to get stuff up and running quickly. ## How to contribute Creating a new test case is pretty simple: ``` $ cd test/integration $ tmt test create --template=shell test/name Test directory '/home/.../polkit/test/integration/test/name' created. Test metadata '/home/.../polkit/test/integration/test/name/main.fmf' created. Test script '/home/.../polkit/test/integration/test/name/test.sh' created. ``` The newly created `test.sh` will be the actual test case, and `main.fmf` contains the test metadata, including test summary & description, test dependencies, runtime, and so on. See [1] for more details. After tweaking the test metadata it's usually a good idea to run `tmt lint test/name` to make sure that the configuration is still valid: ``` $ tmt lint test/name /test/name pass C000 fmf node passes schema validation pass C001 summary key is set and is reasonably long ... ``` To check if the test itself works as expected you can use `tmt run`: ``` $ cd test/name $ tmt run -vvv --all provision --how local tests --name . ... total: 1 test passed ``` The `tmt run` command is _very_ customizable (as is the rest of `tmt`). In this particular example we tell it to run all steps (`--all`) and override the `provision` and `tests` steps to run just one particular test on the local machine. As in previous cases, check the `tmt` documentation [0] and examples [2] for more details. ## Links [0] https://tmt.readthedocs.io/en/stable/overview.html [1] https://tmt.readthedocs.io/en/stable/spec/tests.html [2] https://tmt.readthedocs.io/en/stable/examples.html#run polkit-126/test/integration/dfuzzer/000077500000000000000000000000001474122443600176705ustar00rootroot00000000000000polkit-126/test/integration/dfuzzer/main.fmf000066400000000000000000000010201474122443600212770ustar00rootroot00000000000000summary: Run dfuzzer against polkit's D-Bus interface test: ./test.sh require: - clang - compiler-rt - coreutils - dbus - dbus-devel - dfuzzer - expat-devel - gcc-c++ - gdb - gettext-devel - git - glib2-devel - glibc-devel - gobject-introspection-devel - gtk-doc - libasan - libubsan - llvm - meson - pam-devel - pkgconfig(duktape) - python3-dbusmock - systemd - systemd-container - systemd-devel - util-linux duration: 30m polkit-126/test/integration/dfuzzer/test.sh000077500000000000000000000156061474122443600212160ustar00rootroot00000000000000#!/bin/bash # vi: set sw=4 ts=4 et tw=110: # shellcheck disable=SC2016 # # FIXME: this test can be _drastically_ simplified once we can run a dedicated sanitizer job, see # https://github.com/packit/packit-service/issues/2610 set -eux set -o pipefail # shellcheck source=test/integration/util.sh . "$(dirname "$0")/../util.sh" export ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_invalid_pointer_pairs=2:handle_ioctl=1:print_cmdline=1:disable_coredump=0:use_madv_dontdump=1 export UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 # FIXME: There' a bug in meson where it overrides UBSAN_OPTIONS when MSAN_OPTIONS is not set, see # https://github.com/mesonbuild/meson/pull/13001. Drop this once v1.4.1 is widespread enough export MSAN_OPTIONS=foo export CC="${CC:-clang}" # shellcheck disable=SC2317 at_exit() { set +ex # Let's do some cleanup and export logs if necessary # Collect potential coredumps coredumpctl_collect container_destroy if [[ -n "${TMT_TEST_DATA:-}" && -n "${BUILD_DIR:-}" ]]; then cp -r "$BUILD_DIR/meson-logs" "$TMT_TEST_DATA/" fi } trap at_exit EXIT export BUILD_DIR="$PWD/build-san" # Make sure the coredump collecting machinery is working coredumpctl_init : "=== Prepare polkit's source tree ===" # The integration test suite runs without access to the source tree it was built from. If we need the source # tree (most likely to rebuild polkit) we need to do a little dance to determine the correct references. if [[ -n "${PACKIT_TARGET_URL:-}" ]]; then # If we're running in Packit's context, use the set of provided environment variables to checkout the # correct branch (and possibly rebase it on top of the latest source base branch so we always test the # latest revision possible). git clone "$PACKIT_TARGET_URL" polkit cd polkit git checkout "$PACKIT_TARGET_BRANCH" # If we're invoked from a pull request context, rebase on top of the latest source base branch. if [[ -n "${PACKIT_SOURCE_URL:-}" ]]; then git remote add pr "${PACKIT_SOURCE_URL:?}" git fetch pr "${PACKIT_SOURCE_BRANCH:?}" git merge "pr/$PACKIT_SOURCE_BRANCH" fi git log --oneline -5 elif [[ -n "${POLKIT_TREE:-}" ]]; then # Useful for quick local debugging when running this script directly, e.g. running # # # TMT_TEST_DATA=$PWD/logs POLKIT_TREE=$PWD test/integration/fuzz/sanitizers/test.sh # # from the polkit repo root. cd "${POLKIT_TREE:?}" else # If we're running outside of Packit's context, pull the latest polkit upstream. git clone https://github.com/polkit-org/polkit polkit cd polkit git log --oneline -5 fi : "=== Build polkit with sanitizers ===" MESON_OPTIONS=() if [[ "$CC" == clang ]]; then # See https://github.com/mesonbuild/meson/issues/764 for details MESON_OPTIONS+=(-Db_lundef=false) fi rm -rf "$BUILD_DIR" # FIXME: # - drop -Wno-deprecated-declarations once it's not needed # - generating introspection is currently FUBAR when building with clang + ASan, # but we shouldn't need it here anyway (see https://github.com/mesonbuild/meson/issues/13211) meson setup "$BUILD_DIR" \ --werror \ -Dintrospection=false \ -Dsession_tracking=logind \ -Dgettext=true \ -Dtests=true \ -Db_sanitize=address,undefined \ -Dc_args="-Wno-deprecated-declarations" \ -Dcpp_args="-Wno-deprecated-declarations" \ "${MESON_OPTIONS[@]}" ninja -C "$BUILD_DIR" meson test -C "$BUILD_DIR" --print-errorlogs : "=== Run dfuzzer against polkit running under sanitizers ===" container_prepare # Install our custom-built polkit into the container's overlay DESTDIR="$CONTAINER_OVERLAY" ninja -C "$BUILD_DIR" install # Tweak the polkit.service to make it compatible with sanitizers mkdir -p "$CONTAINER_OVERLAY/etc/systemd/system/polkit.service.d/" cat >"$CONTAINER_OVERLAY/etc/systemd/system/polkit.service.d/sanitizer-env.conf" <&1 >/dev/null | grep -q AddressSanitizer' container_run systemctl show -p Environment polkit.service | grep -q ASAN_OPTIONS # Now we should have a container ready for our shenanigans # Fuzz polkit's own interface run_and_check dfuzzer -v -n org.freedesktop.PolicyKit1 run_and_check --unpriv dfuzzer -v -n org.freedesktop.PolicyKit1 # Shut down the container and check for any sanitizer errors, since some of the errors can be detected only # after we start shutting things down. container_stop check_journal_for_sanitizer_errors # Also, check if polit didn't fail during the lifetime of the container (! journalctl -q -D "/var/log/journal/$CONTAINER_MACHINE_ID" _PID=1 --grep "polkit.service.*Failed with result") exit 0 polkit-126/test/integration/main.fmf000066400000000000000000000000241474122443600176110ustar00rootroot00000000000000require: - polkit polkit-126/test/integration/pkexec/000077500000000000000000000000001474122443600174565ustar00rootroot00000000000000polkit-126/test/integration/pkexec/expect/000077500000000000000000000000001474122443600207465ustar00rootroot00000000000000polkit-126/test/integration/pkexec/expect/SIGTRAP-on-EOF.exp000066400000000000000000000011511474122443600236140ustar00rootroot00000000000000# Issue: EOF in password input causes a coredump # See: # - https://github.com/polkit-org/polkit/pull/431 set timeout 10 expect_before timeout { send_error "Timeout!\n"; exit 124 } spawn pkexec echo "Hello world" expect "Password: " sleep .5 # Send EOF send -- "\x04" expect eof catch wait result set signal [lindex $result 5] set ec [lindex $result 3] if { $signal != "" } { send_error "pkexec died with signal $signal\n" exit 128 } send_error "pkexec exited with EC $ec\n" # We expect exit code 127 (failed authentication), if it's anything else propagate it further exit [expr $ec == 127 ? 0 : $ec] polkit-126/test/integration/pkexec/expect/basic-auth.exp000066400000000000000000000015161474122443600235070ustar00rootroot00000000000000# Do a basic password authentication via pkexec # # Usage: expect basic-auth.exp set timeout 10 expect_before timeout { send_error "Timeout!\n"; exit 124 } set password [lindex $argv 0] # We need at least two arguments: a password and a command to run if { [llength $argv] < 2 } { send_error "Missing required arguments\n" exit 1 } # The expression below skips over the first argument in $argv (password), and {*} expands # the resulting list into separate arguments to spawn() spawn pkexec {*}[lrange $argv 1 end] expect "Password: " sleep .5 send -- "$password\r" expect eof catch wait result set signal [lindex $result 5] set ec [lindex $result 3] if { $signal != "" } { send_error "pkexec died with signal $signal\n" exit 128 } send_error "pkexec exited with EC $ec\n" exit $ec polkit-126/test/integration/pkexec/main.fmf000066400000000000000000000001301474122443600210660ustar00rootroot00000000000000summary: Tests for polkit's pkexec utility test: ./test.sh require: - expect - sudo polkit-126/test/integration/pkexec/test.sh000077500000000000000000000143561474122443600210050ustar00rootroot00000000000000#!/bin/bash set -eux set -o pipefail EXPECT_SCRIPTS="$PWD/expect" TEST_USER="polkit-testuser" TEST_USER_PASSWORD="hello-world-$SRANDOM" # notsecret TMP_DIR="$(mktemp -d)" at_exit() { set +e : "Cleanup" userdel -rf "$TEST_USER" systemctl restart polkit rm -rf "$TMP_DIR" rm -f /etc/polkit-1/rules.d/00-test.rules } trap at_exit EXIT : "Setup" useradd "$TEST_USER" echo "$TEST_USER_PASSWORD" | passwd --stdin "$TEST_USER" # We need the Expect scripts to be somewhere accessible to the $TEST_USER cp -r "$EXPECT_SCRIPTS"/* "$TMP_DIR/" chown -R "$TEST_USER" "$TMP_DIR" # Temporarily allow $TEST_USER to gain root privileges cat >/etc/polkit-1/rules.d/00-test.rules < instead of just which confuses grep's anchor ($), so let's # strip it to make matching easier sed -i 's/\r$//g' "$TMP_DIR/environment.log" # pkexec preserves a very limited subset of env variables (see environment_variables_to_save # in pkexec's main()) # # Variables set by pkexec grep -qE "^HOME=/root$" "$TMP_DIR/environment.log" grep -qE "^LOGNAME=root$" "$TMP_DIR/environment.log" grep -qE "^PKEXEC_UID=$(id -u "$TEST_USER")$" "$TMP_DIR/environment.log" grep -qE "^USER=root$" "$TMP_DIR/environment.log" # pkexec resets $PATH to a predefined safe list (! grep -qE "^PATH=.*nope.*$" "$TMP_DIR/environment.log") # Inherited variables grep -qE "^LANG=C$" "$TMP_DIR/environment.log" grep -qE "^TERM=linux$" "$TMP_DIR/environment.log" # Ignored variables (! grep -qE "^FOO=" "$TMP_DIR/environment.log") (! grep -qE "^LD_PRELOAD=" "$TMP_DIR/environment.log") rm -f "$TMP_DIR/environment.log" : "Ellipsis in a long command line" # See: https://github.com/polkit-org/polkit/commit/322c014ccf21db7cd223192fa237178645c492e6 sudo -u "$TEST_USER" expect "$TMP_DIR/basic-auth.exp" "$TEST_USER_PASSWORD" \ echo $(printf "arg%d; " {0..128}) | tee "$TMP_DIR/long-cmdline.log" grep -Eq "Authentication is needed to run \`.*/echo arg0; arg1;.* ... .*; arg127; arg128;' as the super user" "$TMP_DIR/long-cmdline.log" grep -q "AUTHENTICATION COMPLETE" "$TMP_DIR/long-cmdline.log" rm -f "$TMP_DIR/long-cmdline.log" : "Don't die with SIGTRAP on EOF in password prompt" # See https://github.com/polkit-org/polkit/commit/6c9c07981f7ac7e7dfde05fa8210ae4204d31139 sudo -u "$TEST_USER" expect "$TMP_DIR/SIGTRAP-on-EOF.exp" | tee "$TMP_DIR/SIGTRAP-on-EOF.log" grep -q "AUTHENTICATION FAILED" "$TMP_DIR/SIGTRAP-on-EOF.log" grep -q "Not authorized" "$TMP_DIR/SIGTRAP-on-EOF.log" rm -f "$TMP_DIR/SIGTRAP-on-EOF.log" polkit-126/test/integration/plans/000077500000000000000000000000001474122443600173145ustar00rootroot00000000000000polkit-126/test/integration/plans/upstream-ci.fmf000066400000000000000000000003211474122443600222330ustar00rootroot00000000000000# vi: set sw=2 ts=2 et ft=yaml tw=80: # This plan discovers and executes all (enabled) integration tests summary: Upstream integration test suite /: inherit: false discover: how: fmf execute: how: tmt polkit-126/test/integration/systemd/000077500000000000000000000000001474122443600176675ustar00rootroot00000000000000polkit-126/test/integration/systemd/main.fmf000066400000000000000000000000701474122443600213020ustar00rootroot00000000000000summary: Check integration with systemd test: ./test.sh polkit-126/test/integration/systemd/rules/000077500000000000000000000000001474122443600210215ustar00rootroot00000000000000polkit-126/test/integration/systemd/rules/start-restart-stop-unit.rules000066400000000000000000000006121474122443600266530ustar00rootroot00000000000000polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units") { if (subject.user == "polkit-testuser" && action.lookup("unit") == "start-restart-stop.service" && (action.lookup("verb") == "restart" || action.lookup("verb") == "stop" || action.lookup("verb") == "start")) { return polkit.Result.YES; } } }); polkit-126/test/integration/systemd/test.sh000077500000000000000000000025101474122443600212030ustar00rootroot00000000000000#!/bin/bash set -eux set -o pipefail TEST_RULES="$PWD/rules" TEST_USER="polkit-testuser" at_exit() { set +e : "Cleanup" userdel -rf "$TEST_USER" rm -f /etc/polkit-1/rules.d/99-test.rules systemctl reload polkit } trap at_exit EXIT : "Setup" mkdir -p /run/systemd/system/ useradd "$TEST_USER" # Close stdin, so we get an instant error (Interactive authentication required) instead of having to deal # with an interactive authentication prompt exec 0<&- : "Allow $TEST_USER to start/restart/stop a simple systemd unit" # Use `systemctl edit --full ...` in the future cat >/run/systemd/system/start-restart-stop.service < and bind-mounted into the # container; that way we can fetch the container journal for debugging even if something goes horribly # wrong mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER_NAME.service.d" cat >"/run/systemd/system/systemd-nspawn@$CONTAINER_NAME.service.d/override.conf" <"$CONTAINER_OVERLAY/etc/machine-id" : >"$CONTAINER_OVERLAY/etc/hostname" # Create a non-root user, so we can test session bus stuff as well mkdir -p "$CONTAINER_OVERLAY/etc/sysusers.d/" cat >"$CONTAINER_OVERLAY/etc/sysusers.d/testuser.conf" <&2 "No container to start (missing call to container_prepare()?)" return 1 fi machinectl start "$CONTAINER_NAME" timeout --foreground 30s bash -ec "until systemd-run -M $CONTAINER_NAME --wait --pipe true; do sleep .5; done" # is-system-running returns > 0 if the system is running in degraded mode, but we don't care about that, we # just need to wait until the bootup is finished container_run systemctl is-system-running -q --wait || : } container_stop() { # Note: machinectl poweroff doesn't wait until the container shuts down completely, stop stop the service # behind it instead which does wait systemctl stop "systemd-nspawn@${CONTAINER_NAME:?}.service" } # Run a command in a container as a root. container_run() { systemd-run -M "${CONTAINER_NAME:?}" --wait --pipe "$@" } # Same as above, but run the command under a specific user. container_run_user() { local user="${1:?}" shift systemd-run -M "$user@${CONTAINER_NAME:?}" --user --wait --pipe "$@" } container_destroy() { if [[ -z "$CONTAINER_NAME" ]]; then return 0 fi if systemctl -q is-active "systemd-nspawn@$CONTAINER_NAME.service"; then container_stop fi # Export the container journal and sanitizer logs if $TMT_TEST_DATA is set, either by TMT directly or # manually. if [[ -n "${TMT_TEST_DATA:-}" ]]; then mkdir -p "$TMT_TEST_DATA" journalctl -D "/var/log/journal/$CONTAINER_MACHINE_ID" -o short-monotonic >"$TMT_TEST_DATA/$CONTAINER_NAME.log" fi rm -rf "/var/lib/machines/$CONTAINER_NAME" rm -rf "/var/log/journal/$CONTAINER_MACHINE_ID" rm -rf "/run/systemd/system/systemd-nspawn@$CONTAINER_NAME.service.d" systemctl daemon-reload } coredumpctl_init() { local ec if ! systemctl start systemd-coredump.socket; then echo >&2 "Failed to start systemd-coredump.socket" return 1 fi # Note: coredumpctl returns 1 when no coredumps are found coredumpctl --since=now >/dev/null && ec=0 || ec=$? if [[ $ec -ne 1 ]]; then echo >&2 "coredumpctl is not in operative state" return 1 fi # Set the internal coredumpctl timestamp, so we consider coredumps only from now on __COREDUMPCTL_TS="$(date +"%Y-%m-%d %H:%M:%S")" return 0 } # Attempt to dump info about relevant coredumps using the coredumpctl utility. # # Returns: # 0 when no coredumps were found, 1 otherwise coredumpctl_collect() ( set +ex local args=(-q --no-legend --no-pager) local tempfile="$(mktemp)" # Register a cleanup handler # # Note: since this function is a technically a subshell, RETURN trap won't work here # shellcheck disable=SC2064 trap "rm -f '$tempfile'" EXIT if [[ -n "$__COREDUMPCTL_TS" ]]; then args+=(--since "$__COREDUMPCTL_TS") fi if ! coredumpctl "${args[@]}" -F COREDUMP_EXE >"$tempfile"; then echo "No relevant coredumps found" return 0 fi # For each unique executable path call 'coredumpctl info' to get the stack trace and other useful info while read -r path; do local exe local gdb_cmd="set print pretty on\nbt full" coredumpctl "${args[@]}" info "$path" # Make sure we use the built binaries for getting gdb trace # # This is relevant mainly for the sanitizers run, where we don't install the just built revision, so # `coredumpctl debug` pulls in a local binary instead of the built one, which produces useless # results. if [[ -v BUILD_DIR && -d $BUILD_DIR ]]; then # The build directory layout of polkit is not flat, so we need to find the binary first exe="$(find "$BUILD_DIR" -executable -name "${path##*/}" | head -n1)" if [[ -n "$exe" ]]; then gdb_cmd="file $exe\nthread apply all bt\n$gdb_cmd" fi fi # Attempt to get a full stack trace for the first occurrence of the given executable path if gdb -v >/dev/null; then echo -e "\n" echo "Trying to run gdb with '$gdb_cmd' for '$path'" echo -e "$gdb_cmd" | coredumpctl "${args[@]}" debug "$path" echo -e "\n" fi done < <(sort -u "$tempfile") return 1 ) polkit-126/test/meson.build000066400000000000000000000006721474122443600160230ustar00rootroot00000000000000libpolkit_test_helper = static_library( 'polkit-test-helper', sources: 'polkittesthelper.c', dependencies: glib_dep, ) libpolkit_test_helper_dep = declare_dependency( include_directories: '.', dependencies: glib_dep, link_with: libpolkit_test_helper, ) test_wrapper = find_program('wrapper.py') test_data_dir = meson.current_source_dir() / 'data' subdir('polkit') if not get_option('libs-only') subdir('polkitbackend') endif polkit-126/test/polkit/000077500000000000000000000000001474122443600151565ustar00rootroot00000000000000polkit-126/test/polkit/meson.build000066400000000000000000000007411474122443600173220ustar00rootroot00000000000000test_units = [ 'polkitunixusertest', 'polkitunixgrouptest', 'polkitunixnetgrouptest', 'polkitidentitytest', ] c_flags = [ '-D_POLKIT_COMPILATION', '-D_POLKIT_BACKEND_COMPILATION', ] foreach test_unit: test_units exe = executable( test_unit, test_unit + '.c', dependencies: libpolkit_gobject_dep, c_args: c_flags, ) test( test_unit, test_wrapper, args: ['--data-dir', test_data_dir, exe.full_path()], timeout: 30, ) endforeach polkit-126/test/polkit/polkitidentitytest.c000066400000000000000000000132011474122443600212730ustar00rootroot00000000000000/* * Copyright (C) 2011 Google Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Nikki VonHollen */ #include "glib.h" #include #include /* Test helper types */ struct ComparisonTestData { const gchar *subject_a; const gchar *subject_b; gboolean equal; }; /* Test definitions */ static void test_string (const void *_subject) { const gchar *subject = (const gchar *) _subject; PolkitIdentity *identity; GError *error = NULL; gchar *subject_new; /* Create the subject from a string */ identity = polkit_identity_from_string (subject, &error); g_assert (identity); g_assert_no_error (error); /* Create new string for identity */ subject_new = polkit_identity_to_string (identity); /* Make sure they match */ g_assert_cmpstr (subject_new, ==, subject); g_free (subject_new); g_object_unref (identity); } static void test_gvariant (const void *_subject) { const gchar *subject = (const gchar *) _subject; PolkitIdentity *identity, *new_identity; GError *error = NULL; GVariant *value; /* Create the subject from a string */ identity = polkit_identity_from_string (subject, &error); g_assert_no_error (error); g_assert (identity); /* Create a GVariant for the subject */ value = polkit_identity_to_gvariant (identity); g_assert (value); /* Unserialize the subject */ new_identity = polkit_identity_new_for_gvariant (value, &error); g_assert_no_error (error); g_assert (new_identity); g_variant_unref (value); /* Make sure the two identities are equal */ g_assert (new_identity); g_assert (polkit_identity_equal (identity, new_identity)); g_object_unref (identity); g_object_unref (new_identity); } static void test_comparison (const void *_data) { struct ComparisonTestData *data = (struct ComparisonTestData *) _data; PolkitIdentity *identity_a, *identity_b; GError *error = NULL; guint hash_a, hash_b; /* Create identities A and B */ identity_a = polkit_identity_from_string (data->subject_a, &error); g_assert_no_error (error); g_assert (identity_a); identity_b = polkit_identity_from_string (data->subject_b, &error); g_assert_no_error (error); g_assert (identity_b); /* Compute their hashes */ hash_a = polkit_identity_hash (identity_a); hash_b = polkit_identity_hash (identity_b); /* Comparison to self should always work */ g_assert (polkit_identity_equal (identity_a, identity_a)); /* Are A and B supposed to match? Test hash and comparators */ if (data->equal) { g_assert_cmpint (hash_a, ==, hash_b); g_assert (polkit_identity_equal (identity_a, identity_b)); } else { g_assert_cmpint (hash_a, !=, hash_b); g_assert (!polkit_identity_equal (identity_a, identity_b)); } g_object_unref (identity_a); g_object_unref (identity_b); } /* Test helpers */ struct ComparisonTestData comparison_test_data [] = { {"unix-user:root", "unix-user:root", TRUE}, {"unix-user:root", "unix-user:john", FALSE}, {"unix-user:john", "unix-user:john", TRUE}, {"unix-group:root", "unix-group:root", TRUE}, {"unix-group:root", "unix-group:jane", FALSE}, {"unix-group:jane", "unix-group:jane", TRUE}, #ifdef HAVE_SETNETGRENT {"unix-netgroup:foo", "unix-netgroup:foo", TRUE}, {"unix-netgroup:foo", "unix-netgroup:bar", FALSE}, #endif {"unix-user:root", "unix-group:root", FALSE}, #ifdef HAVE_SETNETGRENT {"unix-user:jane", "unix-netgroup:foo", FALSE}, #endif {NULL}, }; static void add_comparison_tests (void) { unsigned int i; for (i = 0; comparison_test_data[i].subject_a != NULL; i++) { struct ComparisonTestData *test_data = &comparison_test_data[i]; gchar *test_name = g_strdup_printf ("/PolkitIdentity/comparison_%d", i); g_test_add_data_func (test_name, test_data, test_comparison); g_free(test_name); } } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_data_func ("/PolkitIdentity/user_string_0", "unix-user:root", test_string); g_test_add_data_func ("/PolkitIdentity/user_string_1", "unix-user:john", test_string); g_test_add_data_func ("/PolkitIdentity/user_string_2", "unix-user:jane", test_string); g_test_add_data_func ("/PolkitIdentity/group_string_0", "unix-group:root", test_string); g_test_add_data_func ("/PolkitIdentity/group_string_1", "unix-group:john", test_string); g_test_add_data_func ("/PolkitIdentity/group_string_2", "unix-group:jane", test_string); g_test_add_data_func ("/PolkitIdentity/group_string_3", "unix-group:users", test_string); #ifdef HAVE_SETNETGRENT g_test_add_data_func ("/PolkitIdentity/netgroup_string", "unix-netgroup:foo", test_string); g_test_add_data_func ("/PolkitIdentity/netgroup_gvariant", "unix-netgroup:foo", test_gvariant); #endif g_test_add_data_func ("/PolkitIdentity/user_gvariant", "unix-user:root", test_gvariant); g_test_add_data_func ("/PolkitIdentity/group_gvariant", "unix-group:root", test_gvariant); add_comparison_tests (); return g_test_run (); } polkit-126/test/polkit/polkitunixgrouptest.c000066400000000000000000000040021474122443600215010ustar00rootroot00000000000000/* * Copyright (C) 2011 Google Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Nikki VonHollen */ #include "glib.h" #include static void test_new (void) { PolkitUnixGroup *group; group = POLKIT_UNIX_GROUP (polkit_unix_group_new (0)); g_assert (group); gint group_gid = polkit_unix_group_get_gid (group); g_assert_cmpint (group_gid, ==, 0); g_object_unref (group); } static void test_new_for_name (void) { GError *error = NULL; PolkitUnixGroup *group; group = POLKIT_UNIX_GROUP (polkit_unix_group_new_for_name ("root", &error)); g_assert (group); g_assert_no_error (error); gint group_gid = polkit_unix_group_get_gid (group); g_assert_cmpint (group_gid, ==, 0); g_object_unref (group); } static void test_set_gid (void) { PolkitUnixGroup *group; group = POLKIT_UNIX_GROUP (polkit_unix_group_new (0)); polkit_unix_group_set_gid (group, 5); gint group_gid = polkit_unix_group_get_gid (group); g_assert_cmpint (group_gid, ==, 5); g_object_unref (group); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/PolkitUnixGroup/new", test_new); g_test_add_func ("/PolkitUnixGroup/new_for_name", test_new_for_name); g_test_add_func ("/PolkitUnixGroup/set_gid", test_set_gid); return g_test_run (); } polkit-126/test/polkit/polkitunixnetgrouptest.c000066400000000000000000000042331474122443600222160ustar00rootroot00000000000000/* * Copyright (C) 2011 Google Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Nikki VonHollen */ #include "glib.h" #include #include static void test_new (void) { PolkitUnixNetgroup *netgroup; const char *netgroup_name; netgroup = POLKIT_UNIX_NETGROUP (polkit_unix_netgroup_new ("testgroup")); g_assert (netgroup); netgroup_name = polkit_unix_netgroup_get_name (netgroup); g_assert_cmpstr (netgroup_name, ==, "testgroup"); g_object_unref (netgroup); } static void test_set_name (void) { PolkitUnixNetgroup *netgroup; const char *netgroup_name; char new_name_buf [] = "foo"; netgroup = POLKIT_UNIX_NETGROUP (polkit_unix_netgroup_new ("testgroup")); polkit_unix_netgroup_set_name (netgroup, new_name_buf); netgroup_name = polkit_unix_netgroup_get_name (netgroup); g_assert_cmpstr (netgroup_name, ==, "foo"); memcpy(new_name_buf, "bar", 3); netgroup_name = polkit_unix_netgroup_get_name (netgroup); g_assert_cmpstr (netgroup_name, ==, "foo"); polkit_unix_netgroup_set_name (netgroup, new_name_buf); netgroup_name = polkit_unix_netgroup_get_name (netgroup); g_assert_cmpstr (netgroup_name, ==, "bar"); g_object_unref (netgroup); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); #ifdef HAVE_SETNETGRENT g_test_add_func ("/PolkitUnixNetgroup/new", test_new); g_test_add_func ("/PolkitUnixNetgroup/set_name", test_set_name); #endif return g_test_run (); } polkit-126/test/polkit/polkitunixusertest.c000066400000000000000000000046711474122443600213370ustar00rootroot00000000000000/* * Copyright (C) 2011 Google Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Nikki VonHollen */ #include "glib.h" #include struct user_entry { const gchar *name; gint uid; }; static struct user_entry user_entries [] = { {"root", 0}, {"john", 500}, {"jane", 501}, {NULL}, }; static void test_new (void) { unsigned int i; for (i = 0; user_entries[i].name != NULL; i++) { gint uid = user_entries[i].uid; PolkitUnixUser *user; user = POLKIT_UNIX_USER (polkit_unix_user_new (uid)); g_assert (user); gint user_uid = polkit_unix_user_get_uid (user); g_assert_cmpint (user_uid, ==, uid); g_object_unref (user); } } static void test_new_for_name (void) { unsigned int i; for (i = 0; user_entries[i].name != NULL; i++) { const gchar *name = user_entries[i].name; gint expect_uid = user_entries[i].uid; GError *error = NULL; PolkitUnixUser *user; user = POLKIT_UNIX_USER (polkit_unix_user_new_for_name (name, &error)); g_assert (user); g_assert_no_error (error); gint user_uid = polkit_unix_user_get_uid (user); g_assert_cmpint (user_uid, ==, expect_uid); g_object_unref (user); } } static void test_set_uid (void) { PolkitUnixUser *user; user = POLKIT_UNIX_USER (polkit_unix_user_new (0)); polkit_unix_user_set_uid (user, 5); gint user_uid = polkit_unix_user_get_uid (user); g_assert_cmpint (user_uid, ==, 5); g_object_unref (user); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/PolkitUnixUser/new", test_new); g_test_add_func ("/PolkitUnixUser/new_for_name", test_new_for_name); g_test_add_func ("/PolkitUnixUser/set_uid", test_set_uid); return g_test_run (); } polkit-126/test/polkitbackend/000077500000000000000000000000001474122443600164665ustar00rootroot00000000000000polkit-126/test/polkitbackend/meson.build000066400000000000000000000007261474122443600206350ustar00rootroot00000000000000test_unit = 'test-polkitbackendjsauthority' deps = [ libpolkit_gobject_dep, libpolkit_test_helper_dep, ] c_flags = [ '-D_POLKIT_COMPILATION', '-D_POLKIT_BACKEND_COMPILATION', ] exe = executable( test_unit, test_unit + '.c', include_directories: top_inc, dependencies: deps, c_args: c_flags, link_with: libpolkit_backend, ) test( test_unit, test_wrapper, args: ['--data-dir', test_data_dir, '--mock-dbus', exe.full_path()], timeout: 90, ) polkit-126/test/polkitbackend/test-polkitbackendjsauthority.c000066400000000000000000000332761474122443600247420ustar00rootroot00000000000000/* * Copyright (C) 2011 Google Inc. * Copyright (C) 2012 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Nikki VonHollen * David Zeuthen */ #include "glib.h" #include #include #include #include #include /* see test/data/etc/polkit-1/rules.d/10-testing.rules */ /* Test helper types */ static PolkitBackendJsAuthority *get_authority (void); static PolkitBackendJsAuthority * get_authority (void) { gchar *rules_dirs[3] = {0}; PolkitBackendJsAuthority *authority; rules_dirs[0] = polkit_test_get_data_path ("etc/polkit-1/rules.d"); rules_dirs[1] = polkit_test_get_data_path ("usr/share/polkit-1/rules.d"); rules_dirs[2] = NULL; g_assert (rules_dirs[0] != NULL); g_assert (rules_dirs[1] != NULL); authority = g_object_new (POLKIT_BACKEND_TYPE_JS_AUTHORITY, "rules-dirs", rules_dirs, NULL); g_free (rules_dirs[0]); g_free (rules_dirs[1]); return authority; } static void test_get_admin_identities_for_action_id (const gchar *action_id, const gchar *const *expected_admins) { PolkitBackendJsAuthority *authority = NULL; PolkitSubject *caller = NULL; PolkitSubject *subject = NULL; PolkitIdentity *user_for_subject = NULL; PolkitDetails *details = NULL; GError *error = NULL; GList *admin_identities = NULL; GList *l; guint n; authority = get_authority (); caller = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); subject = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); user_for_subject = polkit_identity_from_string ("unix-user:root", &error); g_assert_no_error (error); details = polkit_details_new (); /* Get the list of PolkitUnixUser objects who are admins */ admin_identities = polkit_backend_interactive_authority_get_admin_identities (POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority), caller, subject, user_for_subject, TRUE, /* is_local */ TRUE, /* is_active */ action_id, details); for (l = admin_identities, n = 0; l != NULL; l = l->next, n++) { PolkitIdentity *test_identity = POLKIT_IDENTITY (l->data); gchar *s; g_assert (expected_admins[n] != NULL); s = polkit_identity_to_string (test_identity); g_assert_cmpstr (expected_admins[n], ==, s); g_free (s); } g_assert_cmpstr (expected_admins[n], ==, NULL); g_list_free_full (admin_identities, g_object_unref); g_clear_object (&details); g_clear_object (&user_for_subject); g_clear_object (&subject); g_clear_object (&caller); g_clear_object (&authority); } static void test_get_admin_identities (void) { struct { const gchar *action_id; const gchar *expected_admins[5]; } test_cases[] = { { "com.example.doesntmatter", { "unix-group:admin", "unix-user:root" } }, { "net.company.action1", { "unix-group:admin" } }, { "net.company.action2", { "unix-group:users" } }, #ifdef HAVE_SETNETGRENT { "net.company.action3", { "unix-netgroup:foo" } }, #endif }; guint n; for (n = 0; n < G_N_ELEMENTS (test_cases); n++) { test_get_admin_identities_for_action_id (test_cases[n].action_id, test_cases[n].expected_admins); } } /* ---------------------------------------------------------------------------------------------------- */ typedef struct RulesTestCase RulesTestCase; struct RulesTestCase { const gchar *test_name; const gchar *action_id; const gchar *identity; const gchar *vars; PolkitImplicitAuthorization expected_result; }; static const RulesTestCase rules_test_cases[] = { /* Check basics */ { "basic0", "net.company.productA.action0", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED, }, { "basic1", "net.company.productA.action1", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED, }, /* actions without explict rules aren't automatically NOT_AUTHORIZED */ { "basic2", "net.company.productA.action2", "unix-user:john", NULL, POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN, }, /* Ordering tests ... we have four rules files, check they are * evaluated in order by checking the detail set by each rules * * - etc/polkit-1/rules.d/10-testing.rules (file a) * - usr/share/polkit-1/rules.d/10-testing.rules (file b) * - etc/polkit-1/rules.d/15-testing.rules (file c) * - usr/share/polkit-1/rules.d/20-testing.rules (file d) * * file. */ { /* defined in file a, b, c, d - should pick file a */ "order0", "net.company.order0", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { /* defined in file b, c, d - should pick file b */ "order1", "net.company.order1", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { /* defined in file c, d - should pick file c */ "order2", "net.company.order2", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, /* variables */ { "variables1", "net.company.group.variables", "unix-user:root", "foo=1", POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { "variables2", "net.company.group.variables", "unix-user:root", "foo=2", POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED, }, { "variables3", "net.company.group.variables", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED, }, /* check group membership */ { /* john is a member of group 'users', see test/etc/group */ "group_membership_with_member", "net.company.group.only_group_users", "unix-user:john", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { /* sally is not a member of group 'users', see test/etc/group */ "group_membership_with_non_member", "net.company.group.only_group_users", "unix-user:sally", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, /* check netgroup membership */ { /* john is a member of netgroup 'foo', see test/etc/netgroup */ "netgroup_membership_with_member", "net.company.group.only_netgroup_users", "unix-user:john", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { /* sally is not a member of netgroup 'foo', see test/etc/netgroup */ "netgroup_membership_with_non_member", "net.company.group.only_netgroup_users", "unix-user:sally", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, /* spawning */ { "spawning_non_existing_helper", "net.company.spawning.non_existing_helper", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { "spawning_successful_helper", "net.company.spawning.successful_helper", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { "spawning_failing_helper", "net.company.spawning.failing_helper", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { "spawning_helper_with_output", "net.company.spawning.helper_with_output", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { "spawning_helper_timeout", "net.company.spawning.helper_timeout", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, /* runaway scripts */ { "runaway_script", "net.company.run_away_script", "unix-user:root", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, { /* highuid1 is not a member of group 'users', see test/data/etc/group */ "group_membership_with_non_member(highuid22)", "net.company.group.only_group_users", "unix-user:highuid2", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, { /* highuid2 is not a member of group 'users', see test/data/etc/group */ "group_membership_with_non_member(highuid21)", "net.company.group.only_group_users", "unix-user:highuid2", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, { /* highuid1 is not a member of group 'users', see test/data/etc/group */ "group_membership_with_non_member(highuid24)", "net.company.group.only_group_users", "unix-user:2147483648", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, { /* highuid2 is not a member of group 'users', see test/data/etc/group */ "group_membership_with_non_member(highuid23)", "net.company.group.only_group_users", "unix-user:4000000000", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, { /* john is authorized to do this, see 10-testing.rules */ "john_action", "net.company.john_action", "unix-user:john", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { /* only john is authorized to do this, see 10-testing.rules */ "jane_action", "net.company.john_action", "unix-user:jane", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, { /* highuid2 is authorized to do this, see 10-testing.rules */ "highuid2_action", "net.company.highuid2_action", "unix-user:highuid2", NULL, POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, }, { /* only highuid2 is authorized to do this, see 10-testing.rules */ "highuid1_action", "net.company.highuid2_action", "unix-user:highuid1", NULL, POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, }, }; /* ---------------------------------------------------------------------------------------------------- */ static void rules_test_func (gconstpointer user_data) { const RulesTestCase *tc = user_data; PolkitBackendJsAuthority *authority = NULL; PolkitSubject *caller = NULL; PolkitSubject *subject = NULL; PolkitIdentity *user_for_subject = NULL; PolkitDetails *details = NULL; GError *error = NULL; PolkitImplicitAuthorization result; authority = get_authority (); caller = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); subject = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); user_for_subject = polkit_identity_from_string (tc->identity, &error); g_assert_no_error (error); details = polkit_details_new (); if (tc->vars != NULL) { gchar *s; const gchar *key; const gchar *value; s = g_strdup (tc->vars); key = s; value = strchr (key, '='); g_assert (value != NULL); *((gchar *) value) = '\0'; value += 1; polkit_details_insert (details, key, value); g_free (s); } result = polkit_backend_interactive_authority_check_authorization_sync (POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority), caller, subject, user_for_subject, TRUE, TRUE, tc->action_id, details, POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN); g_assert_cmpint (result, ==, tc->expected_result); g_clear_object (&details); g_clear_object (&user_for_subject); g_clear_object (&subject); g_clear_object (&caller); g_clear_object (&authority); } static void add_rules_tests (void) { guint n; for (n = 0; n < G_N_ELEMENTS (rules_test_cases); n++) { const RulesTestCase *tc = &rules_test_cases[n]; gchar *s; s = g_strdup_printf ("/PolkitBackendJsAuthority/rules_%s", tc->test_name); g_test_add_data_func (s, &rules_test_cases[n], rules_test_func); g_free (s); } } /* ---------------------------------------------------------------------------------------------------- */ int main (int argc, char *argv[]) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); //polkit_test_redirect_logs (); g_test_add_func ("/PolkitBackendJsAuthority/get_admin_identities", test_get_admin_identities); add_rules_tests (); return g_test_run (); }; polkit-126/test/polkittesthelper.c000066400000000000000000000037331474122443600174300ustar00rootroot00000000000000/* * Copyright (C) 2011 Google Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Nikki VonHollen */ #include "polkittesthelper.h" #include /* TODO: Log handling with unit tests is horrible. Figure out a way to always * show logs, without munging up test output. For now, we hide them * unless --verbose is used with g_test_message(...). */ void polkit_test_log_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { g_test_message("%s", message); } /** * Send all future log messages to g_test_message(...). * * Logs will only be shown when test programs are run with --verbose. */ void polkit_test_redirect_logs (void) { g_log_set_default_handler (polkit_test_log_handler, NULL); } /** * Get absolute path to test data. * * Requires POLKIT_TEST_DATA environment variable to point to root data dir. * * @param relpath Relative path to test data * @return Full path to data as string. Free with g_free(). */ gchar * polkit_test_get_data_path (const gchar *relpath) { const gchar *root = getenv ("POLKIT_TEST_DATA"); if (root == NULL) return NULL; return g_strconcat(root, "/", relpath, NULL); } polkit-126/test/polkittesthelper.h000066400000000000000000000023231474122443600174270ustar00rootroot00000000000000/* * Copyright (C) 2011 Google Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Nikki VonHollen */ #ifndef POLKIT_TEST_HELPER_H_ #define POLKIT_TEST_HELPER_H_ #include "glib.h" void polkit_test_log_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data); void polkit_test_redirect_logs (void); gchar *polkit_test_get_data_path (const gchar *relpath); #endif polkit-126/test/wrapper.py000077500000000000000000000041411474122443600157110ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import atexit import os import subprocess import sys import dbus import dbus.mainloop.glib import dbusmock def setup_test_namespace(data_dir): print(f"Test data dir: {data_dir}") # Setup a new mount & user namespace, so we can use mount() unprivileged (see user_namespaces(7)) euid = os.geteuid() egid = os.getegid() try: os.unshare(os.CLONE_NEWNS|os.CLONE_NEWUSER) # Map root to the original EUID and EGID, so we can actually call mount() inside our namespace with open("/proc/self/uid_map", "w") as f: f.write(f"0 {euid} 1") with open("/proc/self/setgroups", "w") as f: f.write("deny") with open("/proc/self/gid_map", "w") as f: f.write(f"0 {egid} 1") # Overmount /etc with our own version subprocess.check_call(["mount", "--bind", os.path.join(data_dir, "etc"), "/etc"]) except PermissionError: print("Lacking permissions to set up test harness, skipping") sys.exit(77) except AttributeError: print("Python 3.12 is required for os.unshare(), skipping") sys.exit(77) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("test_executable", help="test executable to run in our own test namespace") parser.add_argument("--data-dir", type=str, required=True, help="path to test data directory (with our own /etc/{passwd,group,...} files)") parser.add_argument("--mock-dbus", action="store_true", help="set up a mock system D-Bus using dbusmock") args = parser.parse_args() setup_test_namespace(args.data_dir) if args.mock_dbus: dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) dbusmock.DBusTestCase.start_system_bus() atexit.register(dbusmock.DBusTestCase.stop_dbus, dbusmock.DBusTestCase.system_bus_pid) print(f"Executing '{args.test_executable}'") sys.stdout.flush() os.environ["POLKIT_TEST_DATA"] = args.data_dir subprocess.check_call(args.test_executable, shell=True)