diff options
author | David Zeuthen <davidz@redhat.com> | 2009-05-15 15:30:25 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2009-05-15 15:30:25 -0400 |
commit | c8c3d835d24fc4ce5a9c596c7d55d85a0311e8d1 (patch) | |
tree | a752d649a78ae1ad961aa337621b0154040ffa97 | |
parent | ccf8f97948205eed26dc2003ec0984b90034e8af (diff) | |
download | polkit-c8c3d835d24fc4ce5a9c596c7d55d85a0311e8d1.tar.gz |
Add a pkexec(1) command
-rw-r--r-- | actions/org.freedesktop.policykit.policy.in | 10 | ||||
-rw-r--r-- | data/org.freedesktop.PolicyKit1.Authority.xml | 2 | ||||
-rw-r--r-- | docs/man/Makefile.am | 7 | ||||
-rw-r--r-- | docs/man/pkexec.xml | 155 | ||||
-rw-r--r-- | docs/polkit/Makefile.am | 1 | ||||
-rw-r--r-- | docs/polkit/polkit-1-docs.xml | 1 | ||||
-rw-r--r-- | po/POTFILES.in | 2 | ||||
-rw-r--r-- | po/da.po | 72 | ||||
-rw-r--r-- | src/examples/Makefile.am | 39 | ||||
-rw-r--r-- | src/examples/frobnicate.c | 68 | ||||
-rw-r--r-- | src/examples/org.freedesktop.policykit.examples.pkexec.policy.in | 22 | ||||
-rw-r--r-- | src/polkit/polkitauthority.c | 1 | ||||
-rw-r--r-- | src/polkitbackend/polkitbackendlocalauthority.c | 2 | ||||
-rw-r--r-- | src/programs/Makefile.am | 56 | ||||
-rw-r--r-- | src/programs/pkexec-action-lookup.c | 205 | ||||
-rw-r--r-- | src/programs/pkexec.c | 473 |
16 files changed, 1091 insertions, 25 deletions
diff --git a/actions/org.freedesktop.policykit.policy.in b/actions/org.freedesktop.policykit.policy.in index 72aa67e..d44a0c1 100644 --- a/actions/org.freedesktop.policykit.policy.in +++ b/actions/org.freedesktop.policykit.policy.in @@ -48,4 +48,14 @@ <allow_active>auth_admin_keep</allow_active> </defaults> </action> + + <action id="org.freedesktop.policykit.exec"> + <_description>Run programs as another user</_description> + <_message>Authentication is required to run a program as another user</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin</allow_active> + </defaults> + </action> </policyconfig> diff --git a/data/org.freedesktop.PolicyKit1.Authority.xml b/data/org.freedesktop.PolicyKit1.Authority.xml index abe0d3f..2347302 100644 --- a/data/org.freedesktop.PolicyKit1.Authority.xml +++ b/data/org.freedesktop.PolicyKit1.Authority.xml @@ -108,7 +108,7 @@ <!-- ---------------------------------------------------------------------------------------------------- --> - <!-- An enumeration for results when checking for an authorization --> + <!-- An structure containing the results of an authorization check --> <annotation name="org.gtk.EggDBus.DeclareStruct" value="AuthorizationResult"> <annotation name="org.gtk.EggDBus.DocString.Summary" value="Authorization Results"/> <annotation name="org.gtk.EggDBus.DocString" value="Describes the result of calling org.freedesktop.PolicyKit1.Authority.CheckAuthorization()."/> diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 51db971..67522c1 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -6,16 +6,21 @@ if MAN_PAGES_ENABLED man_MANS = \ PolicyKit-1.8 \ polkit-1.1 \ + pkexec.1 \ $(NULL) %-1.8 %-1.1 : %.xml - $(XSLTPROC) -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< + $(XSLTPROC) -nonet --xinclude http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< + +pkexec.1 : pkexec.xml + $(XSLTPROC) -nonet --xinclude http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< endif # MAN_PAGES_ENABLED EXTRA_DIST = \ PolicyKit.xml \ polkit.xml \ + pkexec.xml \ $(NULL) clean-local: diff --git a/docs/man/pkexec.xml b/docs/man/pkexec.xml new file mode 100644 index 0000000..0dd6105 --- /dev/null +++ b/docs/man/pkexec.xml @@ -0,0 +1,155 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +<!ENTITY version SYSTEM "../version.xml"> +]> +<refentry id="pkexec.1" xmlns:xi="http://www.w3.org/2003/XInclude"> + <refentryinfo> + <title>pkexec</title> + <date>May 2009</date> + <productname>PolicyKit-1</productname> + </refentryinfo> + + <refmeta> + <refentrytitle>pkexec</refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo class="version"></refmiscinfo> + </refmeta> + + <refnamediv> + <refname>pkexec</refname> + <refpurpose>Execute a command as another user</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>pkexec</command> + <arg><option>--version</option></arg> + <arg><option>--help</option></arg> + </cmdsynopsis> + + <cmdsynopsis> + <command>pkexec</command> + <group> + <arg choice="plain"> + <option>--user</option> + <replaceable>username</replaceable> + </arg> + </group> + <arg choice="plain"><replaceable>PROGRAM</replaceable></arg> + <group rep="repeat"> + <arg choice="plain"><replaceable>ARGUMENTS</replaceable></arg> + </group> + </cmdsynopsis> + + </refsynopsisdiv> + + <refsect1><title>DESCRIPTION</title> + <para> + <command>pkexec</command> allows an authorized user to + execute <replaceable>PROGRAM</replaceable> as another + user. If <replaceable>username</replaceable> is not specified, + then the program will be executed as the administrative super + user, <emphasis>root</emphasis>. + </para> + </refsect1> + + <refsect1><title>RETURN VALUE</title> + <para> + Upon successful completion, the return value is the return value + of <replaceable>PROGRAM</replaceable>. If the calling process is + not authorized or an authorization could not be obtained through + authentication or an error occured, <command>pkexec</command> + exits with a return value of 127. + </para> + </refsect1> + + <refsect1><title>SECURITY NOTES</title> + <para> + Executing a program as another user is a privileged + operation. By default the required authorization (See + <xref linkend="pkexec-required-authz"/>) 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. + </para> + <para> + The environment that <replaceable>PROGRAM</replaceable> will run + it, will be set to a minimal known and safe environment in order + to avoid injecting code + through <literal>LD_LIBRARY_PATH</literal> or similar + mechanisms. In addition the <literal>PKEXEC_UID</literal> + environment variable is set to the user id of the process + invoking <command>pkexec</command>. As a + result, <command>pkexec</command> will not allow you to run X11 + applications as another user. + </para> + </refsect1> + + <refsect1 id="pkexec-required-authz"><title>REQUIRED AUTHORIZATIONS</title> + <para> + By default, + the <emphasis>org.freedesktop.policykit.exec</emphasis> + authorization is required unless an action definition file is + present for the program in question. To require another + authorization, it can be specified using the <emphasis>org.freedesktop.policykit.exec.path</emphasis> annotation on an action (See <xref linkend="pkexec-example"/> for details). + </para> + </refsect1> + + <refsect1 id="pkexec-example"><title>EXAMPLE</title> + <para> + To specify what kind of authorization is needed to execute the + program <filename>/usr/bin/pk-example-frobnicate</filename> as + another user, simply write an action definition file like this + </para> + <programlisting> +<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../src/examples/org.freedesktop.policykit.examples.pkexec.policy"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting> + <para> + and drop it in + the <filename>/usr/share/polkit-1/actions</filename> directory + under a suitable name (e.g. matching the namespace of the + action). Note that in addition to specifying the program, the + authentication message, description, icon and defaults can be + specified. + </para> + <para> + Note that <command>pkexec</command> does no validation of + the <replaceable>ARGUMENTS</replaceable> passed + to <replaceable>PROGRAM</replaceable>. In the normal case (where + administrator authentication is required every + time <command>pkexec</command> is used), this is not a + problem. 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 <emphasis>suid</emphasis> program). + </para> + </refsect1> + + <refsect1><title>AUTHOR</title> + <para> + Written by David Zeuthen <email>davidz@redhat.com</email> with + a lot of help from many others. + </para> + </refsect1> + + <refsect1> + <title>BUGS</title> + <para> + Please send bug reports to either the distribution or the + polkit-devel mailing list, + see the link <ulink url="http://lists.freedesktop.org/mailman/listinfo/polkit-devel"/> + on how to subscribe. + </para> + </refsect1> + + <refsect1> + <title>SEE ALSO</title> + <para> + <citerefentry> + <refentrytitle>PolicyKit-1</refentrytitle><manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> +</refentry> diff --git a/docs/polkit/Makefile.am b/docs/polkit/Makefile.am index 0674221..a60dab9 100644 --- a/docs/polkit/Makefile.am +++ b/docs/polkit/Makefile.am @@ -54,6 +54,7 @@ content_files = \ ../../src/polkit/docbook-interface-org.freedesktop.PolicyKit1.AuthenticationAgent.xml \ ../man/PolicyKit.xml \ ../man/polkit.xml \ + ../man/pkexec.xml \ $(NULL) # Images to copy into HTML directory diff --git a/docs/polkit/polkit-1-docs.xml b/docs/polkit/polkit-1-docs.xml index c2da1ea..452c5a7 100644 --- a/docs/polkit/polkit-1-docs.xml +++ b/docs/polkit/polkit-1-docs.xml @@ -105,6 +105,7 @@ <title>Manual Pages</title> <xi:include href="../man/PolicyKit.xml"/> <xi:include href="../man/polkit.xml"/> + <xi:include href="../man/pkexec.xml"/> </reference> <index> diff --git a/po/POTFILES.in b/po/POTFILES.in index 61bc0ac..00f5500 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -2,3 +2,5 @@ # Please keep this file sorted alphabetically. [encoding: UTF-8] actions/org.freedesktop.policykit.policy.in +src/examples/org.freedesktop.policykit.examples.pkexec.policy.in +src/programs/pkexec-action-lookup.c @@ -1,51 +1,93 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# Danish translations for PolicyKit. +# Copyright (C) 2009 Red Hat, Inc. +# This file is distributed under the same license as the PolicyKit package. +# David Zeuthen <davidz@redhat.com>, 2009. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"Project-Id-Version: DeviceKit-disks\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-12-07 16:14-0500\n" -"PO-Revision-Date: 2008-12-07 16:17-0500\n" +"POT-Creation-Date: 2009-05-15 13:45-0400\n" +"PO-Revision-Date: 2009-05-12 17:01-0400\n" "Last-Translator: David Zeuthen <davidz@redhat.com>\n" -"Language-Team: LANGUAGE <LL@li.org>\n" +"Language-Team: Danish <dansk@dansk-gruppen.dk>\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 "Authentication is required to grant authorizations to other users" -msgstr "Autentificering er påkrævet for at autorisere andre brugere" +msgstr "Autorisering er påkrævet for at autorisere andre brugere" #: ../actions/org.freedesktop.policykit.policy.in.h:2 msgid "" "Authentication is required to modify the defaults for implicit authorizations" -msgstr "Autentificering er påkrævet for ændre implicit autorisering" +msgstr "Autorisering er påkrævet for ændre implicit autorisering" #: ../actions/org.freedesktop.policykit.policy.in.h:3 msgid "Authentication is required to read authorizations of other users" -msgstr "Autentificering er påkrævet for at læse andre brugers autoriseringer" +msgstr "Autorisering er påkrævet for at læse andre brugers autoriseringer" #: ../actions/org.freedesktop.policykit.policy.in.h:4 msgid "Authentication is required to revoke authorizations other users" -msgstr "Autentificering er påkrævet for at fjerne en autosering fra en anden bruger" +msgstr "" +"Autorisering er påkrævet for at fjerne en autosering fra en anden bruger" #: ../actions/org.freedesktop.policykit.policy.in.h:5 +msgid "Authentication is required to run a program as another user" +msgstr "Autorisering er påkrævet for at afvikle et program som en anden bruger" + +#: ../actions/org.freedesktop.policykit.policy.in.h:6 msgid "Grant authorizations to other users" msgstr "Autoriser en anden bruger" -#: ../actions/org.freedesktop.policykit.policy.in.h:6 +#: ../actions/org.freedesktop.policykit.policy.in.h:7 msgid "Modify defaults for implicit authorizations" msgstr "Konfigurer implicit autorisering" -#: ../actions/org.freedesktop.policykit.policy.in.h:7 +#: ../actions/org.freedesktop.policykit.policy.in.h:8 msgid "Read authorizations of other users" msgstr "Læs andre brugers autoriseringer" -#: ../actions/org.freedesktop.policykit.policy.in.h:8 +#: ../actions/org.freedesktop.policykit.policy.in.h:9 msgid "Revoke authorizations from other users" msgstr "Fjern autorisering fra en anden bruger" +#: ../actions/org.freedesktop.policykit.policy.in.h:10 +msgid "Run programs as another user" +msgstr "Kør et program som en anden bruger" + +#: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:1 +msgid "" +"Authentication is required to run the PolicyKit example program Frobnicate" +msgstr "Autorisering er påkrævet for at afvikle PolicyKit eksemplet Frobnicate" + +#: ../src/examples/org.freedesktop.policykit.examples.pkexec.policy.in.h:2 +msgid "Run the PolicyKit example program Frobnicate" +msgstr "Kør PolicyKit eksemplet Frobnicate" + +#. Translator: %s is a fully qualified path to the executable +#: ../src/programs/pkexec-action-lookup.c:110 +#, c-format +msgid "Authentication is needed to run `%s' as the super user" +msgstr "Autorisering er påkrævet for at afvikle `%s' som super bruger" + +#. Translator: %s is a fully qualified path to the executable +#: ../src/programs/pkexec-action-lookup.c:115 +#, c-format +msgid "Authentication is needed to run `%s' as another user" +msgstr "Autorisering er påkrævet for at afvikle `%s' som en anden bruger" + +#: ../src/programs/pkexec-action-lookup.c:159 +msgid "Command" +msgstr "Program" + +#: ../src/programs/pkexec-action-lookup.c:168 +msgid "Super User (root)" +msgstr "Super Bruger (root)" + +#: ../src/programs/pkexec-action-lookup.c:170 +msgid "Run As" +msgstr "Bruger" diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am index 1c40863..b30155b 100644 --- a/src/examples/Makefile.am +++ b/src/examples/Makefile.am @@ -15,7 +15,12 @@ INCLUDES = \ -D_REENTRANT \ $(NULL) -noinst_PROGRAMS = cancel +bin_PROGRAMS = +noinst_PROGRAMS = + +# ---------------------------------------------------------------------------------------------------- + +noinst_PROGRAMS += cancel cancel_SOURCES = cancel.c @@ -24,9 +29,39 @@ cancel_CFLAGS = \ $(NULL) cancel_LDADD = \ - $(GLIB_LDADD) \ + $(GLIB_LIBS) \ $(top_builddir)/src/polkit/libpolkit-gobject-1.la \ $(NULL) + +# ---------------------------------------------------------------------------------------------------- + +bin_PROGRAMS += pk-example-frobnicate + +pk_example_frobnicate_SOURCES = frobnicate.c + +pk_example_frobnicate_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(NULL) + +pk_example_frobnicate_LDADD = \ + $(GLIB_LIBS) \ + $(NULL) + +polkit_actiondir = $(datadir)/polkit-1/actions + +dist_polkit_action_DATA = org.freedesktop.policykit.examples.pkexec.policy + +@INTLTOOL_POLICY_RULE@ + +#check: +# $(top_builddir)/tools/polkit-policy-file-validate-1 $(top_srcdir)/policy/$(dist_polkit_action_DATA) + +DISTCLEANFILES = org.freedesktop.policykit.examples.pkexec.policy + +EXTRA_DIST = org.freedesktop.policykit.examples.pkexec.policy.in + +# ---------------------------------------------------------------------------------------------------- + clean-local : rm -f *~ diff --git a/src/examples/frobnicate.c b/src/examples/frobnicate.c new file mode 100644 index 0000000..6b6b8ff --- /dev/null +++ b/src/examples/frobnicate.c @@ -0,0 +1,68 @@ +/* + * 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 <davidz@redhat.com> + */ + +#include <glib.h> +#include <unistd.h> +#include <sys/types.h> + +int +main (int argc, char *argv[]) +{ + gchar *args; + gchar **env; + guint n; + int ret; + gchar cwd[PATH_MAX]; + + ret = 1; + args = NULL; + env = NULL; + + if (getcwd (cwd, sizeof cwd) == NULL) + { + g_printerr ("Error getting cwd: %m"); + 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: + + g_free (args); + g_strfreev (env); + + return ret; +} diff --git a/src/examples/org.freedesktop.policykit.examples.pkexec.policy.in b/src/examples/org.freedesktop.policykit.examples.pkexec.policy.in new file mode 100644 index 0000000..b2139cf --- /dev/null +++ b/src/examples/org.freedesktop.policykit.examples.pkexec.policy.in @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE policyconfig PUBLIC + "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd"> +<policyconfig> + + <vendor>Examples for the PolicyKit Project</vendor> + <vendor_url>http://hal.freedesktop.org/docs/PolicyKit/</vendor_url> + + <action id="org.freedesktop.policykit.example.pkexec.run-frobnicate"> + <_description>Run the PolicyKit example program Frobnicate</_description> + <_message>Authentication is required to run the PolicyKit example program Frobnicate</_message> + <icon_name>audio-x-generic</icon_name> <!-- just an example --> + <defaults> + <allow_any>no</allow_any> + <allow_inactive>no</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/pk-example-frobnicate</annotate> + </action> + +</policyconfig> diff --git a/src/polkit/polkitauthority.c b/src/polkit/polkitauthority.c index 5505346..82c9d91 100644 --- a/src/polkit/polkitauthority.c +++ b/src/polkit/polkitauthority.c @@ -467,6 +467,7 @@ polkit_authority_check_authorization_finish (PolkitAuthority *authority g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_authority_check_authorization_async); result = NULL; + real_result = NULL; local_error = NULL; _polkit_authority_check_authorization_finish (authority->real, diff --git a/src/polkitbackend/polkitbackendlocalauthority.c b/src/polkitbackend/polkitbackendlocalauthority.c index f677fc3..98e5dd6 100644 --- a/src/polkitbackend/polkitbackendlocalauthority.c +++ b/src/polkitbackend/polkitbackendlocalauthority.c @@ -1457,6 +1457,8 @@ get_admin_auth_identities (PolkitBackendLocalAuthority *authority) priv = POLKIT_BACKEND_LOCAL_AUTHORITY_GET_PRIVATE (authority); + ret = NULL; + error = NULL; admin_identities = polkit_backend_config_source_get_string_list (priv->config_source, "Configuration", diff --git a/src/programs/Makefile.am b/src/programs/Makefile.am index 30aae64..abb9f87 100644 --- a/src/programs/Makefile.am +++ b/src/programs/Makefile.am @@ -15,18 +15,62 @@ INCLUDES = \ -D_REENTRANT \ $(NULL) -bin_PROGRAMS = polkit-1 +# ---------------------------------------------------------------------------------------------------- + +bin_PROGRAMS = polkit-1 pkexec + +# ---------------------------------------------------------------------------------------------------- polkit_1_SOURCES = polkit.c -polkit_1_CFLAGS = \ - $(GLIB_CFLAGS) \ +polkit_1_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(NULL) + +polkit_1_LDADD = \ + $(GLIB_LDADD) \ + $(top_builddir)/src/polkit/libpolkit-gobject-1.la \ + $(NULL) + +# ---------------------------------------------------------------------------------------------------- + +pkexec_SOURCES = pkexec.c + +pkexec_CFLAGS = \ + $(GLIB_CFLAGS) \ $(NULL) -polkit_1_LDADD = \ - $(GLIB_LDADD) \ - $(top_builddir)/src/polkit/libpolkit-gobject-1.la \ +pkexec_LDADD = \ + $(GLIB_LDADD) \ + $(top_builddir)/src/polkit/libpolkit-gobject-1.la \ $(NULL) +polkitmodulesdir = $(libdir)/polkit-1/backends +polkitmodules_LTLIBRARIES = libpolkit-pkexec-action-lookup.la + +libpolkit_pkexec_action_lookup_la_SOURCES = \ + pkexec-action-lookup.c \ + $(NULL) + +libpolkit_pkexec_action_lookup_la_CFLAGS = \ + -DPOLKIT_BACKEND_I_KNOW_API_IS_SUBJECT_TO_CHANGE \ + -DG_LOG_DOMAIN=\"pkexec-action-lookup\" \ + $(GLIB_CFLAGS) \ + $(NULL) + +libpolkit_pkexec_action_lookup_la_LDFLAGS = \ + -export_dynamic -avoid-version -module -no-undefined \ + -export-symbols-regex '^g_io_module_(load|unload)' \ + $(NULL) + +libpolkit_pkexec_action_lookup_la_LIBADD = \ + $(top_builddir)/src/polkitbackend/libpolkit-backend-1.la \ + $(NULL) + +# ---------------------------------------------------------------------------------------------------- + clean-local : rm -f *~ + +install-exec-hook : + -chmod 4755 $(DESTDIR)$(bindir)/pkexec diff --git a/src/programs/pkexec-action-lookup.c b/src/programs/pkexec-action-lookup.c new file mode 100644 index 0000000..69b911f --- /dev/null +++ b/src/programs/pkexec-action-lookup.c @@ -0,0 +1,205 @@ +/* + * 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 <davidz@redhat.com> + */ + +#include "config.h" + +#include <polkitbackend/polkitbackend.h> + +#include <glib/gi18n-lib.h> + +#define POLKIT_EXEC_TYPE_ACTION_LOOKUP (polkit_exec_action_lookup_get_type()) +#define POLKIT_EXEC_ACTION_LOOKUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_EXEC_TYPE_ACTION_LOOKUP, PolkitExecActionLookup)) +#define POLKIT_EXEC_ACTION_LOOKUP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_EXEC_TYPE_ACTION_LOOKUP, PolkitExecActionLookupClass)) +#define POLKIT_EXEC_ACTION_LOOKUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_EXEC_TYPE_ACTION_LOOKUP, PolkitExecActionLookupClass)) +#define POLKIT_EXEC_IS_ACTION_LOOKUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_EXEC_TYPE_ACTION_LOOKUP)) +#define POLKIT_EXEC_IS_ACTION_LOOKUP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_EXEC_TYPE_ACTION_LOOKUP)) + +typedef struct _PolkitExecActionLookup PolkitExecActionLookup; +typedef struct _PolkitExecActionLookupClass PolkitExecActionLookupClass; + +struct _PolkitExecActionLookup +{ + GObject parent; +}; + +struct _PolkitExecActionLookupClass +{ + GObjectClass parent_class; +}; + +GType polkit_exec_action_lookup_get_type (void) G_GNUC_CONST; + +static void polkit_backend_action_lookup_iface_init (PolkitBackendActionLookupIface *iface); + +#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init) \ +{ \ + const GInterfaceInfo g_implement_interface_info = { \ + (GInterfaceInitFunc) iface_init, NULL, NULL \ + }; \ + g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \ +} + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (PolkitExecActionLookup, + polkit_exec_action_lookup, + G_TYPE_OBJECT, + 0, + _G_IMPLEMENT_INTERFACE_DYNAMIC (POLKIT_BACKEND_TYPE_ACTION_LOOKUP, + polkit_backend_action_lookup_iface_init)) + +static void +polkit_exec_action_lookup_init (PolkitExecActionLookup *lookup) +{ +} + +static void +polkit_exec_action_lookup_class_finalize (PolkitExecActionLookupClass *klass) +{ +} + +static void +polkit_exec_action_lookup_class_init (PolkitExecActionLookupClass *klass) +{ +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +polkit_exec_action_lookup_get_message (PolkitBackendActionLookup *lookup, + const gchar *action_id, + GHashTable *details, + PolkitActionDescription *action_description) +{ + gchar *ret; + const gchar *s; + const gchar *s2; + + ret = NULL; + + if (g_strcmp0 (action_id, "org.freedesktop.policykit.exec") != 0) + goto out; + + s = g_hash_table_lookup (details, "program"); + if (s == NULL) + goto out; + + s2 = g_hash_table_lookup (details, "uid"); + if (s2 == NULL) + goto out; + + if (g_strcmp0 (s2, "0") == 0) + { + /* Translator: %s is a fully qualified path to the executable */ + ret = g_strdup_printf (_("Authentication is needed to run `%s' as the super user"), s); + } + else + { + /* Translator: %s is a fully qualified path to the executable */ + ret = g_strdup_printf (_("Authentication is needed to run `%s' as another user"), s); + } + + out: + return ret; +} + +static gchar * +polkit_exec_action_lookup_get_icon_name (PolkitBackendActionLookup *lookup, + const gchar *action_id, + GHashTable *details, + PolkitActionDescription *action_description) +{ + gchar *ret; + + ret = NULL; + + /* explicitly left blank for now */ + + return ret; +} + +static GHashTable * +polkit_exec_action_lookup_get_details (PolkitBackendActionLookup *lookup, + const gchar *action_id, + GHashTable *details, + PolkitActionDescription *action_desc) +{ + const gchar *s; + const gchar *s2; + GHashTable *ret; + + ret = NULL; + + if (action_desc != NULL && + polkit_action_description_get_annotation (action_desc, "org.freedesktop.policykit.exec.path") == NULL) + goto out; + + ret = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); + + s = g_hash_table_lookup (details, "command-line"); + if (s != NULL) + { + g_hash_table_insert (ret, + _("Command"), + g_strdup (s)); + } + + s = g_hash_table_lookup (details, "user"); + s2 = g_hash_table_lookup (details, "uid"); + if (s != NULL) + { + if (g_strcmp0 (s2, "0") == 0) + s = _("Super User (root)"); + g_hash_table_insert (ret, + _("Run As"), + g_strdup (s)); + } + + out: + return ret; +} + +static void +polkit_backend_action_lookup_iface_init (PolkitBackendActionLookupIface *iface) +{ + iface->get_message = polkit_exec_action_lookup_get_message; + iface->get_icon_name = polkit_exec_action_lookup_get_icon_name; + iface->get_details = polkit_exec_action_lookup_get_details; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +void +g_io_module_load (GIOModule *module) +{ + bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + + polkit_exec_action_lookup_register_type (G_TYPE_MODULE (module)); + + g_io_extension_point_implement (POLKIT_BACKEND_ACTION_LOOKUP_EXTENSION_POINT_NAME, + POLKIT_EXEC_TYPE_ACTION_LOOKUP, + "pkexec action lookup extension " PACKAGE_VERSION, + 0); +} + +void +g_io_module_unload (GIOModule *module) +{ +} diff --git a/src/programs/pkexec.c b/src/programs/pkexec.c new file mode 100644 index 0000000..86f70a5 --- /dev/null +++ b/src/programs/pkexec.c @@ -0,0 +1,473 @@ +/* + * 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 <davidz@redhat.com> + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <grp.h> +#include <pwd.h> +#include <errno.h> + +#include <polkit/polkit.h> + +static void +usage (int argc, char *argv[]) +{ + GError *error; + + error = NULL; + if (!g_spawn_command_line_sync ("man pkexec", + NULL, + NULL, + NULL, + &error)) + { + g_printerr ("Cannot show manual page: %s\n", error->message); + g_error_free (error); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +find_action_for_path (PolkitAuthority *authority, + const gchar *path) +{ + GList *l; + GList *actions; + gchar *action_id; + GError *error; + + actions = NULL; + action_id = NULL; + error = NULL; + + 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 *path_for_action; + + path_for_action = polkit_action_description_get_annotation (action_desc, "org.freedesktop.policykit.exec.path"); + if (path_for_action == NULL) + continue; + + if (g_strcmp0 (path_for_action, path) == 0) + { + action_id = g_strdup (polkit_action_description_get_action_id (action_desc)); + 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; +} + + +int +main (int argc, char *argv[]) +{ + guint n; + guint ret; + gboolean opt_show_help; + gboolean opt_show_version; + PolkitAuthority *authority; + PolkitAuthorizationResult *result; + PolkitSubject *subject; + GHashTable *details; + GError *error; + gchar *action_id; + gchar *command_line; + gchar **exec_argv; + gchar *path; + struct passwd pwstruct; + struct passwd *pw; + gchar pwbuf[8192]; + gchar *s; + const gchar *environment_variables_to_save[] = { + "LANG" + "LANGUAGE", + "LC_ALL", + "LC_MESSAGES", + "SHELL", + "TERM", + + /* For now, avoiding pretend that running X11 apps as another user in the same session + * will ever work... See + * + * https://bugs.freedesktop.org/show_bug.cgi?id=17970#c26 + * + * and surrounding comments for a lot of discussion about this. + */ +#if 0 + "DESKTOP_STARTUP_ID", + "DISPLAY", + "XAUTHORITY", + "DBUS_SESSION_BUS_ADDRESS", + "ORBIT_SOCKETDIR", +#endif + NULL + }; + GPtrArray *saved_env; + gchar *opt_user; + pid_t pid_of_caller; + struct stat statbuf; + gchar procbuf[32]; + + ret = 127; + authority = NULL; + subject = NULL; + details = NULL; + result = NULL; + action_id = NULL; + saved_env = NULL; + path = NULL; + command_line = NULL; + opt_user = NULL; + + /* check for correct invocation */ + if (geteuid () != 0) + { + g_print ("pkexec must be setuid root\n"); + goto out; + } + + /* 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; + 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; + } + + opt_user = g_strdup (argv[n]); + } + 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; + } + + if (opt_user == NULL) + opt_user = g_strdup ("root"); + + /* 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) + { + usage (argc, argv); + goto out; + } + 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); + argv[n] = path = s; + } + if (stat (path, &statbuf) != 0) + { + g_printerr ("Error getting information about %s: %m\n", path); + goto out; + } + 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; + + g_ptr_array_add (saved_env, g_strdup (key)); + g_ptr_array_add (saved_env, g_strdup (value)); + } + + /* 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: %m\n"); + goto out; + } + + /* Look up information about the user we care about */ + if (getpwnam_r (opt_user, &pwstruct, pwbuf, sizeof pwbuf, &pw) != 0) + { + g_printerr ("Error getting information for user %s: %m\n", opt_user); + goto out; + } + + /* Initialize the GLib type system - this is needed to interact with the + * PolicyKit daemon + */ + g_type_init (); + + /* now check if the program that invoked us is authorized */ + 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); get process group leader instead - for example, this + * happens when launching via gnome-panel (alt+f2, then 'pkexec gedit'). + */ + pid_of_caller = getpgrp (); + } + + /* paranoia: check that the uid of pid_of_caller matches getuid() */ + g_snprintf (procbuf, sizeof procbuf, "/proc/%d", pid_of_caller); + if (stat (procbuf, &statbuf) != 0) + { + g_printerr ("Error determing pid of caller (pid %d): %m\n", (gint) pid_of_caller); + goto out; + } + if (statbuf.st_uid != getuid ()) + { + g_printerr ("User of caller (%d) does not match our uid (%d)\n", statbuf.st_uid, getuid ()); + goto out; + } + + authority = polkit_authority_get (); + subject = polkit_unix_process_new (pid_of_caller); + + details = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); + + g_hash_table_insert (details, "command-line", g_strdup (command_line)); + s = g_strdup_printf ("%s (%s)", pw->pw_gecos, pw->pw_name); + g_hash_table_insert (details, "user", s); + s = g_strdup_printf ("%d", (gint) pw->pw_uid); + g_hash_table_insert (details, "uid", s); + g_hash_table_insert (details, "program", g_strdup (path)); + + action_id = find_action_for_path (authority, path); + g_assert (action_id != NULL); + + 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->message); + goto out; + } + + if (polkit_authorization_result_get_is_authorized (result)) + { + /* do nothing */ + } + else if (polkit_authorization_result_get_is_challenge (result)) + { + g_printerr ("Authorization requires authentication but no authentication was found.\n"); + goto out; + } + else + { + g_printerr ("Not authorized.\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]; + + if (!g_setenv (key, value, TRUE)) + { + g_printerr ("Error setting environment variable %s to '%s': %m\n", + key, + value); + goto out; + } + } + + /* if not changing to uid 0, become uid 0 before changing to the user */ + if (pw->pw_uid) + { + setreuid (0, 0); + if ((geteuid () != 0) || (getuid () != 0)) + { + g_printerr ("Error becoming uid 0: %m\n"); + goto out; + } + } + + /* become the user */ + if (setgroups (0, NULL) != 0) + { + g_printerr ("Error setting groups: %m\n"); + goto out; + } + if (initgroups (pw->pw_name, pw->pw_gid) != 0) + { + g_printerr ("Error initializing groups for %s: %m\n", pw->pw_name); + 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: %m\n", pw->pw_uid, pw->pw_gid); + goto out; + } + + /* change to home directory */ + if (chdir (pw->pw_dir) != 0) + { + g_printerr ("Error changing to home directory %s: %m\n", pw->pw_dir); + goto out; + } + + /* exec the program */ + if (execv (path, exec_argv) != 0) + { + g_printerr ("Error executing %s: %m\n", path); + goto out; + } + + /* if exec doesn't fail, it never returns... */ + g_assert_not_reached (); + + out: + if (result != NULL) + g_object_unref (result); + + g_free (action_id); + + if (details != NULL) + g_hash_table_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 (path); + g_free (command_line); + g_free (opt_user); + + return ret; +} + |