From a7ada11442f74bfdcd015159b50673982a4beea6 Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 1 Aug 2026 16:23:40 +0300 Subject: [PATCH] polkit: vendor upstream client libraries (libpolkit-gobject-1 + agent-1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kf6-kauth deliberately uses the real polkit backend (KAUTH_BACKEND_NAME= POLKITQT6-1), and PolkitQt6-1 links the upstream polkit CLIENT libraries, which nothing provided (redbear-polkit is only the org.freedesktop.PolicyKit1 daemon). This makes the KDE authorization mechanism actually work, not just compile. - New recipe local/recipes/libs/polkit (vendored polkit 124, full-fork path=source) built with -Dlibs-only=true so polkitd + the JS rules engine are skipped (redbear-polkit is the authority daemon). glib/gio/expat deps already exist. - src/meson.build: build src/polkitagent even under libs-only — libpolkit-agent-1 + its PAM auth helper are client-side, needed by PolkitQt6-1, and depend only on libpolkit-gobject-1 + glib + PAM (pam-redbear, the same PAM SDDM uses). Only the daemon/backend/programs stay gated off. - pam-redbear: add the standard PAM_MAX_MSG_SIZE/PAM_MAX_RESP_SIZE constants its header was missing (polkit-agent-helper-1 needs them). - polkit-qt6: depend on polkit; it now finds polkit-gobject-1 + polkit-agent-1 (v124) and builds. Auth prompts verify passwords via pam-redbear; authorization decisions come from the redbear-polkit daemon over D-Bus. --- .../source/include/security/pam_appl.h | 3 + local/recipes/libs/polkit-qt6/recipe.toml | 1 + local/recipes/libs/polkit/recipe.toml | 56 + .../recipes/libs/polkit/source/.dir-locals.el | 1 + .../recipes/libs/polkit/source/.gitlab-ci.yml | 77 + .../source/.gitlab/issue_templates/Default.md | 17 + .../merge_request_templates/Default.md | 7 + local/recipes/libs/polkit/source/.packit.yaml | 30 + local/recipes/libs/polkit/source/AUTHORS | 0 local/recipes/libs/polkit/source/COPYING | 482 +++ local/recipes/libs/polkit/source/ChangeLog | 0 local/recipes/libs/polkit/source/HACKING.md | 108 + local/recipes/libs/polkit/source/NEWS.md | 1777 ++++++++ local/recipes/libs/polkit/source/README.md | 90 + .../libs/polkit/source/actions/meson.build | 10 + .../org.freedesktop.policykit.policy.in | 21 + .../libs/polkit/source/data/meson.build | 54 + ...desktop.PolicyKit1.AuthenticationAgent.xml | 61 + .../org.freedesktop.PolicyKit1.Authority.xml | 438 ++ .../data/org.freedesktop.PolicyKit1.conf.in | 20 + .../org.freedesktop.PolicyKit1.service.in | 5 + .../polkit/source/data/policyconfig-1.dtd | 26 + .../libs/polkit/source/data/polkit-1.in | 6 + .../polkit/source/data/polkit-agent-1.pc.in | 11 + .../polkit/source/data/polkit-gobject-1.pc.in | 17 + .../libs/polkit/source/data/polkit.conf.in | 1 + .../libs/polkit/source/data/polkit.service.in | 35 + .../libs/polkit/source/docs/PORTING-GUIDE | 44 + local/recipes/libs/polkit/source/docs/TODO | 20 + .../polkit/source/docs/extensiondir.xml.in | 1 + .../libs/polkit/source/docs/man/meson.build | 36 + .../libs/polkit/source/docs/man/pkaction.xml | 102 + .../libs/polkit/source/docs/man/pkcheck.xml | 224 + .../libs/polkit/source/docs/man/pkexec.xml | 284 ++ .../polkit/source/docs/man/pkttyagent.xml | 156 + .../libs/polkit/source/docs/man/polkit.xml | 1020 +++++ .../libs/polkit/source/docs/man/polkitd.xml | 86 + .../libs/polkit/source/docs/meson.build | 23 + .../polkit/source/docs/polkit-1-diagrams.svg | 1064 +++++ .../source/docs/polkit-architecture.png | Bin 0 -> 63503 bytes ...kit-authentication-agent-example-wheel.png | Bin 0 -> 55334 bytes .../polkit-authentication-agent-example.png | Bin 0 -> 46245 bytes ...desktop.PolicyKit1.AuthenticationAgent.xml | 141 + ...e-org.freedesktop.PolicyKit1.Authority.xml | 960 +++++ .../polkit/source/docs/polkit/meson.build | 36 + .../polkit/source/docs/polkit/overview.xml | 343 ++ .../source/docs/polkit/polkit-1-docs.xml | 84 + .../source/docs/polkit/polkit-1-sections.txt | 373 ++ .../polkit/source/docs/polkit/polkit-1.types | 21 + .../libs/polkit/source/docs/version.xml.in | 1 + .../libs/polkit/source/gettext/its/polkit.its | 8 + .../libs/polkit/source/gettext/its/polkit.loc | 6 + local/recipes/libs/polkit/source/meson.build | 430 ++ .../libs/polkit/source/meson_options.txt | 21 + .../libs/polkit/source/meson_post_install.py | 69 + local/recipes/libs/polkit/source/po/ChangeLog | 0 local/recipes/libs/polkit/source/po/LINGUAS | 23 + local/recipes/libs/polkit/source/po/Makevars | 78 + .../recipes/libs/polkit/source/po/POTFILES.in | 8 + .../libs/polkit/source/po/POTFILES.skip | 1 + local/recipes/libs/polkit/source/po/cs.po | 216 + local/recipes/libs/polkit/source/po/da.po | 204 + local/recipes/libs/polkit/source/po/de.po | 221 + local/recipes/libs/polkit/source/po/hr.po | 204 + local/recipes/libs/polkit/source/po/hu.po | 214 + local/recipes/libs/polkit/source/po/id.po | 204 + local/recipes/libs/polkit/source/po/it.po | 212 + local/recipes/libs/polkit/source/po/ka.po | 209 + .../recipes/libs/polkit/source/po/meson.build | 5 + local/recipes/libs/polkit/source/po/nl.po | 209 + local/recipes/libs/polkit/source/po/nn.po | 207 + local/recipes/libs/polkit/source/po/pl.po | 215 + local/recipes/libs/polkit/source/po/pt.po | 209 + local/recipes/libs/polkit/source/po/pt_BR.po | 206 + local/recipes/libs/polkit/source/po/ro.po | 211 + local/recipes/libs/polkit/source/po/ru.po | 217 + local/recipes/libs/polkit/source/po/sk.po | 211 + local/recipes/libs/polkit/source/po/sv.po | 214 + local/recipes/libs/polkit/source/po/tr.po | 218 + local/recipes/libs/polkit/source/po/uk.po | 210 + local/recipes/libs/polkit/source/po/zh_CN.po | 196 + local/recipes/libs/polkit/source/po/zh_TW.po | 196 + .../libs/polkit/source/src/examples/cancel.c | 157 + .../polkit/source/src/examples/frobnicate.c | 83 + .../polkit/source/src/examples/meson.build | 27 + ...esktop.policykit.examples.pkexec.policy.in | 21 + .../source/src/examples/polkit-raw-dbus.py | 41 + .../libs/polkit/source/src/meson.build | 21 + .../libs/polkit/source/src/polkit/meson.build | 136 + .../libs/polkit/source/src/polkit/polkit.h | 50 + .../src/polkit/polkitactiondescription.c | 382 ++ .../src/polkit/polkitactiondescription.h | 65 + .../source/src/polkit/polkitauthority.c | 2081 ++++++++++ .../source/src/polkit/polkitauthority.h | 227 ++ .../src/polkit/polkitauthorityfeatures.c | 29 + .../src/polkit/polkitauthorityfeatures.h | 49 + .../src/polkit/polkitauthorizationresult.c | 304 ++ .../src/polkit/polkitauthorizationresult.h | 61 + .../polkit/polkitcheckauthorizationflags.c | 29 + .../polkit/polkitcheckauthorizationflags.h | 50 + .../polkit/source/src/polkit/polkitdetails.c | 235 ++ .../polkit/source/src/polkit/polkitdetails.h | 58 + .../src/polkit/polkitenumtypes.c.template | 39 + .../src/polkit/polkitenumtypes.h.template | 24 + .../polkit/source/src/polkit/polkiterror.c | 55 + .../polkit/source/src/polkit/polkiterror.h | 62 + .../polkit/source/src/polkit/polkitidentity.c | 386 ++ .../polkit/source/src/polkit/polkitidentity.h | 82 + .../src/polkit/polkitimplicitauthorization.c | 117 + .../src/polkit/polkitimplicitauthorization.h | 69 + .../source/src/polkit/polkitpermission.c | 865 ++++ .../source/src/polkit/polkitpermission.h | 56 + .../polkit/source/src/polkit/polkitprivate.h | 82 + .../polkit/source/src/polkit/polkitsubject.c | 543 +++ .../polkit/source/src/polkit/polkitsubject.h | 108 + .../source/src/polkit/polkitsystembusname.c | 681 ++++ .../source/src/polkit/polkitsystembusname.h | 65 + .../src/polkit/polkittemporaryauthorization.c | 226 ++ .../src/polkit/polkittemporaryauthorization.h | 56 + .../polkit/source/src/polkit/polkittypes.h | 84 + .../source/src/polkit/polkitunixgroup.c | 282 ++ .../source/src/polkit/polkitunixgroup.h | 59 + .../source/src/polkit/polkitunixnetgroup.c | 245 ++ .../source/src/polkit/polkitunixnetgroup.h | 58 + .../source/src/polkit/polkitunixprocess.c | 1281 ++++++ .../source/src/polkit/polkitunixprocess.h | 83 + .../src/polkit/polkitunixsession-systemd.c | 504 +++ .../source/src/polkit/polkitunixsession.c | 527 +++ .../source/src/polkit/polkitunixsession.h | 64 + .../polkit/source/src/polkit/polkitunixuser.c | 315 ++ .../polkit/source/src/polkit/polkitunixuser.h | 60 + .../polkit/source/src/polkitagent/meson.build | 119 + .../source/src/polkitagent/polkitagent.h | 37 + .../polkitagentenumtypes.c.template | 39 + .../polkitagentenumtypes.h.template | 24 + .../polkitagent/polkitagenthelper-bsdauth.c | 163 + .../src/polkitagent/polkitagenthelper-pam.c | 309 ++ .../polkitagent/polkitagenthelper-shadow.c | 204 + .../polkitagent/polkitagenthelperprivate.c | 145 + .../polkitagent/polkitagenthelperprivate.h | 46 + .../src/polkitagent/polkitagentlistener.c | 827 ++++ .../src/polkitagent/polkitagentlistener.h | 149 + .../src/polkitagent/polkitagentmarshal.list | 1 + .../src/polkitagent/polkitagentsession.c | 668 +++ .../src/polkitagent/polkitagentsession.h | 54 + .../src/polkitagent/polkitagenttextlistener.c | 668 +++ .../src/polkitagent/polkitagenttextlistener.h | 45 + .../source/src/polkitagent/polkitagenttypes.h | 50 + .../source/src/polkitbackend/50-default.rules | 12 + .../polkit/source/src/polkitbackend/init.js | 93 + .../source/src/polkitbackend/meson.build | 83 + .../source/src/polkitbackend/polkitbackend.h | 40 + .../polkitbackend/polkitbackendactionlookup.c | 182 + .../polkitbackend/polkitbackendactionlookup.h | 123 + .../polkitbackend/polkitbackendactionpool.c | 1140 ++++++ .../polkitbackend/polkitbackendactionpool.h | 78 + .../polkitbackend/polkitbackendauthority.c | 1548 +++++++ .../polkitbackend/polkitbackendauthority.h | 286 ++ .../src/polkitbackend/polkitbackendcommon.c | 661 +++ .../src/polkitbackend/polkitbackendcommon.h | 162 + .../polkitbackendduktapeauthority.c | 1163 ++++++ .../polkitbackendinteractiveauthority.c | 3597 +++++++++++++++++ .../polkitbackendinteractiveauthority.h | 150 + .../polkitbackendjsauthority.cpp | 1364 +++++++ .../polkitbackend/polkitbackendjsauthority.h | 75 + .../src/polkitbackend/polkitbackendprivate.h | 29 + .../polkitbackendsessionmonitor-systemd.c | 487 +++ .../polkitbackendsessionmonitor.c | 515 +++ .../polkitbackendsessionmonitor.h | 66 + .../src/polkitbackend/polkitbackendtypes.h | 40 + .../polkit/source/src/polkitbackend/polkitd.c | 265 ++ .../source/src/polkitbackend/toarray.pl | 15 + .../polkit/source/src/programs/meson.build | 16 + .../polkit/source/src/programs/pkaction.c | 227 ++ .../libs/polkit/source/src/programs/pkcheck.c | 669 +++ .../libs/polkit/source/src/programs/pkexec.c | 1081 +++++ .../polkit/source/src/programs/pkttyagent.c | 319 ++ .../recipes/libs/polkit/source/src/symbol.map | 6 + .../polkit/source/subprojects/mocklibc.wrap | 10 + .../packagecache/mocklibc-1.0-2-wrap.zip | Bin 0 -> 3989 bytes .../packagecache/mocklibc-1.0.tar.gz | Bin 0 -> 294222 bytes .../libs/polkit/source/test/data/etc/group | 8 + .../libs/polkit/source/test/data/etc/netgroup | 5 + .../libs/polkit/source/test/data/etc/passwd | 7 + .../localauthority.conf.d/10-test.conf | 2 + .../localauthority/10-test/com.example.pkla | 14 + .../etc/polkit-1/rules.d/10-testing.rules | 198 + .../etc/polkit-1/rules.d/15-testing.rules | 21 + .../share/polkit-1/rules.d/10-testing.rules | 17 + .../share/polkit-1/rules.d/20-testing.rules | 21 + .../localauthority/10-test/com.example.pkla | 6 + .../libs/polkit/source/test/meson.build | 28 + .../libs/polkit/source/test/mocklibc/AUTHORS | 1 + .../libs/polkit/source/test/mocklibc/COPYING | 202 + .../polkit/source/test/mocklibc/ChangeLog | 10 + .../libs/polkit/source/test/mocklibc/INSTALL | 365 ++ .../polkit/source/test/mocklibc/Makefile.am | 5 + .../libs/polkit/source/test/mocklibc/NEWS | 0 .../libs/polkit/source/test/mocklibc/README | 121 + .../source/test/mocklibc/bin/Makefile.am | 27 + .../source/test/mocklibc/bin/mocklibc-test.in | 136 + .../source/test/mocklibc/bin/mocklibc.in | 34 + .../polkit/source/test/mocklibc/configure.ac | 38 + .../polkit/source/test/mocklibc/example/group | 4 + .../source/test/mocklibc/example/netgroup | 5 + .../source/test/mocklibc/example/passwd | 3 + .../source/test/mocklibc/src/Makefile.am | 10 + .../polkit/source/test/mocklibc/src/grp.c | 156 + .../polkit/source/test/mocklibc/src/netdb.c | 100 + .../source/test/mocklibc/src/netgroup-debug.c | 84 + .../source/test/mocklibc/src/netgroup-debug.h | 58 + .../source/test/mocklibc/src/netgroup.c | 345 ++ .../source/test/mocklibc/src/netgroup.h | 144 + .../polkit/source/test/mocklibc/src/pwd.c | 99 + .../polkit/source/test/polkit/meson.build | 27 + .../source/test/polkit/polkitidentitytest.c | 199 + .../source/test/polkit/polkitunixgrouptest.c | 81 + .../test/polkit/polkitunixnetgrouptest.c | 77 + .../source/test/polkit/polkitunixusertest.c | 103 + .../source/test/polkitbackend/meson.build | 33 + .../polkitbackendjsauthoritytest-wrapper.py | 80 + .../test-polkitbackendjsauthority.c | 495 +++ .../polkit/source/test/polkittesthelper.c | 68 + .../polkit/source/test/polkittesthelper.h | 36 + recipes/libs/polkit | 1 + 225 files changed, 46133 insertions(+) create mode 100644 local/recipes/libs/polkit/recipe.toml create mode 100644 local/recipes/libs/polkit/source/.dir-locals.el create mode 100644 local/recipes/libs/polkit/source/.gitlab-ci.yml create mode 100644 local/recipes/libs/polkit/source/.gitlab/issue_templates/Default.md create mode 100644 local/recipes/libs/polkit/source/.gitlab/merge_request_templates/Default.md create mode 100644 local/recipes/libs/polkit/source/.packit.yaml create mode 100644 local/recipes/libs/polkit/source/AUTHORS create mode 100644 local/recipes/libs/polkit/source/COPYING create mode 100644 local/recipes/libs/polkit/source/ChangeLog create mode 100644 local/recipes/libs/polkit/source/HACKING.md create mode 100644 local/recipes/libs/polkit/source/NEWS.md create mode 100644 local/recipes/libs/polkit/source/README.md create mode 100644 local/recipes/libs/polkit/source/actions/meson.build create mode 100644 local/recipes/libs/polkit/source/actions/org.freedesktop.policykit.policy.in create mode 100644 local/recipes/libs/polkit/source/data/meson.build create mode 100644 local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.AuthenticationAgent.xml create mode 100644 local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.Authority.xml create mode 100644 local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.conf.in create mode 100644 local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.service.in create mode 100644 local/recipes/libs/polkit/source/data/policyconfig-1.dtd create mode 100644 local/recipes/libs/polkit/source/data/polkit-1.in create mode 100644 local/recipes/libs/polkit/source/data/polkit-agent-1.pc.in create mode 100644 local/recipes/libs/polkit/source/data/polkit-gobject-1.pc.in create mode 100644 local/recipes/libs/polkit/source/data/polkit.conf.in create mode 100644 local/recipes/libs/polkit/source/data/polkit.service.in create mode 100644 local/recipes/libs/polkit/source/docs/PORTING-GUIDE create mode 100644 local/recipes/libs/polkit/source/docs/TODO create mode 100644 local/recipes/libs/polkit/source/docs/extensiondir.xml.in create mode 100644 local/recipes/libs/polkit/source/docs/man/meson.build create mode 100644 local/recipes/libs/polkit/source/docs/man/pkaction.xml create mode 100644 local/recipes/libs/polkit/source/docs/man/pkcheck.xml create mode 100644 local/recipes/libs/polkit/source/docs/man/pkexec.xml create mode 100644 local/recipes/libs/polkit/source/docs/man/pkttyagent.xml create mode 100644 local/recipes/libs/polkit/source/docs/man/polkit.xml create mode 100644 local/recipes/libs/polkit/source/docs/man/polkitd.xml create mode 100644 local/recipes/libs/polkit/source/docs/meson.build create mode 100644 local/recipes/libs/polkit/source/docs/polkit-1-diagrams.svg create mode 100644 local/recipes/libs/polkit/source/docs/polkit-architecture.png create mode 100644 local/recipes/libs/polkit/source/docs/polkit-authentication-agent-example-wheel.png create mode 100644 local/recipes/libs/polkit/source/docs/polkit-authentication-agent-example.png create mode 100644 local/recipes/libs/polkit/source/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.AuthenticationAgent.xml create mode 100644 local/recipes/libs/polkit/source/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.Authority.xml create mode 100644 local/recipes/libs/polkit/source/docs/polkit/meson.build create mode 100644 local/recipes/libs/polkit/source/docs/polkit/overview.xml create mode 100644 local/recipes/libs/polkit/source/docs/polkit/polkit-1-docs.xml create mode 100644 local/recipes/libs/polkit/source/docs/polkit/polkit-1-sections.txt create mode 100644 local/recipes/libs/polkit/source/docs/polkit/polkit-1.types create mode 100644 local/recipes/libs/polkit/source/docs/version.xml.in create mode 100644 local/recipes/libs/polkit/source/gettext/its/polkit.its create mode 100644 local/recipes/libs/polkit/source/gettext/its/polkit.loc create mode 100644 local/recipes/libs/polkit/source/meson.build create mode 100644 local/recipes/libs/polkit/source/meson_options.txt create mode 100644 local/recipes/libs/polkit/source/meson_post_install.py create mode 100644 local/recipes/libs/polkit/source/po/ChangeLog create mode 100644 local/recipes/libs/polkit/source/po/LINGUAS create mode 100644 local/recipes/libs/polkit/source/po/Makevars create mode 100644 local/recipes/libs/polkit/source/po/POTFILES.in create mode 100644 local/recipes/libs/polkit/source/po/POTFILES.skip create mode 100644 local/recipes/libs/polkit/source/po/cs.po create mode 100644 local/recipes/libs/polkit/source/po/da.po create mode 100644 local/recipes/libs/polkit/source/po/de.po create mode 100644 local/recipes/libs/polkit/source/po/hr.po create mode 100644 local/recipes/libs/polkit/source/po/hu.po create mode 100644 local/recipes/libs/polkit/source/po/id.po create mode 100644 local/recipes/libs/polkit/source/po/it.po create mode 100644 local/recipes/libs/polkit/source/po/ka.po create mode 100644 local/recipes/libs/polkit/source/po/meson.build create mode 100644 local/recipes/libs/polkit/source/po/nl.po create mode 100644 local/recipes/libs/polkit/source/po/nn.po create mode 100644 local/recipes/libs/polkit/source/po/pl.po create mode 100644 local/recipes/libs/polkit/source/po/pt.po create mode 100644 local/recipes/libs/polkit/source/po/pt_BR.po create mode 100644 local/recipes/libs/polkit/source/po/ro.po create mode 100644 local/recipes/libs/polkit/source/po/ru.po create mode 100644 local/recipes/libs/polkit/source/po/sk.po create mode 100644 local/recipes/libs/polkit/source/po/sv.po create mode 100644 local/recipes/libs/polkit/source/po/tr.po create mode 100644 local/recipes/libs/polkit/source/po/uk.po create mode 100644 local/recipes/libs/polkit/source/po/zh_CN.po create mode 100644 local/recipes/libs/polkit/source/po/zh_TW.po create mode 100644 local/recipes/libs/polkit/source/src/examples/cancel.c create mode 100644 local/recipes/libs/polkit/source/src/examples/frobnicate.c create mode 100644 local/recipes/libs/polkit/source/src/examples/meson.build create mode 100644 local/recipes/libs/polkit/source/src/examples/org.freedesktop.policykit.examples.pkexec.policy.in create mode 100755 local/recipes/libs/polkit/source/src/examples/polkit-raw-dbus.py create mode 100644 local/recipes/libs/polkit/source/src/meson.build create mode 100644 local/recipes/libs/polkit/source/src/polkit/meson.build create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkit.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitactiondescription.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitactiondescription.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitauthority.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitauthority.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitauthorityfeatures.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitauthorityfeatures.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitauthorizationresult.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitauthorizationresult.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitcheckauthorizationflags.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitcheckauthorizationflags.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitdetails.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitdetails.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitenumtypes.c.template create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitenumtypes.h.template create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkiterror.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkiterror.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitidentity.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitidentity.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitimplicitauthorization.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitimplicitauthorization.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitpermission.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitpermission.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitprivate.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitsubject.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitsubject.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitsystembusname.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitsystembusname.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkittemporaryauthorization.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkittemporaryauthorization.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkittypes.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixgroup.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixgroup.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixnetgroup.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixnetgroup.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixprocess.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixprocess.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixsession-systemd.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixsession.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixsession.h create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixuser.c create mode 100644 local/recipes/libs/polkit/source/src/polkit/polkitunixuser.h create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/meson.build create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagent.h create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagentenumtypes.c.template create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagentenumtypes.h.template create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-bsdauth.c create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-pam.c create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-shadow.c create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelperprivate.c create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelperprivate.h create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagentlistener.c create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagentlistener.h create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagentmarshal.list create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagentsession.c create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagentsession.h create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagenttextlistener.c create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagenttextlistener.h create mode 100644 local/recipes/libs/polkit/source/src/polkitagent/polkitagenttypes.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/50-default.rules create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/init.js create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/meson.build create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackend.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionlookup.c create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionlookup.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionpool.c create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionpool.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendauthority.c create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendauthority.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendcommon.c create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendcommon.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendduktapeauthority.c create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendinteractiveauthority.c create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendinteractiveauthority.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendjsauthority.cpp create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendjsauthority.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendprivate.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor-systemd.c create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor.c create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendtypes.h create mode 100644 local/recipes/libs/polkit/source/src/polkitbackend/polkitd.c create mode 100755 local/recipes/libs/polkit/source/src/polkitbackend/toarray.pl create mode 100644 local/recipes/libs/polkit/source/src/programs/meson.build create mode 100644 local/recipes/libs/polkit/source/src/programs/pkaction.c create mode 100644 local/recipes/libs/polkit/source/src/programs/pkcheck.c create mode 100644 local/recipes/libs/polkit/source/src/programs/pkexec.c create mode 100644 local/recipes/libs/polkit/source/src/programs/pkttyagent.c create mode 100644 local/recipes/libs/polkit/source/src/symbol.map create mode 100644 local/recipes/libs/polkit/source/subprojects/mocklibc.wrap create mode 100644 local/recipes/libs/polkit/source/subprojects/packagecache/mocklibc-1.0-2-wrap.zip create mode 100644 local/recipes/libs/polkit/source/subprojects/packagecache/mocklibc-1.0.tar.gz create mode 100644 local/recipes/libs/polkit/source/test/data/etc/group create mode 100644 local/recipes/libs/polkit/source/test/data/etc/netgroup create mode 100644 local/recipes/libs/polkit/source/test/data/etc/passwd create mode 100644 local/recipes/libs/polkit/source/test/data/etc/polkit-1/localauthority.conf.d/10-test.conf create mode 100644 local/recipes/libs/polkit/source/test/data/etc/polkit-1/localauthority/10-test/com.example.pkla create mode 100644 local/recipes/libs/polkit/source/test/data/etc/polkit-1/rules.d/10-testing.rules create mode 100644 local/recipes/libs/polkit/source/test/data/etc/polkit-1/rules.d/15-testing.rules create mode 100644 local/recipes/libs/polkit/source/test/data/usr/share/polkit-1/rules.d/10-testing.rules create mode 100644 local/recipes/libs/polkit/source/test/data/usr/share/polkit-1/rules.d/20-testing.rules create mode 100644 local/recipes/libs/polkit/source/test/data/var/lib/polkit-1/localauthority/10-test/com.example.pkla create mode 100644 local/recipes/libs/polkit/source/test/meson.build create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/AUTHORS create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/COPYING create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/ChangeLog create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/INSTALL create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/Makefile.am create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/NEWS create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/README create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/bin/Makefile.am create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/bin/mocklibc-test.in create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/bin/mocklibc.in create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/configure.ac create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/example/group create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/example/netgroup create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/example/passwd create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/src/Makefile.am create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/src/grp.c create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/src/netdb.c create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/src/netgroup-debug.c create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/src/netgroup-debug.h create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/src/netgroup.c create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/src/netgroup.h create mode 100644 local/recipes/libs/polkit/source/test/mocklibc/src/pwd.c create mode 100644 local/recipes/libs/polkit/source/test/polkit/meson.build create mode 100644 local/recipes/libs/polkit/source/test/polkit/polkitidentitytest.c create mode 100644 local/recipes/libs/polkit/source/test/polkit/polkitunixgrouptest.c create mode 100644 local/recipes/libs/polkit/source/test/polkit/polkitunixnetgrouptest.c create mode 100644 local/recipes/libs/polkit/source/test/polkit/polkitunixusertest.c create mode 100644 local/recipes/libs/polkit/source/test/polkitbackend/meson.build create mode 100755 local/recipes/libs/polkit/source/test/polkitbackend/polkitbackendjsauthoritytest-wrapper.py create mode 100644 local/recipes/libs/polkit/source/test/polkitbackend/test-polkitbackendjsauthority.c create mode 100644 local/recipes/libs/polkit/source/test/polkittesthelper.c create mode 100644 local/recipes/libs/polkit/source/test/polkittesthelper.h create mode 120000 recipes/libs/polkit diff --git a/local/recipes/libs/pam-redbear/source/include/security/pam_appl.h b/local/recipes/libs/pam-redbear/source/include/security/pam_appl.h index 396b97cb54..6418c7a05a 100644 --- a/local/recipes/libs/pam-redbear/source/include/security/pam_appl.h +++ b/local/recipes/libs/pam-redbear/source/include/security/pam_appl.h @@ -64,6 +64,9 @@ typedef int pam_item_type; #define PAM_CHANGE_EXPIRED_AUTHTOK 0x0020 #define PAM_MAX_NUM_MSG 32 +/* Standard Linux-PAM size limits (values from Linux-PAM's _pam_types.h). */ +#define PAM_MAX_MSG_SIZE 512 +#define PAM_MAX_RESP_SIZE 512 #define PAM_PROMPT_ECHO_OFF 1 #define PAM_PROMPT_ECHO_ON 2 diff --git a/local/recipes/libs/polkit-qt6/recipe.toml b/local/recipes/libs/polkit-qt6/recipe.toml index efe4f3126f..23cc1130ea 100644 --- a/local/recipes/libs/polkit-qt6/recipe.toml +++ b/local/recipes/libs/polkit-qt6/recipe.toml @@ -20,6 +20,7 @@ template = "custom" dependencies = [ "qtbase", "redbear-polkit", + "polkit", # libpolkit-gobject-1 client lib to link against ] script = """ DYNAMIC_INIT diff --git a/local/recipes/libs/polkit/recipe.toml b/local/recipes/libs/polkit/recipe.toml new file mode 100644 index 0000000000..f7eb314b9b --- /dev/null +++ b/local/recipes/libs/polkit/recipe.toml @@ -0,0 +1,56 @@ +# polkit — PolicyKit authorization framework (client libraries only). +# +# Red Bear OS uses its own org.freedesktop.PolicyKit1 D-Bus daemon +# (local/recipes/system/redbear-polkit, a Rust reimplementation). But the KDE +# stack (kf6-kauth -> PolkitQt6-1) links the upstream *client* library +# libpolkit-gobject-1, which speaks the standard org.freedesktop.PolicyKit1 +# D-Bus interface to whatever daemon owns that bus name — i.e. redbear-polkit. +# +# So this recipe builds ONLY the polkit libraries (libpolkit-gobject-1, +# libpolkit-agent-1) via upstream's `-Dlibs-only=true`, which skips polkitd +# entirely (no JS rules engine, no PAM). The libraries are glib/gio based; the +# actual authorization decisions are made by the redbear-polkit daemon. +# +# upstream: https://gitlab.freedesktop.org/polkit/polkit (tag 124) +[source] +# Vendored full-fork model: committed source/ tree (offline, reproducible). +path = "source" + +[build] +template = "custom" +dependencies = [ + "glib", + "expat", + "dbus", + # libpolkit-agent-1 authenticates via PAM (same pam-redbear shim SDDM uses), + # so polkit's auth prompts verify passwords the same way as login. + "pam-redbear", +] +script = """ +DYNAMIC_INIT + +export PKG_CONFIG="${COOKBOOK_PKG_CONFIG:-x86_64-unknown-redox-pkg-config}" +export PKG_CONFIG_SYSROOT_DIR="${COOKBOOK_SYSROOT}" +export PKG_CONFIG_LIBDIR="${COOKBOOK_SYSROOT}/usr/lib/pkgconfig:${COOKBOOK_SYSROOT}/share/pkgconfig" + +export CC="x86_64-unknown-redox-gcc" +export CXX="x86_64-unknown-redox-g++" +export AR="x86_64-unknown-redox-ar" +export STRIP="x86_64-unknown-redox-strip" +export RANLIB="x86_64-unknown-redox-ranlib" +export CFLAGS="-I${COOKBOOK_SYSROOT}/usr/include --sysroot=${COOKBOOK_SYSROOT} -Wno-error=deprecated-declarations -Wno-error=incompatible-pointer-types -Wno-error=implicit-function-declaration" +export LDFLAGS="--sysroot=${COOKBOOK_SYSROOT}" + +cookbook_meson \\ + -Dlibs-only=true \\ + -Dintrospection=false \\ + -Dexamples=false \\ + -Dtests=false \\ + -Dman=false \\ + -Dgtk_doc=false \\ + -Dsession_tracking=ConsoleKit +""" + +[package] +version = "124" +description = "polkit 124 client libraries (libpolkit-gobject-1, libpolkit-agent-1) for Red Bear OS. Built with -Dlibs-only (no polkitd/JS/PAM); the authorization daemon is redbear-polkit. Provides the polkit-gobject-1 pkg-config module that PolkitQt6-1 / kf6-kauth link against." diff --git a/local/recipes/libs/polkit/source/.dir-locals.el b/local/recipes/libs/polkit/source/.dir-locals.el new file mode 100644 index 0000000000..abf24af09a --- /dev/null +++ b/local/recipes/libs/polkit/source/.dir-locals.el @@ -0,0 +1 @@ +((c-mode . ((indent-tabs-mode . nil))))) diff --git a/local/recipes/libs/polkit/source/.gitlab-ci.yml b/local/recipes/libs/polkit/source/.gitlab-ci.yml new file mode 100644 index 0000000000..4d9944667b --- /dev/null +++ b/local/recipes/libs/polkit/source/.gitlab-ci.yml @@ -0,0 +1,77 @@ +image: fedora:latest + +variables: + DEPENDENCIES: gcc + gcc-c++ + libtool + autoconf + automake + gettext + gettext-devel + gtk-doc + gobject-introspection-devel + make + libxslt + pkgconfig(gio-2.0) + pkgconfig(mozjs-91) + pkgconfig(duktape) + expat-devel + pkgconfig(libsystemd) + pkgconfig(systemd) + pam-devel + python3-dbusmock + meson + git + dbus-devel + +before_script: + - dnf upgrade -y --nogpgcheck fedora-release fedora-repos* + - dnf update -y && dnf install -y $DEPENDENCIES + + +build_stable: + stage: test + script: + - meson setup + -D authfw=pam + -D examples=true + -D gtk_doc=true + -D introspection=true + -D man=true + -D session_tracking=libsystemd-login + -D tests=true + -D cpp_args="-D_FORTIFY_SOURCE=2" + builddir + - meson compile -C builddir + - meson test -C builddir + - meson install -C builddir + - DESTDIR=$(pwd)/DESTDIR meson install -C builddir + artifacts: + name: 'test logs' + when: 'always' + paths: + - 'builddir/meson-logs/*.txt' + + +pages: + stage: deploy + script: + - meson setup + -D gtk_doc=true + -D man=true + builddir + - meson compile -C builddir polkit-1-doc:custom + - rm -rf public && mkdir public + - cp -r builddir/docs/polkit/html/* public + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_TAG + + +workflow: + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_PIPELINE_SOURCE == 'push' + diff --git a/local/recipes/libs/polkit/source/.gitlab/issue_templates/Default.md b/local/recipes/libs/polkit/source/.gitlab/issue_templates/Default.md new file mode 100644 index 0000000000..f73c06fa75 --- /dev/null +++ b/local/recipes/libs/polkit/source/.gitlab/issue_templates/Default.md @@ -0,0 +1,17 @@ +## Current behaviour, description of the problem +[State what seems to be the biggest issue here]: # + + +## Desired behaviour +[What polkit should do, but doesn't]: # + + +## Reproducer +[What are the steps to reproduce the issue. This will help us to find the root cause faster.]: # + + +## Detailed description +[Please try to be as descriptive, yet concise here]: # +[Version of polkit]: # +[Version of OS]: # +[Anything else related to the issue]: # diff --git a/local/recipes/libs/polkit/source/.gitlab/merge_request_templates/Default.md b/local/recipes/libs/polkit/source/.gitlab/merge_request_templates/Default.md new file mode 100644 index 0000000000..a10ec09c7e --- /dev/null +++ b/local/recipes/libs/polkit/source/.gitlab/merge_request_templates/Default.md @@ -0,0 +1,7 @@ +## 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.]: # diff --git a/local/recipes/libs/polkit/source/.packit.yaml b/local/recipes/libs/polkit/source/.packit.yaml new file mode 100644 index 0000000000..d5de98ae65 --- /dev/null +++ b/local/recipes/libs/polkit/source/.packit.yaml @@ -0,0 +1,30 @@ +# See the documentation for more information: +# https://packit.dev/docs/configuration/ + +specfile_path: polkit.spec + +# add or remove files that should be synced +files_to_sync: + - polkit.spec + - .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 + +srpm_build_deps: + - wget + - sed + +actions: + post-upstream-clone: + - "wget https://src.fedoraproject.org/rpms/polkit/raw/rawhide/f/polkit.spec -O polkit.spec" + - "wget https://src.fedoraproject.org/rpms/polkit/raw/rawhide/f/polkit.sysusers -O polkit.sysusers" + - "sed -ri '/^Patch[0-9]+/d' polkit.spec" + +jobs: +- job: copr_build + trigger: pull_request + targets: + - fedora-all diff --git a/local/recipes/libs/polkit/source/AUTHORS b/local/recipes/libs/polkit/source/AUTHORS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/local/recipes/libs/polkit/source/COPYING b/local/recipes/libs/polkit/source/COPYING new file mode 100644 index 0000000000..20c8451170 --- /dev/null +++ b/local/recipes/libs/polkit/source/COPYING @@ -0,0 +1,482 @@ + 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! diff --git a/local/recipes/libs/polkit/source/ChangeLog b/local/recipes/libs/polkit/source/ChangeLog new file mode 100644 index 0000000000..e69de29bb2 diff --git a/local/recipes/libs/polkit/source/HACKING.md b/local/recipes/libs/polkit/source/HACKING.md new file mode 100644 index 0000000000..d6cee62d71 --- /dev/null +++ b/local/recipes/libs/polkit/source/HACKING.md @@ -0,0 +1,108 @@ +SCM +=== + + - 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` + diff --git a/local/recipes/libs/polkit/source/NEWS.md b/local/recipes/libs/polkit/source/NEWS.md new file mode 100644 index 0000000000..a2ef1d07f0 --- /dev/null +++ b/local/recipes/libs/polkit/source/NEWS.md @@ -0,0 +1,1777 @@ +## 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 diff --git a/local/recipes/libs/polkit/source/README.md b/local/recipes/libs/polkit/source/README.md new file mode 100644 index 0000000000..4d0106a042 --- /dev/null +++ b/local/recipes/libs/polkit/source/README.md @@ -0,0 +1,90 @@ +OVERVIEW +======== + +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 found +on project's Gitlab Pages + https://polkit.pages.freedesktop.org/polkit + + +Old reference can be found at + https://www.freedesktop.org/software/polkit/docs/latest/ + + +RELEASES +======== + +Latest releases are available at polkit's Gitlab Releases page: + https://gitlab.freedesktop.org/polkit/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 freedesktop.org GitLab at + + https://gitlab.freedesktop.org/polkit/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 creating new issue and checking the ***This issue is confidential*** checkbox. + + https://gitlab.freedesktop.org/polkit/polkit/issues + + +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 merge requests and patch suggestions are welcome! If you want to contribute, a merge request on this Gitlab instance is a preferred way, yet not the only one. Please consult other options with this upsteam's maintainers. + +Should you already have a freedesktop.org Gitlab account, please file your merge request. In this case, please **don't force-push any further changes** into the merge request and add a new commit into the MR instead. + +Please note that there are restrictions in effect on FreeDesktop.org's instance of Gitlab, therefore new users need to take action before contributing directly via their own fork: +https://gitlab.freedesktop.org/freedesktop/freedesktop/-/wikis/home#warning-restrictions-due-to-spam-warning + + + +Thank you in advance. diff --git a/local/recipes/libs/polkit/source/actions/meson.build b/local/recipes/libs/polkit/source/actions/meson.build new file mode 100644 index 0000000000..1e3f370f3f --- /dev/null +++ b/local/recipes/libs/polkit/source/actions/meson.build @@ -0,0 +1,10 @@ +policy = '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, +) diff --git a/local/recipes/libs/polkit/source/actions/org.freedesktop.policykit.policy.in b/local/recipes/libs/polkit/source/actions/org.freedesktop.policykit.policy.in new file mode 100644 index 0000000000..dc5c219fdb --- /dev/null +++ b/local/recipes/libs/polkit/source/actions/org.freedesktop.policykit.policy.in @@ -0,0 +1,21 @@ + + + + + + + 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 + + + + diff --git a/local/recipes/libs/polkit/source/data/meson.build b/local/recipes/libs/polkit/source/data/meson.build new file mode 100644 index 0000000000..74d24547f8 --- /dev/null +++ b/local/recipes/libs/polkit/source/data/meson.build @@ -0,0 +1,54 @@ +service_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 + configure_file( + input: 'polkit-1.in', + output: '@BASENAME@', + configuration: pam_conf, + install: true, + install_dir: pam_prefix, + ) +endif + +if session_tracking == 'libsystemd-login' + configure_file( + input: 'polkit.service.in', + output: '@BASENAME@', + configuration: service_conf, + install: true, + install_dir: systemd_systemdsystemunitdir, + ) + + configure_file( + input: 'polkit.conf.in', + output: '@BASENAME@', + configuration: service_conf, + install: true, + install_dir: systemd_sysusers_dir, + ) +endif + +install_data( + 'policyconfig-1.dtd', + install_dir: pk_datadir / 'polkit-1' +) diff --git a/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.AuthenticationAgent.xml b/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.AuthenticationAgent.xml new file mode 100644 index 0000000000..482332f6fc --- /dev/null +++ b/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.AuthenticationAgent.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.Authority.xml b/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.Authority.xml new file mode 100644 index 0000000000..453ffc8dbd --- /dev/null +++ b/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.Authority.xml @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.conf.in b/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.conf.in new file mode 100644 index 0000000000..c749207c24 --- /dev/null +++ b/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.conf.in @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.service.in b/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.service.in new file mode 100644 index 0000000000..10bbab014f --- /dev/null +++ b/local/recipes/libs/polkit/source/data/org.freedesktop.PolicyKit1.service.in @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=org.freedesktop.PolicyKit1 +Exec=@libprivdir@/polkitd --no-debug +User=root +SystemdService=polkit.service diff --git a/local/recipes/libs/polkit/source/data/policyconfig-1.dtd b/local/recipes/libs/polkit/source/data/policyconfig-1.dtd new file mode 100644 index 0000000000..e46e2b48eb --- /dev/null +++ b/local/recipes/libs/polkit/source/data/policyconfig-1.dtd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/libs/polkit/source/data/polkit-1.in b/local/recipes/libs/polkit/source/data/polkit-1.in new file mode 100644 index 0000000000..142dadd3a6 --- /dev/null +++ b/local/recipes/libs/polkit/source/data/polkit-1.in @@ -0,0 +1,6 @@ +#%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@ diff --git a/local/recipes/libs/polkit/source/data/polkit-agent-1.pc.in b/local/recipes/libs/polkit/source/data/polkit-agent-1.pc.in new file mode 100644 index 0000000000..6402c2f839 --- /dev/null +++ b/local/recipes/libs/polkit/source/data/polkit-agent-1.pc.in @@ -0,0 +1,11 @@ +prefix=@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 diff --git a/local/recipes/libs/polkit/source/data/polkit-gobject-1.pc.in b/local/recipes/libs/polkit/source/data/polkit-gobject-1.pc.in new file mode 100644 index 0000000000..8fff452e12 --- /dev/null +++ b/local/recipes/libs/polkit/source/data/polkit-gobject-1.pc.in @@ -0,0 +1,17 @@ +prefix=@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 diff --git a/local/recipes/libs/polkit/source/data/polkit.conf.in b/local/recipes/libs/polkit/source/data/polkit.conf.in new file mode 100644 index 0000000000..20535acb53 --- /dev/null +++ b/local/recipes/libs/polkit/source/data/polkit.conf.in @@ -0,0 +1 @@ +u @polkitd_user@ @polkitd_uid@ "User for polkitd" diff --git a/local/recipes/libs/polkit/source/data/polkit.service.in b/local/recipes/libs/polkit/source/data/polkit.service.in new file mode 100644 index 0000000000..e6db351fc4 --- /dev/null +++ b/local/recipes/libs/polkit/source/data/polkit.service.in @@ -0,0 +1,35 @@ +[Unit] +Description=Authorization Manager +Documentation=man:polkit(8) + +[Service] +Type=dbus +BusName=org.freedesktop.PolicyKit1 +CapabilityBoundingSet=CAP_SETUID CAP_SETGID +DeviceAllow=/dev/null rw +DevicePolicy=strict +ExecStart=@libprivdir@/polkitd --no-debug +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 diff --git a/local/recipes/libs/polkit/source/docs/PORTING-GUIDE b/local/recipes/libs/polkit/source/docs/PORTING-GUIDE new file mode 100644 index 0000000000..a89ff78414 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/PORTING-GUIDE @@ -0,0 +1,44 @@ +PolicyKit 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 + diff --git a/local/recipes/libs/polkit/source/docs/TODO b/local/recipes/libs/polkit/source/docs/TODO new file mode 100644 index 0000000000..5cd211d999 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/TODO @@ -0,0 +1,20 @@ + +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 diff --git a/local/recipes/libs/polkit/source/docs/extensiondir.xml.in b/local/recipes/libs/polkit/source/docs/extensiondir.xml.in new file mode 100644 index 0000000000..44e242c2dc --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/extensiondir.xml.in @@ -0,0 +1 @@ +@libdir@/polkit-1/extensions diff --git a/local/recipes/libs/polkit/source/docs/man/meson.build b/local/recipes/libs/polkit/source/docs/man/meson.build new file mode 100644 index 0000000000..241e82cebd --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/man/meson.build @@ -0,0 +1,36 @@ +xsltproc = 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 diff --git a/local/recipes/libs/polkit/source/docs/man/pkaction.xml b/local/recipes/libs/polkit/source/docs/man/pkaction.xml new file mode 100644 index 0000000000..0f0fe316ea --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/man/pkaction.xml @@ -0,0 +1,102 @@ + + +]> + + + 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 + + + diff --git a/local/recipes/libs/polkit/source/docs/man/pkcheck.xml b/local/recipes/libs/polkit/source/docs/man/pkcheck.xml new file mode 100644 index 0000000000..ae40476628 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/man/pkcheck.xml @@ -0,0 +1,224 @@ + + +]> + + + 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 + + + diff --git a/local/recipes/libs/polkit/source/docs/man/pkexec.xml b/local/recipes/libs/polkit/source/docs/man/pkexec.xml new file mode 100644 index 0000000000..14bea982df --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/man/pkexec.xml @@ -0,0 +1,284 @@ + + +]> + + + 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 occured, 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 + + + diff --git a/local/recipes/libs/polkit/source/docs/man/pkttyagent.xml b/local/recipes/libs/polkit/source/docs/man/pkttyagent.xml new file mode 100644 index 0000000000..a4ba7faf3c --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/man/pkttyagent.xml @@ -0,0 +1,156 @@ + + +]> + + + 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 + + + diff --git a/local/recipes/libs/polkit/source/docs/man/polkit.xml b/local/recipes/libs/polkit/source/docs/man/polkit.xml new file mode 100644 index 0000000000..081f72a092 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/man/polkit.xml @@ -0,0 +1,1020 @@ + + +]> + + + 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 |<---------+ ++------------------+ | + ^ | + | +--------------------------------------+ + | | /usr/share/polkit-1/actions/*.policy | + | +--------------------------------------+ + | + +--------------------------------------+ + | /etc/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 + /etc/polkit-1/rules.d and + /usr/share/polkit-1/rules.d + directories by sorting the files in lexical order based on the + basename on each file (if there's a tie, files in + /etc + are processed before files in + /usr). + For example, for the following four + files, the order is + + + /etc/polkit-1/rules.d/10-auth.rules + /usr/share/polkit-1/rules.d/10-auth.rules + /etc/polkit-1/rules.d/15-auth.rules + /usr/share/polkit-1/rules.d/20-auth.rules + + + Both 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, an exception + will be thrown which normally results in the function being + terminated (the current limit is 15 seconds). This is used to + catch runaway scripts. If the duktape JavaScript backend is + compiled in, instead of mozjs, no exception will be thrown—the + script will be killed right away (same timeout). + + + + 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 + + + diff --git a/local/recipes/libs/polkit/source/docs/man/polkitd.xml b/local/recipes/libs/polkit/source/docs/man/polkitd.xml new file mode 100644 index 0000000000..763a67ee0a --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/man/polkitd.xml @@ -0,0 +1,86 @@ + + +]> + + + 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 + + + diff --git a/local/recipes/libs/polkit/source/docs/meson.build b/local/recipes/libs/polkit/source/docs/meson.build new file mode 100644 index 0000000000..0d780991a1 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/meson.build @@ -0,0 +1,23 @@ +pk_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 diff --git a/local/recipes/libs/polkit/source/docs/polkit-1-diagrams.svg b/local/recipes/libs/polkit/source/docs/polkit-1-diagrams.svg new file mode 100644 index 0000000000..d595ce8847 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/polkit-1-diagrams.svg @@ -0,0 +1,1064 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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) + + diff --git a/local/recipes/libs/polkit/source/docs/polkit-architecture.png b/local/recipes/libs/polkit/source/docs/polkit-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..57e1ba2dbaabf9d567b4e25a76d92ac64e68a8b4 GIT binary patch literal 63503 zcmb5V2T)UO)GoS1??nUzDG~*gE(l0(5k#uedkp%oGTGUCC#-k9t3A(JJ3>=kiJXLy1ONbX<(H5*0Dz;9ec=%i zV9&I3X=-BM@Z9B}O)vmj>=2) zZUagBc=-DtS!Z^*K;<7zNXs2Ysyx6bXb?PMvSsg{d!8u zg=n}+*{YNxvohjbQRJqOh^>vLyL*V7>KBWQjLby4N@7ANiTN{ul-6QU-Dx$3;Lom* z(T_<9jvT?t9QEowXN3KcDk%;tWGAPZTcnya6On#1=|b zABCs?-+_TD4iEOTJe-FElt6_&1)+-rRQ}xpj#P%RW3Ld!wp$|zuqP;1Alh8{Z+=8v zFggQ3P4SSAH@V1nzK&eeeBn)=ft{6~e&ky=`@7yv&3r6Q(Wpj0lTDOY%+GEYomu2Y zjc@TpM`e)9_6o`@9nBOsbYSmotslN}&P8*iNCez5CuR$EZA#9FVxF${jiiGHl27}K zstHx*+A|uBH`(}~qy3U|D=G{c{R5^15q$~QhO3*Sy9Ih9&nk7&yrPw7tNgcWmz8-( z#7%@!_MT|zxYv?ZvX|*dT|7;W9$|79HKDZ=NfCC0Rzk6Fl`HkR_8f8DP9lhBm6OE? zG*n$(T_i=7gO{ECg^IHB(49MX(A~<)hL&&ca6WouM@>zwesFML1-yj)_#gfJ_Vx42 zmVJ+7UL-+2^ZU-%KcpX>a2X|9zocdIEKS(s3RS8}F)+uOK1jN8lzQ$56Bm-?n4LIv zDVi+oCz}?t+0fuQ*ea5+8~6vV z@lwDw+2*7|&+^UJzv`CRPaAtEQJ%d|o{AwViUP2kcKQ`iS@w}ZC;c6&_TKwYYiY@H zcIrEq5lr_P{;9!Mst!C>Bx+RuE=Oa$df&8HLHO8oxVzI?*a~rOC&j=3JVm6w-=uDENiHThunnGR zm8V{Bt28|_fw|`*WmTdu};yPNwvTCyRAT8htZ_@CdDWf zgV)ViYy8~(Y8R|z^wY1psMUOdaxPHRx&H&c>2Csx&(+^wKol8InI+dxoO%=9B?NYA zq`5^!Mk>r2R_|UR?c+p+ta|&?<}9eGI|SKPIwlHUFV(Q`eJn8|-IfUWQ>2%tKB0n- z_eg)jpO*){5;InG{VOLsHAVVJ)u*u|>LHIks?5ZwN->P2wlplfv3S0Dw$^BCoIa!A z%?cf`E$#O#xUDZ`PF*p_clZPTO6q6Dqd|JJtFq7xvw$Ftfk#{7H?t_z;ILXsPg`5N z*hGo(`C%r#>2t?t_goK$*#|a)uW3ERc+j1-vhP!JOatBppTl4hz<&F$Fj@wI?dL&F zi(goST#K}ue9nvpq`b4_Wo7LSeuZfb{Dm5)z8kEv$MA=R)AO$F1>dY?DR_(BA0t{h z`=JtVn8Dz>kBd%AnoJ6`;xX>X8? zU!}QQ=KAj0YSPm&QRivh?{6aMN}3VEMWO6(f7iT~9-FG&6IiO9=h~6+D()f;A*^Mk zV>0ghD(&zqG`FnzHfUsSuX);{GfROz`FP`KYKt}4m>!@=7hj?A-O_!(ziyX7n<7eB zGF9;d2T;AfJ~cG!M~sZV>BuZ=E}Ve=DW}wRnw{Er(@%2QFGHP>iTRX206gwV`7Lx@ zUuHB@Gn?2QB=Dv=G@q}EUaDuD5Wu1cDYUJizc-5Xif;VZHLv@WViNFS8;;|j(@zIv z(1zyGDSsQ(>j~p+V+c+v~QMz)Bu{C*#307=51?W@R`L;eR(-u zibgsa-(c-ta`9yWF&ku;(@gnAwC6(}ba~K{;>1FUkyT*i-62B}$Asrw6J|)%?Zwvj z&oKneH^(DIakhP2H>khA%S2oT9}u9S`fARoa;r`{0mmHa+a`1c>Xzc(BS_DM0D>ni)A*5X#E{B?hh5-HmBXZF@YsY!5(pkWE9Mx;=yD(aB5mF4OP}>yYKw@xy}sCLSARR@u{B+@^Hswv=)4YjXqNJd0~ zMII^U#DX#v;y5PC!oto!zIah@ox8hYRPzl7MtON6<%FEK6A%DW>wJbB_rEK9I-<^@ z^KSgeXM&T~ZYUO7ooAJ6UQc+6lg-}a_&X%>-lqikMIGs42YxRtAO;6VeJytR`0|?l z&Q~G<&5nVA<=yQ7r-8{zL1j}SHbG@SY)r&#U7b6>d-ra%BJlD9nX8+dV0L!)XO%?$ z)AgYry>If~pnWjeDZidLMX{qM9CdxFTVT9QfVN&NJzx&J6Z^=k}>woil`O=(~L> zqC^3^DdPVkdX@VGU*2th>bqoVPlw{7$jFWhSA!rImz}lqBc#8_hFhbXrly;RN0pYg zN7YZw)(?z;(`5ZfqqVH8!~XK_Dl}T-WtWGBbJVN&#niO4zWCnw$aB~Ig-YN}_~tfw z=j77vYC`Ilr1ex&zp)CNhn9OY2L?^{T0_o#$tIQRMh!NRw^8}|f|s`asq1S?aYWFa zUVPFEx}HRS;f%PUq?xEQN%E7RLs1K$=^y&J30RLM6c` z1LZT+C*I<)e>_qP?aSCBgqEmwe?5rriH}5SRJ*?OT9r0wOuw?Yxhj0}`(E& zN!x4Q@?97$h+^KHK$ZK9c4ZMeT09_k_Nn7o$M zpOy%{Abb!?wb+cUGrHa-F<4kwe5@s)P%T$;)J6pjSC$xcx=h!PkOM)<;=<1axq1qY zxXbW5W}4q#Z6i-h@#Fb3{ANO9WSWfyA3S*CGyJ_JtT%xc1WVQ_oZ-^Xgmr)IiM8XCkU90ARr9vl0X&CLbxlN*bQ z>g5aG=*<|L2m?NqjuS^x)78`G(+!@&A+`g~AmIJY&CriXT2ewFq_xE;S0lqZ?t zGwR0gW-|8Syh*M9>4>`GL;C&5Z{1)rbrXG-L^hmuiMjlO2o`>7 z7I1}<19QFu`&o=73#&|5y>)~_qY%6?hMSIRji(so#_9Hm(^0_KX#6%-#2#KH0gsJl*5JvMZs{z(#_9wI=s`@NHnh$0){X z|3cjozA?w~?YV57A_`J^KAzZv*dFgV&f58!w?;pYhu8S^2YtGSe^j$pr~Ul-vp+{M zX4iXl+h9+LSH)V&AXg46!eEJUeMJ{mP)Ca3%bjZzaqs5M#YM}hyLaAbj#Tk9f}Q?+ zVY_c^Z2X3$I#t4Z_qj=(>)-nQ{e9Y%o`k6GPc;$653x{%a=mS`k_A`!BFLL!%XzdfO`>b{&^?k4X~71QeI?tW76I!Cdw(R;^l_|15lgm-Flir|mQ zcy7HO2Q-s@#d3tVQq;v|xcx{vAq)%VybON2t)!9AaxrZmZ5Ca!Y~AM=W>*N6m&|-O zA8dATguU;^X>;^dOylla$7kQ4zt@M5tlmiZW!kxNUxk=*!bIQ*r%QfPku z<3wS{3EkS}o$&*!P7;^73idAXd6!5!;rf7;SFxL8g(iO*UCc0`5CTm3_nm2!%=%!K zf(=g@AsN#jZ>*|3xwZ3MIn0^^YFGPGPFGXw-HUK#-CljNXjy38W0iLM?)lTQyP6o2nH)Y3#uWPJFbac_ zGqYNH!6;@{7ms&dP}`9AqT7>N-(Qp%{_Vf0U2a>p4(V_ba7<=f9Ot#gjn(a%mLSF= z*^wsSqxCtYe@3#9t@=s<7QE=xI8Ej!`eDQ;k^ZN>46K2AuY+&l8zZ?Tme7*dzdzE4 zH(#GD1dG1q8gDYF2YDH}#q)IslX_;pD}ym3 zc@rqlE!_>)T=l>r$iZSQ#TA8Wm-m?IoPAz)AZ~bc<3@Kl}EsHo0X38iw9}hnP^8X+&2OU;=(&jvm*R{~9 z1R<@GnOb@5X5@@jw`m1hTTEi!y9@$aVTq<(wgUH5U-u@)yc_jrgE(UjlvvYPg#3;R zFu}JbuCw#OgaKj}^4GtWqR;SR?t4F|w9zhj!!G=#P^U=uTVkT#HddnnZ*hx9@-*nZ zy}Z;f7JK;{0Rc^VPEJm*fjYO9SKYtDNYeNWvfG%6)7|K>k;Df$O5o+5Z8oTk zOB$?`oSCDko{>_S|gZDpCN_?dOS!hgC(2^w&*G3TyVTIY4|d3g@z068Cwih2(p zC^5vSDa)YF{?c%cU-?`dJM&jNingN6TOq)oe@hMyI%ubSyE#f6d=NsJ%s*3}wW1e7 zu#+L;wp@Chr;+&7k#Q$Q>}U-=XKYAxuz-y(RTRwPp|vjaHyw2YGN?*F%;q7q@;ozc zs0%O`M3C{7mIAOk3GZPt_9?!-mSjb&N_T_xMMZ0;ccyA)vAjfOE>$c80Dn1>6lD`%eUccD0tP7g9SFEgd5NRu=;{(}%o2g;qd#=*5qOS3Y}g3I~X zx=Od+X;OlBYfQpQOqzqQ-+o{yCw^Mk$>;F6J<{~P*ACZAgDJ8Is^)IkV6XK~r8$pV zM|?QfdG;k1Ap9Z?mwyO;6|Sh*AiX+53)X1xJ%T1jhlif|2T>$RQBmrJB+DdgLV-1JCVCnKY9_R zCJk2;C5{uf4IaAz1i-6!F3+dp;tf|}k@QHX7+&ps&96gtpJSqg0r|8=vop5T zJHOmRQ3f9$bSixK@PPvFF1OdtRJCIeF)?xJylQHlYs-C?ZERfeuJrEwOkAp;CI1q{ zngNfb=TErZopuTKc=v9Igq|;8y^NOoVsEnXR8~`SFgBgP+2{BGX|x!GrFg(`bXdxv z@Xch!P@;l@R4W;D^^;8xgzt7O<8Y)iT1vmc=Rl&?(n^Xl_4!|uqBlS80e^hY%26w4 zg)2R?B2)({CXKsHvv~tgBt(__%gGf({X5Bxm+>Ztsb&jbWBZ*qf<$ zKPSP$h0oX9l_ks)9z@l6N?OgA&X>RDgGgtPwi-iwXkG zlJVg1Hx-yh*Lg-?;296vapYH6N>4lwg9~wVsS@O#iirK;BDVbabmDU|IS9z0*0Wk$ z$vIHBHz#S~l$W8~vqr6NKGx>t)ywrIcS8A049mP$dZ>9bd^vCcD6f%jp?_~eBB_rr zyX|On9+&p>+UJkHW9l6zW_szky*MQX4B$<)-E$(gJuwPJiXLJVrJ7P3Sl9V@&pY2Rrca&^|EA(`MG&Z5Ol6L z8Cp{^(l<6HxjO58Yq3gxp%YqQk55Xcx_de0NSNfCVf^CEI#x_y-ul<}i%1+toq{{2 zd;7LC{L;U(&pKy5L; zBy1HLK0b9um(k3fb@lb^lGCNL_)4`(*8Esx4yH*MNDtIx?NN!V!PL%+7<@cSFEA87 zIr~K0i>03jr;hjLXHc$xjS`yJudZpl`_nEb`u_Z(z0^qU(rbo9{E-d5l}K1p&?~#4 zkadt^4+y03y*!aiIK~3gHr5OY?}oAc#U($|P^y@&&Q8wrM&-C5vO@iX++&fIAS~Y* z8`hzoqRS>IK2@Dmwzq?-b)lSjw{LzP@ldI>s&6fhKchfrwjuV;BjQ-^=MoZ*@gF`| zJ}U3IZw=Rk^BjA`a}6bynVp)}+iTTdnVKrQcgC1WZvRN^w+;A?MTj$yf8k1gCq*R6 zY5u!%oef+7Y4HMEA=jrI|KppNGp#2jL8qP=Th&U`8 z{#49B<)!+#^K89Ub6Qer@!07UX4$;nga@lHJ@9%Ht0-aLp{$BHfgmuR* zZ`GfUEZy#4`zM`oSU3IuV+dluVt*@<D^yIJu()s*aY-`}_K(fy$t4R<1kt zynFPXoBc_$v8tKlpNAhl^n-*e)(WvB3h$*w~m z%JE{L$d>6@%&a!pbw2yXLd&Xk1mUx_ydi|4a;DiJy8qfF&pG0VQT$i4KUC`lpPV1Asm!AM&K+uH z=&v{aZmt$0g%?Xw;*0du@nF~fCym#YyR4_sl9bsSn+tyYrNu=u%E;=@=3mH!44JbGc|vGx>1{_6QGxZ7rVOaSQ={{~UQxXUhIc|4wK3kRYoIjm zs=)dk8x_CfD7(ag%b+>&Z8!a)JG8G-i{^jc{A4cj!izEzagJgiD1KV$ux(n|X!=I} zt5SCldrZLDT6j^K@mTPIH)GRC<3VIpt&>^B^~S!+U)ml?9q9BI1BIKuwG@6|%Yg_C zjSFXjva;#{gEsU_Wn9rnNjE3wK*dDA{Y0*sO}R+^dZygJ4i)6$KaS~Z+*kX<+PGPl z&N+1x_JG^D>(ZspX#T-+<=<`PTNWKPQPT-^uDc(EfYj%1zdWaEN3K3%?(0?rXr4p# z^z=&1YJ+Gec+C>$-+~KhFgjOt*q5|@pGU?P=|J8c&icaiQR)z}e*!C|e6S`=BIfpV(q>58*{@t+H70owu zLT+Mka)YGC)>2qe^&sHq)1Mt--1Mde6LqFcA=ML&sOTq4d3aSjqbIAsGU({)gLWFG zBl2mCq>xft1kDor4n+u0X^*7P!}?1n#f>@X_jPcnN; zjT|FVn%e`cjAx9$5(Y``e+%MfXtYr?5VeU~@e;b0*$DpbuX*~RH<#bPL9UaaDClQV%lx6I*(2?LQP$CzLtra>M?(N@V))E zk4emZcw;tiO*`L+jQ}{L?e~VBASTU(^zTpD8*j%3$25@vWdR302u$Tfxc|9N8Z*71 z^Un3u6KosW9h*?=<)iW(zWez3Rj>=#%@n`8`uVWam}ev1G<{VZv-L>dRMdX*(P|_m ziU<$36dvQRTkx8Fyq8d{dGUYG0!;Thi8aeb5>T^k$p^$nlLx57XzQ1dAfVhG)1M8Eo4j`I`b$uj5M13q%TT{CTjWh& zi>JThm^g1G*8D1t%htQ>kI7uBB}Y!>)1C9a8BL#e`-^%!qHyQ&yvZ4sjMKWW1#x$= z`398G#f*j?NtLN_(2#v>e7KOD8{5H1c9CD#Z4JhPJiuJw-XEvt)7&+{{ywQU((nbbpzRjvRFgC{>_Uq?#Uc(O3d+c>0V3ntz*(w+t90*YZ6u&>p z{a#aPgGzYKdr_U_FssfWa@Uudy4u<^K8K1Dg2NNbeV6nx)L|??e!e%*tQiSyXN=j( znn=SEs$L^s(^0ZPM_qCcRXlcw#4g%?(m3DOb2&b@ndIjLWqPCLVk&?(j}Kw43qxf54OV0_G3Sx;~4 zMyC0jW{ba=Wlgj8pSkKKOPUxOQ+iRV-y3z(to>BYW%{SVZA+-sx%436SXau(KW0T-X0c#W) zMMwT;dpUwfH}Qdm+b77P)1YVh+nGKwS1egHl&Z#d{PX_8^N#m@#VUf{1ibO=E2iDs zo4q9s8KQc6J$@X4C{Ti@hQdie`tpz)bn5#02|V>hDfq6Kd>*|x!l=gCo&ccNqh~on zJtf9wW-GvMGS}wN)+~VT=)7&fg;~N4{X2pV6AKg@*E z-ObZ;Y_%lGyKtqF*9z3q-5oKpYvP~pzSp1CNy&Ozs#|1;?vdj5)-E=D$^zi@;&U7a zNwF+ES05gSxJSvAW8=o<5;}tXrWkpQk6Wqt-WF6u4fcGZJ*f z|Cco-^;@bJ4+U(fyNf1KEmfyZ|L^)y=K!Gx^QdQF#x2IcxXc9kJ3&&&tN(1LI>t0b zw@Q0xt1R$%Y9Q!+5dAE2Mk^6 zCS;Sqq_T;knC>2pKCf%dkTUr-vUVF;GC&^eoY`3G|6Qy-RouHTA+AU(dua0B*6vR8 zSS4(&jDS<&Gd2|^A~Le5#0E)$L)`V9^^fq>@Z&K3FrVyz0!_a({-?MDo zm1BeP9p*y2%>A#_G9>&boO;0T#n2xbqW|ZI6p+`?ZQjPV6RG%sW*Z87VBri!LIf-9jpa6I+Az3dVi54s+el*Ww&Ea|=}ZS)>|IdV=*NL(+*q z9rJG7=cgG6>(b@NDJR>aflYt{w(C~msft)<-J<2u9^BUI>P&st&uKL2V6)Vt%YDCy z^0!!nnEbL=%~9#aouNGRRog7CjwHhRYa_NnVi5*^g`00%+4vk*Ltz))mGwUmaCeM5 z?fRO}TH>aCPFdo;Xl;4;;6%$n!b zuu>DINluq53m5K{Nls4`jt{7%==(bVwZGUNsy6Z_#;TVsnO#oBWKs%KoonwTVh@Fq zC+B_=NKxe~GGMlU_0&Guz^cz_9L<55auTeB3OYjL421IR{>?|Ze?q7|f%c_ZE`Dpf zTlr+NE%#Ae2O}Zq`QP<W$ugN>f`BUE3_Nc~D^t8pA_tS;?6bpU5WeqorjNeA19Op7-+b<>|I-#QH55g) zC`a{L7*N`cvw0*Gd|5a3(2b81m;ga!@eNPz61oz%umGN{T8%6u)Z_&FY_Mz*Wn#!N zzmT_wY~`>Pkt`?B$u#?gGdAK}d~`3DN?uYuVXnwgGh=`nXu$nLy6`05LLS@a{^-_6 z#YRnzvyTfC0E9qJ1XTE$Jg{+qQKHn-%YGmyibIjZ)dcIz@FrLms%<>(Tt`=58VV#tGNp!DJ`8QEd?GR7e2vzfOX1DMnEI~ z=jkLm6}lb`H(Xz1BErnQ0R16fu!T}o_vq2L0QtV=&OkP38+V^uC}ZO28^OK^41;d! z=szbT)WmgoeDDayj&7RcFp~zXvgUeaj>S`e$C(qN=?JUsv|6k_{3i)?b)#|f$uOsC za8=)Y8^&Y45gi~G*AAeyoB2ROl#A0aKK#WzZEik{C}LlI>bjs?9ci}JgWysnb~Ru%At zUhKx9m|&3n^mL%;x>+-h!3Fm*wBSA$>@)CXAU72((wQ+dtvSkJ1_FK*n~IK3-**a( z_M2sdrHgoO4;_itPMN9BcLJ+`>)0thPB~;=W^65qaD>TSJTv_>52kWFe(M2k4Blg$Sx%(^HG<@W!vw!!NC)d`y5@ z$kxN|IS9`(+{Y-+!`evU#AaXM2NFVGt zw1M~5K?{LOqVn+6Xv6@h4rinN4&{OcZgG?*QjGP^5G7>vWk#G$5tH^npE58&^;&9+ zTsAemEIZBWHt-9fCPfeBq{#paS=Uu$9&Vo zn5+g3f6#MtT5E&SnH1v8*D{`ZBQc@8b%nR{TaAnxmui|wTY5n|J&mFYzNbei zno{yE@|bdAg7VMsxr!e8e)R9G50%oI^thu00*P?x-pp=MjM6mFlMTSE^TkZ%XEX?l z)^>J`Rl%V?0*#=15&3C3_rY!ixUkBLBd(3*pW8B4{ILiPfurZpQj)QUHV9n&86>5F z4Yb)OgLm9?(!eg|#%?yn9jCB!{UmV8^LNbyXtedJ0eXhOTd!u%h0}~jXux81E$bth z+R5SM^D-ITYPCWCl2^FTJ4IB?i7eDmZsk!JzI@*>d3gBu;9d3i$3&5;f`ERhOmL^j z>%B2QQU@Q``mvw^kDkkcLgU`$ZBKxaI(GJ_BiOB$XZNHW-W3n`n?!UuThwD(K}CV? zSvUKtbsF_77da~8iH&9-8}1Bct_kIi(A=Y_KG9QjpBtCMsFObc^QTlm0Kn>Y3j zH)5SVKVmsA^W(IGLP_^GoFa2Fhv;!OKm~3m4kHJ^S#(=Yz=BPFcilmZD+`}yTrUco z-L8pzn+05Djl&&qh1p!%|P)-(Q|!)#0} z@Oj)(Le@n2AVbZRz?HG(sK!xFwol2BL|7G?Cd24(&1GBEBmq2<>N2F!RC%llV?3#R zt3@)}pijHaw1%_rFkiT=I7ay+30~3G`6qdN)tWsGGHohFKWUA8;@b$3^Iagx+CANV zytU$$Kdysx!io=hi{W<@quvUN@edwziPpe$rKJ?*q+34$=11qrEl6QDZ=`Q%Cr+&< zwP_=Gz!Eg->S1Kbu4m`kYHVpg8zN2oTt?4&cF#GU8lai+tsB1%SsB2CnD(4dk z8(bEeHa|gLQ7X7-y+cN80*MH*ry*8Eo02$LrCp6}XnT&YTM8e4WYOJ_5xTxd&}=We zj~t!LmAD1ZnffB^v<#4?R_K6BxoCP{8tQlj@gsIm7lEVg&M_7?w})Uq*3Hc`g44?s z&O?y~jjKnYT^7gwzCn4Y&mRq^a=@eX$-|nhdSjP?@9xOYWJq(KK7g?z&Iz%d7)ivx z{vbVDHY$tm`}G}%uDSb#dgB!f+W^3gIM1{)d<@x@g^&Zq1e#HZGVunf#qNL8G4({$ zW0tG9BA4>O6&mf%Ji^81#tFV)+$@~sD4s7=C5jA3>O@m55?kLl-WZoJ+2#xFGPKGp z%}%tCkNt=pEng0D1XXc>&u{r4be7d;c9cK~+^+neHSiq1=Z5fgqLnfeD11AH-co~W zEmmLVbCYyZzoz^rvDiO}UGxbVIPQXt1dc57@5^a$g8fOaKS{pCPC*=gtOk}js^zU| zpmU&w7XT?d9}$xBFdcBC&7$&!1y%050__+!ayE#_gKT=>iY${^Xz!hef9MjHE2HuS zggSA~?zg01iC?OE2BBMg^LsF1h&FcbsiPlCVFjm#j8e7S0e%QJ1b(Ah9HJU}0I({| zs;_;(3eO_*udfwjzPbbSgM9k_6W(f-6?BDT&Pz?};o;x5#QE)psn#AzpE#Bq=0kRK zwiI(Gl8(2q%I9E_oFGJQb!!<6xoaWXGF9_-x)nR)l}uq5NA*iC=o+KPB<6;<)Dabu zmzS55@(@zmiM}Tt@Vvo$FB;pS#-cGW>G{QlsRn5gDo^fKqyxg@nL=4 zF5v8MxT}|!mq%P^7pBH#K6<4$(dt(yakTTNF8Q0An~7oaa_5;kEVoAG`x)Dsp*ITv zVX5cOjKU-YA*v&H!4e2n4s3&ywY9bNv1wBsR)h+8MuwT4pL_X-i2MTFsYju%<#lz> zmtY{=!gxLtalYrz&x-#k#`(0BkoDW?!GSW1u)`?3Qtbs3LlF?Wd%-|~mPR4~iSV}m&Wx>T`fKYH+`6V8Gh1cSA-SkHf` z@=rUO8c4f)We7!;;l>e&K7bB)eS5tI{@+|2*aXc)iOE9Iw@`o!7Zx~(V~xUq z{C^e1f0q}VlP1yV5D*%tJPMxf?vWDO73PJLuLe=aX0a~Dec^H!=)k@-g~`_^WPm&2t6R5B$EjC8}g`j^bUAy`vV!x>VMy6Arev&I3fi; zfQrG{{P33vf%J<*J9wHCSWsu6fOUa31&dWPKoY+?^*0N+aPgmnBRe5@`t|?94m`XA zO`nzErcdM<|5pM6iGCYTB{psY{8+iw+MTM67#AXm)m?eFJnoS8*B-{sMMRuz~h`peS!1!K+CWq7d1XjHbcGI6_V?s9hW zBvYpSYMyWuH|};`#=IGDW3l=uJUz4P$(skkDen0HEch=KMEhRLBcKG-)Pvq)9wd+f z2ITN{FcD+j0s>kA>4zHui%7X`I16bAi%> zeRS=`IbTR^mD8!4;OLAtuJ!{PU-aXa3W|zn)1C8cfh#|St(AMfw@ENRQ*#8FbHK_F z=UyjiKe{5|`Gl|uM14ulG?*o77KntC0B|el$db#O{ya+I8H)(hu&%pcF2V)M{Buqu zLhM<}<^kS51x!v|oli#8WyJB-fZm)+%WP&3ys9;Rh1!c{2V?r<#By>Ht%YJS-e1ou zu|h+&IDsV)dXzrtE)HZAzr_eWGmK3!F&r2!dGIC#VifV1u0;*|XyV;h5EXd7yO(Gv zJQco<5AKN-ZFrO&1gT1Zm*UisFHlf3vt^rKPaiHhsrZ8(Zk}(AZt)i0lJ!dNHoWNX zK-+>w30-koMXN*zpMcOWZTsxY6n_&W0kk$a-#j3;63M>gN2XPeBOt0VYN+P874_OO#;P|yJlD^?C(Ep#=x1k?opJp~IBLF6#oYvv1i7&tl z6x*(2dhU1*}RgVMibf!&H1ppa-bZ zB2Qr#E3=Butnhe+Vr z@7c^f0d2}@siSsyJ^^eP5RvyB5XIG`-BhefNfMJASaXc`y3?{qm_#{5&s8^tMwk8R zZ9!i~tlR(L*(^kHmSFs(04Bon*TJZgBU-#cycT;P4;X!K$S_T0eqYF?wkL&hGtdL{ zWneWBJ3m5Sd?=1{B#?tZI@(*WQKPFm}kZBm}*5*TF?%hB=id1&>bsPNx}9GZ{vyS9$)kZcGhQ?{upj zXPZzzVcx~RHQw`!O(O$O%t+8A%KCUP!8RMLKuo-_t?A8pfgit7HZZ#K& zk-!UWeSo1`Ab?^l9@(OPkpVP-@;lI-$0|P+0PUel2(bVD!ZoE6U3O>a2uR4&vhjzS z7A4LSwHW8OJW+N*k;0?^zbQPb5fB+LNWDq&!%~wqfFR#|kpZaA!V%~dHIyZfsj*yL zR&Ao^1mS`52hfdzJuaTc4`B0|tEov@AE727;|~pz-g!E6ihd~_3cMbFlm=W3IBh-z zxtmbE*14Y9X0SYO8OO~#*HlR2$ahiQ1^Q)?F@(nf>;Tj7wEl=6JPr;8d}vzkLDOX+ zS^$|^o-;k7jhp4;O_(z+tQoP>J&X$Ndni~}WjE4jwkOT{Xu;V$-V|oTUxy8AyP~Q| z2;$w758S=CfpKF;-&~}Fk^9|CjdV|sf&hgT%bcbcw*hlq=ys-Ax&u2h;6y>Kr&JLV z8If;AAZn9FPHRC~t~NOFZ3Ay?%R_ZuijJ%9f=HGcdJ;y$u_;?ZjGIrXDO0qzt2;gK zH~9l_f?YgtAQ;avUtM5=BMJ!|n+Gm9Q0_dn><@j^xwOy+T zHj((F@dn7^^t^H2)_5O%Kgyk6AZQr(=5iR-*$BsJ(2@!55z}~K`*Dg zzWV2K6v@ao?1cLGdrBBh2_J@Jz6O=NgR< zF##3hQK>+t;py(XoCEj6!=ht3MJZ zfIFD0qSh|ubf}_cPzksK9ow#=k|w{S67K^|>Dc-cq~6b$0xBL0Uw$!1 zg+F%wcM021QnCU77^@Aa&D5QB7Z1NyU1eFuk7Q!$w$LG zDKDDx!^ap~oFE^VmCkcDh zddvymYcm(y*T14JtTqVqQRJgz$wk{^Su#`2F?$M_;Xpid=F!k=?33mg07G){IG$i! z?0?S!%!OPaFWXGBrSn`PDl516M}lT=33ti!op6gj*Pwaozf2w2ps;b+k&~UYa$(`8ZFYD zFE+$|jB6gPR@N?=FEbMeFoCm4A;%A?#|E4pVz`-+AZ_++$TWEig;WtzcBvsPFrglj zSmM38(@ZRkml*4QVP(#7Xvke8V_Ky)h?|h2seq{Ao1XN35q#XE+U`eX%)<5jbM2Az z++gM0k@TiQ05a3B;}z~bN2z*K_;&6<#5OUOXdkPLTk#KRy(b>HoAumC)D)R<`!S7BqlL7<;Ilg(=6b819!1m9?(r4>+FUkiD^Gi3DH1%$)@dbLhreic~ zYt|`fAqIYx(5Y{<#F@O*I-o&H2e{J%*n7w^gHUjCgqPt*{Al28SNgnvax!sucAIsj zN9205(-mljEdNHJ3`%VB`H%;mxZAa2vqwHa0&znYAYG@?>?(2Tx41iy^^}^S_@hf$ zmlrx98s1DGK(OhKhb6VKh*K9^emN3i5MCNE%e8tW@!ojcoKPi;u0v%pOG%<#IZv;- z%ZEmggM1^h`^a9Jo=ea1%bUEQOFgeWePje&UBKOs9xq~MNpduQm<|hBC7aMQKmCcd zVJo(k!7MO-5ZQxlav(k2uyo-YQa>C%C)oGu*%T|9;@QmL1sX?WsO}>Qq&iGay8ndvd!% zlvQx&_jSAEzKI7e*KdUa%C+P1-~tAe+{za>&?zzu0Oe zwg3$3RvHuO&%#LG27F zug0;MPtzbUek0EegM~Z6VktMKe7|9TkTFb*q7OBtU1pmtlw9x*=h7Il{-gKrH}woG zq!#lqo?HOk=kNdO1u^tFrdlUgCh*?K2FCkCj2bs*&ZB--WE@r;e5d$6S55>B?q4Wb zm4y<9;K!x}Trwv61g09T!Z0KY0QyY%tr_9QVINw-kn(p(JKc^e@aYo8ZX5JAF7sW5 zA334-5L5}!&=!t|hNpwBLx7`vQ;w6t6%$WVIUv0)x%N0R)U>Su>hbe_<~PclYA}L} zz%KG0&n`HO22)X*5}7JD0CLHJ^#olPJ*DwW`oUU*ruim>kX_dz%|FbCHuu@15x({C zSm9GNw_Src0)P%pCl4_U`$dm6U8h;SMgrIaJ`}l~Ki?w4Vd4H=Y$b0cr)_4vaQ=k? zqYZUPl{*&xiva5mThW=)gTw-@ge<3Gvs^Es0Y=1C>4u&O0wMKS`JNN9LnCM~iN1T~ zEeR>RyqWk5!;5ZlJhKD2#46Hy@(Vl1QS_!vm9x-ci_puad1^Or48UR-|Augy=ChU9 zBl)Z3BR}>8k%znUmV@Dso&phX0)msxLpHh^kwdnFpR zeWBNi%bfr<tR-G>ZFkW1Te7-_HM#;B;z!@>WB%;o^B0)ghsHSS zAt>nFTsj7m4{_A3I6rg^L=~1pSFeYLu?1oF0ZyT3u9S@aL5*1r<}xL{tkG`e z0cT}(5|d_K*Yf0lJn$_WEwCnxiMAZ@(+G$TOFJLBbyKe84_sOe6;5}L65rLO;bruwX+_NR&ZZj=j~CJ$ zT=8L0dTd|`YpTj1QiuWgk`QYW5)@C?^{+lBNEC!!JlKzfl=~0;xdxFR_+>cfBaGPG z4{fDKQ*oHm^6I`5O&gRKCXMyTw`+5+8Z<-uDU}SUzJLYct<6`2-mE`uYQ9Xm^lUp{ z?3;6F$hAIv7j|Xe4%jI_-J+PS$V7`Dz*QPqE3|FlaO+AjMlt1BsyZh`_ot5+id^Z;TII%l_Nmr>UTG%r^biPmkT{xtP1$?C-CyGA~*gY^wy0cWCz z_WE=l=UtE;6edb`M#dSL#s^wPucXHyObXF38&)nD^NuGdd_#G^qU{@LTH09+h!V-B z_XEr7`i9n(`g#UY8TfJTL#4w`oJ{w|MN@L;&5{k!Toq}vD=kqHO4jqY;utA1%m~@{ z)}wz&xy!}jxXjO%@(VM4wmlrqNmzh=zYfhkQN%B$Z(`o6x31{uwP#yxJd@QfR@?g| zeM^8oRzWGoBdfA>?$h&eD@2hA*V{~a%D@|ba&vM~4QaOBvfxHPTI4y5kPk&)sCWMt zCMD$mp@=^GV@r3lhnKTd$F zlSi)4s5;pw=J8d@u?}b4?tOirJ#fgS)}H!$X;*-)Tw3S9`+G z>a)l1Y@B@Uhp#4VeVn2FnvQCoj{A)q_KHG|Y_8nE_r+xSmm?xt*S+0d3f{tm$Set+ zU#0YgMy7k*_xRXa3)&lbYPISXa_QjsPw#6d-wxU0Y$B1rMaOdO%>cr}nlJx@P9 zt(+1|@Q}zh@`LJ^cRJcj?H6Zd$c@@Q|Ms_(6YJFP64w@@*n}S}x|fRBRohnD9OtwH zKb~CnRm2rOH@JT#0lqOW>$Mdq@fyEAb8leK+jE8k$-CJ3r{)n8=tljR^(>a1a;*`& zdRd>$89PGP(~S#bT74TN)9wYx2 z_l=cW9gVQA-Qd2o^ic0zeR}h3)Pi$bK9mAzIN56ajeoKPaj^nKEvhtY_skWF(ziRV zV^~VxkmsTT-B4cdgLe0K-R7R?2SxJkFctsgp$C2FMKWzr!CQkD3I!%$5~DORq1J0WIJ zzYy&d8Kwz0+xC)d8*yDnMrJFTcykni7$Q`UI^9BNHe1h}levCLTi3lxqfXN!#)sK3 zwBmVklmb|kUd_r<@R{dgpDabD@U%&PpG!*;e1fqsX>9T^GBx?xlzg3FF>9VG2NBI1 za5^40KIPo>w!{P<6dMJ5pD0_33_PO$609_~U(CSf%nen|)m^u_^tvv|7w*h3%?_TQ z=#0T(W_6hBOp)v*wV0x)RbI`Rxbo@xA9|&};bo+2b9Ke*V@j6Gjh1WBkwc|$diT>Z zmmd#2Lv_x^OC^Mb3+e6Xo@X$O-FP`e$&jqH4n6D#eoEEm@x2xS3t-3|5hl|@l2|fN zb*nZ@LawrWwi9b2(wy%9WDY|4ce;Y#gb?`kX`f5_(Y5=r>BEHL%16!v+6Jpd#r+et!zKLuu zxmP?#!Fqg;gCzi`R4FQqIrV;V1jbGj@eImuOFK=)%RZn7pYdA8u+7=0Nipj9|g`1iC}G=y^?{p9~HqAu6OaDP_(pK&f1S@Z#HL?=tcLrq9WQH z3dBO00}Xym3NuLP(bgJwU0MFXMTKu|ku!_%9MKf=h3lpH*dnm)t2UOouw#Grh~*mC znGKzdPQ*puWO5{S6HW3wMtgqw%s`rnJK5w!&E7LGEElezTzI)-N|hK6a^3I5JMO%hQYo$nP;S?V#>Z-lB6lq&B>1tpy{A%C2|J|P zl~7Y_b@0bds4Wm1e-WrxtotEt4;A1E&Re7BysQ`5Q z++N$HOtH+HvO>ru)3GY&XdGjq#Q{fONlVjEw)(U5HAZ>OGPEV%?r_i=e3txmNUc2M zNI3`~($Xf~mJV~YBe<29#*^oQ=kmP+K$!(xfR&u9ES?U(!(%?EVt?5ux|V~Mk?h%v z^IZ5f&D_WM0B5c!aqja(l z27wra9%zC-g+Bcr*iC=I(`gWiYbhG$0I&sGaK-2sQ%E%?jW2Z9botCla4FbzsVMwa z8Q0IB8A!M?J=kPe&CYg73`l+c*5b6wcFP5OJq8}?bY^l(N3x3Z&`58OeDU_=VIhtvs7rK&+kL=;hRXyPnh z5l(0{BqZg>9+}^_q>~4BtZM`w>Kjn)d!0Dvu8oe=59)g`4*cMWo|>} zsqi)r8u@j&McMEfPVL0-MCSmNaRv12)VpJTe})CF&XFJ{1S}HRQxd;b5)x^Ro{S{| z!yX-D3P`E@7P$2ufk1e}&)c|3^haJKTeQc|P!g#sDz`$cNe!jA5QO-aSM@w-Q{-#3=URIo;3gr*W3t|}A7M%(0=zhCw@ZZ@? zdjyIwVKKn`zCQh)o`78gv@i#mNkcqINMtm+Uyb|s#TzyaWPn(xa{f-&p{t~`j>y}{ z)-+!!VGpF4a|LydTG8IF*X7cReJ!zZwhs;FtEP4IozTFamEW7!%cY8f!7&t-n*GbC zWzzd3EA4mJ2e!VxaKLjqH#IrKd5mes_*;!wBL!mK-=DAk(MGU`NyubPiK~>S<3X3q zPU|&$h~eUR>!K11@gCUxecyA)qKAQ4zD)Poc3k@9shN)4;AUUSRzBSo7GPsnN|>a* z+DrWLV_&;YGsCjE{$+V-V1QGQyz+XBFXufs(D7aGyZ7_(fZ&+gIg-HZA3ATY&HRH2 z@j0~tp_?T|bSz05$`6w=I81om!OM0=~4=S~r{7=rU zIE#WIBxp+=Xum)#B4|01)p^U|-?p^e)@Rxrz|jzs&(e*ut%S}XX~uU%Xn@=&Z8-!`Z!d|$?Z(< z6`ls`N$A2>elDK|G&4qzMsc4o%Nq=5J7aoO$jWa1bR$nMM( z7*yZ^u)_CSxKIV3w?V^0f;6oTN2&~6ywsk_@aI`@_Go*x`PJGqug2Bu;vz_aySrJ5 zoAc{%?mp*rGdJtHg}asTcZ&zag1@Qf^v4eX1rJS!u&Y*0VHnDD7)*p-Cd>&Pia(S< z9?)_G0vY!4<|sbn@p^dw$<&!n9Jn7i+qSUt59UVdXINp9#|`|M}LEzp>#~mw=6BEz-{%HzFulN#tHcQrzj$Kq4pro?(o!i zizfPn_;&Wu{1|X5FF#IAS+#CGI8c*qw%PhFckOnkDiZQWEP7nQe~Le&EZ z@JqVkpQrEr_6T!Ss`EUmADyd-XDnkpJjQqAo24ZIcJau&RmnO0MX7w38Rxy{^9<0@ zkZU7w?6kiBDa6_2LYAU5z^$;cb(D8NbZw)$TIrf!M)YE*(_j%TUSg^%GgtRyTd@|? zOaBDl<_Y6>Z88k^SxoF(st7hvB%1FRUy>vb*TGZFZ(cXQKAJ(K!f*%Q3uF)aOmBet zOC6ce{^STQ1KdyY6RI(n6ZfrZTWu&KoFD(}9})DaSA>8S(J)NPZwyYh*AjNG1n*cA zif-rlSkSJ&zsOZz#*b;|c1V`Xp@y#b<&wQ&2do5rzey#}iFVAmsA{-v=~R=3a5>Tx}# zyQ*bBv9+3UxyZ07Tk4QlQ+TZk&yB&xA11*PGzV??{w=M#JmTvDHjqY~fbXyXK7ed7 z!#@MIyH?^j6W3tM0uROkhh?B8-jO9sae|h0q1I-1;-QX?mKJY;q^;$jf9rwh^k{h# zgMVF|jyC)s#y1Q-Ag?V)c*pf+=D@i+J5@gaJO0I;bG}j;=+O*%H_y72KJR$if(!5q z|4&KHiZrIvrUP-v$je9{O@T^uIrPI%TF8XZ;$K(5OMg%4daJwSNV$->B8 zmZ9}>8NS>Jf`I^bm8A+hUXsHe&_6#f%l~f!j{W)eDsQFv`N0!%o7iJPYdTBBY00?+ zcuG=>6wynj>sNJI0jz`_v(e~3OEDYkwn4Ai3z0(e-Az&#o$L2=J?|UKKUnH56KX+Y z3?)Z1%FA<_@%{XK%~y0si7-BzH1-}5pG~WhSJ|eZ#<72hA?Cnkx7rIz33@AK-RKeu ze6<0Ug3+$IH^1sGrjK{3-les~XCzEShV=?eeKO5Y#Oy-pZIEHaR4mThphx*{WtAou zc%~q)IRh0~6H*kAz~RU#bO8OGEt*_gD#N4!oqk&9Xs-B*;_O#vk|D)~uPQd)q>c{8 za=(WcuWWD`0SSytw z&o!F#YrXn<7}rC}{@-R5f|(2FC#-LEY#{-emHlja#ypuaU7{u+cC6@)1b1?ftj;~>v%1bOK|6R6@e=bRR_=T2$P7n?KC z5mX;O7}-|IMb`GOelgR@Gj5&x*4|;#9eL@w3|0F6{FABiJUzZ;a$$rbK}2jCQU#WEfQ_43_;Cm0)5a1~{54(fK9uoP8C zPk5x2dBy`0@PTaze$K&yuDvmfkp4{IIIsub^Dqb#T7(}61TJChP&!Tk4=hCwEcu$W zu>H}y>-QB^C(p?tkKS3|{`%{f+%v_rkKtwrduBf_;0o6VfszC!onEJK>&Fe#4RGsA z{;5Fc-||#3T>2n}8$XO2Z#FMgiY!wtM7-Ka>W50nCJ#HJ{*f6>Sfzjtr|X^QjCav~ z9RpL4qBO-HZZMCB2|flo8w^BJ=h~jUYlOxEkH8T0^w7h2(|X~dXaOR}pCF#Ctq58R zl@i?(v8z-x8?#ysgNc)@S}g%z51mai%FdOhveeRC4m?7IS_mWUfK;1jGxu-z7+fqY zN!A&BHD^0IW9V6sh?5df1fhyp;#(Hocipm^p!wwh*U3H|!jWXPsio~0Vpg}!pVKno zrj;~iROd??t0Ao5nU63cCr}&TK{^fX=QUkzi{tv-P`Wi_`0R@4_Z#1NID$%2wb^FU z=)ztc-R7(aTm2!kLhp(RQ0mGcDhBBdjPmia{qeofB&Px-mW%7Uy09cooI$2eS&M79 z;^HJ^b-yF`dOxe(M9}f@v|b9R700l?$~^ZOQya}(&=REouwuRA>&q?dVJA&3puJk^ zwFnY3POJ`5oBgjhdv5_faRkM;c4(kgwjoix0T#N|m8Xe%A%?hqNx)&l#oM(!HjR>z zm515Ky@=4Zb4f0%S$(8UvcHtD99i^i!!vUb;(|yr0{X5 zK2i}-Nm5Pw>t-MDyKHr9T+-+si0KDT~&nAo&AHkL^p%K>o28nMPu5$NB& zrT9T52~Pr8i|Cgf?2M6`-F7EcI1Aucb!t0aRoW74YkUtE62|SAg*crk3hSxv+vEJh z>3VfgL<&atGlDToptPYCFgjR)kC00tEE2CJ$po~l{i~+0dNO4D5xLYGGNBj$mkXf$ znP5kXkpPtsFk5VspQ_0~5JSV~J6lV(UuBMHwJ+LkTekED%%~*)5>W-hM!-t5;RjJk zdH|^(S$tc3J7U67l$5cTpVM#7c5hb9xSt2}Y ztPG>ui4w`+S_DM?th8*Ri)iHptd?#5)LJPphm@fZIiAvKp(e8|dK z$s2$wun69ADCDjaIbTZZw`0xvU4P(60z=_tqP1rRT(s4tbDh(bSjHUEe!U3QNXQ=cFR=`-QW>*5+LZ#MH5l((pS>e5EChWmtN`;4b=1K#Y2S>SZe=7eY;j?I19c$Z} zr7XgRn{&84&rhZAucKiFE3AMas6&KPLM&q*o}XSpm<+&IpcmQBlY_j+F>iJs%=7bwVE}!0c zm6k?giC+5Km-1O2gzfgwlE`bk(gb6$vcwtxK}(iAca@mrJ10dDqn|5O3luqN2L|gi zTjjhHeNYU{9BAjU-63)wB^!>PE-Niarw-}{Nfs0Dz!(tKtdD$uFyF(fF5G>hM9`9N`2 z2rvKYz5-)S1syIpIjvMfChuo%C(8Kp3#ngF^@*{{++rR~hu=~_*nvFtR~5a<{RIyeVI8)yiwO z{32zwdakxMEnNs*Q^T&!Ij>=(X`NW2ns!t?X}(;|Hu(sOoW>9o$2pnaeWVwlP8Xa@s~$2N%1vB2ZSoy@JTy#BVEBpMBm$EXS;TtM0)>+A%lPwQ zCm4xP2l_=Gkqb#?hr}sE)JCgO~{ynoE zf>m+7aPPqsrw;9(>1pqU z!&Qtj@lH^z)(WaTo7N3TC?uN!Lz|)6ZFXKp^)xjVN;#U!4L(CfiaY4S6$7K`u;j$O ze;Bsym-!77gD^t@)OWck(b4%FuXd~?hxh@zBNW)jYcRJB!W3eSw&aWns~66(tcN?z z#}wUG9km?1alAPS%y?F#a|?s73Fz%&^W_9$+R}Ijcb4X#d?jM1kbB)%L*Dc?@M!GO z1R?$-fFc!soztEm%oTtcz?Xq6{s$+22m#kHvq+foWrSWy8^JAy9x*lUv8cRWpQ=}u zhK+petD+u|K--ACTCHB^b2WAZ3Z9-4u{lJ3Lx5_>ZNVlM@UBrKrs{7fh^kT_DEhB9?rpEXlA-ME6`OS&aLG*XD9qOTE#&nk^b`;jKucdnfBk=7 zff*PI-!&EQGcd8JK14FCo_^aQNC6=Z3QI!LNS9&$o-~AOFO|=i8Mf(>QD6P>?}H@# zCu7OjN5oBp+(FjSAHPX@VJ#e?^2SQQ?ADEzZ`KhTUp_}xA-qb_`7!1)MR~w8RW8|o zH-jkSheje&vQ@A7Nziq<;YrI4PJ6~v!+y9=6;^0Bs4%J18|~{|Qu{4H zjO?5QW`Zyo0SNX4hj^b7aq_m#5(b#Q>kY<7NLB3=#uEh}uAQ&X&VIUlQLz@5e4u%z1JUXkS}oDY4k#*yqQf}Pv_?%f-=Ejs|y5h!Et|QXoo|WX>QeO^UT~YI5HyL%3mz90ekbkp1 zck7i=&mneD*eN-Nk}B@MImbX>F>@x#rwMOJ>9?vjOslq0$5O|F>H3ER2gam?A2`%C)j?jtkX$Qs$)%PZd zMNqMHT%5@35h)nJm^}GkP-p-cp@=a>e-$Omj*s9o5|+OX+YEgzCNGe#V-gs3>{8&N zd$aM60LbR^la5Bug>6KA z9nGJ05S#wy5PY-0$j0o#@x$@c0uc+g5xPSU@!#gI2)^OXf6py+n{I_*#+*SX->mUsz7BPI2N6170F{KEqoclr`((>7zgaUFzOQsR-h%3VRI@V_2 z*RggXQg|R!Fd-n7!;4VnS{672V|J}lbZ!x%uKmI^h&1>4;{p5)#{2||GoXjVjraC5 z4Pg4ee;0`5H>8&52lP-V+Co1l+QY@r1)0HTzxT6e9lgUl_>EbGD8l2XS61J^<*g$J zp_k|Si9G;3^Ny&8|9P<^V02G}al_~sD{zJ;2lW-VurOjl?QS{X0|0##&*BkK6>sDL zIQ;KcuTK>TOlX9HXjx&aHV=C{Ow5TvVD+mq*7<>Yp-wv~HqbTteCg3TZpQKb>c+;x zqWIiCGYgmGb$^jzJhYOBIvtBc{Rv!K6eXT527T^Dn!!1p)$>&$74o{Blul$SveLQk zovVrpX@7Kx2nvFo?$+Houx&HvaJoHh!|){a92NiUeoe+>HwyLwa5MXE;P<+Q3btJ8 zStxRcX$iwgAWl&At5%!aow*yZ2S_|*xGKlH;1S^4V_8|In~z{c>^auMwFqmG;fUob2f9{Pr9=LK2!fimX7(6>D%i864m_>6s`=)y$fB`Aw*v2g+v>F0 zEK)%U?5&YUhJlweaX@7Z$KRK(C`tbMNi#?dIb@$A@d{LNp?7 z)vMFf{{6}38{8pBGG2$DUEpnC*x1=|IW4DtFF*9)v7`+#>ojeW;^J;CR~rwdS9e7;Fk)+Cw+bW7SSB?iw_7GBd#by7JcXnjLCDz0Kfr z#`yU7oTXZYM<%aleXsJ>LDtFNd}$|9(EyLDg}SSckayzI0#rBw9i)ga-NvPxn&|qu zJ@*upd|SWt55dZDS73OAQQOnm7j;eUSUw0dpvh?u`{73T<%{`5ZcppwrPCca^e2G;y1}dcJKMODEYt`4Ms3@O&p^FVK>b`evCY z7R3ynYdm`Mcz1zoI+EUq2e7fRY3q^*dWiIV!;M>CU-x>;q&{x{)a2|4wRX8TsXHnB zz{K>+n#{bYksd$f0!8&A#EWgB^9hvqSjuk z|NOY8tE)TDZL{K})nYVoJ4(Xercni2#$J}s!qf14%~1&~Y&w;nT!zr{b8x00Elfu$ zr{xMF21a{kYU10q<6~0q-P3;)N#)vL^Psbh<#;J$O))XCjy4#C5a!#b8}7_U->AR- zuja~p09*2VTGv197@2|#dKYG6F4!U}tx_ z4BC#7#xc9&OsNN7hFqKHY&j=D$Ipmz#P77*4wioJe+ofuJZ~Qpwk=nnLtU)5(%43ZkN-!Slk8mDw7T1+GpWf_yIfm)BrB2XC+m7m-?pZmTEJY{g4>7!_qK zI*E|>zrb*uzO1OoCB+JxWh$V*zyFkw&!~U(>Ha#3l5&5!;bh9U!3v3#!)}e>;DOf> zY}n)daCs+K>iZ(W7M(!r@4)yI#DK~5V3a;23RE0gS;A_^ozd_NK9|SUWW~H|QxlUx zv6lP&Z?Z4XTCIzYV>(qr+>FGp#uY@FyCE9J$SQ}J?!P|SxV#{Wk5j4)ahe2GSJoxS zEo^X5kP$pA?9VdYnqJeV5Hx?V|3q~C$_{uAeFE6?D*1WiyYLJpj;-y5WMu*h*#7vrcow~9n+uZ>>~KWn;DAR4wP=2)Te}3XX1;e|%VEKH zO(Y-uT-9c`JtaT6LghgtHt#QAzPLLE^Z;`SF7j*p#khh-dM2 zUT$*!R1UUcDG~op8oD1ELZg{DTdHvZMlat{iYh25Oo2z;)*ztc+o1wr2Pd?xr6uM} zK4*q_oKl2tF#UmXe%KbLoFW%BRLo$t9jFDk`1oS5!z?Q^EAc$8TdMRs1J({^D?D4K zD)iEw-Az;zV*x4}YF^scA8Kk|ZkwrHlf2$2TPQyoeyy#OlPr^yN3ivPbc41ULxM+{ zDkFj)INr=S;rm^cGSw2Z)$-+{%I7Cn#y7}1U}690IEvip=$=EPKp~-2&KHX-zKoHR zA17^3E?|p=GH&fwkGf~D$xOSC$XHQZLc~WYD(*6aI6bAbhVY4($1Ur z{&-Vmt4AFrwsD?)1B#zkZkC60Fpyf|^GElafjC;u7^3g|?IZ1dt24!Gip!lTirOmN0ngrwF;h|6r0)ZXc zbXcC07belWoj6on1BpoN$yJxDJ(FBHDgI5T2Q+lamw_n@jv01J006r@q19Q=$|;mg zV*^fy;}mBlEbXXS{I?-ttg|7(0ARYia;YU3sz97A1iLM$jJ0Yv zUiv)UlfVIbdaJb{zEG)Wt_qWrcY+7U;{sjqsK4#b{!*`!nT#DD9B#%>lxa1DJw4nM zf@O!M&?3RexPKWR?t9fqPn3)Pk|apw!vT1Bcsd{^voIt`N#=Z!3YU~@6MlNU(DGE1uAqpiIZT3uDAF4d4x zFr)J<2y+!`9CIQxI(o|o76HzBzQ$J1l#GlFsd2>A$Rk3Qa>Gh$yI~oNX=HB8D7)2xj0jpFEw)ZGo|lhM$VhT#$n?1ns*oR z!)Q`X;8|h3Q4{g7T7rYTWtEMylte7mR@AH)VStf9Kc&OPn9K&vM$TJtY=5d)iE0lv zUgH8wN%tYDPou_6GvVZ0I%AF08>SNQ5f>>#RFnhc=2ws&dqkwn!;~uQZ{XHKD%Uen z(RytaSV)Bb!Uj)(NTtC951le}ZpF?5Sdl8nJ49T(b%`w7V7kenOrJb{!d107Ne}MP z?641c@G5}Ibd1O6y&Uj8coU8ej4}49BmL>-VC_{u>+uFDf6lAaUbP;PhDh-Lzkc1% z=w73trMLgv+^GKd%eoLq`4nEcEqn~5f3SbB2x-E|(nvFE=D7XkGXC=6IY0A9YR>q< zr8~Uk@MwRcWzCUZU+hO)OnM~xbeYWr)Q+KvkF34NWUt&2O8lDi%xZVSuF~DxXy*qB zO}jv2QJMD`3IEQ@Le14f(LfP0Fjr$QpMR?IF9D@lEX%@M=-%+2?CO03S~0KLxv<^~ zj)CrKt3$)dL&nMpdxvjFEq7|m2ZMi8!B@k^+@q^@q?*RN>lY7WNuDvDO!@0jx4e9a zy&!17Mtn#NmG_f(Y5(xlm64u71>8RLwG**C`Iy%{2!gcoSwp^XvMAI?;F1{Ft@AjW z?ysPs?V-MNCH!k1CC!^uoByJ9e^=h_dIv31cR$VcWO09V=e$(mO?E_*VD>siqV~=L zhSVaDBW_!JIE5Lh4c3`W_S0e~o6Q5~Hcw5|u&Nlt)k`d1pMKvAbki<-KYFuyq8}3M zZ2zOd-Fggfz*c!Fdmbw^PD*TOVRSd0EmdQeERRv%{2}rErH`=UEl?x<_wJ+z1`rltMknmzZ0P@czk3)X_MWG-5a zET)$$aXs!s!iBn_k=?Z5XlS3kL+S=7S%j8a+o3j#=jq291LmIJ!-U)=zG9jokLyJd znFFq|t;f_W7fUrh@)?3e*HdtQFk0weyy~V)PC_aWeYp%iK{gcMi$zUZURhl5ZdCN} z)ArK93s3H(qz@RT3C;ZhXu zFJt9F(y$@`10NqIXcqrKaK@(G(*5QzXW4C_7F5AQWdC7Z;YsZ3WF(pA9X{Yxf-9!1wgIbP)L=DYwvD!E<#u3lvlfy&OgR@ta;pX=P-z93b{)^$~ zm51q-SxX@m_9ly?;tv-tBU-|)J4?<0tLfkL@-`K3TdTIZyCG`Rq{j(!kwOv;0u9pb z4fJQv%IX-TBFRFU@NMgyvQH;yS$vO^cVr_kzvSOMfF;46#<8fm%?gmHtaC)v-zQOxG z9V9@&u5g?0PfW@}q=WOKp|;WTQP&LZ4AgCFZssMs`P;=_pDo6u?-qaS1`0a({F_I` z(vRA%`|8|d>G3diA|VE!`FSw(3`j`J zD!Zv&j!u%5I3x_Fq!6S?dG1VF)FQXavA8pMMU76MCkJOSTV?vNw4RggJl|TJ{~h4p$;iAZF{K_WN!zyB&!8>0vgV~#E8KjD zDtgllogJMQs2{mZ_TQ9M*HmwX-jIy$IpSiG{yW;(iGu^C14~S``CJG1+2667mX-wa zW8B}u-xkdC>pjt2845ohcU#x=j(OOeVMH8p4e&Pdm|tmnXW|(wwJyiW9rJ2*x3oQ- z96f`Bn{QdE^zP=#P~Gu5yX{G*z&l?0y^;5Yo_%_BKRuNh9Me>!CqY}{&Zx}Dky~cp7Ue_baW`fZ0Km~ zIOTanS=6$4W$n~96a=RWIQ_MqOY3HS<20Hi*8Yb5YPKk?E$h)cY1QOWb!bNDJzv9b zkEivkVic42i1W2(%D8}A!7Mw1{O5Fw$D`l2X*Tzc%4;q37rOC7TfCfK(H6N|N*t8( zN#EH^AoX(NVx>*H_w7(< zs8XDAMm^K~U-!O25{(Ramj!#PzN#r?AKbrCA~Z>gadNWtHhN7@DIdwu`4E7wI9<#u z(Tz`6$6L*Mw=S0rIw}?_Qmr#rsCi%A*~Cq+cC z|1O1JUhvTvJ|k(+88vS2T%ZEt#Cm`D#si_ivIsOeR=)`OfWZgMe(gllGCc^)`-L0wWYtU!JhoqV;;o3))Jtb}YCR*%&Tn_!rpG-#nR?Y6rP03v#{yrW zbE&t${o~(@k<3-J2d}x500WAce?nR2{x{rGk;KKc_ZI`c2)I$021tH5kjuI}L;7!P z+1c+sua6E6g&Z&u{x26`W8+3WYbxVIZ@=|NZ%X_w>5j&4zaR6 zZ_4vS+E94^oA0f+Rtafi8Co`sD=o8QyagQ&hF*n8_{L33rNSIJ?i0IvO$pvfIO!UgX7_Yp&>Cz-xJ{n>pvRG zWr{fggs{dkTEf^t+h`3nhBqXSYrFO4-H}q7w7JswqvSv!USG+95NdwUy zuA>C-ad8retm?AIp)ib0Kvf20-ip5Ad%^-q?SCW++b@g@CJc(TccO~57r76-z2MJg zNy+t@GA-aECe_F6^v{&BDQQ>zkG@nCg~U{meJQI>aa5Gp*kOpgwzvZ_i;~q|4^a{U z=EC6)^;jQc5n1t9n<&B~S#~6_`)g%mMaYMol^Y>cl8v^H5(U=V-ueu*l59yh1j9SD zY&~C>*!l~FmE-JbGL?y`vrd-+MNXAnA=+xIviVTkJ)UUj? zh`HnoG#UqUHa@ZtIKfL6P7|T>3GO+2NLEZsgRuJ7$O5&=ie#_aDV9e1oiU9me)VKE zJtLRHZuN84;E-A0K{ivNt$$*LK*e-VYi4V6!9m4Xy6p7j7D%f&q^K z0dp3p0+v|!9v+!*kzq!SeSR^H4D>s0j;ZjI&4c~Cb!X&;$fnF}Cm0u;Q7rsBu%-mX z5MWqMbQIk^+VAc@-V=jYO?(u0#5JXv*OmI@YIuy+_s`p%{>IYy$%o>1|t%VU+rI2{U520retE0kx3P3#FjA} zO@8}n)O17rJv`CY7m3EkVHbBBjc11ruV)0_W1o|}QwPr*#qlS>$0s~qxt{(X7bK^x z7uiDam0;BNdzcalN#uKrI=-!7Qo&;+5jN9Gqh}bHN|_7UVBSSz^4Kj#?yY`aU$Nxx z15a@sPLNSk^`ATL(o6=d)Q^=_YR9;aab)D z<{%$%byJO~G6MMo@s>zlPBDbc0kEf()xq5jZ~-e&-@f~e7s(HPr65OxQ;R!9@*=W-HpP)H5`3I=+lE7%6^n*1n0r3(RBx#bk2samVd;s*8pV2+;@wZzcPJ0VTPqthu5Gb0 zr=CCCKa%>E*X9X!fsLe$zE)$-F%uX5BYW{Yl=K?wER9b`j^4wKDQ>O{Lh=d8Gw?y6 zEjIvz_v2x(ml|O<&!$)~S*C2*$1V%=4FQh9qEYS9+kj}ZXuQ5R9HzfNm?eer6WQB= zl$L*Mr^EOOoEIz8!mv%lE%OiVm{O;VAL;}R>MDY z%a~BL6SAOZ%BGvG2Avk+{w=HZd8ZG@*A_(x z6te34+kzR0N`w~S#Q|QUfA>LfgUutK+vMTFDYwQm(4IQ@tv@ZbPDZ+Uw@=UiOqPG? zXWX}ad15q^cv=EM&_e1{6@*Pm&05KFn1il>Hk>8);I{;396_G}9s>jgrsAu0V&?GI>CQarT5=JJDQ=zYpN(RYUgKYF<-FpmQ&CY?*YrEq!g!tQPf2_bNqUCu3q}g%1!cY48U^y!aTl=$1faWQ;M5R5t)Yx>p zsAr_8E$h&~TWoY~oxZ`r?+m?aim7wY^fd7+{DnM{Fl_Q|T+}W}nCaDhx~NsGo95k& zD!(1nsEzu&-;5#238gNMo zxY1{LH6P9Lno0*WM$hOnacn%|SbVS;wgdMx_4PyV4f~WYVC$OTSRCAQ)%AnD4jVk) zcZ(XiD299>hV4Yo=RUUGe&ZKdp$mp)$TT04`^(C)K5ujJqUv}7#@gjre_OKtzR&vm zTLFFPfnK3Cu^CU07wc~p*)TngLww>&hh^dDqvi=yaRy)E{rTv{NbK$aY3X8AdxrMM z-Nk}yu7JHhnq+lEhUDc%@1h3w80$3d=`_2I11zdv*KdNGBst0Smd0gkqKYYZfq_bB z`7_-;-mLa5qah(r`zg)$a+Bo71kp@>K8Q;?j`BVljPYewd`fzljr45d)vFK z0ck_jnH5rI{KS54bnBp9`Wm7?Qo*tS+gn3G6_FuneQ*U6_Y3Gm7FObLo2*UfJsP!W zf4Wv0Jawym(DbY&e%0GiezBxwtFtY2J+t>SKb`eBJs4PcgpC5->7J6iDirEb3 zorvVw(~4M}B{cA4waj@Ve~3lmm>FPf=5q(-JeftkmxAkT>`dP!*EtRiZM?ePII|o~ zv7ArGmU(D)!&OX$KIkG!UVbjP*JLy#z=i%#vG9KhQ>Pp*lmo*>PP7sT_$@NRC3fI1 z;D}QxRFL#Eh#WX3Vy#F|Bg1CBSr@KDkMWc*XdelJ9h9Bay#!vyJ-lq#knx0iD_JWU zbfY8+a9=b?&&Sz~Rm8S=kFOB}mFPv|#2F7;-{$uRU%+@iY$5eny;dck-~QD1GrSe; zU{ud%)>%okEpzpE+26_UQXw|}F6bkVXpd5D6mJlNHK&=8zP9HCXWbqQbI+&&pU8D8 zZO-pnM#ws9$1liub&Ii`O4wJ3;~OIR+Jr!v99 z6BQ8H@o(hiPbyK&!Nb1(XHZLOPf}0Ioe!5Mg_cr~e%cPOM)XV&e#?w4$Tp%1&R_8D zbZKt0Dc?F^Vsa9tTdJ!T?#IMAURL_9*)q-pYv5S7G8{YX1CTLMm18D4o*g6<%CY^` zwbP4?_M(bK-M%tuZnYhEhLex`T4N^V!=W1q`A44c*?DH*w|Uih(&j|S58!l+EbNN`d5;SXnwp!ko@`0`8)MN-Uq8q{ z0xHWbo6#pRi^(+g4(}9+s97!K!1nZiUYN>Y48y<1s+3zQ_w-N*u;-oxbl7^?mt|%& zdaL{t>LDlyZHa%1({USU#$jB7b^8~6ccpZecyQomVynXCyT2a(vAnfhtA^*)(K*S6 z>J#o~RY`F#IlLG0=&!|BZC92+XV76WEwhZ5{Y0Y%s>qh33z{XBG~x;@D_dbXr^K1Wi0)n_`5J-kRVYw!s+~33Z(7lruG{wqe)t^PQXy#JChb15$0-p5 z5xIH%Hv1F4hLfxNWEOuSK4=bNqp!))J$Ju(+UN%p0}{gp59+0mbq55(vzVv1Gl?#3fjR!IlWo-2_UzhmmcPfY?28u_4KBQ= zs4QEk=T6yP+bmdb{GJEx7ZaeLgNJHiM_rN{bLa!kdt369?F<3M z&)vRFCdjm04^3Ts_XN$I%7dWQQALf&^MDBv3PAj1htXwqjtbpp#hcElMU~T4L#?k} zI+i2->U>2^+`Bbzfp97jkiRI68qcW1alaad+|pVg5`Dc!g-$EjZ3$gB+>Sj*AQ;>AFW$t&3Hmu6peZW}h*tZIMzN}bvx z-w-g*M$>Qf#(gHmMu>9Zp%MoYcKSnpmO31G?!B`@6U`zi%sAt1ediVXD=OMbGc0a< ziBM_5TC03VXRmA{sy9>M)hx;O^V*{!l3ApKM0zW7i0BnwJ@@QLQI*TGLZpF+&yTbI zHO5?7=6R`{$H*)%=We*f4AZQ4neMhi-Df+I5`WXh7p6o&qMfcfy@b6YVc zYou0o!np0*KFozoyG-G{M&gWaruW2&(RaP#M~~MY#~vIb4dV^xDD~8K?*!7fK~Li5 zM~}G|b1A~*ZH$)wWkP%^HUx9Dr{=Iw4YmE0536-ZGzyX)^~f37m1wIkKD_rWiox*M ziOzVsb-zt0ck;5y@6;6?pnV?6(rCZZx-~l`4Q-z7S=4ZxBCjF9UKzDT&O7Y@27xJz4ExtrpA0puR|r*!{0>g1QHVRi#zj+j1vAcKeW*k zdzOzMt88DHDk^;S#9qKx$Gi_VFB|gZ76R)QGk_QM* zw$4Uc6(WCIy?^iH8_iS`{w+EgjLl27Xn&&~P80pv=!_X=|J7QkKHV1|_fX2Pfn<9q zPdUNkY;j?MM1N52R#I|@)N%HBb$Yg8KztyjYWuR<;?>M}G36a(2o(W| zP5?QT#aNy~q`vm`DWtkX46OrSUIct`Ad6DWo3?MI?n9#~^&L}?!8DmZAm>=ZAX7DnSk z`l&W*eqz>OR_*DPFJ8%9*kh1^9)Tt9GfFScZ60P0Hl1*xV0WUKCFArwD#~Ium4yn+ zf`88APZn(%iCOG@=^D)ZBAIAlU|2wxPG4Hxpb?>QKSYW&e!{sl_94Z$V88mFI`zHP z#vS9naW0`59czK|i`^ZR<+b#zqIL!K*yVBltYy?V)2bec9%tjx!Q-{aQ{^Sf<@4=L zMZ1YdZOZqLs<-NF_J+Gg(>$E$v){p0w11*fqVqq7f2=lX_Tf|QA2TSt~w;#Jph5GjGyxAbm`qsA?3fJW|HJ9jx)<}B? zhY%HY^%CXLf>dtoYVs*3CC6jjj#@D&cSL05?dy&g*yD1;zEW|q{8q0sEy1DDnrgdp zgC2!1UpDU-vEm=36POX3n)*EpR;c`NMNe49x1Ijmj4Rg01EaK8 z*u=-r-i@?YXBg+DR=MoY7&nXepYQiUWTKfE0DyfBd31U)Ri%EGLc3^^&fl6_4P%T7~gq>(F6xYzvhRzost#3DgK`3tLY<(9;gMxODiY1{HP( zbxnlrMC-c*4~g*1VBf=ouzn>y*FzhFA`sjr5(|<$oV|;l>iS-{Ei_ zuEZ9Vbm7)7M?3z3zcgg#aUNW{F9pp{Wd&^VyRZB)^ADGkf^>aFLI_A&s?E<2XP1oL zR=Pk2x&p7JEhdUIJzvSby62f8I{Y`8h|*(l|F9)FDG7h_fpMZ&yTkyY+sT|O}BVg6%1_VDt0q$pk=)GFEFO*4D=;U$n?q0g!XHv z_yz}wmd$UxnP980Vz+DG&S1nV!+O;e&!vvJED}b-A^~3y%#dy=N#f9~nzWWX!+7HC z%q*81lVX7FSTVT^`d@i3c)GQESc5?XlA*a9(erpwJ0XPJjr|YEOS}%qMd&(Gn(6xu z-##G3y=f(K1|2PUh@kBd!#~1giC@h+slhMKH;=ZbwTS?x*?Ki$Vg7$hj6Iy3TQ8Nu z?9W=fQyZF2&>ZNZW}Gi>9J>AB?`)lAAl-}*H|kV@35Mz72nr(@h!NWXae zUtd4~HAU51rmPfe7%Yt|quR|z3Mrr)uOB5uZJ4vjW* zAzPi1vt~~QHvfH9+VzdIyGOe*pP~KRi!wXOze3^Nj~)r(ydcsO6Szp8h6{Rb`IkNJ z&vK6bOkgST=y(B3f^}T~%({4w2zP$IkkXBYmG#k!>^Bc|vx9PTA!`K9#l@T=0z#{M zL`13AJGOf8Oz#<9A)!PUH>Mb8X%Fb1B04XRX!1X5A*Ys*m833nJ6kU z$RWUX$>{luq}DojTQ?(Eo6D)|q&c#wiOf13jgqa^njGK+SWb``i{ofjh++Dp+}jLw(m zM>`duF549I_2Aw1(jFO6m5(UqR#UBp;SWR_kVTZMstKU(RX-Qjd)@W#PeQFA<$$8x(@Fo!y_WryobYt)U1y>IywN7Dy&`t-ndv240;7<(Zz5KwjX%WCi+MO>UJ?_7(`aD`^>6W|9u*;3g?yTfOEK9Jbn z%@6M#A?O5nP_9)nwqVVfpWARw?$YJ%$`P8OOv24tYb;%)Tgcx&L)?8Y_z2zYrJB)n zde|8vz#uNcaYKb^aVc@;6|FM-QmsQv$-L=+C>~JS+Ujs$#lqbedSI0%?i!L)?2)dM zSdyRm@n_PB6nOVW>+OGX5D`TeUw?J7c(lE>HHOqJd15t~VF%81w02D`EpOGG*nacVb@P8W3ZbWzPkxnkIuIgHJkK|7pmFm8}*`ZeNcI*TQ;xbvSA?`+a&--d_ zwm*-Z1S>3PxXWc^XSav!09y?-V$f!L#->&wfnOBSh;@(s`$HeFxH>DrL88uZXE z(V<<)?`y66qcmMHT8MVa5Ia~OF4cFqIyiw#mO;9K)J&6^a>eKp!;}=shLSg|(AwTV z9WVTymJ#yq-I3`d@7MaNLQ%+!nTdgIwVE^`r=2!0m-FwBUK>oOrgxVyONmzojFra=iKC_WYyZm`lx1@=8Rh*m+N4a){qilRJL6YvapO>SrSp z+@_h{w&fMgzG=CiUnLEfiD?%iQqoh4NmwK-%^?ouFd67tpY*TL$?E!A9h{)HwzfCJ zoos3evCec1^u$N=bLOc^38__$<0Vv9Ru+k?IUty%-dtMS2jvkGUfyKgKME4M4Pqn*ubrkqhcg+sgEbeirGh&a@gxLMTYWW(su_8ka zcD8!croeW^G%cHIpTl*RWEvjHF}HKK;Z_=6>DH5fT~8pX{ecTsS!xaZLb`So`cEn2 zw^iz94!^@OJXK2fb>Xh7SBBjY;kQsRVO=VSx527xXGQQn&H*+yHb2F?*G4&wJE4WI zuf?#g4>I$)p|?Yi&$r+kM}mPFE!!&&$`jSxv;9Sd?@FhQ6eP+W3Ov)Q8vD~`E`qon zsvS@c(P<7}1>tmt6Sv}X9UL<(F%Iowy}^g<-?7L0s&t`7L-wyOTKlIoKH_Z*A}@=EB;N+NgnnB4Da(Ao zv#N^(-BE)+8J>6Mt&V-vv+Lgr`c)?^K#p9ttX9KlKP6>t&jQX zj1A=phdEU$3D3gT(dYvcQZbEQV z$iaYg+r7^NQ?YU0MYDXhdt9*vy>3e#;K;IXKiNtp2kgZH=4@84u;aC80%>1MQLn$9 z$L@$o2K}B<=uRB=qO{G)2u!0ry^)l?b3dTnhh2Ti_~q$B-=MWMujipFVUX4e<5@Wc zMH=s5>#e1qZ3CMV{+K(R7%6{SmeW(0X9N4BftQ+A#|3KfXnpvVncHCI;L~@8g@-oN zuFa8+-t>UIq)+X#f)hr1#@}ATzv;ZkumQi2EAb0&PR@*sH+JTq|1w)JnQ`{FIU>#w z7kF+}whdV=mTJrH1SDQjJL72hVu5NSF7o}8c%r(e`T>tdM=90kTWsj!W6TnB z-q5PeRAy&)h?$C6Mqu{gZNzV-eGcC`^Mv^vZ|PksMv}=&Gt&QL%S!+^Pg^Np%l(rP zPk7X~__2+dI&5vo9frlbcWW=rH^)4-zmPa2>d*Fkqw$ms*oeCaJQ*30yGL;Ee5Efd zfZpxmC*B46<41MVf%M6HfH0HflwWUeBJ+$xH;>62rlS$+UOO3S(lP~^|A?2V4LY}@ zPTGa%))NGwU+AArxB9Lfgb}7I@^$*)#~5eXz*QmoQT!pP9%8X z#9+ZRH5Dbh_tn1;x4rYD#M^ZSW3B!d>n!>eWl0HfY~CJc`&FP3qjG(5f=7S2GjBI4 zb1fQ0Cn+k}E?xM{)P#3^4^kMa7G;V~qw_EfnM6z+E zq?G{QO}1zD({xJ_Mn7DBHGP-%$Hj%wk5ypvN&-NJx-Q-4eBtM){?v({;^O@Cnh?In?;|z@5P5Vm-{>IuK3RmU zEY$vHWLm`Tj-!wHx2J1r_CrYrrB5;KPjm4}qX59$wfZ-5MqIbKqAuT}T9UK4e#X&}gZK|9%5KIz;sbx~LBv66d9$++DXA`tWa;A_e$57_t~*p z-@0I6Dty#iT~UrbVnV1 z3k&DN2s;`T{z47T!VZq=U=23$r3G`-!+iNJ+cO_$qnNj+E8f2{#q-A+!BtO+v{3`G zk6tH*b|b`v>7{)99F_G@U+&>`LLhdLhOJE-ml+bqzymI66MpkE*XY4y-jgNR@>;H< zJ3g<;oDXKTj|%FTzv6=Y1%M6Dz6X_v;9Tnb!y%Vdp}Lu~g{_zbN3#w*Gr{m(^zk-C zQ;ZP0WRHtf=9+BdXbgIZ~3SI10m~O z%j*)|W)lU4wCK~T3!GP0$jigMZ!}c37c6u6eM}^ksq6}KLnfv@h{pxhmwFXUN<;Vew;q&{1_7WisrH1Y1Mhs^reHH3v-j5Zp4VI>9j` zLBnCftQhsql)lT`cdpWfeCy>A^Ui74W2rKQGL`SJ>Z=&x*&z@$tmn?03HL?p;#KSQ70tQVYlX;}oPToy?I&q`!uTCNA6hTY9|d^>(s)A@ z9b&*WAo^QO<4J+3fUFXy9H@C&rVJy#l)428a;YPO`Q#6k=QUqt6=!Jw! z6H;vPw-^TaP8_rhwBhfj@p;7X35W4n?*GjN;ce(2-8>_emacenyolVxaTzi0KriTp zQA8)`!49+mNx*vT{+(ei-vKexKCu&E33rJ;hz`i1g>=(GAYfviMbcU}Onpc$7krGn z!!TQk-8X7-x)E!Y&B#sWj(mgZ65e@cCs|d9Z z5n}7YDzZuePkw@cRnfF4$p~nh^fH?_0Wgc>cAKn|wi!X2j->57po^jN{>nT@%(*cSh56tS~4Q>Jogd5Gz5BM9jaj?t8yr$U~6$m1|v#IgQz zcxNTrs=wyZs6#?*F&1wWrC!Q>S|eqC8>CNy(TW}9Z)JSu{K3uO`tbq=y>Yn#^_p8Ze` z%U4UvFXL^zb?LEKZO_rr<|@H#U^wzyH|1Co<-tE|wS8ag-(Wo+4i{47JEM8TIL=b| zp?0_vaCv*l33>LK>5IHl@qfoe_8jpLXkwR|-XvL~7>O@`1v|JuFR!4qA$POB7;aif z67YphYPtm9j3!7IcA(f4@hxT~xB@&ovN(A}aDkl|DiWy0?EvP?BbYu!)D5|K1t$>S ztevn&8v!Nn#wj9R5)OJ5QPmew6%fI9PQv<-)a`2w**v(m|6z+cL~_#6c%`ojF5>BRD}6k@9o|{?{fK{Z?tO zH|1FX2SAgz=Wz-;Xu(vD}b{bVoCnOl9{|#OT3U=^RoFqJkLh(klQT(E{i!KA(y(-}xj=U6=Qv^i_?4SY&z;RCC zJC=XxK*+iM-;48zyvwdvJ}bPGCWak61H|#(f-6uJ_!(avp-xT#E)In82ea;RMqlG& z0@9wZe#V!VCXV1!1QdZ_Tv~)!GFtSZ>{ll2(F;OOPE+=i6=EO)a61dCiisvfN#5Nc zja>sOo+Dsxj1;V0Q3@arc17_$mOo$r?ErRc^m>3LZTbe#vt#f74L~*)R+v9*N_P3` ze0zkYaWoFfT)rGb$SLfNbyp^2#r(p5!GS!J0?Z=naGk$EuM82@Qb9HmeB>yjq4zMk zQ2vFBo`@=9AnG|n+!Yz`&P2o7_3`G7kD~ZIXgcJOXSa>JWu>Jf_=tjXff2y3Q%wJ6 z_6rQgpXF~hmyd4*B||&`64LT-F?}3bpaZ7^pDFZ}Qq_%&(Heo_JcJ@m(c)zLd%Zv6 zD>;IABC4F+m`GVM-#|Y}Ng0Y0X%Z{@N2&7yFD2O}gPAp-`i2VP^cWJ%VrBoq7V&pQ zf^?grLSjyCh%zq^lr@|sEI{r1h+!j}ys@1B-eUWaf?Mkr9Vu=WiJ25c)pT-?Y@g@9 zvr37OAVfd#@6?nf1_=(Y<25kq#&m}j^Xb30m*7442xTrozfdZ!CF{_S;446bXTo(O zydM=m3bJb=`IKN_g|p6z)qEYCz;(lbDscqhgE3Da3Mccxmkn`h*{@&!dsYrP_kPHolhmeas1{}drT0Wq50tg9)bSJCGwC?}+t;%blK@>%7OdzY$iU;_t zixbLz{olN@(QqXfBHTA>lP~}OU_$`q&UA~DGLsFP{TF;e1hfK8OUP=Wia63|1-Sk@ zJyOnjT8N%fmfP5aWu&aEw2vUj@0>(Zz@=gj6(q5F&rGn-33hm&(x( zlu~62iIj_Plw96h3rSc5We^11@&wMl*nqT{7_FB%XaIbZx*@3J@t+yT%6L`PP_W(ra_V2cAI{Z&T>esvID_-1wh)B3E~fjUP`6OGbsDB7dxlS_S6Ilrj8F<5k2 z^xuT2={!kEZV$bithyhf`j|lm+l-kJlaPRBUiDlZU(>N1(!m>ftRAYtpqX{ds6X7k zvriNmta?gPtQBvnud7OOl=$=9pm5)12aST8M|P;xt%n{xrc3bpJs7U&3$KkJ=le zU%v~|Q=ML(RA;YE3Xz?pM~{q_qg4_8-?x5H$FZwIWQ<% zq*6=2g!sWv?2ln$^)P7qzw-vuxOIAvXIblcR!RIP~SbYLV()3@kS@+iF$ofC#8^8dVIrNm=L}R;vi0IS4~i%-6+Ks|HxaF zpP>~a?hk^=_N%_z(1U{__$+Ukkp-=RqzJmbf{&&2J=0}e}22;`hSZn12eK@S~^*{N;m^^ z>V&M`|H&U60e~9?gMy3bi>^D0I$R*?|i z6%1Gm;4y797yB?(S&_D?9M9o<+30R}G`G3X=0EGV^!+8CU>{iEFp`&-hf9Dz0tPW? zc#W%#+uy&!$}~8-_mwVWw))yF>>kx|ZAx3eR#qlgR8Z)xon(^nuI^pugA}xAw9D6- zkCz}xnWg(L=imPDa#@U9SZmNOxNJNdl$i(2#?yqJ751l8)#o+5OS?J49vVnLV@EQO zgcheZe%I#LxB>z@JUaHKqoLtQ6Lv_83=a>FF>M7Q33_sJb7f9b36NrIyl;ozlXMm1O{|h6NkkGz;%-{JZiM`@r2?zpskf?N@qDr*>VZFA& zfAZ&#C_p2II=MNhqH$Z8sEh1egOe#0ijg>kO|elc^{Ak?wpP#sL`+e(wzgg?(pNCV z2f&KQ+xYmKHoYyat&#L1wh1{IW>UmNvEat&n~o|lpcqM+C5LE&VP|i(wBpu5B$+Ry zEYb-U8e!m(rKsea17wVh_GDC4kH!<^<>i%~78@Zj@Mqcg@8)-VbsMVbS65z2!aNxywJf_+O!wCTPvGwy>p z+;Fj*s1T~Ds?xkTJ8K1kAl*@P?&yhUM1XpRxP_?2rjDV%zdui`Qt%|)^!4XYpFSNO zjkL!zOZhx3HEnN8e?X|p7E4%TiM-43+}Vs9xNLLDJpxIAsl2>=w6nAGLlQN$jbXig zBWiOq?Jn@@3rRV|$|~9QU`-7Q-n!hs{P)f3m&~=KU+3@e3TbKS396@hs>(`AL+Mg> zN!Kx~uR4V;ytYnVnyH@|1mktEfFWe>MKDaw$izhX>GS8Oj~_n#GdDM9;W><^tv^qO9h__T(0%mRT@6xZ>o0-M9nk}}qL(zpkN1Nfd)q;JmF1;sb zcz8~N4nNp+^(N+m?7UK3T&!SQXTb0FCx+=)C`l^;U|W|q023(8YHtrDU}w*L0s=vp z4osg-aA42Dc**72m5q%JBDXu@j0iyZ9gTlkU$@Sc@;wRgH^VGA0UfAw_w+jS1eTXm zZ2AjdL6S=Ter#su(vCX!QxPkjZQZ(*e&UCH?kibW6o?I~K4POch4W^$Ph~ zLR825;^^rmX_bGK1W(K1&(jt#GEb$UXPRBy&KWydgj{ynShin z@N%zPPOZ_P907hh#Kpz!bUZr?!P&@FN#->b-Wn~8W(1OWO-=S@o!TSYHqaNRGj(g+5#si>(f^}q$4g8gMs{<|F?A)1<+3Vr%uQ3e14dmOaaJJ?D3 zpC9%m^O{Ijo==)a8@GB4oJBt~ELrv3>iqS~;#d1=4m>tH+x+5qTamkeV6?WcP&IQ4 zqg6T)zc!Fgz<6@Ja6`-|X8RFXl{DKiP7lSg;2Vt#x7XQC_(5#mlUHLg?HslWlsHRg2dHNpffw8T8G+YQ zON{%udja@U#Mp2KG4lEi0)r#@gBAAUmwo86!dfy(bgUXQ9@0O zK`?tc;t*N4fo`j+j~_oSj`hS#`0?4lRjhi;=(#r6=p7=~re9%f$YIW1?0}w#3^ZB;EWy&t}wDM}BUg#7ncmO2lO4gNDYs359e4~yvn zzz|5&%IfN)o!JJbYzl4&1j3WtCl5kO33y@8>j;6e^70y3H-k)o->3Y1i?ycFLJbB{ z35mk#+1XN@j-fwFqTc&?;nC4x4wX?)PtP`XeOu8TOw2FajhEyDK^ijH!vXB~2kcEcVp>?qzjS`*j`;yne>HZ(qmr?D3@g=&Y=O^3q7gYt(;yXJoA=7$pZnXhAZf;;BYXg{jg@LZ4 zlU4FatD%Tfqmjv3T6A=Ife8rl!z*GpBd7(gMvs|!^4~&>%Z+7;Mx*br#XcE0SsOSm zRL`oo1?)IFItgxr0AF)szkh@H+`WnMhPBpv=Y=c*DcFHvqGZ5qzhN;m9V}81NEf!D z$k6vYCL!rg7jmFqHgkeo50_0l=4);EZJq9^bZfwh@^rXLMaoPAeFs3%VCe1jTU1PZ zM_ojVK^kq|ckL*x*>jpBlkHOM97`kg!4o zfVp7Mkctp|{`|D&d3TLqHs~nCB@g1rxga&x#mS5teNT8mtHpY(NHe=_Hv1`n_s zgv66u%=d6WgD6JtPS4LRO|ID}!iRpiEOWC9EZk`L?HM8EnikituY?Q?468BOuQKR` z?ce%?nwxs&$gcTEo^?RQ+o~KD&=MALS_s221Bow*nVI?MWNRA2biEdND}wdH*wb-m zCzaiHczAdOG&T3am%Fzu2Q#u1c( zwL!acpvgR=5}w=Qxvv`>7nzQZkpkhP9+$s~CO5}RZijc7lK=q?j=EbHJsdJS^9^ta zSibZQ5w*XbLW>6|DXR;Ynv~Cs=KL-or=ZZn0n&sWFI8(Qw7a7Q=^&dl@82h-xgQ@N zXQ#@{PCp|2^uU*rh4*$4>t_O9(4NH)ygHhki8T#qd_U0So=US5Dj6_W5*I&e0mN*e z?||~B7#QmP1$hdHiizc09}9>i!eGoc0atmZ?HmzRwSS6=zW#-q8sfbP9D$o!dV`2) zcsLK9KOYir_y3LDF#Nw-fPkaT#Cw37{UnmNVSj>|xp~aiy;TT3IcczmhC2Xn57%do z-URq3Dcl5mT+JfF0Y3t>>`4PF^5K~c>yOR;$v0AXKol2CI&@RYGg^L5+#9%W3J%r$fH5vNkJzN zZ{T$jPhG1!S}%JCced8MX8zD-RJOrk_IMqY6&RqI@4A|>8Od&9BCXic;)j2a@su9mF=?@$ZG?0?Z%c!dy9*kn{2HwIpaJ&Y(lQAb zn9$Sf(a4mn8mnNrfB%EUd&kURNjJ>Ge7nhflScdHexF9Bv~Q9X+hdAvw_lR+w>QuW zTa)>MN>zRm1ef$W+Fvz2Ki)XtUc^gaeej^;25s_0s87OU$9@%5ff+Il_R~#Xe`PU< zNO~#!m@@@BI=U_?zr!b!#Sec|gm;Jy9yHg2xMVKCkHI4}GR?hPBhzgS_U);dSl9i> z3(WJ)GRDn5MK2uQkUW+{FU+&AvWj~|bXB|O{GS_djwUlAvkf#yL= z(i+O2fMje5hMu08QJ9{cmIuf%GG0US+5sR#odcjYIAT9QkM{I%G6G1L#H3)u{tk$C ziB`_P=rn0#BxhlfQQ?Z+vYpP6w|)Qbnc7VhWW^Eygn|OyLC4SA+dI5ggLGzU%2`B2 zIXj6XQ#14bP4h`*D%r99)M`j}~b2;wkscQ;$v@yNFMM+@=2 zdv#IYzV*4#qzQxSE<7wOCZF>@#Yd>7oLn$Bm$(57kO>wubp8H4b+kPz0{x)COPiVZVwqBbj((@Rf4Q|K1Z~IG&ugp(H?L}}G#p-%r92?)9~^|U0baHCXQE<{y7(VF zAHr>{tgKW5ouS5MDAuR2`D7F8jDi|Lt?UvwVu(! zFCu>ap_>~UQd?jNhV>w`lRVFCDQZq!|dwI52`?B%z@lhpW5-6p)x+C-` zs!oTi^-!GcAG& z-ZSGvwY(`r^yYvzZ*_MxOAUx{XCa1vga3HB`-}Aabu!O*LuDnI^z|h)#Pa^rCr_04 z11`NN@7%fblI#{OJw5U3WbW`mI|eXHBD4u&pC-j-6l)jGJTA-&17*1m7(&Up!d`R> z*^WJ-3Q?*CONLHtLF-EwBo>EK(B1-l1>dvDdI$Bhx>Ok_lRpu30=6Xdl0Whr8yjW8 z7y-DiBOaIq|AC7szNem^C)Vuj>~+Tz8H`NJrtJX^Q%!2=naaw_=SS$tKr!!;JObcG zkBLwsqEF7Y8y3?X7FrrzKrmgClbv}?6r`#x1PAt#p~VAwsSvRKs5Y1E=AWx zxYJ@xP{)fx4QZ@fgdCe@hP1bZa|##KS-3e%IvYg=zB-)B&5TdN|BxdYx~*V^TX?acXa+XJT@=0#~{0i9Lum zBD+;(w>_gzFJ$l4F+cDOIX|%74`$U13iI+(qFF?Ex{EN^k5x~hXT6Dwelngrw!o+C z?DxMXA&4@vaJk!UrNexD)vXuD*jlGWX`UpdxLYrWKy^94xw%Q-Zh@Xi@;E;BR+o=8 z27xzuA?&&O0KdlkI|BognKnqb31IY);!%U$>@h>QT&EbS#kkpHbkOuirS0A4CpWGoAoQNf@{(6|>F z&VQXe1;&s@YbWuRK0Vm2tbD$qSqL zRcuTC4F6<(bRk{9ndo)8o2{A1r5ktm-d&yBU74}{sIaOa{p_%#v#?n9P$8SS)16b7 z<;&`1g4?%K^@_C~X@aJ6?V|QTm4>jrDcBR3AW5CWvqurmSp90OE=wlr_8De)v(~wZ~K$-xmw-9>~ zI8vdOPLm8bs@fT=T7F%n^l;byXb>i0N{2ConI}~m zlvJI$7JW2ACHnf-3pGmwq322htFzfbW04X;EZ(8f${P*YJ~jz zFVnJRiTAd*@1<*E7AW!WfKZl$N4#$kv|wD%Rmz=%`kQ9H3YD69A{_J=O3dY{2;QW`mTpyniI1l-Xa4%4V5?1MPw4C3(~(<%p9n@ zcdlcGxXa9}?vwjDAOBx-Ul~;P$L+dK0t5Mto4H|cc83D$IIkL9D8J_aJgTo$iv zq-~6)@A>@FQp@BvU;1wf&R4-WUEAAfwCb=0$p^mHt(1#kzB`zw`Gk2>g|>{eGgbm~ zGltcTe{X-^N3h04z+>~mbX?s(#zTShHy3z8Z9hL2KvFTT1Dy(5cNMfs&Is{()pg|gkGW{Oilz~Wkh$X) z5CYI`et;vlL1h(p#2z`#``<%D+7Y~CiRrb@$ps_^Fse1)Vsnx&cVi$b_< zx|IL-YVM%FV@hVoj*Mfh{hvz)yj$|9kEGtP>xljz9b$|o^}~e{F^4i&Z=>%$6)q43 zq{^k=l=U9}*b!rL^jFxZk?N^9q2Fi%NFnmyadxh}GRG0YWwL<)TA{WW>&*+GL*Y^+ zy1}{}r}tSM<15ZocD~`!m#<3MPTg#^AD?itE4;PEL$nmX)Z*r=r`L2;IMl4IPq5l{ zA&>GLX82c($5%$@JJyYZ<}CxX3|ly?@;o?3jdi=YGTZ`Ru-YsPJ6fIH%62(eH&bp& zm$kCLsx`3kezA{n@~N#w)ady_W!~%k;>cb1s$KOc!;77v%(Z=#KthXg+0N*>>t1|1 zTV5?S^15qc%dm0h>4A4J`-jH|SG z7QdGpsFdQ`F|b#8@kBsw1T?9FM=>T03`jbDGl___VKjZA@nS%L!Kk1#%(_oAaE`1iRV!yC;v<1(>9 zpql0ea+0inl->@SfNr~Ni1-2bO(4>C4gHX5_mKN`)YvdDZv8vU5{6`-Jpnu?9XPz? zQ4&6SUe3M$H}~G6B@iu_(bL74%1{Q=>z|we{?k{0NCEFrQ)8TMN&YO5hyTum3q+fN zcqT+Zz|4J$78}GM5ZPhW@8};HaZzouSEAtG^&CNw2gntCDN6DxJzZZJlPE75U`@KH zKJHSFwB_?5Ggx%q(-r>6GeT^_S@(2?Nw3b<+2U$o4Z;2=jYI7f(^977Zb)~uWGnP&e!z4dwq5#dj6f)=h=wi%q08j zEeFMSCN&3}N~Hl?uiFW*pSji~mBf{x#9d`8ZsWE%k(A#~+Z-HKHPV#x44brK;4oO< zCvR^ljXTO#$>A8aI3~cRD}sOq*SuuG6zC6Crigd9akW@*39#Qnf$Mzd&I8(TsdPVu ztkY{~5*ePepo2+9Mzs`hGbeZs()oukY^_%PSq4(5PY#{C zuUTdKt*)bzCySo@-->05XgfT+C*Ou!i39R8SPZ_P=`ncn3LWTd34X+6g3guUN5uaa zQ}mobFAo2!!$+^9|E3FiYJJvXHSJCmV(al7r3<2!R#Z?JIyrzM?&?J>p66zH>gU_d zv^gKVy<1Q@uG_uoSD}Hy8<(hcz_1+-H?+eb*_s=9f{fL9PBJj@sGqW)LH+Qa4)r1p zATw9$q)$>l%1Z`!#c}00Lhh7sa15A(I2|2bM$CN9VE*t2A0Kkk!vlx|DBYg89(?@! z*U8N0P*zeMEuk>(09IumXnB>XTki1NU(Nb5RDhIxeoQ4PDM?UbJvqU{!m?gN^iWq< zH!SR)yvm=4o5NLu%K)`jFg|N|_DuXjyIe0-o#u4dR2JyJysINk_0Qgqa9i~n)hBfe zu9m#%NpY#@hlsxC1TZ|^7?EwcL_ct%EsY)R${NnjNbxsF$w&rI?Cq|82@Vdv+n}-4 z&h{Q3%Hp%JGfUwh&4IMmsKqX7P2}U}&mA0v53|31z5gD*DIXRZ`YOd-WCy)`7Sap@?E4TuD7)YQIM6eI{>kpg6p zIoTQ0FC?4%+w4?ON1nA>^16w@gpN@mIM>Pam_nqEB<1BR7F$AgT3RHx9XU)@wxFj@ zdRp2eM@PqB`SE2HDV{%e{NTdOsy!>>d4V}!9U<#-G^!-oXeq36*Xp81?%2h)d$`L4J|ES59YRKGpio>{+#|@>lbjA zmh!##ZL1NA@q_o+S_~uIg zr@hAi&PSg%q_I)#dtF^!oJg{lLY8-MFhPbE>%GS2=4(N;LaZ|K@|jj6)zNs!H^)sK z3Uhk099SAGJAYw1hnS|}WxHeMx>$nT80 zh6vj$lg$547Ck0rCb`?#TzCD(jd>RT&&3_>?TUbW%E=9!GI9?v+x4a`GCz5U~R&EUw3!0ZVL5urKF@J zx9f<4f}(gRAfRKTjUQ04{SaNx8+a5#=RH025+3_|TldExZ~eTpgTg`TyM6Ibiqsx9 z6o9gX-(hb_e)jbAyQ}%+@z4;HY-gvquKUrvACtcRAQ2(aJv-U3=;-B{rK#Y{2tk>} z;L_aO92k-mj?pc+Kz~*2C*39~ti64G|R;oktL$%QM3xBMA@JM+bSGw)A|0 z%F6h8c`NtFDr{E}sA_gBg&^?NbjTatHRvS)tCtfM9{v)K>S_HpC10twgoFf-{fbhE z`5rSDm-2W=hm47d38QL%s}Xy|DEQeLnwrEb9XcsW z?`vvm<{*=7_Y=DVT87s_hjRV>gMETmuGyA-|86c|d%UN}8-G5a#Nhuc^roNK@7{sP z=;#Z{4_xtT3Y;^ zkSO0}U`R6<`IGf)f?nLnPF7V#XM-J#?U@MTP+D42M zZ%wVNWT#7|zP#_%K2Hc}WmV1Q3h02009uWP#I3sOOf_G}7#jz>*luEBWrca8dSiK+ zDLXee4HHpPRn58N>-|RQ1XKhlN?s+k{+|JB2%-r}Co0ZY1lHXN0>5yRX)^aKr6grI z`S*6a!42d^aO!-1N_~U3X4JzjGA71c&~4~jcGu|I`uggLhPC5Usz576#jx{oFRy3W zmI_(n1HXs9-asCKua@j3T#b@W4EH*&Q`X4B z?5v^-Ku&?2Khekz3i6|-(VIbe^>_fn;`@OvUi#!A2&FW7o;RR8W zw+X6AjFy*{?ys(_NW$$2rz=cewNg+}_)-@yaMJXvu(tL*V7WGC#hf{;0lbrsxVYwF z@wPtwo}W<~X<})(EBN&G{h5oWP|<2@~l^;L3Tm@R|Ji6Ga7?e4VO=?;Ek!q=_f%Wz-*Va4h57dkhOl z5ag==Cp%dgnMMRLHa51F=^w9fb^MqYAgeRT-2n?KLbuoScm>Z#B*Z}{Qz47cN9(+a z$AZXzUs$#5YUOR{v?asn&o96qD8`~0c@=4sbAOH??ez4Dqjy6`J$1?-ZEepsH)M9j z9Sz&J1V1A6zq?$bPj-Osn~D2)jXoqiTrAmisZk2rBX zZ!stgS5{W0?)Ct?T(}r>1R<+d`S0!1G}XaZ*uZO~gtQvXt*jh3`4oQp_A;-sG6oZ= zpWT)WDk>rec=Qh9rJs<%0&$Lpn_wa~5*!tksiUGIPP;ryzm!FL;NUQ0TXz;oO-pM{ zQKkQ0R(=_piiz3p@2_Or!45T(_ngrn>A&Zr#EM#?Rw8 zzZMf2lX>y@VttHfaiaG#1O*6qFUNM(b3T(i82XeWbgnCy)GeBQ&YbWnm6l& z5_l{kH8nLUZA`VsG@lguVqYHI7_S;Uf`2|hH!ZVstG*1YO~YgJWrd)blslwR?fnQQ z2Il*BcG_B66b>6h+fR?0Zg4o_2RX{Al9N+N;ows!aeJPhzOi_aQyDX&As~Ite3+bC`5~2RcrPF>HAITIbl9mWrlktcm2C zH$n97@pmVym$g+@O@$1bKAQ@p+c-K}_xATV*YfioU$&%<4~>i@Rb*kwqwy5`X4I2n z$Ol|Wxn56-?L*Sl5QQv;og=p>c}Ds3rlFxPj$6NzfAaG3suQzU_%tu#5A^g@+=A(M zMpINrb`T_1?>R?yQ0FgmTP6s^P4Bxz}xBE(m}wB5Xly~S|P*FfKjwdh-MIF`uQ;^OeVsRpbV zG3HB9bGh_cR~KQyP5O>9tKlC9ySuAj4PiLNrM&gdD(wTx6cm?X6aIAN*)x%=J}ECt zlo`XG?F3ME#KcgvL7e%E-rY?@>8QP}J~$0tv8>i=#NrK;w(fKKl9%k=K{_C9P(jEd_Njhh-epv}tt@k7MY)AJ&G zA%l@3L`>m24d+L;sP^fV5(});(^GSjJH2}v)!zh&JtDHyHD1LC@hhP` zK0ddIWY)?(B_D<~_R4JCr|-E;B=(v4aW zn3ZprSB7GwB7vCP5twYmyiBM`!R){ zKuZThaY;$Tm&b;4+E}@)t8Fx;hYlhC0VDqxatF@3^J5DdKe3gef~()~Xr68zJTD5m zl`hwwf4FH^r&O{#xk_tS7$L>r?S67ND)C%g@Ls6orfZ1m0jk=?!n2Mhio>*<7gh`# zcZtB<)xre1X6gRY{Imb&m%mRGu9=-&P86lm)4!mtSM(-3xCtU?1?{18Qb;90CcIsx zk_=mJ&>0&=gp{(lghZWJ-ERF^r%7kr5!F=U7}oU@r)lKc&u{ppuq@)#-*USC^IO@J z?o9)-5Bg*u_Br6l|L>oMB)-CXHPqX?GteG);tvTEF$u|kuF`!&Ly|g0CIML@$$v*j zdSWx>6WLEvH{kv>w9DT#P$6F3i;4re+N+(5yG-Nh0_cn-t1r{l&05!5?9R=jp4n!X zl7dN$p?F_)bsXA}OqjoUFgfLqf~z-I^;IIzk8e;eIEw5b~UU_x@XY z`XueGJynr+!;s4q99L;wccpz=|KnTGbrD=QclTw8Wjse0`2+Ox6bFf9z5A>Y;GP=pD*Kg(3%)WVQIUY_0Hnd+f9>@>En*)*0gr~a_)N#8(j69H97Oo|?4-}a+?+E_fvFHS z$Gp0gf~-8Q>FuN4;~f6$Cj@_=rIHB8p@2ZN5_)~-P)3pl+ zzQ?(BQ!uRr*#Xm$|280 zLxUnOIr*)Tl@+Uy)0A)#?5K8|_(HF)Vq>Ogu}XlFykqcs-`d-DYVVBg-)XG78VaWdw-;uff$8cE->(iBeexwO@Mld zd0aq={cL541?~D+NjoUI0|-F{_@0fFHSRj{4YV6C!D~P%_*(5*M_WX+c6(Ca^3L;{ zH|3x0*VfZiJDt0|baHYcM1=o*B1u%&)Kmq9>j>mVe2_IUw9KIt3=LB!N?9}sm7F6b zB)X}V=JpI4Zt2=lmah#}^a-)Cbr;msXlqJK=NL@H@ia>u9S>k4aVN9*i+qloa#N@Z zIWf#TYwr>C4*f!L3H#)@85#2yn3#LC5SblFYwt=cp@0&tDBt z7!+$i@1*K-tI*oOKny4X9Q02H@?u?hZ;=h$a{5F7^HMcmD;Xr&ZHUgezp{$TLDS;t zbA7sstSmoVv8nlzj;2iD*=jf+AS;4 zKu@14M=l_*jX*=i4gv8o;5|muKYZe<$Hs0&vFf-jZ!9owXiWwQoxiF!Ssz(}3_k*L z@TKN^40bZn>gpP$iBCPMZ_l2e<-#M3;G3DD0?P*{27*R|($d5%AmVm_sI(sr)}i-^ z?323ff|FF5!VKV5G{4q|Le6rNgbzn$W2uylRg{#zK7k5!m{?e`2$G$h!xcos$KBX9 zk>EJf=tmhC7$^&x!SjIE+&2(CQ1jz$Z*Q|Ex*waJ*V1}c!+Un@vfQiZC)T@qXsE7E z?mlsPq-bAUq6se(XyfWlinu5#DT)z<+j7i_2+?a;Oq?7explaBOV=&rgl>@tLFj)&6efsHC}D)@s;uVR96k0spG-DhM>%+m zDV}%?m9df1iN$bO46MABlN&_Y+AccEDk>_@8d_S$Puw948T{4cp`)$6{wt|(x45|E z+5yOKo`5L1WG#QK^uS?sCNwl6MnmJ$v|?r#1sBRFLG2;8nS}+%dYs&ykf*S-FWad$ zedvs(zl7PK1nE;P<@_oocrPe8ioVOivZC2>blB+30uvJp($4+;eFi@;OhITD^Wa0f3As1~gC?7EuT-%*nC9g5V)rL*Lw-S&f=V@gwpZ zkm!3l6B7e<7w1*Kp!w-1wBT%Nefzf;=N1sCS+Rcd zx_rBG?2z@Zjl%)M=1qE5*1xB_uc4X56RT6puG6Dhn8jG4@80VI=o495%01cGm}cpf zWtLME))%(XR^f7186nAlQrkLO^ao1;Ghp0ox+M;Ima?+4-0dv3>e4*-S0zjkdFY*t>WnVI*L)1g|MOmmF}R1jPr z%-7u{Yxv~vPr0S)R!xU^y?y)46}ABlTScNgTS|!lieHLnRmExEbW!bov+v)D=akZU z3o{V@cL@_iT~S)vfsLips5k8eNqqYony24h>|29aaqYF`Fmx~mu-XLAcX@d_Bz45v z#ALt(WR5wxxoZw{mk~YC8Pd^5 z;$UY)PzT%y$B12>46?%hEX$*KtlT<6!s;(F3yQWJ z9FIkC7dqo(eF-o*6#HZ#*;*Eskiftc7NC|U_3xiu8m=0XlI0S?&4Qy?9`Q1p7p+*6 z(}~^0#U>?s_by?q%F|O*+x;x$Ys3RhGtLoO*7uibV{g(-Yz3w~&Q<*vR<;0K*$VG? z)YkznPR?MphZ|p3L~x&hovXPrARu6-ZN5DYu$39zDAp_}D>#_TT}YN88#@4KK7a>n zS{@Jp$!00N^YxXeG9QiHthLZ8a^X60L9w5;gwVS{r13eev5~OfgFSj|T^Ur9Av>!* zsSB#Cx*i4=7Na;w+wayeUx2?d`}o9X?d`V!0mC*lF>$D~-~*N~ z?50SYnCO&);dq!_ZHw7F&`2A-`YB$u*5*Tii&nm+34?}l-$>fy)9#( z&^~;~Du|lyO%I{MTilfhqP^gu=N~H@6xSc(n{q`%Nj-o5h_!Tg-vf21gH}1nGFiGi zIv5^A6wIW7+e8t}4KK3KFyDG1h@P==x*7Nf&t1_Qu@8KyIbPg&SA3mDqrm( z;{|Zw;_+p+6FO5Da=AUFgphlpcKVq~ zhBE+4SCA=S`b+>`Uz?rno5gJcKGvdcy!`R`Uq2#`%lOo&4b~fPg%NFSZ8>$gw!*mDz_toj&m5JkQ!o=Vyqt~ysx(o^#YSMT2_8yuP4RdF^HbaAP1CV%(SpI&N zt`GrGba(H@IOV+vDZYFx7!a^Pv}n-;F_ZegfPDVbFo)^Fg~j=47(mU+3L0(L$W!N) z@KtC;o`-?Bxx2M}6f-#aB^_0bAaG1c<^FzpC1Y?8u_tyPQqdA@sd)9r8{U(9Y(EnEgIBXHnV2998fQO}DdjyT8T=m&0-6L~O`i2toWh2Tb_=KYz~8p3+%cTUP`I zf`HcPTPXzO?>bG0URzr7VSWQSz%t<#>~BS7QGn@&d@m|8b3I&}K2T~7pnO6Gd@oUYCxE^e9y#e6sE&=X+diwX|g>Urf-)?P~>VtOn`*YZu35kj4%=OKfy1Kgu?Hn8y z4kMc(3jNN^$arzGKI)4$01bfqR996qC;27wl^0j8n3ZjgJwBl3TFGnX_!1kt3+7u< zDk`cW9v+?y$g0@DJ~xDmsQhZ26MLvbMfS+!REda)$jauw9T)HtTPTSf!w(BnM<&-7={lQGzjSlyZ3O z8Ej!s;CcDr(v`a}c2NnqpS=s7nu-GHL0-P-2P*e35hVsap?6cEWeL-JIX2aPTQI7r za#7?pjdup@<_jVSe`{;B=p91M#g2rt;!I9Op)*dti{YY!g47gN#rxsrJ|f947{jV+ zbn%e-pX2WK1E~@cQO%9~8UJNDLWZHp9Cjp=z-cx#drux`uz%nj$`)^6yuBUKYLxot z&mX+NtsQgM#AV&Z5@0So(Rivk10>q8sxsQ*SArX-e$V=V<`^8SnpQNOsd=oU;#`M)ScZu26 z{Rl+v-o0y-CG!D?m*)k6$k*)bPav_sk9KY=5c;KIBMgAx0a8-Dc{;vui|>gWWF9-LYWULfWSq;#e06Fpx?B8b<~O#NWyB<>uu&fk)`HRbQXdW2B4? ztgUiMBcOt@B*GFE4GBx7FR*ZqIzMiY>|V_A!M=P;u(NxR<1srSFE7vbQ!2dugLppD z#1B3s*%{4Zd3kBx-q^kXLAo=BG3{+{Goc(%GfWXuFe?LFA=j7o5qU8IH9obR>7>~ktJX~=-+|=xf6FeIR?>jv(rLMb80j8C! zC#y_90-36$x3>pCE`O8POo9BiH<(lwUs;;99zd0Y6}K)U6b}`zn^UON+nszA9z}*I;f^ef?o+R=ZFJ(|7i@kJaXaH#%^0_4`M-E4>P+bPQ~kLw8Y(>Zt&#V#|GTBX zQ*72}1X+yrYt?kmn*25WlUQkG>H;!*j6gcwO5j$PVxx3`=8Z?Q0Mj?pT6mALe0-x?qD+-(xj+ALH8?DKfN9Jat$3=E9C ztWZTh_mDrKZ6h!$+fw@^wXM&%#&Edrpf>I�SSmPOghLcK3`9hXxM$4rpDvjw9v! zS<#><9sl!>`m|tO!6o-Hzn*r%!~0Z{dTrgc27jrp74$=uPTMnAmwG(`0X$m0`OTB+ z+BN?HSmy+ftGbpid`SNLuWZWtw5svjg)&5TkJeHhHr9ss22*cdtY_$v9bDWzeIRoE z=OX}3JwAX8;s2LU;=;IRWuspDYWOYKKghVaN-SXC?ae>o6|4~O{O})bpM96tLU-tP-SyIIU}p3zwp(H yo5-!U_GG5*ElAk8iIF56_}jZS+|PaNoHj~Xh~gvuwMYd1ym&4rmM^0B?tcIa;{0;} literal 0 HcmV?d00001 diff --git a/local/recipes/libs/polkit/source/docs/polkit-authentication-agent-example-wheel.png b/local/recipes/libs/polkit/source/docs/polkit-authentication-agent-example-wheel.png new file mode 100644 index 0000000000000000000000000000000000000000..be6e982845e688044dd0a30d33291e5ec1eeef67 GIT binary patch literal 55334 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�Td9u@>IO9 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 V!<_3t_b~tf002ovPDHLkV1mpE*1iA$ literal 0 HcmV?d00001 diff --git a/local/recipes/libs/polkit/source/docs/polkit-authentication-agent-example.png b/local/recipes/libs/polkit/source/docs/polkit-authentication-agent-example.png new file mode 100644 index 0000000000000000000000000000000000000000..c5c5131b921ac2fdf5c4ec46187e7520465b4f22 GIT binary patch literal 46245 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 z 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 ghu2rbL&$G_k|Y%iPwLM20Py3x literal 0 HcmV?d00001 diff --git a/local/recipes/libs/polkit/source/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.AuthenticationAgent.xml b/local/recipes/libs/polkit/source/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.AuthenticationAgent.xml new file mode 100644 index 0000000000..ab27b2f663 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.AuthenticationAgent.xml @@ -0,0 +1,141 @@ + + + + + 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. + + + + + + + diff --git a/local/recipes/libs/polkit/source/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.Authority.xml b/local/recipes/libs/polkit/source/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.Authority.xml new file mode 100644 index 0000000000..2dd01a0cfb --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.Authority.xml @@ -0,0 +1,960 @@ + + + + + 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 +} + + +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. + + + + + + + + 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. + + + + diff --git a/local/recipes/libs/polkit/source/docs/polkit/meson.build b/local/recipes/libs/polkit/source/docs/polkit/meson.build new file mode 100644 index 0000000000..7c5f5a09f2 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/polkit/meson.build @@ -0,0 +1,36 @@ +doc_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, +) diff --git a/local/recipes/libs/polkit/source/docs/polkit/overview.xml b/local/recipes/libs/polkit/source/docs/polkit/overview.xml new file mode 100644 index 0000000000..2fa55bfc73 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/polkit/overview.xml @@ -0,0 +1,343 @@ + + + +]> + + 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. + + + + diff --git a/local/recipes/libs/polkit/source/docs/polkit/polkit-1-docs.xml b/local/recipes/libs/polkit/source/docs/polkit/polkit-1-docs.xml new file mode 100644 index 0000000000..ad1f5beccd --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/polkit/polkit-1-docs.xml @@ -0,0 +1,84 @@ + + + +]> + + + 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 + + + diff --git a/local/recipes/libs/polkit/source/docs/polkit/polkit-1-sections.txt b/local/recipes/libs/polkit/source/docs/polkit/polkit-1-sections.txt new file mode 100644 index 0000000000..e7db6e377f --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/polkit/polkit-1-sections.txt @@ -0,0 +1,373 @@ +
+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 +
diff --git a/local/recipes/libs/polkit/source/docs/polkit/polkit-1.types b/local/recipes/libs/polkit/source/docs/polkit/polkit-1.types new file mode 100644 index 0000000000..6354d125bb --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/polkit/polkit-1.types @@ -0,0 +1,21 @@ +polkit_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 diff --git a/local/recipes/libs/polkit/source/docs/version.xml.in b/local/recipes/libs/polkit/source/docs/version.xml.in new file mode 100644 index 0000000000..d78bda9342 --- /dev/null +++ b/local/recipes/libs/polkit/source/docs/version.xml.in @@ -0,0 +1 @@ +@VERSION@ diff --git a/local/recipes/libs/polkit/source/gettext/its/polkit.its b/local/recipes/libs/polkit/source/gettext/its/polkit.its new file mode 100644 index 0000000000..1c37e6bee7 --- /dev/null +++ b/local/recipes/libs/polkit/source/gettext/its/polkit.its @@ -0,0 +1,8 @@ + + + + + diff --git a/local/recipes/libs/polkit/source/gettext/its/polkit.loc b/local/recipes/libs/polkit/source/gettext/its/polkit.loc new file mode 100644 index 0000000000..c7427ec672 --- /dev/null +++ b/local/recipes/libs/polkit/source/gettext/its/polkit.loc @@ -0,0 +1,6 @@ + + + + + + diff --git a/local/recipes/libs/polkit/source/meson.build b/local/recipes/libs/polkit/source/meson.build new file mode 100644 index 0000000000..2f81c907d8 --- /dev/null +++ b/local/recipes/libs/polkit/source/meson.build @@ -0,0 +1,430 @@ +project( + 'polkit', ['c'], + version: '124', + license: 'LGPL2+', + default_options: [ + 'buildtype=debugoptimized', + 'prefix=/usr', + 'cpp_std=c++17', + ], + meson_version: '>= 0.50.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_h = 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_h.set_quoted(define[0], define[1]) +endforeach + +# Globally define_GNU_SOURCE and therefore enable the GNU extensions +config_h.set('_GNU_SOURCE', true) + +# functions +check_functions = [ + 'clearenv', + 'fdatasync', + 'setnetgrent', +] + +foreach func: check_functions + config_h.set('HAVE_' + func.to_upper(), cc.has_function(func)) +endforeach + +# compiler flags +common_c_flags = [ + # FIXME: this should go as 'c_std=c99' in project's default_options. + # https://github.com/mesonbuild/meson/issues/1889 + # https://github.com/mesonbuild/meson/pull/6729 + '-std=c99', + '-DHAVE_CONFIG_H', +] +compiler_flags = [] +compiler_c_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 + +add_project_arguments(common_c_flags + compiler_c_flags, language: 'c') + +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) + +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.') + +duktape_req_version = '>= 2.2.0' + +js_engine = get_option('js_engine') +libs_only = get_option('libs-only') +if libs_only + js_engine = '' +endif + +if js_engine == 'duktape' + 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_h.set('HAVE_' + func.to_upper(), cc.has_function(func, prefix : '#include ')) +elif js_engine == 'mozjs' + js_dep = dependency('mozjs-115') + + _system = host_machine.system().to_lower() + if _system.contains('freebsd') + config_h.set('__BSD_VISIBLE', 1) + endif +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_pkgconfig_variable('system_bus_services_dir', define_variable: ['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_h.set('HAVE_' + host_system.to_upper(), true) + +# Check whether setnetgrent has a return value +config_h.set('HAVE_NETGROUP_H', cc.has_header('netgroup.h')) + +if config_h.get('HAVE_SETNETGRENT', false) + setnetgrent_return_src = ''' + #include + #ifdef HAVE_NETGROUP_H + #include + #else + #include + #endif + int main() { + int r = setnetgrent (NULL); + }; + ''' + + config_h.set('HAVE_SETNETGRENT_RETURN', cc.compiles(setnetgrent_return_src, name: 'setnetgrent return support')) +endif + +# Select wether to use libsystemd-login, libelogind or ConsoleKit for session tracking +session_tracking = get_option('session_tracking') +enable_logind = (session_tracking != 'ConsoleKit') +if enable_logind + if session_tracking == 'libsystemd-login' + logind_dep = dependency('libsystemd', required: false) + if not logind_dep.found() + logind_dep = dependency('libsystemd-login', not_found_message: 'libsystemd support requested but libsystemd or libsystemd-login library not found') + endif + else + logind_dep = dependency('libelogind', not_found_message: 'libelogind support requested but libelogind library not found') + endif + + func = 'sd_uid_get_display' + config_h.set10('HAVE_' + func.to_upper(), cc.has_function(func, dependencies: logind_dep)) + func = 'sd_pidfd_get_session' + config_h.set10('HAVE_' + func.to_upper(), cc.has_function(func, dependencies: logind_dep)) + + # systemd unit / service files + systemd_systemdsystemunitdir = get_option('systemdsystemunitdir') + if systemd_systemdsystemunitdir == '' and session_tracking == 'libsystemd-login' + systemd_dep = dependency('systemd', not_found_message: 'systemd required but not found, please provide a valid systemd user unit dir or disable it') + # FIXME: systemd.pc file does not use variables with relative paths, so `define_variable` cannot be used + systemd_systemdsystemunitdir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir') + endif + + systemd_sysusers_dir = systemd_dep.get_pkgconfig_variable('sysusers_dir', default: '/usr/lib/sysusers.d') +endif +config_h.set('HAVE_LIBSYSTEMD', enable_logind) + +config_h.set('HAVE_PIDFD_OPEN', cc.get_define('SYS_pidfd_open', prefix: '#include ') != '') + +# User for running polkitd +polkitd_user = get_option('polkitd_user') +config_h.set_quoted('POLKITD_USER', polkitd_user) + +polkitd_uid = get_option('polkitd_uid') +config_h.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_h.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_h.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]).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 + +pam_include = get_option('pam_include') +if pam_include == '' + if ['suse', 'solaris'].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 + +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 + +configure_file( + output: 'config.h', + configuration: config_h, +) + + +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.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' +if enable_logind + output += ' systemdsystemunitdir: ' + systemd_systemdsystemunitdir + '\n' +endif +output += ' polkitd user: ' + polkitd_user + ' \n' +if polkitd_uid != '-' + output += ' polkitd UID: ' + polkitd_uid + ' \n' +endif +output += ' Javascript engine: ' + js_engine + '\n' +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) diff --git a/local/recipes/libs/polkit/source/meson_options.txt b/local/recipes/libs/polkit/source/meson_options.txt new file mode 100644 index 0000000000..71588bae01 --- /dev/null +++ b/local/recipes/libs/polkit/source/meson_options.txt @@ -0,0 +1,21 @@ +option('session_tracking', type: 'combo', choices: ['libsystemd-login', 'libelogind', 'ConsoleKit'], value: 'ConsoleKit', description: 'session tracking (libsystemd-login/libelogind/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('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', ''], 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('js_engine', type: 'combo', choices: ['mozjs', 'duktape'], value: 'duktape', description: 'javascript engine') diff --git a/local/recipes/libs/polkit/source/meson_post_install.py b/local/recipes/libs/polkit/source/meson_post_install.py new file mode 100644 index 0000000000..efb75a1de6 --- /dev/null +++ b/local/recipes/libs/polkit/source/meson_post_install.py @@ -0,0 +1,69 @@ +#!/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, + ) + ) diff --git a/local/recipes/libs/polkit/source/po/ChangeLog b/local/recipes/libs/polkit/source/po/ChangeLog new file mode 100644 index 0000000000..e69de29bb2 diff --git a/local/recipes/libs/polkit/source/po/LINGUAS b/local/recipes/libs/polkit/source/po/LINGUAS new file mode 100644 index 0000000000..a7813a38a3 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/LINGUAS @@ -0,0 +1,23 @@ +# please keep this list sorted alphabetically +# +cs +da +de +hr +hu +id +it +ka +nl +nn +pl +pt +pt_BR +ro +ru +sk +sv +tr +uk +zh_CN +zh_TW diff --git a/local/recipes/libs/polkit/source/po/Makevars b/local/recipes/libs/polkit/source/po/Makevars new file mode 100644 index 0000000000..b72f80beb1 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/Makevars @@ -0,0 +1,78 @@ +# 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 diff --git a/local/recipes/libs/polkit/source/po/POTFILES.in b/local/recipes/libs/polkit/source/po/POTFILES.in new file mode 100644 index 0000000000..0e15629a3d --- /dev/null +++ b/local/recipes/libs/polkit/source/po/POTFILES.in @@ -0,0 +1,8 @@ +# 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 diff --git a/local/recipes/libs/polkit/source/po/POTFILES.skip b/local/recipes/libs/polkit/source/po/POTFILES.skip new file mode 100644 index 0000000000..845ca06775 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/POTFILES.skip @@ -0,0 +1 @@ +.pc diff --git a/local/recipes/libs/polkit/source/po/cs.po b/local/recipes/libs/polkit/source/po/cs.po new file mode 100644 index 0000000000..066da266dd --- /dev/null +++ b/local/recipes/libs/polkit/source/po/cs.po @@ -0,0 +1,216 @@ +# 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" diff --git a/local/recipes/libs/polkit/source/po/da.po b/local/recipes/libs/polkit/source/po/da.po new file mode 100644 index 0000000000..9af1b24e7d --- /dev/null +++ b/local/recipes/libs/polkit/source/po/da.po @@ -0,0 +1,204 @@ +# 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 `$(program)' as the super user" +msgstr "Der kræves autentifikation for at køre `$(program)' 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 `$(program)' as user $(user.display)" +msgstr "" +"Der kræves autentifikation for at køre `$(program)' 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" diff --git a/local/recipes/libs/polkit/source/po/de.po b/local/recipes/libs/polkit/source/po/de.po new file mode 100644 index 0000000000..e2fea89c55 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/de.po @@ -0,0 +1,221 @@ +# 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" diff --git a/local/recipes/libs/polkit/source/po/hr.po b/local/recipes/libs/polkit/source/po/hr.po new file mode 100644 index 0000000000..6aa23d074e --- /dev/null +++ b/local/recipes/libs/polkit/source/po/hr.po @@ -0,0 +1,204 @@ +# 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 `$(program)' as the super user" +msgstr "Potrebna je ovjera za pokretanje `$(program)' 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 `$(program)' as user $(user.display)" +msgstr "" +"Potrebna je ovjera za pokretanje `$(program)' 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" diff --git a/local/recipes/libs/polkit/source/po/hu.po b/local/recipes/libs/polkit/source/po/hu.po new file mode 100644 index 0000000000..1c119f153d --- /dev/null +++ b/local/recipes/libs/polkit/source/po/hu.po @@ -0,0 +1,214 @@ +# 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 `$(program)' as the super user" +msgstr "Hitelesítés szükséges a(z) „$(program)” 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 `$(program)' as user $(user.display)" +msgstr "" +"Hitelesítés szükséges a(z) „$(program)” 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" diff --git a/local/recipes/libs/polkit/source/po/id.po b/local/recipes/libs/polkit/source/po/id.po new file mode 100644 index 0000000000..dbbb070e5d --- /dev/null +++ b/local/recipes/libs/polkit/source/po/id.po @@ -0,0 +1,204 @@ +# 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 `$(program)' as the super user" +msgstr "" +"Otentikasi dibutuhkan untuk menjalankan `$(program)' 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 `$(program)' as user $(user.display)" +msgstr "" +"Otentikasi dibutuhkan untuk menjalankan `$(program)' 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" diff --git a/local/recipes/libs/polkit/source/po/it.po b/local/recipes/libs/polkit/source/po/it.po new file mode 100644 index 0000000000..b1667fc765 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/it.po @@ -0,0 +1,212 @@ +# 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 `$(program)' as the super user" +msgstr "È richiesto autenticarsi per lanciare «$(program)» 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 `$(program)' as user $(user.display)" +msgstr "" +"È richiesto autenticarsi per lanciare «$(program)» 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" diff --git a/local/recipes/libs/polkit/source/po/ka.po b/local/recipes/libs/polkit/source/po/ka.po new file mode 100644 index 0000000000..eb8850fcad --- /dev/null +++ b/local/recipes/libs/polkit/source/po/ka.po @@ -0,0 +1,209 @@ +# 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" diff --git a/local/recipes/libs/polkit/source/po/meson.build b/local/recipes/libs/polkit/source/po/meson.build new file mode 100644 index 0000000000..f74641733c --- /dev/null +++ b/local/recipes/libs/polkit/source/po/meson.build @@ -0,0 +1,5 @@ +i18n.gettext( + pk_api_name, + data_dirs: data_dir, + preset: 'glib', +) diff --git a/local/recipes/libs/polkit/source/po/nl.po b/local/recipes/libs/polkit/source/po/nl.po new file mode 100644 index 0000000000..b30acee43f --- /dev/null +++ b/local/recipes/libs/polkit/source/po/nl.po @@ -0,0 +1,209 @@ +# 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 `$(program)' as the super user" +msgstr "" +"Authenticatie is vereist om `$(program)' 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 `$(program)' as user $(user.display)" +msgstr "" +"Authenticatie is vereist om `$(program)' 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" diff --git a/local/recipes/libs/polkit/source/po/nn.po b/local/recipes/libs/polkit/source/po/nn.po new file mode 100644 index 0000000000..b8c65f20bb --- /dev/null +++ b/local/recipes/libs/polkit/source/po/nn.po @@ -0,0 +1,207 @@ +# 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 `$(program)' as the super user" +msgstr "Krev autentisering for å køyra «$(program)» 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 `$(program)' as user $(user.display)" +msgstr "" +"Krev autentisering for å køyra «$(program)» 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" diff --git a/local/recipes/libs/polkit/source/po/pl.po b/local/recipes/libs/polkit/source/po/pl.po new file mode 100644 index 0000000000..b1a7683af1 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/pl.po @@ -0,0 +1,215 @@ +# 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" diff --git a/local/recipes/libs/polkit/source/po/pt.po b/local/recipes/libs/polkit/source/po/pt.po new file mode 100644 index 0000000000..b64349bd84 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/pt.po @@ -0,0 +1,209 @@ +# 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 `$(program)' as the super user" +msgstr "" +"A autenticação é necessária para executar `$(program)' 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 `$(program)' as user $(user.display)" +msgstr "" +"A autenticação é necessária para executar `$(program)' 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" diff --git a/local/recipes/libs/polkit/source/po/pt_BR.po b/local/recipes/libs/polkit/source/po/pt_BR.po new file mode 100644 index 0000000000..0b4b91572c --- /dev/null +++ b/local/recipes/libs/polkit/source/po/pt_BR.po @@ -0,0 +1,206 @@ +# Brazilian Portuguese translation for polkit. +# Copyright (C) 2018 polkit's COPYRIGHT HOLDER +# This file is distributed under the same license as the polkit package. +# Rafael Fontenelle , 2015, 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-08-20 21:47+0000\n" +"PO-Revision-Date: 2018-09-02 22:46-0200\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: Virtaal 1.0.0-beta1\n" + +#: ../actions/org.freedesktop.policykit.policy.in.h:1 +msgid "Run a program as another user" +msgstr "Executa um programa como outro usuário" + +#: ../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 usuário" + +#: ../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 "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: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 "" +"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: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 `$(program)' as the super user" +msgstr "" +"A autenticação é necessária para executar `$(program)' 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:800 +msgid "Authentication is needed to run `$(program)' as user $(user.display)" +msgstr "" +"A autenticação é necessária para executar `$(program)' como o usuário " +"$(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 arquivo FD quando o agente é registrado" + +#: ../src/programs/pkttyagent.c:49 +msgid "FD" +msgstr "FD" + +#: ../src/programs/pkttyagent.c:53 +msgid "Register the agent for the specified process" +msgstr "Registra 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 "Registra o agente para o dono 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" \ No newline at end of file diff --git a/local/recipes/libs/polkit/source/po/ro.po b/local/recipes/libs/polkit/source/po/ro.po new file mode 100644 index 0000000000..fe33ac2dab --- /dev/null +++ b/local/recipes/libs/polkit/source/po/ro.po @@ -0,0 +1,211 @@ +# 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 `$(program)' as the super user" +msgstr "" +"Este necesară autentificarea pentru a rula „$(program)” 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 `$(program)' as user $(user.display)" +msgstr "" +"Este necesară autentificarea pentru a rula „$(program)” 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" diff --git a/local/recipes/libs/polkit/source/po/ru.po b/local/recipes/libs/polkit/source/po/ru.po new file mode 100644 index 0000000000..b06c05e60b --- /dev/null +++ b/local/recipes/libs/polkit/source/po/ru.po @@ -0,0 +1,217 @@ +# 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" diff --git a/local/recipes/libs/polkit/source/po/sk.po b/local/recipes/libs/polkit/source/po/sk.po new file mode 100644 index 0000000000..9fdc224131 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/sk.po @@ -0,0 +1,211 @@ +# 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 `$(program)' as the super user" +msgstr "" +"Na spustenie programu „$(program)“ 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 `$(program)' as user $(user.display)" +msgstr "" +"Na spustenie programu „$(program)“ 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" diff --git a/local/recipes/libs/polkit/source/po/sv.po b/local/recipes/libs/polkit/source/po/sv.po new file mode 100644 index 0000000000..48bb770ba6 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/sv.po @@ -0,0 +1,214 @@ +# 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" diff --git a/local/recipes/libs/polkit/source/po/tr.po b/local/recipes/libs/polkit/source/po/tr.po new file mode 100644 index 0000000000..cbfc1d9a54 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/tr.po @@ -0,0 +1,218 @@ +# 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" diff --git a/local/recipes/libs/polkit/source/po/uk.po b/local/recipes/libs/polkit/source/po/uk.po new file mode 100644 index 0000000000..d1b6c5509a --- /dev/null +++ b/local/recipes/libs/polkit/source/po/uk.po @@ -0,0 +1,210 @@ +# 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 `$(program)' as the super user" +msgstr "" +"Для запуску «$(program)» від імені суперкористувача слід пройти розпізнавання" + +#. 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 `$(program)' as user $(user.display)" +msgstr "" +"Для запуску «$(program)» від імені користувача $(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" diff --git a/local/recipes/libs/polkit/source/po/zh_CN.po b/local/recipes/libs/polkit/source/po/zh_CN.po new file mode 100644 index 0000000000..c69eb27492 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/zh_CN.po @@ -0,0 +1,196 @@ +# 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 `$(program)' as the super user" +msgstr "需要授权:作为超级用户身份运行 \"$(program)\" " + +#. 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 `$(program)' as user $(user.display)" +msgstr "需要授权:作为用户 \"$(user.display)\" 运行 \"$(program)\" " + +#: ../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" diff --git a/local/recipes/libs/polkit/source/po/zh_TW.po b/local/recipes/libs/polkit/source/po/zh_TW.po new file mode 100644 index 0000000000..580017fdc3 --- /dev/null +++ b/local/recipes/libs/polkit/source/po/zh_TW.po @@ -0,0 +1,196 @@ +# 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 `$(program)' as the super user" +msgstr "必須先核對身份才能以超級使用者執行「$(program)」" + +#. 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 `$(program)' as user $(user.display)" +msgstr "必須先核對身份才能以 $(user.display) 使用者執行「$(program)」" + +#: ../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" diff --git a/local/recipes/libs/polkit/source/src/examples/cancel.c b/local/recipes/libs/polkit/source/src/examples/cancel.c new file mode 100644 index 0000000000..02a8b63288 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/examples/cancel.c @@ -0,0 +1,157 @@ +/* + * 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 "config.h" +#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; +} diff --git a/local/recipes/libs/polkit/source/src/examples/frobnicate.c b/local/recipes/libs/polkit/source/src/examples/frobnicate.c new file mode 100644 index 0000000000..29e0cbfeb0 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/examples/frobnicate.c @@ -0,0 +1,83 @@ +/* + * 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 "config.h" + +#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; +} diff --git a/local/recipes/libs/polkit/source/src/examples/meson.build b/local/recipes/libs/polkit/source/src/examples/meson.build new file mode 100644 index 0000000000..8c18de5406 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/examples/meson.build @@ -0,0 +1,27 @@ +policy = '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, +) diff --git a/local/recipes/libs/polkit/source/src/examples/org.freedesktop.policykit.examples.pkexec.policy.in b/local/recipes/libs/polkit/source/src/examples/org.freedesktop.policykit.examples.pkexec.policy.in new file mode 100644 index 0000000000..ec52416c7a --- /dev/null +++ b/local/recipes/libs/polkit/source/src/examples/org.freedesktop.policykit.examples.pkexec.policy.in @@ -0,0 +1,21 @@ + + + + + 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 + + + diff --git a/local/recipes/libs/polkit/source/src/examples/polkit-raw-dbus.py b/local/recipes/libs/polkit/source/src/examples/polkit-raw-dbus.py new file mode 100755 index 0000000000..47484b4b79 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/examples/polkit-raw-dbus.py @@ -0,0 +1,41 @@ +#!/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 diff --git a/local/recipes/libs/polkit/source/src/meson.build b/local/recipes/libs/polkit/source/src/meson.build new file mode 100644 index 0000000000..13c6bd48b4 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/meson.build @@ -0,0 +1,21 @@ +src_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') +# Red Bear: build the client-side agent library (libpolkit-agent-1) + its PAM +# auth helper even under libs-only. It's needed by PolkitQt6-1 (the agent +# component) and depends only on libpolkit-gobject-1 + glib + PAM (pam-redbear) — +# NOT on the daemon/backend. Only polkitbackend/polkitd/programs (the JS rules +# engine + daemon) stay gated off, since redbear-polkit is the authority daemon. +subdir('polkitagent') +if not get_option('libs-only') + subdir('polkitbackend') + subdir('programs') +endif + +enable_examples = get_option('examples') +if enable_examples + subdir('examples') +endif diff --git a/local/recipes/libs/polkit/source/src/polkit/meson.build b/local/recipes/libs/polkit/source/src/polkit/meson.build new file mode 100644 index 0000000000..9699fea6b2 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/meson.build @@ -0,0 +1,136 @@ +name = '@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}', + 'datadir=' + ('${prefix}' / pk_datadir), + '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 diff --git a/local/recipes/libs/polkit/source/src/polkit/polkit.h b/local/recipes/libs/polkit/source/src/polkit/polkit.h new file mode 100644 index 0000000000..bfe4c7de8d --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkit.h @@ -0,0 +1,50 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitactiondescription.c b/local/recipes/libs/polkit/source/src/polkit/polkitactiondescription.c new file mode 100644 index 0000000000..ed0655e36d --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitactiondescription.c @@ -0,0 +1,382 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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); +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitactiondescription.h b/local/recipes/libs/polkit/source/src/polkit/polkitactiondescription.h new file mode 100644 index 0000000000..c900624583 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitactiondescription.h @@ -0,0 +1,65 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitauthority.c b/local/recipes/libs/polkit/source/src/polkit/polkitauthority.c new file mode 100644 index 0000000000..71d527c62d --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitauthority.c @@ -0,0 +1,2081 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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, + 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); + if (g_strcmp0 (signal_name, "Changed") == 0) + { + 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); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +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; +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitauthority.h b/local/recipes/libs/polkit/source/src/polkit/polkitauthority.h new file mode 100644 index 0000000000..921b712579 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitauthority.h @@ -0,0 +1,227 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitauthorityfeatures.c b/local/recipes/libs/polkit/source/src/polkit/polkitauthorityfeatures.c new file mode 100644 index 0000000000..16a9125901 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitauthorityfeatures.c @@ -0,0 +1,29 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "polkitcheckauthorizationflags.h" +#include "polkitprivate.h" + + diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitauthorityfeatures.h b/local/recipes/libs/polkit/source/src/polkit/polkitauthorityfeatures.h new file mode 100644 index 0000000000..01aea78de4 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitauthorityfeatures.h @@ -0,0 +1,49 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitauthorizationresult.c b/local/recipes/libs/polkit/source/src/polkit/polkitauthorizationresult.c new file mode 100644 index 0000000000..877a9a62d1 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitauthorizationresult.c @@ -0,0 +1,304 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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 */ +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitauthorizationresult.h b/local/recipes/libs/polkit/source/src/polkit/polkitauthorizationresult.h new file mode 100644 index 0000000000..7f93bee912 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitauthorizationresult.h @@ -0,0 +1,61 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitcheckauthorizationflags.c b/local/recipes/libs/polkit/source/src/polkit/polkitcheckauthorizationflags.c new file mode 100644 index 0000000000..16a9125901 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitcheckauthorizationflags.c @@ -0,0 +1,29 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "polkitcheckauthorizationflags.h" +#include "polkitprivate.h" + + diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitcheckauthorizationflags.h b/local/recipes/libs/polkit/source/src/polkit/polkitcheckauthorizationflags.h new file mode 100644 index 0000000000..4baa0d1975 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitcheckauthorizationflags.h @@ -0,0 +1,50 @@ +/* + * 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. + * + * Possible flags when checking authorizations. + */ +typedef enum +{ + POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE = 0, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1<<0), +} PolkitCheckAuthorizationFlags; + +G_END_DECLS + +#endif /* __POLKIT_CHECK_AUTHORIZATION_FLAGS_H */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitdetails.c b/local/recipes/libs/polkit/source/src/polkit/polkitdetails.c new file mode 100644 index 0000000000..b16aadc514 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitdetails.c @@ -0,0 +1,235 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} + diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitdetails.h b/local/recipes/libs/polkit/source/src/polkit/polkitdetails.h new file mode 100644 index 0000000000..ea2b425153 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitdetails.h @@ -0,0 +1,58 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitenumtypes.c.template b/local/recipes/libs/polkit/source/src/polkit/polkitenumtypes.c.template new file mode 100644 index 0000000000..7704ccad83 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitenumtypes.c.template @@ -0,0 +1,39 @@ +/*** 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 (&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 ***/ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitenumtypes.h.template b/local/recipes/libs/polkit/source/src/polkit/polkitenumtypes.h.template new file mode 100644 index 0000000000..2ce48a65a3 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitenumtypes.h.template @@ -0,0 +1,24 @@ +/*** 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 ***/ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkiterror.c b/local/recipes/libs/polkit/source/src/polkit/polkiterror.c new file mode 100644 index 0000000000..89b900705e --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkiterror.c @@ -0,0 +1,55 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkiterror.h b/local/recipes/libs/polkit/source/src/polkit/polkiterror.h new file mode 100644 index 0000000000..e49cabfaa1 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkiterror.h @@ -0,0 +1,62 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitidentity.c b/local/recipes/libs/polkit/source/src/polkit/polkitidentity.c new file mode 100644 index 0000000000..793f17d5d1 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitidentity.c @@ -0,0 +1,386 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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 (&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; +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitidentity.h b/local/recipes/libs/polkit/source/src/polkit/polkitidentity.h new file mode 100644 index 0000000000..50491ad9fc --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitidentity.h @@ -0,0 +1,82 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitimplicitauthorization.c b/local/recipes/libs/polkit/source/src/polkit/polkitimplicitauthorization.c new file mode 100644 index 0000000000..7dffb3f9bf --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitimplicitauthorization.c @@ -0,0 +1,117 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitimplicitauthorization.h b/local/recipes/libs/polkit/source/src/polkit/polkitimplicitauthorization.h new file mode 100644 index 0000000000..64b168492e --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitimplicitauthorization.h @@ -0,0 +1,69 @@ +/* + * 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 optional): 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitpermission.c b/local/recipes/libs/polkit/source/src/polkit/polkitpermission.c new file mode 100644 index 0000000000..d4b245915b --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitpermission.c @@ -0,0 +1,865 @@ +/* + * 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_CONFIG_H +# include "config.h" +#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; + + /* 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 void on_authority_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); +} + +static void +polkit_permission_finalize (GObject *object) +{ + PolkitPermission *permission = POLKIT_PERMISSION (object); + + g_free (permission->action_id); + g_free (permission->tmp_authz_id); + g_object_unref (permission->subject); + + if (permission->authority != NULL) + { + g_signal_handlers_disconnect_by_func (permission->authority, + on_authority_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); + + 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_warning ("Error checking authorization for action id %s: %s", + permission->action_id, + error->message); + g_error_free (error); + } + g_object_unref (permission); +} + +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 +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; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitpermission.h b/local/recipes/libs/polkit/source/src/polkit/polkitpermission.h new file mode 100644 index 0000000000..3640f0c89c --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitpermission.h @@ -0,0 +1,56 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitprivate.h b/local/recipes/libs/polkit/source/src/polkit/polkitprivate.h new file mode 100644 index 0000000000..0ba8277293 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitprivate.h @@ -0,0 +1,82 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitsubject.c b/local/recipes/libs/polkit/source/src/polkit/polkitsubject.c new file mode 100644 index 0000000000..05c73c2583 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitsubject.c @@ -0,0 +1,543 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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 (&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); +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitsubject.h b/local/recipes/libs/polkit/source/src/polkit/polkitsubject.h new file mode 100644 index 0000000000..616d95e87e --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitsubject.h @@ -0,0 +1,108 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitsystembusname.c b/local/recipes/libs/polkit/source/src/polkit/polkitsystembusname.c new file mode 100644 index 0000000000..f4b3372c9a --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitsystembusname.c @@ -0,0 +1,681 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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); + + 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; +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitsystembusname.h b/local/recipes/libs/polkit/source/src/polkit/polkitsystembusname.h new file mode 100644 index 0000000000..38d31f715d --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitsystembusname.h @@ -0,0 +1,65 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkittemporaryauthorization.c b/local/recipes/libs/polkit/source/src/polkit/polkittemporaryauthorization.c new file mode 100644 index 0000000000..5e07678546 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkittemporaryauthorization.c @@ -0,0 +1,226 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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); +} + diff --git a/local/recipes/libs/polkit/source/src/polkit/polkittemporaryauthorization.h b/local/recipes/libs/polkit/source/src/polkit/polkittemporaryauthorization.h new file mode 100644 index 0000000000..792a3e88f0 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkittemporaryauthorization.h @@ -0,0 +1,56 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkittypes.h b/local/recipes/libs/polkit/source/src/polkit/polkittypes.h new file mode 100644 index 0000000000..e0cf6531ce --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkittypes.h @@ -0,0 +1,84 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixgroup.c b/local/recipes/libs/polkit/source/src/polkit/polkitunixgroup.c new file mode 100644 index 0000000000..fe01dcfb68 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixgroup.c @@ -0,0 +1,282 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixgroup.h b/local/recipes/libs/polkit/source/src/polkit/polkitunixgroup.h new file mode 100644 index 0000000000..3165cf9d06 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixgroup.h @@ -0,0 +1,59 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixnetgroup.c b/local/recipes/libs/polkit/source/src/polkit/polkitunixnetgroup.c new file mode 100644 index 0000000000..83f8d4a13e --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixnetgroup.c @@ -0,0 +1,245 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixnetgroup.h b/local/recipes/libs/polkit/source/src/polkit/polkitunixnetgroup.h new file mode 100644 index 0000000000..873d4280ad --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixnetgroup.h @@ -0,0 +1,58 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixprocess.c b/local/recipes/libs/polkit/source/src/polkit/polkitunixprocess.c new file mode 100644 index 0000000000..e05e5812c9 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixprocess.c @@ -0,0 +1,1281 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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 -1; + } + + 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); +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixprocess.h b/local/recipes/libs/polkit/source/src/polkit/polkitunixprocess.h new file mode 100644 index 0000000000..7c9addec92 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixprocess.h @@ -0,0 +1,83 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixsession-systemd.c b/local/recipes/libs/polkit/source/src/polkit/polkitunixsession-systemd.c new file mode 100644 index 0000000000..c34f36a909 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixsession-systemd.c @@ -0,0 +1,504 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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 */ +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixsession.c b/local/recipes/libs/polkit/source/src/polkit/polkitunixsession.c new file mode 100644 index 0000000000..40817de55a --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixsession.c @@ -0,0 +1,527 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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 */ +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixsession.h b/local/recipes/libs/polkit/source/src/polkit/polkitunixsession.h new file mode 100644 index 0000000000..2674abe56a --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixsession.h @@ -0,0 +1,64 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixuser.c b/local/recipes/libs/polkit/source/src/polkit/polkitunixuser.c new file mode 100644 index 0000000000..234a6976c5 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixuser.c @@ -0,0 +1,315 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} diff --git a/local/recipes/libs/polkit/source/src/polkit/polkitunixuser.h b/local/recipes/libs/polkit/source/src/polkit/polkitunixuser.h new file mode 100644 index 0000000000..2f227d4d62 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkit/polkitunixuser.h @@ -0,0 +1,60 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitagent/meson.build b/local/recipes/libs/polkit/source/src/polkitagent/meson.build new file mode 100644 index 0000000000..bee38201d5 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/meson.build @@ -0,0 +1,119 @@ +name = '@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 = [ + expat_dep, + 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 + +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, +) diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagent.h b/local/recipes/libs/polkit/source/src/polkitagent/polkitagent.h new file mode 100644 index 0000000000..6f163d1a0e --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagent.h @@ -0,0 +1,37 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagentenumtypes.c.template b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentenumtypes.c.template new file mode 100644 index 0000000000..e6cb139b44 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentenumtypes.c.template @@ -0,0 +1,39 @@ +/*** 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 (&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 ***/ diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagentenumtypes.h.template b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentenumtypes.h.template new file mode 100644 index 0000000000..24d6f902ba --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentenumtypes.h.template @@ -0,0 +1,24 @@ +/*** 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 ***/ diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-bsdauth.c b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-bsdauth.c new file mode 100644 index 0000000000..b105180d68 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-bsdauth.c @@ -0,0 +1,163 @@ +/* + * 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 "config.h" +#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; +} diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-pam.c b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-pam.c new file mode 100644 index 0000000000..3ea3a3f2e8 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-pam.c @@ -0,0 +1,309 @@ +/* + * 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 "config.h" +#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); + 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; +} diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-shadow.c b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-shadow.c new file mode 100644 index 0000000000..e87791541a --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelper-shadow.c @@ -0,0 +1,204 @@ +/* + * 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 "config.h" +#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; +} diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelperprivate.c b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelperprivate.c new file mode 100644 index 0000000000..1f32c0a18c --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelperprivate.c @@ -0,0 +1,145 @@ +/* + * 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 "config.h" +#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 () +{ + fflush (stdout); + fflush (stderr); +#ifdef HAVE_FDATASYNC + fdatasync (fileno(stdout)); + fdatasync (fileno(stderr)); +#else + fsync (fileno(stdout)); + fsync (fileno(stderr)); +#endif + usleep (100 * 1000); +} diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelperprivate.h b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelperprivate.h new file mode 100644 index 0000000000..c5145b5eed --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenthelperprivate.h @@ -0,0 +1,46 @@ +/* + * 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 (); + +#endif /* __POLKIT_AGENT_HELPER_PRIVATE_H */ diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagentlistener.c b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentlistener.c new file mode 100644 index 0000000000..ec271d524b --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentlistener.c @@ -0,0 +1,827 @@ +/* + * 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 "config.h" + +#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); +} + diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagentlistener.h b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentlistener.h new file mode 100644 index 0000000000..854e36d83b --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentlistener.h @@ -0,0 +1,149 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagentmarshal.list b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentmarshal.list new file mode 100644 index 0000000000..c4effb63a8 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentmarshal.list @@ -0,0 +1 @@ +VOID:STRING,BOOLEAN diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagentsession.c b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentsession.c new file mode 100644 index 0000000000..491d9dcf7a --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentsession.c @@ -0,0 +1,668 @@ +/* + * 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 "config.h" +#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 (&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); +} diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagentsession.h b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentsession.h new file mode 100644 index 0000000000..4123c2d678 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagentsession.h @@ -0,0 +1,54 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttextlistener.c b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttextlistener.c new file mode 100644 index 0000000000..99af1d1a62 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttextlistener.c @@ -0,0 +1,668 @@ +/* + * 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 "config.h" + +#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; + + 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) + { + gint c; + c = getc (listener->tty); + if (c == '\n') + { + /* ok, done */ + break; + } + else if (c == EOF) + { + tcsetattr (fileno (listener->tty), TCSAFLUSH, &ots); + g_error ("Got unexpected EOF while reading from controlling terminal."); + abort (); + 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); + + 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_error ("Got unexpected EOF while reading from controlling terminal."); + abort (); + break; + } + 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; +} diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttextlistener.h b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttextlistener.h new file mode 100644 index 0000000000..87aa5031b4 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttextlistener.h @@ -0,0 +1,45 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttypes.h b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttypes.h new file mode 100644 index 0000000000..bcc59fb15c --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitagent/polkitagenttypes.h @@ -0,0 +1,50 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/50-default.rules b/local/recipes/libs/polkit/source/src/polkitbackend/50-default.rules new file mode 100644 index 0000000000..f427ae1839 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/50-default.rules @@ -0,0 +1,12 @@ +/* -*- 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:wheel"]; +}); diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/init.js b/local/recipes/libs/polkit/source/src/polkitbackend/init.js new file mode 100644 index 0000000000..79c80b6672 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/init.js @@ -0,0 +1,93 @@ +/* -*- 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 +}; diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/meson.build b/local/recipes/libs/polkit/source/src/polkitbackend/meson.build new file mode 100644 index 0000000000..c35e6c2f10 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/meson.build @@ -0,0 +1,83 @@ +name = '@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), + '-D_XOPEN_SOURCE=700', + '-D_GNU_SOURCE=1', +] + +if js_engine == 'duktape' + sources += files('polkitbackendduktapeauthority.c') + deps += libm_dep + deps += thread_dep +elif js_engine == 'mozjs' + add_languages('cpp') + sources += files('polkitbackendjsauthority.cpp') +endif + +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, +) + +install_data( + '50-default.rules', + 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, +) diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackend.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackend.h new file mode 100644 index 0000000000..afa4be3238 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackend.h @@ -0,0 +1,40 @@ +/* + * 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 */ + + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionlookup.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionlookup.c new file mode 100644 index 0000000000..20747e7921 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionlookup.c @@ -0,0 +1,182 @@ +/* + * 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 "config.h" +#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 (&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); +} + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionlookup.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionlookup.h new file mode 100644 index 0000000000..254507b970 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionlookup.h @@ -0,0 +1,123 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionpool.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionpool.c new file mode 100644 index 0000000000..3894fe9132 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionpool.c @@ -0,0 +1,1140 @@ +/* + * 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 "config.h" +#include +#include +#include +#include + +#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 *message; + + 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->message); + + 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 *lang); + +typedef struct +{ + /* directory with .policy files, e.g. /usr/share/polkit-1/actions */ + GFile *directory; + + GFileMonitor *dir_monitor; + + /* maps from action_id to a ParsedAction struct */ + GHashTable *parsed_actions; + + /* maps from URI 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_DIRECTORY, +}; + +#define POLKIT_BACKEND_ACTION_POOL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), POLKIT_BACKEND_TYPE_ACTION_POOL, PolkitBackendActionPoolPrivate)) + +enum +{ + CHANGED_SIGNAL, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE (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_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_PRIVATE (pool); + + if (priv->directory != NULL) + g_object_unref (priv->directory); + + if (priv->dir_monitor != NULL) + g_object_unref (priv->dir_monitor); + + 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_PRIVATE (pool); + + switch (prop_id) + { + case PROP_DIRECTORY: + g_value_set_object (value, priv->directory); + 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_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; + GError *error; + + pool = POLKIT_BACKEND_ACTION_POOL (object); + priv = POLKIT_BACKEND_ACTION_POOL_GET_PRIVATE (pool); + + switch (prop_id) + { + case PROP_DIRECTORY: + priv->directory = g_value_dup_object (value); + + error = NULL; + priv->dir_monitor = g_file_monitor_directory (priv->directory, + G_FILE_MONITOR_NONE, + NULL, + &error); + if (priv->dir_monitor == NULL) + { + g_warning ("Error monitoring actions directory: %s", error->message); + g_error_free (error); + } + else + { + g_signal_connect (priv->dir_monitor, + "changed", + (GCallback) dir_monitor_changed, + pool); + } + 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; + + g_type_class_add_private (klass, sizeof (PolkitBackendActionPoolPrivate)); + + /** + * PolkitBackendActionPool:directory: + * + * The directory to load action description files from. + */ + g_object_class_install_property (gobject_class, + PROP_DIRECTORY, + g_param_spec_object ("directory", + "Directory", + "Directory to load action description files from", + G_TYPE_FILE, + 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 (GFile *directory) +{ + PolkitBackendActionPool *pool; + + pool = POLKIT_BACKEND_ACTION_POOL (g_object_new (POLKIT_BACKEND_TYPE_ACTION_POOL, + "directory", directory, + 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_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, + locale); + message = _localize (parsed_action->localized_message, + parsed_action->message, + 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_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; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +ensure_file (PolkitBackendActionPool *pool, + GFile *file) +{ + PolkitBackendActionPoolPrivate *priv; + gchar *contents; + GError *error; + gchar *uri; + + priv = POLKIT_BACKEND_ACTION_POOL_GET_PRIVATE (pool); + + uri = g_file_get_uri (file); + + if (g_hash_table_lookup (priv->parsed_files, uri) != NULL) + goto out; + + error = NULL; + if (!g_file_load_contents (file, + NULL, + &contents, + NULL, + NULL, + &error)) + { + g_warning ("Error loading file with URI '%s': %s", uri, error->message); + goto out; + } + + if (!process_policy_file (pool, + contents, + &error)) + { + g_warning ("Error parsing file with URI '%s': %s", uri, error->message); + g_free (contents); + goto out; + } + + g_free (contents); + + /* steal uri */ + g_hash_table_insert (priv->parsed_files, uri, NULL); + uri = NULL; + + out: + g_free (uri); +} + +static void +ensure_all_files (PolkitBackendActionPool *pool) +{ + PolkitBackendActionPoolPrivate *priv; + GFileEnumerator *e; + GFileInfo *file_info; + GError *error; + + priv = POLKIT_BACKEND_ACTION_POOL_GET_PRIVATE (pool); + + e = NULL; + + if (priv->has_loaded_all_files) + goto out; + + error = NULL; + e = g_file_enumerate_children (priv->directory, + "standard::name", + G_FILE_QUERY_INFO_NONE, + NULL, + &error); + if (error != NULL) + { + g_warning ("Error enumerating files: %s", error->message); + goto out; + } + + while ((file_info = g_file_enumerator_next_file (e, NULL, &error)) != NULL) + { + const gchar *name; + + name = g_file_info_get_name (file_info); + /* only consider files with the right suffix */ + if (g_str_has_suffix (name, ".policy")) + { + GFile *file; + + file = g_file_get_child (priv->directory, name); + + ensure_file (pool, file); + + g_object_unref (file); + } + + g_object_unref (file_info); + + } /* for all files */ + + priv->has_loaded_all_files = TRUE; + + out: + + if (e != NULL) + g_object_unref (e); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +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_message_nolang; + + /* the value of xml:lang for the thing we're reading in _cdata() */ + char *elem_lang; + + 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_message_nolang); + pd->policy_message_nolang = 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; +} + +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]); + } + 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]); + } + 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; + 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; + 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; + + 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_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->message = g_strdup (pd->policy_message_nolang); + + 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' + * @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 *lang) +{ + const gchar *result; + gchar **langs; + guint n; + + if (lang == NULL) + { + result = untranslated; + goto out; + } + + /* 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; +} diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionpool.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionpool.h new file mode 100644 index 0000000000..e992eea694 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendactionpool.h @@ -0,0 +1,78 @@ +/* + * 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 (GFile *directory); +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); + +G_END_DECLS + +#endif /* __POLKIT_BACKEND_ACTION_POOL_H */ + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendauthority.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendauthority.c new file mode 100644 index 0000000000..d4c6f7d93f --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendauthority.c @@ -0,0 +1,1548 @@ +/* + * 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 "config.h" +#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, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + +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); +} + +/** + * 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; + + GDBusNodeInfo *introspection_info; + + PolkitBackendAuthority *authority; + + GDBusConnection *connection; + + gulong authority_changed_id; + + 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->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->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 +on_authority_changed (PolkitBackendAuthority *authority, + gpointer user_data) +{ + Server *server = user_data; + GError *error; + + error = NULL; + if (!g_dbus_connection_emit_signal (server->connection, + NULL, /* destination bus name */ + server->object_path, + "org.freedesktop.PolicyKit1.Authority", + "Changed", + NULL, + &error)) + { + g_warning ("Error emitting Changed() signal: %s", error->message); + g_error_free (error); + } +} + +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 (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 (); + + return result; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static const GDBusInterfaceVTable server_vtable = +{ + server_handle_method_call, + server_handle_get_property, + NULL, /* 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->authority = g_object_ref (authority); + + server->authority_changed_id = g_signal_connect (server->authority, + "changed", + G_CALLBACK (on_authority_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; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +void +polkit_backend_authority_log (PolkitBackendAuthority *authority, + const gchar *format, + ...) +{ + GTimeVal now; + time_t now_time; + struct tm *now_tm; + gchar time_buf[128]; + gchar *message; + va_list var_args; + + 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 (LOG_NOTICE, "%s", message); + + g_get_current_time (&now); + now_time = (time_t) now.tv_sec; + 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.tv_usec / 1000, + _color_get (_COLOR_RESET), + message); + + g_free (message); +} diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendauthority.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendauthority.h new file mode 100644 index 0000000000..88df82e41c --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendauthority.h @@ -0,0 +1,286 @@ +/* + * 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; +}; + +/** + * 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 gchar *format, + ...); + +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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendcommon.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendcommon.c new file mode 100644 index 0000000000..f38147e1ae --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendcommon.c @@ -0,0 +1,661 @@ +/* + * 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), + "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. */ + 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_unit = strdup (unit); + if (!*ret_unit) + { + g_warning ("Failed to allocate memory for systemd unit ID"); + goto out; + } + + *ret_no_new_privs = g_variant_get_boolean (no_new_privis_value); + + 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); +} diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendcommon.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendcommon.h new file mode 100644 index 0000000000..28342af53d --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendcommon.h @@ -0,0 +1,162 @@ +/* + * 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 "config.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 */ + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendduktapeauthority.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendduktapeauthority.c new file mode 100644 index 0000000000..c40b907c8e --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendduktapeauthority.c @@ -0,0 +1,1163 @@ +/* + * 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 (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), + "fatal Duktape JS backend error: %s", + (msg ? msg : "no message")); +} + +static void +polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority) +{ + authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority, + POLKIT_BACKEND_TYPE_JS_AUTHORITY, + PolkitBackendJsAuthorityPrivate); +} + +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), + "Loading rules from directory %s", + dir_name); + + dir = g_dir_open (dir_name, + 0, + &error); + if (dir == NULL) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error opening rules directory: %s (%s, %d)", + error->message, g_quark_to_string (error->domain), error->code); + g_clear_error (&error); + } + else + { + 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); + } + } + + 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), + "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), + "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), + "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 *, 3); + authority->priv->rules_dirs[0] = g_strdup (PACKAGE_SYSCONF_DIR "/polkit-1/rules.d"); + authority->priv->rules_dirs[1] = 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); + g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +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 == -1) + { + 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, + GError **error) +{ + 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), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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; + pthread_condattr_t attr; + struct timespec abs_time; + +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK + if ((pthread_err = pthread_condattr_init(&attr))) { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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), + "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); +#endif + err_clean_condattr: +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK + 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), + "Error deleting old rules, not loading new ones"); + goto out; + } + + duk_push_string (cx, "_runAdminRules"); + + if (!push_action_and_details (cx, action_id, details, &error)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error converting action and details to JS object: %s", + error->message); + g_clear_error (&error); + 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), + "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), + "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, &error)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error converting action and details to JS object: %s", + error->message); + g_clear_error (&error); + 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), + "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), + "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_error (cx, DUK_ERR_ERROR, err_str); + + 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; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendinteractiveauthority.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendinteractiveauthority.c new file mode 100644 index 0000000000..f4be303652 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendinteractiveauthority.c @@ -0,0 +1,3597 @@ +/* + * 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 "config.h" +#include +#include +#include +#ifdef HAVE_NETGROUP_H +#include +#else +#include +#endif +#include +#include +#include + +#include +#include "polkitbackendinteractiveauthority.h" +#include "polkitbackendactionpool.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 (PolkitBackendInteractiveAuthority, + polkit_backend_interactive_authority, + POLKIT_BACKEND_TYPE_AUTHORITY); + +#define POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY, PolkitBackendInteractiveAuthorityPrivate)) + +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, "changed"); +} + +static void +polkit_backend_interactive_authority_init (PolkitBackendInteractiveAuthority *authority) +{ + PolkitBackendInteractiveAuthorityPrivate *priv; + GFile *directory; + GError *error; + + /* Force registering error domain */ + (void)POLKIT_ERROR; + + priv = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_PRIVATE (authority); + + directory = g_file_new_for_path (PACKAGE_DATA_DIR "/polkit-1/actions"); + priv->action_pool = polkit_backend_action_pool_new (directory); + g_object_unref (directory); + 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_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; + + + + g_type_class_add_private (klass, sizeof (PolkitBackendInteractiveAuthorityPrivate)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +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_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_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), + "%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_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), + "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), + "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), + "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_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_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_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 (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; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +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_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_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_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_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_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), + "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_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_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_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_caller, + 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), + "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_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), + "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_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_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), + "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; + GTimeVal real_now; + + interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority); + priv = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_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 (); + g_get_current_time (&real_now); + + 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.tv_sec; + real_expires = (ta->time_expires - monotonic_now) / G_USEC_PER_SEC + real_now.tv_sec; + + 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_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_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; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendinteractiveauthority.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendinteractiveauthority.h new file mode 100644 index 0000000000..80c4cf13eb --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendinteractiveauthority.h @@ -0,0 +1,150 @@ +/* + * 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); + +G_END_DECLS + +#endif /* __POLKIT_BACKEND_INTERACTIVE_AUTHORITY_H */ + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendjsauthority.cpp b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendjsauthority.cpp new file mode 100644 index 0000000000..cf28b04e11 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendjsauthority.cpp @@ -0,0 +1,1364 @@ +/* + * 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 + */ + +#include "polkitbackendcommon.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Built source and not too big to worry about deduplication */ +#include "initjs.h" /* init.js */ + +#ifdef JSGC_USE_EXACT_ROOTING +/* See https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/GC/Exact_Stack_Rooting + * for more information about exact stack rooting. + */ +#error "This code is not safe in SpiderMonkey exact stack rooting configurations" +#endif + +/** + * SECTION:polkitbackendjsauthority + * @title: PolkitBackendJsAuthority + * @short_description: JS Authority + * @stability: Unstable + * + * An (SpiderMonkey-based) implementation of #PolkitBackendAuthority that reads + * and evaluates Javascript files and supports interaction with authentication + * agents (virtue of being based on #PolkitBackendInteractiveAuthority). + */ + +/* ---------------------------------------------------------------------------------------------------- */ + +static class JsInitHelperType +{ +public: + JsInitHelperType() + { + /* Disable JIT because it needs W/X mapping, which is not allowed by + * our systemd hardening setting. + */ + JS::DisableJitBackend(); + + JS_Init(); + } + + ~JsInitHelperType() { JS_ShutDown(); } +} JsInitHelper; + +struct _PolkitBackendJsAuthorityPrivate +{ + gchar **rules_dirs; + GFileMonitor **dir_monitors; /* NULL-terminated array of GFileMonitor instances */ + + JSContext *cx; + JS::Heap *js_global; + JSAutoRealm *ac; + JS::Heap *js_polkit; + + GThread *runaway_killer_thread; + GMainContext *rkt_context; + GMainLoop *rkt_loop; + GSource *rkt_source; + GMutex rkt_timeout_pending_mutex; + gboolean rkt_timeout_pending; + + /* A list of JSObject instances */ + GList *scripts; +}; + +static bool execute_script_with_runaway_killer (PolkitBackendJsAuthority *authority, + JS::HandleScript script, + JS::MutableHandleValue rval); + +/* ---------------------------------------------------------------------------------------------------- */ + +static gpointer runaway_killer_thread_func (gpointer user_data); +static void runaway_killer_terminate (PolkitBackendJsAuthority *authority); + +G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY); + +/* ---------------------------------------------------------------------------------------------------- */ + +static const struct JSClassOps js_global_class_ops = { + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + nullptr, // finalize + nullptr, // call + nullptr, // construct + JS_GlobalObjectTraceHook +}; + +static JSClass js_global_class = { + "global", + JSCLASS_GLOBAL_FLAGS, + &js_global_class_ops +}; + +/* ---------------------------------------------------------------------------------------------------- */ +static const struct JSClassOps js_polkit_class_ops = { + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + nullptr, // finalize + nullptr, // call + nullptr, // construct + nullptr // trace +}; + +static JSClass js_polkit_class = { + "Polkit", + 0, + &js_polkit_class_ops +}; + +static bool js_polkit_log (JSContext *cx, unsigned argc, JS::Value *vp); +static bool js_polkit_spawn (JSContext *cx, unsigned argc, JS::Value *vp); +static bool js_polkit_user_is_in_netgroup (JSContext *cx, unsigned argc, JS::Value *vp); + +static JSFunctionSpec js_polkit_functions[] = +{ + JS_FN("log", js_polkit_log, 0, 0), + JS_FN("spawn", js_polkit_spawn, 0, 0), + JS_FN("_userIsInNetGroup", js_polkit_user_is_in_netgroup, 0, 0), + JS_FS_END +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void report_error (JSContext *cx, + JSErrorReport *report) +{ + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (JS_GetContextPrivate (cx)); + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "%s:%u: %s", + report->filename ? report->filename : "", + (unsigned int) report->lineno, + report->message().c_str()); +} + +static void +polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority) +{ + authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority, + POLKIT_BACKEND_TYPE_JS_AUTHORITY, + PolkitBackendJsAuthorityPrivate); +} + +/* authority->priv->cx must be within a request */ +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), + "Loading rules from directory %s", + dir_name); + + dir = g_dir_open (dir_name, + 0, + &error); + if (dir == NULL) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error opening rules directory: %s (%s, %d)", + error->message, g_quark_to_string (error->domain), error->code); + g_clear_error (&error); + } + else + { + 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); + } + } + + 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; + JS::CompileOptions options(authority->priv->cx); + JS::RootedScript script(authority->priv->cx, + JS::CompileUtf8Path (authority->priv->cx, + options, + filename)); + if (!script) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error compiling script %s", + filename); + continue; + } + + /* evaluate the script */ + JS::RootedValue rval(authority->priv->cx); + if (!execute_script_with_runaway_killer (authority, + script, + &rval)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error executing script %s", + filename); + continue; + } + + //g_print ("Successfully loaded and evaluated script `%s'\n", filename); + + num_scripts++; + } + + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Finished loading, compiling and executing %d rules", + num_scripts); + g_list_free_full (files, g_free); +} + +void +polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority) +{ + JS::RootedValueArray<1> args(authority->priv->cx); + JS::RootedValue rval(authority->priv->cx); + + JS::RootedObject js_polkit(authority->priv->cx, authority->priv->js_polkit->get ()); + + args[0].setUndefined (); + if (!JS_CallFunctionName(authority->priv->cx, + js_polkit, + "_deleteRules", + args, + &rval)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error deleting old rules, not loading new ones"); + return; + } + + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Collecting garbage unconditionally..."); + JS_GC (authority->priv->cx); + + 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); + + authority->priv->cx = JS_NewContext (8L * 1024L * 1024L); + if (authority->priv->cx == NULL) + goto fail; + + if (!JS::InitSelfHostedCode (authority->priv->cx)) + goto fail; + + JS::SetWarningReporter(authority->priv->cx, report_error); + JS_SetContextPrivate (authority->priv->cx, authority); + + + { + JS::RealmOptions compart_opts; + + JS::RootedObject global(authority->priv->cx); + + authority->priv->js_global = new JS::Heap (JS_NewGlobalObject (authority->priv->cx, &js_global_class, NULL, JS::FireOnNewGlobalHook, compart_opts)); + + global = authority->priv->js_global->get (); + if (!global) + goto fail; + + authority->priv->ac = new JSAutoRealm(authority->priv->cx, global); + + if (!authority->priv->ac) + goto fail; + + if (!JS::InitRealmStandardClasses (authority->priv->cx)) + goto fail; + + JS::RootedObject polkit(authority->priv->cx); + + authority->priv->js_polkit = new JS::Heap (JS_NewObject (authority->priv->cx, &js_polkit_class)); + + polkit = authority->priv->js_polkit->get (); + + if (!polkit) + goto fail; + + if (!JS_DefineProperty(authority->priv->cx, global, "polkit", polkit, JSPROP_ENUMERATE)) + goto fail; + + if (!JS_DefineFunctions (authority->priv->cx, + polkit, + js_polkit_functions)) + goto fail; + + JS::CompileOptions options(authority->priv->cx); + JS::RootedValue rval(authority->priv->cx); + JS::SourceText source; + if (!source.init (authority->priv->cx, init_js, strlen (init_js), + JS::SourceOwnership::Borrowed)) + goto fail; + + if (!JS::Evaluate (authority->priv->cx, options, source, &rval)) + goto fail; + + if (authority->priv->rules_dirs == NULL) + { + authority->priv->rules_dirs = g_new0 (gchar *, 3); + authority->priv->rules_dirs[0] = g_strdup (PACKAGE_SYSCONF_DIR "/polkit-1/rules.d"); + authority->priv->rules_dirs[1] = g_strdup (PACKAGE_DATA_DIR "/polkit-1/rules.d"); + } + + authority->priv->rkt_context = g_main_context_new (); + authority->priv->rkt_loop = g_main_loop_new (authority->priv->rkt_context, FALSE); + g_mutex_init (&authority->priv->rkt_timeout_pending_mutex); + + authority->priv->runaway_killer_thread = g_thread_new ("runaway-killer-thread", + runaway_killer_thread_func, + authority); + + 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; + + runaway_killer_terminate (authority); + + g_mutex_clear (&authority->priv->rkt_timeout_pending_mutex); + g_main_loop_unref (authority->priv->rkt_loop); + g_main_context_unref (authority->priv->rkt_context); + + 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, + (gpointer*)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); + + delete authority->priv->ac; + delete authority->priv->js_global; + delete authority->priv->js_polkit; + + JS_DestroyContext (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); + + g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* authority->priv->cx must be within a request */ +static void +set_property_str (PolkitBackendJsAuthority *authority, + JS::HandleObject obj, + const gchar *name, + const gchar *value) +{ + JS::RootedValue value_jsval(authority->priv->cx); + if (value) + { + JS::ConstUTF8CharsZ chars(value, strlen(value)); + JS::RootedString str(authority->priv->cx, JS_NewStringCopyUTF8Z(authority->priv->cx, chars)); + value_jsval = JS::StringValue (str); + } + else + value_jsval = JS::NullValue (); + JS_SetProperty (authority->priv->cx, obj, name, value_jsval); +} + +/* authority->priv->cx must be within a request */ +static void +set_property_strv (PolkitBackendJsAuthority *authority, + JS::HandleObject obj, + const gchar *name, + GPtrArray *value) +{ + JS::RootedValue value_jsval(authority->priv->cx); + JS::RootedValueVector elems(authority->priv->cx); + guint n; + + if (!elems.resize(value->len)) + g_error ("Unable to resize vector"); + + for (n = 0; n < value->len; n++) + { + const char *c_string = (const char *) g_ptr_array_index(value, n); + if (c_string) + { + JS::ConstUTF8CharsZ chars(c_string, strlen(c_string)); + JS::RootedString str(authority->priv->cx, JS_NewStringCopyUTF8Z(authority->priv->cx, chars)); + elems[n].setString(str); + } + else + elems[n].setNull (); + } + + JS::RootedObject array_object(authority->priv->cx, JS::NewArrayObject (authority->priv->cx, elems)); + + value_jsval = JS::ObjectValue (*array_object); + JS_SetProperty (authority->priv->cx, obj, name, value_jsval); +} + +/* authority->priv->cx must be within a request */ +static void +set_property_int32 (PolkitBackendJsAuthority *authority, + JS::HandleObject obj, + const gchar *name, + gint32 value) +{ + JS::RootedValue value_jsval(authority->priv->cx); + value_jsval = JS::Int32Value ((gint32) value); + JS_SetProperty (authority->priv->cx, obj, name, value_jsval); +} + +/* authority->priv->cx must be within a request */ +static void +set_property_bool (PolkitBackendJsAuthority *authority, + JS::HandleObject obj, + const gchar *name, + gboolean value) +{ + JS::RootedValue value_jsval(authority->priv->cx); + value_jsval = JS::BooleanValue ((bool) value); + JS_SetProperty (authority->priv->cx, obj, name, value_jsval); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* authority->priv->cx must be within a request */ +static gboolean +subject_to_jsval (PolkitBackendJsAuthority *authority, + PolkitSubject *subject, + PolkitIdentity *user_for_subject, + gboolean subject_is_local, + gboolean subject_is_active, + JS::MutableHandleValue out_jsval, + GError **error) +{ + gboolean ret = FALSE; + gboolean no_new_privs = FALSE; + JS::CompileOptions options(authority->priv->cx); + const char *src; + JS::RootedObject obj(authority->priv->cx); + 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; + JS::RootedObject global(authority->priv->cx, authority->priv->js_global->get ()); + + src = "new Subject();"; + JS::SourceText source; + if (!source.init (authority->priv->cx, src, strlen (src), + JS::SourceOwnership::Borrowed)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Evaluating '%s' failed", src); + goto out; + } + + if (!JS::Evaluate (authority->priv->cx, options, source, out_jsval)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Evaluating '%s' failed", src); + goto out; + } + + obj = out_jsval.toObjectOrNull(); + + 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) + { + g_warning ("pid changed from %d to %d, ignoring", (gint) pid_early, (gint) pid_late); + pid_early = -1; + } + + set_property_int32 (authority, obj, "pid", pid_early); + set_property_str (authority, obj, "user", user_name); + set_property_strv (authority, obj, "groups", groups); + set_property_str (authority, obj, "seat", seat_str); + set_property_str (authority, obj, "session", session_str); + set_property_str (authority, obj, "system_unit", system_unit); + /* If we have a unit, also record if it has the NoNewPrivileges setting enabled */ + if (system_unit) + set_property_bool (authority, obj, "no_new_privileges", no_new_privs); + set_property_bool (authority, obj, "local", subject_is_local); + set_property_bool (authority, obj, "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; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* authority->priv->cx must be within a request */ +static gboolean +action_and_details_to_jsval (PolkitBackendJsAuthority *authority, + const gchar *action_id, + PolkitDetails *details, + JS::MutableHandleValue out_jsval, + GError **error) +{ + gboolean ret = FALSE; + JS::CompileOptions options(authority->priv->cx); + const char *src; + JS::RootedObject obj(authority->priv->cx); + gchar **keys; + guint n; + JS::RootedObject global(authority->priv->cx, authority->priv->js_global->get ()); + + src = "new Action();"; + JS::SourceText source; + if (!source.init (authority->priv->cx, src, strlen (src), + JS::SourceOwnership::Borrowed)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Evaluating '%s' failed", src); + goto out; + } + + if (!JS::Evaluate (authority->priv->cx, options, source, out_jsval)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Evaluating '%s' failed", src); + goto out; + } + + obj = out_jsval.toObjectOrNull(); + + set_property_str (authority, obj, "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 (authority, obj, key, value); + g_free (key); + } + g_strfreev (keys); + + ret = TRUE; + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gpointer +runaway_killer_thread_func (gpointer user_data) +{ + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data); + + g_main_context_push_thread_default (authority->priv->rkt_context); + g_main_loop_run (authority->priv->rkt_loop); + g_main_context_pop_thread_default (authority->priv->rkt_context); + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static bool +js_operation_callback (JSContext *cx) +{ + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (JS_GetContextPrivate (cx)); + JSString *val_str; + JS::RootedValue val(cx); + + /* This callback can be called by the runtime at any time without us causing + * it by JS_TriggerOperationCallback(). + */ + g_mutex_lock (&authority->priv->rkt_timeout_pending_mutex); + if (!authority->priv->rkt_timeout_pending) + { + g_mutex_unlock (&authority->priv->rkt_timeout_pending_mutex); + return true; + } + authority->priv->rkt_timeout_pending = FALSE; + g_mutex_unlock (&authority->priv->rkt_timeout_pending_mutex); + + /* Log that we are terminating the script */ + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), "Terminating runaway script"); + + /* Throw an exception - this way the JS code can ignore the runaway script handling */ + JS_ResetInterruptCallback (authority->priv->cx, TRUE); + val_str = JS_NewStringCopyZ (cx, "Terminating runaway script"); + val = JS::StringValue (val_str); + JS_SetPendingException (authority->priv->cx, val); + JS_ResetInterruptCallback (authority->priv->cx, FALSE); + return false; +} + +static gboolean +rkt_on_timeout (gpointer user_data) +{ + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data); + + g_mutex_lock (&authority->priv->rkt_timeout_pending_mutex); + authority->priv->rkt_timeout_pending = TRUE; + g_mutex_unlock (&authority->priv->rkt_timeout_pending_mutex); + + /* Supposedly this is thread-safe... */ + JS_RequestInterruptCallback (authority->priv->cx); + + /* keep source around so we keep trying to kill even if the JS bit catches the exception + * thrown in js_operation_callback() + */ + return TRUE; +} + +static void +runaway_killer_setup (PolkitBackendJsAuthority *authority) +{ + g_assert (authority->priv->rkt_source == NULL); + + /* set-up timer for runaway scripts, will be executed in + runaway_killer_thread, that is one, permanent thread running a glib + mainloop (rkt_loop) whose context (rkt_context) has a timeout source + (rkt_source) */ + g_mutex_lock (&authority->priv->rkt_timeout_pending_mutex); + authority->priv->rkt_timeout_pending = FALSE; + g_mutex_unlock (&authority->priv->rkt_timeout_pending_mutex); + authority->priv->rkt_source = g_timeout_source_new_seconds (RUNAWAY_KILLER_TIMEOUT); + g_source_set_callback (authority->priv->rkt_source, rkt_on_timeout, authority, NULL); + g_source_attach (authority->priv->rkt_source, authority->priv->rkt_context); + + /* ... rkt_on_timeout() will then poke the JSContext so js_operation_callback() is + * called... and from there we throw an exception + */ + JS_AddInterruptCallback (authority->priv->cx, js_operation_callback); + JS_ResetInterruptCallback (authority->priv->cx, FALSE); +} + +static void +runaway_killer_teardown (PolkitBackendJsAuthority *authority) +{ + JS_ResetInterruptCallback (authority->priv->cx, TRUE); + + g_source_destroy (authority->priv->rkt_source); + g_source_unref (authority->priv->rkt_source); + authority->priv->rkt_source = NULL; +} + +static gboolean +runaway_killer_call_g_main_quit (gpointer user_data) +{ + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data); + g_main_loop_quit (authority->priv->rkt_loop); + return G_SOURCE_REMOVE; +} + +static void +runaway_killer_terminate (PolkitBackendJsAuthority *authority) +{ + GSource *source; + + /* Use a g_idle_source_new () to ensure g_main_loop_quit () is called from + * inside a running rkt_loop. This prevents a possible race condition, where + * we could be calling g_main_loop_quit () on the main thread before + * runaway_killer_thread_func () starts its g_main_loop_run () call; + * g_main_loop_quit () before g_main_loop_run () does nothing, so in such + * a case we would not terminate the thread and become blocked in + * g_thread_join () below. + */ + g_assert (authority->priv->rkt_loop != NULL); + + source = g_idle_source_new (); + g_source_set_callback (source, runaway_killer_call_g_main_quit, authority, + NULL); + g_source_attach (source, authority->priv->rkt_context); + g_source_unref (source); + + g_thread_join (authority->priv->runaway_killer_thread); +} + +static bool +execute_script_with_runaway_killer (PolkitBackendJsAuthority *authority, + JS::HandleScript script, + JS::MutableHandleValue rval) +{ + bool ret; + + // tries to JS_ExecuteScript(), may hang for > RUNAWAY_KILLER_TIMEOUT, + // runaway_killer_thread makes sure the call returns, due to exception + // injection + runaway_killer_setup (authority); + ret = JS_ExecuteScript (authority->priv->cx, + script, + rval); + runaway_killer_teardown (authority); + + return ret; +} + +static bool +call_js_function_with_runaway_killer (PolkitBackendJsAuthority *authority, + const char *function_name, + const JS::HandleValueArray &args, + JS::RootedValue *rval) +{ + bool ret; + JS::RootedObject js_polkit(authority->priv->cx, authority->priv->js_polkit->get ()); + + runaway_killer_setup (authority); + ret = JS_CallFunctionName(authority->priv->cx, + js_polkit, + function_name, + args, + rval); + runaway_killer_teardown (authority); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +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; + JS::RootedValueArray<2> args(authority->priv->cx); + JS::RootedValue rval(authority->priv->cx); + guint n; + GError *error = NULL; + JS::RootedString ret_jsstr (authority->priv->cx); + JS::UniqueChars ret_str; + gchar **ret_strs = NULL; + + if (!action_and_details_to_jsval (authority, action_id, details, args[0], &error)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error converting action and details to JS object: %s", + error->message); + g_clear_error (&error); + goto out; + } + + if (!subject_to_jsval (authority, + subject, + user_for_subject, + subject_is_local, + subject_is_active, + args[1], + &error)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error converting subject to JS object: %s", + error->message); + g_clear_error (&error); + goto out; + } + + if (!call_js_function_with_runaway_killer (authority, + "_runAdminRules", + args, + &rval)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error evaluating admin rules"); + goto out; + } + + if (!rval.isString()) + { + g_warning ("Expected a string"); + goto out; + } + + ret_jsstr = rval.toString(); + ret_str = JS_EncodeStringToUTF8 (authority->priv->cx, ret_jsstr); + if (ret_str == NULL) + { + g_warning ("Error converting resulting string to UTF-8"); + goto out; + } + + ret_strs = g_strsplit (ret_str.get(), ",", -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), + "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)); + + JS_MaybeGC (authority->priv->cx); + + 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; + JS::RootedValueArray<2> args(authority->priv->cx); + JS::RootedValue rval(authority->priv->cx); + GError *error = NULL; + JS::RootedString ret_jsstr (authority->priv->cx); + JS::UniqueChars ret_str; + gboolean good = FALSE; + + if (!action_and_details_to_jsval (authority, action_id, details, args[0], &error)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error converting action and details to JS object: %s", + error->message); + g_clear_error (&error); + goto out; + } + + if (!subject_to_jsval (authority, + subject, + user_for_subject, + subject_is_local, + subject_is_active, + args[1], + &error)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error converting subject to JS object: %s", + error->message); + g_clear_error (&error); + goto out; + } + + if (!call_js_function_with_runaway_killer (authority, + "_runRules", + args, + &rval)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Error evaluating authorization rules"); + goto out; + } + + if (rval.isNull()) + { + /* this fine, means there was no match, use implicit authorizations */ + good = TRUE; + goto out; + } + + if (!rval.isString()) + { + g_warning ("Expected a string"); + goto out; + } + + ret_jsstr = rval.toString(); + ret_str = JS_EncodeStringToUTF8 (authority->priv->cx, ret_jsstr); + if (ret_str == NULL) + { + g_warning ("Error converting resulting string to UTF-8"); + goto out; + } + + g_strstrip (ret_str.get()); + if (!polkit_implicit_authorization_from_string (ret_str.get(), &ret)) + { + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), + "Returned result `%s' is not valid", + ret_str.get()); + goto out; + } + + good = TRUE; + + out: + if (!good) + ret = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; + + JS_MaybeGC (authority->priv->cx); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static bool +js_polkit_log (JSContext *cx, + unsigned argc, + JS::Value *vp) +{ + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (JS_GetContextPrivate (cx)); + bool ret = false; + JS::UniqueChars s; + + JS::CallArgs args = JS::CallArgsFromVp (argc, vp); + + JS::RootedString jsstr (authority->priv->cx); + jsstr = args[0].toString (); + s = JS_EncodeStringToUTF8 (cx, jsstr); + JS::WarnUTF8 (cx, "%s", s.get()); + + ret = true; + + args.rval ().setUndefined (); /* return undefined */ + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static bool +js_polkit_spawn (JSContext *cx, + unsigned js_argc, + JS::Value *vp) +{ + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (JS_GetContextPrivate (cx)); + bool ret = false; + JS::RootedObject array_object(cx); + gchar *standard_output = NULL; + gchar *standard_error = NULL; + gint exit_status; + GError *error = NULL; + JSString *ret_jsstr; + guint32 array_len; + gchar **argv = NULL; + GMainContext *context = NULL; + GMainLoop *loop = NULL; + SpawnData data = {0}; + guint n; + + JS::CallArgs args = JS::CallArgsFromVp (js_argc, vp); + array_object = &args[0].toObject(); + + if (!JS::GetArrayLength (cx, array_object, &array_len)) + { + JS_ReportErrorUTF8 (cx, "Failed to get array length"); + goto out; + } + + argv = g_new0 (gchar*, array_len + 1); + for (n = 0; n < array_len; n++) + { + JS::RootedValue elem_val(cx); + JS::UniqueChars s; + + if (!JS_GetElement (cx, array_object, n, &elem_val)) + { + JS_ReportErrorUTF8 (cx, "Failed to get element %d", n); + goto out; + } + if (!elem_val.isString()) + { + JS_ReportErrorUTF8 (cx, "Element %d is not a string", n); + goto out; + } + JS::RootedString jsstr (authority->priv->cx); + jsstr = elem_val.toString(); + s = JS_EncodeStringToUTF8 (cx, jsstr); + argv[n] = g_strdup (s.get()); + } + + 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)) + { + JS_ReportErrorUTF8 (cx, + "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); + JS_ReportErrorUTF8 (cx, "%s", gstr->str); + g_string_free (gstr, TRUE); + goto out; + } + + ret = true; + + ret_jsstr = JS_NewStringCopyZ (cx, standard_output); + args.rval ().setString (ret_jsstr); + + 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); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + + +static bool +js_polkit_user_is_in_netgroup (JSContext *cx, + unsigned argc, + JS::Value *vp) +{ + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (JS_GetContextPrivate (cx)); + bool ret = false; + JS::UniqueChars user; + JS::UniqueChars netgroup; + bool is_in_netgroup = false; + + JS::CallArgs args = JS::CallArgsFromVp (argc, vp); + +#ifdef HAVE_SETNETGRENT + JS::RootedString usrstr (authority->priv->cx); + usrstr = args[0].toString(); + user = JS_EncodeStringToUTF8 (cx, usrstr); + JS::RootedString netgstr (authority->priv->cx); + netgstr = args[1].toString(); + netgroup = JS_EncodeStringToUTF8 (cx, netgstr); + + if (innetgr (netgroup.get(), + NULL, /* host */ + user.get(), + NULL)) /* domain */ + { + is_in_netgroup = true; + } +#endif + + ret = true; + + args.rval ().setBoolean (is_in_netgroup); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendjsauthority.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendjsauthority.h new file mode 100644 index 0000000000..6fd283bb2d --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendjsauthority.h @@ -0,0 +1,75 @@ +/* + * 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 */ + + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendprivate.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendprivate.h new file mode 100644 index 0000000000..d6fcdbff4e --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendprivate.h @@ -0,0 +1,29 @@ +/* + * 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 */ diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor-systemd.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor-systemd.c new file mode 100644 index 0000000000..1a6107a21c --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor-systemd.c @@ -0,0 +1,487 @@ +/* + * 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 "config.h" +#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); + + 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 (NULL, &sd_source->monitor)) < 0) + { + g_printerr ("Error getting login monitor: %d", 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))); + } + +#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); +} + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor.c new file mode 100644 index 0000000000..ed3075595d --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor.c @@ -0,0 +1,515 @@ +/* + * 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 "config.h" +#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"); +} + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor.h new file mode 100644 index 0000000000..3972326bf9 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendsessionmonitor.h @@ -0,0 +1,66 @@ +/* + * 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 */ + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendtypes.h b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendtypes.h new file mode 100644 index 0000000000..3777991203 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitbackendtypes.h @@ -0,0 +1,40 @@ +/* + * 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 */ + diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/polkitd.c b/local/recipes/libs/polkit/source/src/polkitbackend/polkitd.c new file mode 100644 index 0000000000..d63aae276b --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/polkitd.c @@ -0,0 +1,265 @@ +/* + * 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 "config.h" + +#include +#include + +#include + +#include +#include + +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ + +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 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", NULL}, + {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), + "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), + "Acquired the name org.freedesktop.PolicyKit1 on the system bus"); +} + +static gboolean +on_sigint (gpointer user_data) +{ + g_print ("Handling SIGINT\n"); + g_main_loop_quit (loop); + 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; + } + + setregid (pw->pw_gid, pw->pw_gid); + 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 (pw->pw_dir) != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Error changing to home 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; + + loop = NULL; + opt_context = NULL; + name_owner_id = 0; + sigint_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); + + authority = polkit_backend_authority_get (); + + loop = g_main_loop_new (NULL, FALSE); + + sigint_id = g_unix_signal_add (SIGINT, + on_sigint, + 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"); + g_main_loop_run (loop); + + g_print ("Shutting down\n"); + out: + if (sigint_id > 0) + g_source_remove (sigint_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; +} diff --git a/local/recipes/libs/polkit/source/src/polkitbackend/toarray.pl b/local/recipes/libs/polkit/source/src/polkitbackend/toarray.pl new file mode 100755 index 0000000000..e430885e9a --- /dev/null +++ b/local/recipes/libs/polkit/source/src/polkitbackend/toarray.pl @@ -0,0 +1,15 @@ +#!/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"; diff --git a/local/recipes/libs/polkit/source/src/programs/meson.build b/local/recipes/libs/polkit/source/src/programs/meson.build new file mode 100644 index 0000000000..03794be62b --- /dev/null +++ b/local/recipes/libs/polkit/source/src/programs/meson.build @@ -0,0 +1,16 @@ +programs = [ + ['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 diff --git a/local/recipes/libs/polkit/source/src/programs/pkaction.c b/local/recipes/libs/polkit/source/src/programs/pkaction.c new file mode 100644 index 0000000000..6dd68776be --- /dev/null +++ b/local/recipes/libs/polkit/source/src/programs/pkaction.c @@ -0,0 +1,227 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} + diff --git a/local/recipes/libs/polkit/source/src/programs/pkcheck.c b/local/recipes/libs/polkit/source/src/programs/pkcheck.c new file mode 100644 index 0000000000..1faf6334e0 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/programs/pkcheck.c @@ -0,0 +1,669 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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)); + + 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; + GTimeVal 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); + + g_get_current_time (&now); + + 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.tv_sec); + expires_rel_str = format_reltime (expires - now.tv_sec); + + 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; +} diff --git a/local/recipes/libs/polkit/source/src/programs/pkexec.c b/local/recipes/libs/polkit/source/src/programs/pkexec.c new file mode 100644 index 0000000000..f5754c78a0 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/programs/pkexec.c @@ -0,0 +1,1081 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#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); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +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 (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 environtment 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 */ +#ifdef __linux__ + if (prctl (PR_SET_PDEATHSIG, SIGTERM) != 0) + { + g_printerr ("prctl(PR_SET_PDEATHSIG, SIGTERM) 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 (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 desriptors\n"); + goto out; + } + + /* if not changing to uid 0, become uid 0 before changing to the user */ + if (pw->pw_uid != 0) + { + 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; + } + setregid (pw->pw_gid, pw->pw_gid); + 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; +} + diff --git a/local/recipes/libs/polkit/source/src/programs/pkttyagent.c b/local/recipes/libs/polkit/source/src/programs/pkttyagent.c new file mode 100644 index 0000000000..4d04e9c5d9 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/programs/pkttyagent.c @@ -0,0 +1,319 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} diff --git a/local/recipes/libs/polkit/source/src/symbol.map b/local/recipes/libs/polkit/source/src/symbol.map new file mode 100644 index 0000000000..2d38225a31 --- /dev/null +++ b/local/recipes/libs/polkit/source/src/symbol.map @@ -0,0 +1,6 @@ +{ +global: + polkit_*; +local: + *; +}; diff --git a/local/recipes/libs/polkit/source/subprojects/mocklibc.wrap b/local/recipes/libs/polkit/source/subprojects/mocklibc.wrap new file mode 100644 index 0000000000..af82298cae --- /dev/null +++ b/local/recipes/libs/polkit/source/subprojects/mocklibc.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = mocklibc-1.0 + +source_url = https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/mocklibc/mocklibc-1.0.tar.gz +source_filename = mocklibc-1.0.tar.gz +source_hash = b2236a6af1028414783e9734a46ea051916ec226479d6a55a3bb823bff68f120 + +patch_url = https://wrapdb.mesonbuild.com/v1/projects/mocklibc/1.0/2/get_zip +patch_filename = mocklibc-1.0-2-wrap.zip +patch_hash = 0280f96a2eeb3c023e5acf4e00cef03d362868218d4a85347ea45137c0ef6c56 diff --git a/local/recipes/libs/polkit/source/subprojects/packagecache/mocklibc-1.0-2-wrap.zip b/local/recipes/libs/polkit/source/subprojects/packagecache/mocklibc-1.0-2-wrap.zip new file mode 100644 index 0000000000000000000000000000000000000000..656ebaf9f1fb34e1ef3d6b1cd404421cbba13a1f GIT binary patch literal 3989 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 Xax%~+CHw0%+1cB1Cc$3nzjpruOm)UZ literal 0 HcmV?d00001 diff --git a/local/recipes/libs/polkit/source/subprojects/packagecache/mocklibc-1.0.tar.gz b/local/recipes/libs/polkit/source/subprojects/packagecache/mocklibc-1.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..568a05337fa216429ec0659ff91179da13eaeb17 GIT binary patch literal 294222 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ℑ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 literal 0 HcmV?d00001 diff --git a/local/recipes/libs/polkit/source/test/data/etc/group b/local/recipes/libs/polkit/source/test/data/etc/group new file mode 100644 index 0000000000..b9acab9721 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/etc/group @@ -0,0 +1,8 @@ +root: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: diff --git a/local/recipes/libs/polkit/source/test/data/etc/netgroup b/local/recipes/libs/polkit/source/test/data/etc/netgroup new file mode 100644 index 0000000000..21a27f9791 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/etc/netgroup @@ -0,0 +1,5 @@ +foo (-,john,) +bar (-,jane,) +baz foo bar +all (,,) +none diff --git a/local/recipes/libs/polkit/source/test/data/etc/passwd b/local/recipes/libs/polkit/source/test/data/etc/passwd new file mode 100644 index 0000000000..5cf14a5620 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/etc/passwd @@ -0,0 +1,7 @@ +root: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 diff --git a/local/recipes/libs/polkit/source/test/data/etc/polkit-1/localauthority.conf.d/10-test.conf b/local/recipes/libs/polkit/source/test/data/etc/polkit-1/localauthority.conf.d/10-test.conf new file mode 100644 index 0000000000..d7a9824660 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/etc/polkit-1/localauthority.conf.d/10-test.conf @@ -0,0 +1,2 @@ +[Configuration] +AdminIdentities=unix-user:root;unix-netgroup:bar;unix-group:admin diff --git a/local/recipes/libs/polkit/source/test/data/etc/polkit-1/localauthority/10-test/com.example.pkla b/local/recipes/libs/polkit/source/test/data/etc/polkit-1/localauthority/10-test/com.example.pkla new file mode 100644 index 0000000000..bc64c5e967 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/etc/polkit-1/localauthority/10-test/com.example.pkla @@ -0,0 +1,14 @@ +[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 + diff --git a/local/recipes/libs/polkit/source/test/data/etc/polkit-1/rules.d/10-testing.rules b/local/recipes/libs/polkit/source/test/data/etc/polkit-1/rules.d/10-testing.rules new file mode 100644 index 0000000000..e346b5dd3f --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/etc/polkit-1/rules.d/10-testing.rules @@ -0,0 +1,198 @@ +/* -*- 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; + } + } +}); diff --git a/local/recipes/libs/polkit/source/test/data/etc/polkit-1/rules.d/15-testing.rules b/local/recipes/libs/polkit/source/test/data/etc/polkit-1/rules.d/15-testing.rules new file mode 100644 index 0000000000..00e214b3f7 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/etc/polkit-1/rules.d/15-testing.rules @@ -0,0 +1,21 @@ +/* -*- 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; + } +}); diff --git a/local/recipes/libs/polkit/source/test/data/usr/share/polkit-1/rules.d/10-testing.rules b/local/recipes/libs/polkit/source/test/data/usr/share/polkit-1/rules.d/10-testing.rules new file mode 100644 index 0000000000..1d553f63b8 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/usr/share/polkit-1/rules.d/10-testing.rules @@ -0,0 +1,17 @@ +/* -*- 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; + } +}); diff --git a/local/recipes/libs/polkit/source/test/data/usr/share/polkit-1/rules.d/20-testing.rules b/local/recipes/libs/polkit/source/test/data/usr/share/polkit-1/rules.d/20-testing.rules new file mode 100644 index 0000000000..071f13504f --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/usr/share/polkit-1/rules.d/20-testing.rules @@ -0,0 +1,21 @@ +/* -*- 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 + } +}); diff --git a/local/recipes/libs/polkit/source/test/data/var/lib/polkit-1/localauthority/10-test/com.example.pkla b/local/recipes/libs/polkit/source/test/data/var/lib/polkit-1/localauthority/10-test/com.example.pkla new file mode 100644 index 0000000000..f013c5b9e0 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/data/var/lib/polkit-1/localauthority/10-test/com.example.pkla @@ -0,0 +1,6 @@ +[Super Secret Project Permissions] +Identity=unix-user:root +Action=com.example.restrictedproduct.* +ResultAny=no +ResultInactive=no +ResultActive=auth_self diff --git a/local/recipes/libs/polkit/source/test/meson.build b/local/recipes/libs/polkit/source/test/meson.build new file mode 100644 index 0000000000..3acdbd4df9 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/meson.build @@ -0,0 +1,28 @@ +libpolkit_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, +) + +libmocklibc = subproject('mocklibc').get_variable('libmocklibc') + +test_data_dir = meson.current_source_dir() / 'data' +test_etc_dir = test_data_dir / 'etc' + +test_env = environment() +test_env.set('LD_PRELOAD', libmocklibc.full_path()) +test_env.set('MOCK_PASSWD', test_etc_dir / 'passwd') +test_env.set('MOCK_GROUP', test_etc_dir / 'group') +test_env.set('MOCK_NETGROUP', test_etc_dir / 'netgroup') +test_env.set('POLKIT_TEST_DATA', test_data_dir) + +subdir('polkit') +if not get_option('libs-only') + subdir('polkitbackend') +endif diff --git a/local/recipes/libs/polkit/source/test/mocklibc/AUTHORS b/local/recipes/libs/polkit/source/test/mocklibc/AUTHORS new file mode 100644 index 0000000000..c2347f6f7d --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/AUTHORS @@ -0,0 +1 @@ +Nikki VonHollen diff --git a/local/recipes/libs/polkit/source/test/mocklibc/COPYING b/local/recipes/libs/polkit/source/test/mocklibc/COPYING new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/local/recipes/libs/polkit/source/test/mocklibc/ChangeLog b/local/recipes/libs/polkit/source/test/mocklibc/ChangeLog new file mode 100644 index 0000000000..00dd245bc3 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/ChangeLog @@ -0,0 +1,10 @@ +2011-12-19 Nikki VonHollen + +* Added check for 'id' and 'innetgr' commands before running tests that depend + on them. 'make check' now passes without them, without running tests. + + +2011-12-14 Nikki VonHollen + +* Released version 1.0 with basic NSS passwd, group, and netgroup mocks. + diff --git a/local/recipes/libs/polkit/source/test/mocklibc/INSTALL b/local/recipes/libs/polkit/source/test/mocklibc/INSTALL new file mode 100644 index 0000000000..7d1c323bea --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/INSTALL @@ -0,0 +1,365 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007, 2008, 2009 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/local/recipes/libs/polkit/source/test/mocklibc/Makefile.am b/local/recipes/libs/polkit/source/test/mocklibc/Makefile.am new file mode 100644 index 0000000000..ed6e1853fa --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/Makefile.am @@ -0,0 +1,5 @@ + +SUBDIRS = src bin +EXTRA_DIST = example + +-include $(top_srcdir)/git.mk diff --git a/local/recipes/libs/polkit/source/test/mocklibc/NEWS b/local/recipes/libs/polkit/source/test/mocklibc/NEWS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/local/recipes/libs/polkit/source/test/mocklibc/README b/local/recipes/libs/polkit/source/test/mocklibc/README new file mode 100644 index 0000000000..2bd44efa7e --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/README @@ -0,0 +1,121 @@ += MockLibc 1.1 = + +Mocks of common libc functions who have global state. Version 1.1 focuses on +NSS related methods (user, group, and netgroup queries). + +This library is a re-implementation of specific libc methods, not a tool for +creating mock functions. Use MockLibc to create a consistent environment for +your unit tests, when they need to query system information. + + +== Requirements == + +* Tests require the 'id' and 'innetgr' commands in the PATH + + +== Build == + +$ cd mocklibc-1.1 +$ ./configure +$ make +$ make check + + +== Install == + +$ make install + + +== Example Usage == + +$ id foo +id: foo: No such user +$ export MOCK_PASSWD=./testdata/passwd +$ export MOCK_GROUP=./testdata/group +$ mkdir ./testdata +$ echo “foo:x:9000:9000::/home/foo:/bin/bash” > “$MOCK_PASSWD” +$ echo “mockusers:x:9001:foo” > “$MOCK_GROUP” +$ mocklibc id foo +uid=9000(foo) gid=9000(foo) groups=9000(foo),9001(mockusers) + + +== Use without install == + +mocklibc can be used directly from the bin directory, without being installed: +$ cd mocklibc-1.1 +$ ./configure +$ make +$ bin/mocklibc id foo + + +== Hacking == + +If using a git checkout instead of a source tarball, always run +'autogen --install' before './configure'. Whenever a Makefile.am or +configure.ac is modified, run 'autogen' again without --install. + + +== Mocked Functions == + +NSS Methods completely disregard /etc/nsswitch.conf, similar to using just +"files", but with modified paths. DNS is not modified and no *_r methods will +be implemented in this version. + +* pwd.h (NSS users, configured with MOCK_PASSWD) + * setpwent + * getpwent + * endpwent + * getpwnam + * getpwuid +* grp.h (NSS groups, configured with MOCK_GROUP) + * setpwent + * getpwent + * endpwent + * getpwnam + * getpwuid +* netdb.h (NSS netgroups, no DNS, configured with MOCK_NETGROUP) + * setnetgrent + * getnetgrent + * endnetgrent + * innetgr + + +== Configuration == + +All configuration is handled through environment variables, though specific +mocklibc_* methods may be added in the future for things like time and random +number generation. + +Environment Variables: +* MOCK_PASSWD - Path to /etc/passwd replacement +* MOCK_GROUP - Path to /etc/group replacement +* MOCK_NETGROUP - Path to /etc/netgroup replacement + + +== F.A.Q. == + +* Why not use a chroot? Chroot requires root, and forcing unit tests to run as + root is not desirable. +* Is there something that already does this? There are mock frameworks for C, + but this library is an implementation of specific common mocks C developers + need. A mock of set/get/endgrent still requires some basic code for iterating + group objects. This library provides that. + + +== TODO == + +* Add functions to free unused memory in 'netdb_netgroup.c'. It leaks a ton of + memory every call. See TODO comments in code. + + +== Future == + +The following may be supported in the future, and I'm taking requests for other +functionality at 'vonhollen@gmail.com'. + +Features: +* Redirect syslog messages to file at $MOCK_SYSLOG +* '*_r' methods in pwd.h, grp.h, and netdb.h +* netdb.h: gethostbyname, gethostbyaddr, getaddrinfo, get/freeaddrinfo +* Whitelist apps with $MOCK_ONLY (includes list of argv[0] names) + diff --git a/local/recipes/libs/polkit/source/test/mocklibc/bin/Makefile.am b/local/recipes/libs/polkit/source/test/mocklibc/bin/Makefile.am new file mode 100644 index 0000000000..499c5c787b --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/bin/Makefile.am @@ -0,0 +1,27 @@ + +bin_SCRIPTS = mocklibc + +check_SCRIPTS = mocklibc-test +TESTS = mocklibc-test + +EXTRA_DIST = mocklibc.in mocklibc-test.in +CLEANFILES = mocklibc mocklibc-test + + +# Substitute build variables in shell scripts +# See section "4.8.2 Installation Directory Variables" in autoconf manual + +edit = sed \ + -e 's|@libdir[@]|$(libdir)|g' \ + -e 's|@libname[@]|$(libname)|g' \ + -e 's|@top_srcdir[@]|$(top_srcdir)|g' \ + -e 's|@top_builddir[@]|$(top_builddir)|g' + +mocklibc mocklibc-test: Makefile + $(edit) $(srcdir)/$@.in > $@ + chmod a+x $@ + +mocklibc: $(srcdir)/mocklibc.in +mocklibc-test: $(srcdir)/mocklibc-test.in + +-include $(top_srcdir)/git.mk diff --git a/local/recipes/libs/polkit/source/test/mocklibc/bin/mocklibc-test.in b/local/recipes/libs/polkit/source/test/mocklibc/bin/mocklibc-test.in new file mode 100644 index 0000000000..9f00a77dec --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/bin/mocklibc-test.in @@ -0,0 +1,136 @@ +#!/bin/bash + +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Nikki VonHollen + + +# Figure out where everything is + +MOCKLIBC="@top_builddir@/bin/mocklibc" +ETCDIR="@top_srcdir@/example" + + +# Setup the mock environment + +export MOCK_PASSWD="${ETCDIR}/passwd" +export MOCK_GROUP="${ETCDIR}/group" +export MOCK_NETGROUP="${ETCDIR}/netgroup" + + +# Test helper definitions + +TESTCOUNT=0 +FAILCOUNT=0 + +fail () { + echo "Test Failed:" + echo $@ >&2 + echo + FAILCOUNT=$((FAILCOUNT+1)) +} + +finish () { + if [[ $FAILCOUNT -gt 0 ]] + then + echo "Failed $FAILCOUNT of $TESTCOUNT tests." + exit 1 + else + echo "Passed $TESTCOUNT tests." + exit 0 + fi +} + +assert_true () { + $MOCKLIBC $@ || fail "assert true: $@" + TESTCOUNT=$((TESTCOUNT+1)) +} + +assert_false () { + $MOCKLIBC $@ && fail "assert false: $@" + TESTCOUNT=$((TESTCOUNT+1)) +} + +assert_grep () { + $MOCKLIBC ${@:2} | grep -q "^${1}\$" || fail "'$1' doesn't match output of: ${@:2}" + TESTCOUNT=$((TESTCOUNT+1)) +} + + +# Test implementations + +test_passwd () { + # Test user ids + assert_grep "0" id -u root + assert_grep "500" id -u john + assert_grep "501" id -u jane + + # Test primary groups + assert_grep "root" id -gn root + assert_grep "john" id -gn john + assert_grep "jane" id -gn jane +} + +test_group () { + # Test group lists for users + assert_grep "root" id -Gn root + assert_grep "john users" id -Gn john + assert_grep "jane users" id -Gn jane +} + +test_netgroup () { + # Test whether each user is each netgroup + assert_true innetgr foo -u john + assert_false innetgr foo -u jane + + assert_true innetgr bar -u jane + assert_false innetgr bar -u john + + assert_true innetgr baz -u john + assert_true innetgr baz -u jane + assert_false innetgr baz -u henry + + assert_true innetgr all -u john + assert_true innetgr all -u jane + assert_true innetgr all -u henry + + assert_false innetgr none -u john + assert_false innetgr none -u jane + assert_false innetgr none -u henry + + assert_false innetgr fake -u john +} + + +# Run the tests and print a report + +if (which id >/dev/null 2>&1) +then + test_passwd + test_group +else + echo "No 'id' command found, skipping passwd and group tests." >&2 +fi + +if (which innetgr >/dev/null 2>&1) +then + test_netgroup +else + echo "No 'innetgr' command found, skipping netgroup tests." >&2 +fi + + +finish + diff --git a/local/recipes/libs/polkit/source/test/mocklibc/bin/mocklibc.in b/local/recipes/libs/polkit/source/test/mocklibc/bin/mocklibc.in new file mode 100644 index 0000000000..dc18855ad8 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/bin/mocklibc.in @@ -0,0 +1,34 @@ +#!/bin/bash + +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Nikki VonHollen + + +BASEDIR=`dirname $0` +LIBDIR="${BASEDIR}/../src/.libs" +LIBPATH="${LIBDIR}/@libname@" + +if [[ -f "$LIBPATH" ]] +then + # Include Mocklibc's project build dir if we can find it + export LD_LIBRARY_PATH="${LIBDIR}:${LD_LIBRARY_PATH}" +else + # Use the system version instead, w/o requiring ldconfig + export LD_LIBRARY_PATH="@libdir@:${LD_LIBRARY_PATH}" +fi + +# Exec the requested app, replacing this one +LD_PRELOAD="@libname@" exec $@ diff --git a/local/recipes/libs/polkit/source/test/mocklibc/configure.ac b/local/recipes/libs/polkit/source/test/mocklibc/configure.ac new file mode 100644 index 0000000000..5bd3243c3e --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/configure.ac @@ -0,0 +1,38 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.63]) +AC_INIT([MockLibc], [1.1], [vonhollen@google.com]) +AC_CONFIG_SRCDIR([src]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +AC_PROG_LIBTOOL + +# Checks for header files. +AC_CHECK_HEADERS([netdb.h stdlib.h string.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_UID_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_CHECK_FUNCS([endgrent endpwent memset regcomp strdup]) + +# Build wrapper scripts from templates +AC_SUBST([libname], [libmocklibc.so]) +#AC_CONFIG_FILES([bin/mocklibc], [chmod +x bin/mocklibc], [libname=${libname}]) +#AC_CONFIG_FILES([bin/mocklibc-test], [chmod +x bin/mocklibc-test], +# [libname=${libname}])) + +AC_OUTPUT([ +Makefile +src/Makefile +bin/Makefile +]) diff --git a/local/recipes/libs/polkit/source/test/mocklibc/example/group b/local/recipes/libs/polkit/source/test/mocklibc/example/group new file mode 100644 index 0000000000..e2253f062b --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/example/group @@ -0,0 +1,4 @@ +root:x:0: +users:x:100:john,jane +john:x:500: +jane:x:501: diff --git a/local/recipes/libs/polkit/source/test/mocklibc/example/netgroup b/local/recipes/libs/polkit/source/test/mocklibc/example/netgroup new file mode 100644 index 0000000000..21a27f9791 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/example/netgroup @@ -0,0 +1,5 @@ +foo (-,john,) +bar (-,jane,) +baz foo bar +all (,,) +none diff --git a/local/recipes/libs/polkit/source/test/mocklibc/example/passwd b/local/recipes/libs/polkit/source/test/mocklibc/example/passwd new file mode 100644 index 0000000000..62aa6f5e1c --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/example/passwd @@ -0,0 +1,3 @@ +root:x:0:0:root:/root:/bin/bash +john:x:500:500:John Smith:/home/john:/bin/bash +jane:x:501:501:Jane Doe:/home/jane:/bin/bash diff --git a/local/recipes/libs/polkit/source/test/mocklibc/src/Makefile.am b/local/recipes/libs/polkit/source/test/mocklibc/src/Makefile.am new file mode 100644 index 0000000000..1103b9dc47 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/src/Makefile.am @@ -0,0 +1,10 @@ + +lib_LTLIBRARIES = libmocklibc.la +libmocklibc_la_SOURCES = pwd.c grp.c netdb.c netgroup.c netgroup.h + +bin_PROGRAMS = mocklibc-debug-netgroup +mocklibc_debug_netgroup_SOURCES = netgroup-debug.c netgroup-debug.h +mocklibc_debug_netgroup_LDADD = libmocklibc.la + + +-include $(top_srcdir)/git.mk diff --git a/local/recipes/libs/polkit/source/test/mocklibc/src/grp.c b/local/recipes/libs/polkit/source/test/mocklibc/src/grp.c new file mode 100644 index 0000000000..c671e1fc2d --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/src/grp.c @@ -0,0 +1,156 @@ +/** + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Nikki VonHollen + */ + +#include + +#include +#include +#include +#include + +#define GROUP_CONFIG_KEY "MOCK_GROUP" + +static FILE *global_stream = NULL; + +void setgrent(void) { + if (global_stream) + endgrent(); + + const char *path = getenv(GROUP_CONFIG_KEY); + if (!path) + return; + + global_stream = fopen(path, "r"); +} + +struct group *getgrent(void) { + if (!global_stream) + setgrent(); + + if (!global_stream) + return NULL; + + return fgetgrent(global_stream); +} + +void endgrent(void) { + if (!global_stream) + return; + + fclose(global_stream); + global_stream = NULL; +} + +struct group *getgrnam(const char *name) { + const char *path = getenv(GROUP_CONFIG_KEY); + if (!path) + return NULL; + + FILE *stream = fopen(path, "r"); + if (!stream) + return NULL; + + struct group *entry; + while ((entry = fgetgrent(stream))) { + if (strcmp(entry->gr_name, name) == 0) { + fclose(stream); + return entry; + } + } + + fclose(stream); + return NULL; +} + +struct group *getgrgid(gid_t gid) { + const char *path = getenv(GROUP_CONFIG_KEY); + if (!path) + return NULL; + + FILE *stream = fopen(path, "r"); + if (!stream) + return NULL; + + struct group *entry; + while ((entry = fgetgrent(stream))) { + if (entry->gr_gid == gid) { + fclose(stream); + return entry; + } + } + + fclose(stream); + return NULL; +} + +int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) { + const char *path = getenv(GROUP_CONFIG_KEY); + if (!path) { + *ngroups = 0; + return -1; + } + + FILE *stream = fopen(path, "r"); + if (!stream) { + *ngroups = 0; + return -1; + } + + int default_group_found = 0; + int groups_found = 0; + + // Loop through all groups + struct group *entry; + while ((entry = fgetgrent(stream))) { + // Loop through all users in group + char **cur_user; + for (cur_user = entry->gr_mem; *cur_user; cur_user++) { + // Skip users who don't match arg 'user' + if (strcmp(*cur_user, user)) + continue; + + // Is this the default group? if so, flag it + if (entry->gr_gid == group) + default_group_found = 1; + + // Only insert new entries if we have room + if (groups_found < *ngroups) { + groups[groups_found] = entry->gr_gid; + } + + groups_found++; + } + } + + // Include the default group if it wasn't found + if (!default_group_found) { + if (groups_found < *ngroups) { + groups[groups_found] = group; + } + groups_found++; + } + + // Did we have to leave out some groups? If not, tell how many we found. + int retval = (groups_found > *ngroups) ? -1 : groups_found; + + // Always tell the user how many groups we found via *ngroups + *ngroups = groups_found; + + fclose(stream); + return retval; +} diff --git a/local/recipes/libs/polkit/source/test/mocklibc/src/netdb.c b/local/recipes/libs/polkit/source/test/mocklibc/src/netdb.c new file mode 100644 index 0000000000..944240323b --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/src/netdb.c @@ -0,0 +1,100 @@ +/** + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Nikki VonHollen + */ + +#include "netgroup.h" + +#include + +#include +#include +#include +#include + +#define INNETGR_CHECK(match, value) if (match && value && strcmp(match, value)) continue; + +/** Private static data. */ + +static struct netgroup *global_netgroup_head = NULL; +static struct netgroup_iter global_iter; + +/** Public methods */ + +// REMEMBER: 1 means success, 0 means failure for netgroup methods + +int setnetgrent(const char *netgroup) { + if (!global_netgroup_head) + global_netgroup_head = netgroup_parse_all(); + + struct netgroup *group = netgroup_find(global_netgroup_head, netgroup); + if (!group) { + netgroup_free_all(global_netgroup_head); + global_netgroup_head = NULL; + return 0; + } + + netgroup_iter_init(&global_iter, group); + return 1; +} + +void endnetgrent(void) { + netgroup_free_all(global_netgroup_head); + global_netgroup_head = NULL; +} + +int getnetgrent(char **host, char **user, char **domain) { + if (!global_netgroup_head) + return 0; + + struct entry *result = netgroup_iter_next(&global_iter); + if (!result) + return 0; + + *host = result->data.triple.hostname; + *user = result->data.triple.username; + *domain = result->data.triple.domainname; + return 1; +} + +int innetgr(const char *netgroup, const char *host, const char *user, + const char *domain) { + int retval = 0; + struct netgroup *head = netgroup_parse_all(); + struct netgroup *group = netgroup_find(head, netgroup); + if (!group) { + // Can't find group + netgroup_free_all(head); + return 0; + } + + struct netgroup_iter iter; + netgroup_iter_init(&iter, group); + + struct entry *cur; + while ((cur = netgroup_iter_next(&iter))) { + INNETGR_CHECK(host, cur->data.triple.hostname); + INNETGR_CHECK(user, cur->data.triple.username); + INNETGR_CHECK(domain, cur->data.triple.domainname); + + // No INNETGR_CHECK failed, so we matched! + retval = 1; + break; + } + + netgroup_free_all(head); + return retval; +} diff --git a/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup-debug.c b/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup-debug.c new file mode 100644 index 0000000000..81d6e7285b --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup-debug.c @@ -0,0 +1,84 @@ +/** + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Nikki VonHollen + */ + +#include "netgroup-debug.h" + +#include +#include + +void netgroup_debug_print_entry(struct entry *entry, FILE *stream, unsigned int indent) { + print_indent(stream, indent); + + if (entry->type == TRIPLE_ENTRY) { + fprintf(stream, "triple (%s,%s,%s)\n", + entry->data.triple.hostname, + entry->data.triple.username, + entry->data.triple.domainname); + } else if (entry->type == CHILD_ENTRY) { + fprintf(stream, "child '%s'\n", entry->data.child.name); + struct entry *child; + for (child = entry->data.child.head; child; child = child->next) { + netgroup_debug_print_entry(child, stream, indent + 1); + } + } else { + fprintf(stream, "UNKNOWN_TYPE"); + } +} + +void netgroup_debug_print_group(struct netgroup *group, FILE *stream, unsigned int indent) { + print_indent(stream, indent); + fprintf(stream, "%s\n", group->name); + struct entry *entry; + for (entry = group->head; entry; entry = entry->next) { + netgroup_debug_print_entry(entry, stream, indent + 1); + } +} + +void netgroup_debug_print_group_unrolled(struct netgroup *group, FILE *stream, unsigned int indent) { + print_indent(stream, indent); + fprintf(stream, "%s\n", group->name); + + struct netgroup_iter iter; + netgroup_iter_init(&iter, group); + + struct entry *entry; + while ((entry = netgroup_iter_next(&iter))) { + netgroup_debug_print_entry(entry, stream, indent + 1); + } +} + +void netgroup_debug_print_all(struct netgroup *head, FILE *stream, unsigned int indent) { + struct netgroup *group; + for (group = head; group; group = group->next) { + netgroup_debug_print_group(group, stream, indent); + } +} + +int main(int argc, char **argv) { + struct netgroup *groups = netgroup_parse_all(); + if (argc == 1) + netgroup_debug_print_all(groups, stdout, 0); + else if (argc == 2) { + struct netgroup *group = netgroup_find(groups, argv[1]); + if (!group) + return 1; + netgroup_debug_print_group_unrolled(group, stdout, 0); + } + + return 0; +} diff --git a/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup-debug.h b/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup-debug.h new file mode 100644 index 0000000000..d733c958ac --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup-debug.h @@ -0,0 +1,58 @@ +/** + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Nikki VonHollen + */ + +#ifndef NETGROUP_DEBUG_H_ +#define NETGROUP_DEBUG_H_ + +#include "netgroup.h" + +#include + +/** + * Print entry and it's children to the given stream. + * @param entry Netgroup entry to print + * @param stream Stream to print to + * @param indent Number of indents to use + */ +void netgroup_debug_print_entry(struct entry *entry, FILE *stream, unsigned int indent); + +/** + * Print a single netgroup to the given stream. + * @param group Netgroup to print + * @param stream Stream to print to + * @param indent Number of indents to use + */ +void netgroup_debug_print_group(struct netgroup *group, FILE *stream, unsigned int indent); + +/** + * Print a single netgroup with all triples included recursively. + * @param group Netgroup to print + * @param stream Stream to print to + * @param indent Number of indents to use + */ +void netgroup_debug_print_group_unrolled(struct netgroup *group, FILE *stream, unsigned int indent); + +/** + * Print all netgroups to the given stream. + * @param head Head of list of netgroups + * @param stream Stream to print to + * @param indent Number of indents to use + */ +void netgroup_debug_print_all(struct netgroup *head, FILE *stream, unsigned int indent); + +#endif diff --git a/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup.c b/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup.c new file mode 100644 index 0000000000..06a8a89413 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup.c @@ -0,0 +1,345 @@ +/** + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Nikki VonHollen + */ + +#include "netgroup.h" + +#include +#include +#include +#include +#include +#include + +#define NETGROUP_CONFIG_KEY "MOCK_NETGROUP" +#define NETGROUP_TRIPLE_REGEX "\\(([^,]*),([^,]*),([^\\)]*)\\)" +#define FREE_IF_NOT_NULL(ptr) if (ptr) free(ptr) + +/** Private methods. */ + +/** + * Move the given pointer past any whitespace. + * @param cur Pointer to string (char *) to advance + */ +static void parser_skip_whitespace(char **cur) { + for (; isspace(**cur); (*cur)++) {} +} + +/** + * Copy the next group of non-space characters and move the pointer past + * consumed characters. + * @param cur Pointer to string (char *) to search/advance + * @return Copy of chars consumed. Must be free'd by user. + */ +static char *parser_copy_word(char **cur) { + char *value = *cur; + size_t i; + + // Find the next non-null non-space character + for (i = 0; !isspace(value[i]) && value[i] != '\0'; i++) {} + + // Don't allocate zero-length strings, just die + if (i == 0) { + return NULL; + } + + // Allocate the new string, with room for a null terminator + char *result = malloc(i + 1); + if (!result) { + return NULL; + } + + // Set the current pointer past the parsed region + *cur += i; + + memcpy(result, value, i); + result[i] = '\0'; + return result; +} + +/** + * Print a varaible indentation to the stream. + * @param stream Stream to print to + * @param indent Number of indents to use + */ +void print_indent(FILE *stream, unsigned int indent) { + int i; + for (i = 0; i < indent; i++) + fprintf(stream, " "); +} + +/** + * Connect entries with 'child' type to their child entries. + * @param headentry Head of list of entries that need to be connected + * @param headgroup Head of list of netgroups to connect child entries to + */ +static void netgroup_connect_children(struct entry *headentry, struct netgroup *headgroup) { + struct entry *curentry; + for (curentry = headentry; curentry; curentry = curentry->next) { + // Skip entries that don't have children + if (curentry->type != CHILD_ENTRY) + continue; + + // Set the entry's children to the head of the netgroup with the same name + struct netgroup *group = netgroup_find(headgroup, curentry->data.child.name); + if (group) + curentry->data.child.head = group->head; + } +} + + +/* Public methods. */ + +struct netgroup *netgroup_parse_all() { + const char *path = getenv(NETGROUP_CONFIG_KEY); + if (!path) + return NULL; + + FILE *stream = fopen(path, "r"); + if (!stream) + return NULL; + + struct netgroup *headgroup = NULL; + struct netgroup *lastgroup = NULL; + + // Parse netgroups but don't fill in child entry pointers + for (;;) { + size_t line_alloc = 0; + char * line = NULL; + ssize_t line_size = getline(&line, &line_alloc, stream); + if (line_size == -1) + { + free(line); + break; + } + + struct netgroup *nextgroup = netgroup_parse_line(line); + free(line); + if (!nextgroup) + continue; + + if (!headgroup) { + headgroup = nextgroup; + lastgroup = nextgroup; + } else { + lastgroup->next = nextgroup; + lastgroup = nextgroup; + } + } + + fclose(stream); + + // Fill in child entry pointers + struct netgroup *curgroup; + for (curgroup = headgroup; curgroup; curgroup = curgroup->next) { + netgroup_connect_children(curgroup->head, headgroup); + } + + return headgroup; +} + +void netgroup_free_all(struct netgroup *head) { + struct netgroup *group = head; + struct netgroup *nextgroup; + while (group) { + nextgroup = group->next; + netgroup_free(group); + group = nextgroup; + } +} + +struct netgroup *netgroup_parse_line(char *line) { + char *cur = line; + + // Get the netgroup's name + parser_skip_whitespace(&cur); + char *group_name = parser_copy_word(&cur); + if (!group_name) + return NULL; + + // Create new netgroup object + struct netgroup *result = malloc(sizeof(struct netgroup)); + if (!result) + return NULL; + result->next = NULL; + result->name = group_name; + result->head = NULL; + + // Fill in netgroup entries + struct entry* lastentry = NULL; + for (;;) { + // Get the next word (anything non-space and non-null) + parser_skip_whitespace(&cur); + char *word = parser_copy_word(&cur); + if (!word) + break; + + // Parse the entry + struct entry *entry = netgroup_parse_entry(word); + free(word); + if (!entry) + continue; + + // Connect the entries together in a singly-linked list + if (lastentry) { + lastentry->next = entry; + } else { + result->head = entry; + } + + lastentry = entry; + } + + return result; +} + +void netgroup_free(struct netgroup *group) { + if (!group) + return; + + free(group->name); + netgroup_entry_free_all(group->head); + free(group); +} + +struct entry *netgroup_parse_entry(const char *value) { + // Initialize the regex to match triples only on first call + static int regex_needs_init = 1; + static regex_t regex_triple; + if (regex_needs_init) { + if (regcomp(®ex_triple, NETGROUP_TRIPLE_REGEX, REG_EXTENDED)) + return NULL; + regex_needs_init = 0; + } + + struct entry *result = malloc(sizeof(struct entry)); + if (!result) + return NULL; + + memset(result, 0, sizeof(struct entry)); + + regmatch_t regex_triple_match [4]; + if (regexec(®ex_triple, value, 4, regex_triple_match, 0) == REG_NOMATCH) { + // Match failed, assume entry is a netgroup name + result->type = CHILD_ENTRY; + result->data.child.name = strdup(value); + if (!result->data.child.name) { + netgroup_entry_free(result); + return NULL; + } + } else { + // Match success, entry is a triple + result->type = TRIPLE_ENTRY; + + // Array of pointers to fields to set in triple + char ** triple [3] = { + &result->data.triple.hostname, + &result->data.triple.username, + &result->data.triple.domainname }; + int i; + + // Loop through each potential field in triple + for (i = 0; i < 3; i++) { + regoff_t start = regex_triple_match[i + 1].rm_so; + regoff_t end = regex_triple_match[i + 1].rm_eo; + regoff_t len = end - start; + + if (start == -1 || len == 0) { + // This field is empty, so it matches anything + *triple[i] = NULL; + } else { + // Allocate and copy new field for triple + char *field = malloc(len + 1); + if (!field) { + netgroup_entry_free(result); + return NULL; + } + memcpy(field, &value[start], len); + field[len] = '\0'; + *triple[i] = field; + } + } + } + return result; +} + +void netgroup_entry_free_all(struct entry *head) { + struct entry *entry = head; + struct entry *nextentry; + while (entry) { + nextentry = entry->next; + netgroup_entry_free(entry); + entry = nextentry; + } +} + +void netgroup_entry_free(struct entry *entry) { + if (!entry) + return; + + if (entry->type == TRIPLE_ENTRY) { + FREE_IF_NOT_NULL(entry->data.triple.hostname); + FREE_IF_NOT_NULL(entry->data.triple.username); + FREE_IF_NOT_NULL(entry->data.triple.domainname); + } else { + FREE_IF_NOT_NULL(entry->data.child.name); + } + + free(entry); +} + +struct netgroup *netgroup_find(struct netgroup *head, const char *name) { + struct netgroup *group; + for (group = head; group && strcmp(group->name, name); group = group->next) {} + return group; +} + +void netgroup_iter_init(struct netgroup_iter *iter, struct netgroup *group) { + iter->stack[0] = group->head; + iter->depth = 0; +} + +struct entry *netgroup_iter_next(struct netgroup_iter *iter) { + while (iter->depth >= 0) { + struct entry *cur = iter->stack[iter->depth]; + + if (!cur) { + // Pop current finished entry off stack + iter->depth--; + } else if (cur->type == CHILD_ENTRY) { + // Replace the current location on the stack with the next sibling + iter->stack[iter->depth] = cur->next; + + // Grow the stack + iter->depth++; + if (iter->depth >= NETGROUP_MAX_DEPTH) { + iter->depth = -1; + return NULL; // Too much recursion + } + + // Put this entry's children on top of the stack + struct entry *child = cur->data.child.head; + iter->stack[iter->depth] = child; + } else { + // Replace the current location on the stack with the next sibling + iter->stack[iter->depth] = cur->next; + return cur; + } + } + + return NULL; +} diff --git a/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup.h b/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup.h new file mode 100644 index 0000000000..11cf7ebfef --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/src/netgroup.h @@ -0,0 +1,144 @@ +/** + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Nikki VonHollen + */ + +#ifndef NETGROUP_H_ +#define NETGROUP_H_ + +#define NETGROUP_MAX_DEPTH 32 + +/** + * Netgroup with a name and list of entries. + */ +struct netgroup; + +/** + * Entry in a netgroup, either a triple or sub-group (child). + */ +struct entry; + +struct netgroup { + /* Next netgroup in list. */ + struct netgroup *next; // Next netgroup in list + + /* Netgroup name. */ + char *name; + + /* First entry in list of entries. */ + struct entry *head; +}; + +struct entry { + /* Next entry in list of entries for the parent netgroup. */ + struct entry *next; + + /* Entry type is triple (host,user,domain) or child (netgroup name). */ + enum {CHILD_ENTRY, TRIPLE_ENTRY} type; + + union { + /* Child data if entry is a netgroup name. */ + struct { + /* Child netgroup name. */ + char *name; + + /* Pointer to first entry in child netgroup. */ + struct entry *head; + } child; + + /* Triple data if entry type is triple. */ + struct { + char *hostname; + char *username; + char *domainname; + } triple; + } data; +}; + +/* Recursive netgroup entry iterator. */ +struct netgroup_iter { + struct entry *stack [NETGROUP_MAX_DEPTH]; + int depth; +}; + + +/** + * Load full netgroup database into memory. + * @return Head netgroup + */ +struct netgroup *netgroup_parse_all(); + +/** + * Free a list of netgroups. + * @param head Head of list of netgroups + */ +void netgroup_free_all(struct netgroup *head); + +/** + * Parse a single netgroup. + * @param line Line for netgroup definition + * @return Single netgroup with list of netgroup entries + */ +struct netgroup *netgroup_parse_line(char *line); + +/** + * Free single netgroup. + * @param group Netgroup to free + */ +void netgroup_free(struct netgroup *group); + +/** + * Parse a single netgroup entry. + * @param value Entry triple or name as string + * @return Single netgroup entry + */ +struct entry *netgroup_parse_entry(const char *value); + +/** + * Free a list of netgroup entries. + * @param head Head of list of entries + */ +void netgroup_entry_free_all(struct entry *head); + +/** + * Free a single netgroup entry. + * @param entry Netgroup entry to free + */ +void netgroup_entry_free(struct entry *entry); + +/** + * Find netgroup with given name. + * @param head Head of list of netgroups + * @param name Name to find + * @return Netgroup with name or NULL if not found + */ +struct netgroup *netgroup_find(struct netgroup *head, const char *name); + +/** + * Create recursive iterator over all entries in a netgroup. + * @param iter Pointer to iterator struct + * @param group Group to iterate over + */ +void netgroup_iter_init(struct netgroup_iter *iter, struct netgroup *group); + +/** + * Get the next entry in the netgroup iterator. + * @param iter Pointer to iterator struct + * @return Netgroup entry of type triple, or NULL if done iterating + */ +struct entry *netgroup_iter_next(struct netgroup_iter *iter); + +#endif diff --git a/local/recipes/libs/polkit/source/test/mocklibc/src/pwd.c b/local/recipes/libs/polkit/source/test/mocklibc/src/pwd.c new file mode 100644 index 0000000000..6005a1d776 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/mocklibc/src/pwd.c @@ -0,0 +1,99 @@ +/** + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Nikki VonHollen + */ + +#include + +#include +#include +#include +#include + +#define PASSWD_CONFIG_KEY "MOCK_PASSWD" + +static FILE *global_stream = NULL; + +void setpwent(void) { + if (global_stream) + endpwent(); + + const char *path = getenv(PASSWD_CONFIG_KEY); + if (!path) + return; + + global_stream = fopen(path, "r"); +} + +struct passwd *getpwent(void) { + if (!global_stream) + setpwent(); + + if (!global_stream) + return NULL; + + return fgetpwent(global_stream); +} + +void endpwent(void) { + if (!global_stream) + return; + + fclose(global_stream); + global_stream = NULL; +} + +struct passwd *getpwnam(const char *name) { + const char *path = getenv(PASSWD_CONFIG_KEY); + if (!path) + return NULL; + + FILE *stream = fopen(path, "r"); + if (!stream) + return NULL; + + struct passwd *entry; + while ((entry = fgetpwent(stream))) { + if (strcmp(entry->pw_name, name) == 0) { + fclose(stream); + return entry; + } + } + + fclose(stream); + return NULL; +} + +struct passwd *getpwuid(uid_t uid) { + const char *path = getenv(PASSWD_CONFIG_KEY); + if (!path) + return NULL; + + FILE *stream = fopen(path, "r"); + if (!stream) + return NULL; + + struct passwd *entry; + while ((entry = fgetpwent(stream))) { + if (entry->pw_uid == uid) { + fclose(stream); + return entry; + } + } + + fclose(stream); + return NULL; +} diff --git a/local/recipes/libs/polkit/source/test/polkit/meson.build b/local/recipes/libs/polkit/source/test/polkit/meson.build new file mode 100644 index 0000000000..9c5254f668 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkit/meson.build @@ -0,0 +1,27 @@ +test_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, + exe, + env: test_env, + is_parallel: false, + ) +endforeach diff --git a/local/recipes/libs/polkit/source/test/polkit/polkitidentitytest.c b/local/recipes/libs/polkit/source/test/polkit/polkitidentitytest.c new file mode 100644 index 0000000000..2635c4c4c6 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkit/polkitidentitytest.c @@ -0,0 +1,199 @@ +/* + * 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); + } +} + + +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 (); +} diff --git a/local/recipes/libs/polkit/source/test/polkit/polkitunixgrouptest.c b/local/recipes/libs/polkit/source/test/polkit/polkitunixgrouptest.c new file mode 100644 index 0000000000..6c4959ef9d --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkit/polkitunixgrouptest.c @@ -0,0 +1,81 @@ +/* + * 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 (); +} diff --git a/local/recipes/libs/polkit/source/test/polkit/polkitunixnetgrouptest.c b/local/recipes/libs/polkit/source/test/polkit/polkitunixnetgrouptest.c new file mode 100644 index 0000000000..e1d211e476 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkit/polkitunixnetgrouptest.c @@ -0,0 +1,77 @@ +/* + * 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 (); +} diff --git a/local/recipes/libs/polkit/source/test/polkit/polkitunixusertest.c b/local/recipes/libs/polkit/source/test/polkit/polkitunixusertest.c new file mode 100644 index 0000000000..fc4c77492f --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkit/polkitunixusertest.c @@ -0,0 +1,103 @@ +/* + * 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 (); +} diff --git a/local/recipes/libs/polkit/source/test/polkitbackend/meson.build b/local/recipes/libs/polkit/source/test/polkitbackend/meson.build new file mode 100644 index 0000000000..9fae1551b0 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkitbackend/meson.build @@ -0,0 +1,33 @@ +test_unit = 'test-polkitbackendjsauthority' + +deps = [ + libpolkit_gobject_dep, + libpolkit_test_helper_dep, +] + +c_flags = [ + '-D_POLKIT_COMPILATION', + '-D_POLKIT_BACKEND_COMPILATION', +] + +test_env.set('TOP_BUILD_DIR', build_root) +test_env.set('TOP_SRC_DIR', source_root) + +exe = executable( + test_unit, + test_unit + '.c', + include_directories: top_inc, + dependencies: deps, + c_args: c_flags, + link_with: libpolkit_backend, +) + +prog = find_program('polkitbackendjsauthoritytest-wrapper.py') + +test( + test_unit, + prog, + env: test_env, + is_parallel: false, + timeout: 90, +) diff --git a/local/recipes/libs/polkit/source/test/polkitbackend/polkitbackendjsauthoritytest-wrapper.py b/local/recipes/libs/polkit/source/test/polkitbackend/polkitbackendjsauthoritytest-wrapper.py new file mode 100755 index 0000000000..8e067eb2ba --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkitbackend/polkitbackendjsauthoritytest-wrapper.py @@ -0,0 +1,80 @@ +#!/usr/bin/python3 + +# This program 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 3 of the License, or (at your option) any +# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text +# of the license. + +__author__ = 'Bastien Nocera' +__email__ = 'hadess@hadess.net' +__copyright__ = '(c) 2020 Red Hat Inc.' +__license__ = 'LGPL 3+' + +import unittest +import sys +import subprocess +import dbus +import dbus.mainloop.glib +import dbusmock +import os +import time + +dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + +class TestPolkitBackendJsAuthority(dbusmock.DBusTestCase): + '''Test polkitbackendjsauthoritytest''' + + @classmethod + def setUpClass(klass): + klass.start_system_bus() + klass.mocklibc_path = None + + if 'TOP_BUILD_DIR' in os.environ: + klass.top_build_dir = os.environ['TOP_BUILD_DIR'] + klass.mocklibc_path = klass.top_build_dir + '/subprojects/mocklibc-1.0/bin/mocklibc' + + # suppose autotools over meson + if not os.path.exists(klass.mocklibc_path): + klass.top_build_dir = '../../' + klass.mocklibc_path = klass.top_build_dir + '/test/mocklibc/bin/mocklibc' + print ('Top build dir: %s' % klass.top_build_dir) + print ('mocklibc path: %s' % klass.mocklibc_path) + assert(os.path.exists(klass.mocklibc_path)) + + # WORKAROUND - unzipped mocklibc does not preserve file permissions + os.chmod(klass.mocklibc_path, 0o755) + + klass.top_src_dir = os.path.dirname(os.path.realpath(__file__)) + '/../../' + if 'TOP_SRC_DIR' in os.environ: + klass.top_src_dir = os.environ['TOP_SRC_DIR'] + print ('Top source dir: %s' % klass.top_src_dir) + + os.environ['POLKIT_TEST_DATA'] = klass.top_src_dir + '/test/data' + print ('Polkit test data dir: %s' % os.environ['POLKIT_TEST_DATA']) + + os.environ['MOCK_PASSWD'] = klass.top_src_dir + '/test/data/etc/passwd' + print ('Mock password file: %s' % os.environ['MOCK_PASSWD']) + os.environ['MOCK_GROUP'] = klass.top_src_dir + '/test/data/etc/group' + print ('Mock group file: %s' % os.environ['MOCK_GROUP']) + os.environ['MOCK_NETGROUP'] = klass.top_src_dir + '/test/data/etc/netgroup' + print ('Mock netgroup file: %s' % os.environ['MOCK_NETGROUP']) + + def test_polkitbackendjsauthoritytest(self): + # Add '; exit 0' at the end of the cmd line if launching fails and you + # want to capture the error output + test_path = self.top_build_dir + '/test/polkitbackend/test-polkitbackendjsauthority' + + if not os.path.exists(test_path): + print('\n %s... not found' % test_path) + test_path = self.top_build_dir + '/test/polkitbackend/polkitbackendjsauthoritytest' + + out = subprocess.check_output(self.mocklibc_path + ' ' + test_path, + stderr=subprocess.STDOUT, + shell=True, + universal_newlines=True) + print(out) + +if __name__ == '__main__': + # avoid writing to stderr + unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) diff --git a/local/recipes/libs/polkit/source/test/polkitbackend/test-polkitbackendjsauthority.c b/local/recipes/libs/polkit/source/test/polkitbackend/test-polkitbackendjsauthority.c new file mode 100644 index 0000000000..b187a2ffee --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkitbackend/test-polkitbackendjsauthority.c @@ -0,0 +1,495 @@ +/* + * 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 "config.h" +#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 (); +}; diff --git a/local/recipes/libs/polkit/source/test/polkittesthelper.c b/local/recipes/libs/polkit/source/test/polkittesthelper.c new file mode 100644 index 0000000000..41c4ce56b8 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkittesthelper.c @@ -0,0 +1,68 @@ +/* + * 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); +} + diff --git a/local/recipes/libs/polkit/source/test/polkittesthelper.h b/local/recipes/libs/polkit/source/test/polkittesthelper.h new file mode 100644 index 0000000000..da49b06d90 --- /dev/null +++ b/local/recipes/libs/polkit/source/test/polkittesthelper.h @@ -0,0 +1,36 @@ +/* + * 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 diff --git a/recipes/libs/polkit b/recipes/libs/polkit new file mode 120000 index 0000000000..606d5b24a8 --- /dev/null +++ b/recipes/libs/polkit @@ -0,0 +1 @@ +../../local/recipes/libs/polkit \ No newline at end of file