summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2009-05-15 15:30:25 -0400
committerDavid Zeuthen <davidz@redhat.com>2009-05-15 15:30:25 -0400
commitc8c3d835d24fc4ce5a9c596c7d55d85a0311e8d1 (patch)
treea752d649a78ae1ad961aa337621b0154040ffa97
parentccf8f97948205eed26dc2003ec0984b90034e8af (diff)
downloadpolkit-c8c3d835d24fc4ce5a9c596c7d55d85a0311e8d1.tar.gz
Add a pkexec(1) command
-rw-r--r--actions/org.freedesktop.policykit.policy.in10
-rw-r--r--data/org.freedesktop.PolicyKit1.Authority.xml2
-rw-r--r--docs/man/Makefile.am7
-rw-r--r--docs/man/pkexec.xml155
-rw-r--r--docs/polkit/Makefile.am1
-rw-r--r--docs/polkit/polkit-1-docs.xml1
-rw-r--r--po/POTFILES.in2
-rw-r--r--po/da.po72
-rw-r--r--src/examples/Makefile.am39
-rw-r--r--src/examples/frobnicate.c68
-rw-r--r--src/examples/org.freedesktop.policykit.examples.pkexec.policy.in22
-rw-r--r--src/polkit/polkitauthority.c1
-rw-r--r--src/polkitbackend/polkitbackendlocalauthority.c2
-rw-r--r--src/programs/Makefile.am56
-rw-r--r--src/programs/pkexec-action-lookup.c205
-rw-r--r--src/programs/pkexec.c473
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
diff --git a/po/da.po b/po/da.po
index 0ff6eb5..bd88712 100644
--- a/po/da.po
+++ b/po/da.po
@@ -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;
+}
+