Implement lockdown for the Local Authority implementation
authorDavid Zeuthen <davidz@redhat.com>
Sat, 12 Sep 2009 19:49:53 +0000 (15:49 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Sat, 12 Sep 2009 19:49:53 +0000 (15:49 -0400)
16 files changed:
actions/org.freedesktop.policykit.policy.in
data/org.freedesktop.PolicyKit1.Authority.xml
docs/man/Makefile.am
docs/man/pklalockdown.xml [new file with mode: 0644]
docs/man/pklocalauthority.xml
docs/polkit/polkit-1-docs.xml
docs/polkit/polkit-1-sections.txt
src/polkit/polkitauthorizationresult.c
src/polkit/polkitauthorizationresult.h
src/polkitbackend/polkitbackendinteractiveauthority.c
src/polkitbackend/polkitbackendinteractiveauthority.h
src/polkitbackend/polkitbackendlocalauthority.c
src/polkitbackend/polkitbackendlocalauthorizationstore.c
src/polkitbackend/polkitbackendlocalauthorizationstore.h
src/programs/Makefile.am
src/programs/pklalockdown.c [new file with mode: 0644]

index d44a0c1..3961001 100644 (file)
       <allow_active>auth_admin</allow_active>
     </defaults>
   </action>
+
+  <action id="org.freedesktop.policykit.localauthority.lockdown">
+    <_description>Configure lockdown on the Local Authority</_description>
+    <_message>Authentication is required to configure lock down policy</_message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin</allow_active>
+    </defaults>
+    <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/pklalockdown</annotate>
+  </action>
 </policyconfig>
index 7854a96..e71dc20 100644 (file)
       </annotation>
 
       <annotation name="org.gtk.EggDBus.Struct.Member"  value="Dict<String,String>:details">
-        <annotation name="org.gtk.EggDBus.DocString" value="Details for the result or empty if not authorized. Known key/value-pairs include <literal>polkit.temporary_authorization_id</literal> (if the authorization is temporary, this is set to the opaque temporary authorization id) and <literal>polkit.retains_authorization_after_challenge</literal> (Set to a non-empty string if the authorization will be retained after authentication (if is_challenge is TRUE))."/>
+        <annotation name="org.gtk.EggDBus.DocString" value="Details for the result or empty if not authorized. Known key/value-pairs include <literal>polkit.temporary_authorization_id</literal> (if the authorization is temporary, this is set to the opaque temporary authorization id), <literal>polkit.retains_authorization_after_challenge</literal> (Set to a non-empty string if the authorization will be retained after authentication (if is_challenge is TRUE)) and <literal>polkit.localauthority.lockdown</literal> (set to a non-empty string if the action is locked down via pklalockdown(1))."/>
       </annotation>
     </annotation>
 
index 076608b..6f164c9 100644 (file)
@@ -10,6 +10,7 @@ man_MANS =                            \
        pkexec.1                        \
        pkcheck.1                       \
        pkaction.1                      \
+       pklalockdown.1                  \
        $(NULL)
 
 %.8 %.1 : %.xml
@@ -24,6 +25,7 @@ EXTRA_DIST =                          \
        pkexec.xml                      \
        pkcheck.xml                     \
        pkaction.xml                    \
+       pklalockdown.xml                \
        $(NULL)
 
 clean-local:
diff --git a/docs/man/pklalockdown.xml b/docs/man/pklalockdown.xml
new file mode 100644 (file)
index 0000000..74e4f5d
--- /dev/null
@@ -0,0 +1,136 @@
+<?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="pklalockdown.1" xmlns:xi="http://www.w3.org/2003/XInclude">
+  <refentryinfo>
+    <title>pklalockdown</title>
+    <date>May 2009</date>
+    <productname>polkit</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>pklalockdown</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="version"></refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>pklalockdown</refname>
+    <refpurpose>Configure lockdown for the Local Authority</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>pklalockdown</command>
+      <arg><option>--version</option></arg>
+      <arg><option>--help</option></arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>pklalockdown</command>
+      <arg choice="plain">
+        <option>--lockdown</option>
+        <replaceable>action</replaceable>
+      </arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>pklalockdown</command>
+      <arg choice="plain">
+        <option>--remove-lockdown</option>
+        <replaceable>action</replaceable>
+      </arg>
+    </cmdsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1 id="pklalockdown-description">
+    <title>DESCRIPTION</title>
+    <para>
+      <command>pklalockdown</command> is used to configure lockdown
+      for the Local Authority.
+    </para>
+    <para>
+      The effect of locking down an action is that administrator
+      authentication is always needed in order for subjects to acquire
+      the authorization for the action in question (and the subject
+      has to be in an active session on a local console). The obtained
+      authorization is temporary and as such typically expires five
+      minutes after being obtained.
+    </para>
+    <para>
+      To lock down <replaceable>action</replaceable> use the <option>--lockdown</option> option.
+      To remove a lockdown for <replaceable>action</replaceable> use the <option>--remove-lockdown</option> option.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pklalockdown-required-auhtz">
+    <title>REQUIRED AUTHORIZATIONS</title>
+    <para>
+      The <emphasis>org.freedesktop.policykit.localauthority.lockdown</emphasis>
+      authorization is needed to add or remove lockdown. By default,
+      this authorization requires administrator authentication and
+      cannot be retained.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pklalockdown-impl-details">
+    <title>IMPLEMENTATION DETAILS</title>
+    <para>
+      Lockdown is implemented through <filename>.pkla</filename>
+      files. Locked down actions supersede other most other Local
+      Authority configuration as the <filename>.pkla</filename> files
+      are placed
+      in <filename>/var/lib/polkit-1/localauthority90-mandatory.d</filename>.
+    <para>
+    </para>
+      Programs checking authorizations can check whether an action is
+      locked down via by checking
+      the <emphasis>polkit.localauthority.lockdown</emphasis> key/value pair in
+      the details of the authorization response.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pklalockdown-return-values">
+    <title>RETURN VALUE</title>
+    <para>
+      On success <command>pklalockdown</command> returns 0. Otherwise a
+      non-zero value is returned and a diagnostic message is printed
+      on standard error.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pklalockdown-author"><title>AUTHOR</title>
+    <para>
+      Written by David Zeuthen <email>davidz@redhat.com</email> with
+      a lot of help from many others.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pklalockdown-bugs">
+    <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 id="pklalockdown-see-also">
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>polkit</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>pkcheck</refentrytitle><manvolnum>1</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>pklocalauthority</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index 52dded2..495aa41 100644 (file)
     <para>
       Each group in a <filename>.pkla</filename> must have a name that
       is unique within the file it belongs to. The following keys are
-      required in each group
+      are processed.
     </para>
     <variablelist>
       <varlistentry>
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><emphasis>ReturnValue</emphasis></term>
+        <listitem>
+          <para>
+            A semi-colon separated list of key/value pairs (of the
+            form key=value) that are add to the details of
+            authorization result on positive matches.
+          </para>
+        </listitem>
+      </varlistentry>
     </variablelist>
     <para>
       All keys specified above are required except that only at least
       one
       of <emphasis>RequireAny</emphasis>, <emphasis>RequireInactive</emphasis>
-      and <emphasis>RequireActive</emphasis> is present.
+      and <emphasis>RequireActive</emphasis> is
+      present. The <emphasis>ReturnValue</emphasis> key is optional.
     </para>
   </refsect1>
 
       authorization check matches the data from the authorization
       check, then the authorization result
       from <emphasis>RequireAny</emphasis>, <emphasis>RequireInactive</emphasis>
-      or <emphasis>RequireActive</emphasis> is used. Finally, the
-      authorization entries are consulted using the user identity.
+      or <emphasis>RequireActive</emphasis> is used
+      and <emphasis>ReturnValue</emphasis> is added to the
+      authorization result. Finally, the authorization entries are
+      consulted using the user identity in the same manner.
     </para>
     <para>
       Note that processing continues even after a match. This allows
index f09954b..357efdf 100644 (file)
     <title>Manual Pages</title>
     <xi:include href="../man/polkit.xml"/>
     <xi:include href="../man/polkitd.xml"/>
-    <xi:include href="../man/pklocalauthority.xml"/>
     <xi:include href="../man/pkcheck.xml"/>
     <xi:include href="../man/pkaction.xml"/>
     <xi:include href="../man/pkexec.xml"/>
+    <xi:include href="../man/pklocalauthority.xml"/>
+    <xi:include href="../man/pklalockdown.xml"/>
   </part>
 
   <chapter id="polkit-hierarchy">
index 333e2c8..f5bc3a8 100644 (file)
@@ -64,6 +64,7 @@ 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_local_authority_lock_down
 polkit_authorization_result_get_details
 <SUBSECTION Standard>
 PolkitAuthorizationResultClass
index 40abed1..e55ae88 100644 (file)
@@ -276,3 +276,28 @@ polkit_authorization_result_get_temporary_authorization_id (PolkitAuthorizationR
 
   return ret;
 }
+
+/**
+ * polkit_authorization_result_get_local_authority_lock_down:
+ * @result: A #PolkitAuthorizationResult.
+ *
+ * Gets whether the action is locked down in the Local Authority via pklalockdown(1).
+ *
+ * This method simply reads the value of the key/value pair in @details with the
+ * key <literal>polkit.localauthority.lockdown</literal>.
+ *
+ * Returns: %TRUE if the authorization is or will be temporary.
+ */
+gboolean
+polkit_authorization_result_get_local_authority_lock_down (PolkitAuthorizationResult *result)
+{
+  gboolean ret;
+  PolkitDetails *details;
+
+  ret = FALSE;
+  details = polkit_authorization_result_get_details (result);
+  if (details != NULL && polkit_details_lookup (details, "polkit.localauthority.lockdown") != NULL)
+    ret = TRUE;
+
+  return ret;
+}
index ea479fe..ae00f83 100644 (file)
@@ -52,6 +52,7 @@ gboolean           polkit_authorization_result_get_is_authorized              (P
 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_local_authority_lock_down  (PolkitAuthorizationResult *result);
 
 /* ---------------------------------------------------------------------------------------------------- */
 
index bf88c2b..add7a60 100644 (file)
@@ -715,7 +715,8 @@ check_authorization_sync (PolkitBackendAuthority         *authority,
                                                                                           session_is_active,
                                                                                           action_id,
                                                                                           details,
-                                                                                          implicit_authorization);
+                                                                                          implicit_authorization,
+                                                                                          result_details);
 
   /* first see if there's an implicit authorization for subject available */
   if (implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED)
@@ -844,9 +845,12 @@ polkit_backend_interactive_authority_get_admin_identities (PolkitBackendInteract
  * @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.
+ * @out_details: A #PolkitDetails object that will be return to @caller.
  *
  * Checks whether @subject is authorized to perform the action
- * specified by @action_id and @details.
+ * specified by @action_id and @details. The implementation may
+ * append key/value pairs to @out_details to return extra information
+ * to @caller.
  *
  * The default implementation of this method simply returns @implicit.
  *
@@ -862,7 +866,8 @@ polkit_backend_interactive_authority_check_authorization_sync (PolkitBackendInte
                                                                gboolean                           subject_is_active,
                                                                const gchar                       *action_id,
                                                                PolkitDetails                     *details,
-                                                               PolkitImplicitAuthorization        implicit)
+                                                               PolkitImplicitAuthorization        implicit,
+                                                               PolkitDetails                     *out_details)
 {
   PolkitBackendInteractiveAuthorityClass *klass;
   PolkitImplicitAuthorization ret;
@@ -883,7 +888,8 @@ polkit_backend_interactive_authority_check_authorization_sync (PolkitBackendInte
                                              subject_is_active,
                                              action_id,
                                              details,
-                                             implicit);
+                                             implicit,
+                                             out_details);
     }
 
   return ret;
index 9820dac..408c3e4 100644 (file)
@@ -83,7 +83,8 @@ struct _PolkitBackendInteractiveAuthorityClass
                                                            gboolean                           subject_is_active,
                                                            const gchar                       *action_id,
                                                            PolkitDetails                     *details,
-                                                           PolkitImplicitAuthorization        implicit);
+                                                           PolkitImplicitAuthorization        implicit,
+                                                           PolkitDetails                     *out_details);
 
   /*< private >*/
   /* Padding for future expansion */
@@ -138,7 +139,8 @@ PolkitImplicitAuthorization polkit_backend_interactive_authority_check_authoriza
                                                           gboolean                           subject_is_active,
                                                           const gchar                       *action_id,
                                                           PolkitDetails                     *details,
-                                                          PolkitImplicitAuthorization        implicit);
+                                                          PolkitImplicitAuthorization        implicit,
+                                                          PolkitDetails                     *out_details);
 
 G_END_DECLS
 
index a3fb7f2..932706e 100644 (file)
@@ -79,7 +79,8 @@ static PolkitImplicitAuthorization polkit_backend_local_authority_check_authoriz
                                                           gboolean                           subject_is_active,
                                                           const gchar                       *action_id,
                                                           PolkitDetails                     *details,
-                                                          PolkitImplicitAuthorization        implicit);
+                                                          PolkitImplicitAuthorization        implicit,
+                                                          PolkitDetails                     *out_details);
 
 
 G_DEFINE_TYPE_WITH_CODE (PolkitBackendLocalAuthority,
@@ -257,7 +258,8 @@ polkit_backend_local_authority_check_authorization_sync (PolkitBackendInteractiv
                                                          gboolean                           subject_is_active,
                                                          const gchar                       *action_id,
                                                          PolkitDetails                     *details,
-                                                         PolkitImplicitAuthorization        implicit)
+                                                         PolkitImplicitAuthorization        implicit,
+                                                         PolkitDetails                     *out_details)
 {
   PolkitBackendLocalAuthority *local_authority;
   PolkitBackendLocalAuthorityPrivate *priv;
@@ -296,7 +298,8 @@ polkit_backend_local_authority_check_authorization_sync (PolkitBackendInteractiv
                                                                details,
                                                                &ret_any,
                                                                &ret_inactive,
-                                                               &ret_active))
+                                                               &ret_active,
+                                                               out_details))
             {
               if (subject_is_local && subject_is_active)
                 {
@@ -330,7 +333,8 @@ polkit_backend_local_authority_check_authorization_sync (PolkitBackendInteractiv
                                                            details,
                                                            &ret_any,
                                                            &ret_inactive,
-                                                           &ret_active))
+                                                           &ret_active,
+                                                           out_details))
         {
           if (subject_is_local && subject_is_active)
             {
index 1c898f7..bdd0103 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "config.h"
 
+#include <string.h>
 #include <polkit/polkit.h>
 #include "polkitbackendlocalauthorizationstore.h"
 
@@ -79,6 +80,8 @@ typedef struct
   PolkitImplicitAuthorization result_any;
   PolkitImplicitAuthorization result_inactive;
   PolkitImplicitAuthorization result_active;
+
+  GHashTable *return_value;
 } LocalAuthorization;
 
 static void
@@ -89,6 +92,8 @@ local_authorization_free (LocalAuthorization *authorization)
   g_list_free (authorization->identity_specs);
   g_list_foreach (authorization->action_specs, (GFunc) g_pattern_spec_free, NULL);
   g_list_free (authorization->action_specs);
+  if (authorization->return_value != NULL)
+    g_hash_table_unref (authorization->return_value);
   g_free (authorization);
 }
 
@@ -105,6 +110,7 @@ local_authorization_new (GKeyFile      *key_file,
   gchar *result_any_string;
   gchar *result_inactive_string;
   gchar *result_active_string;
+  gchar **return_value_strings;
   guint n;
 
   identity_strings = NULL;
@@ -112,6 +118,7 @@ local_authorization_new (GKeyFile      *key_file,
   result_any_string = NULL;
   result_inactive_string = NULL;
   result_active_string = NULL;
+  return_value_strings = NULL;
 
   authorization = g_new0 (LocalAuthorization, 1);
 
@@ -221,6 +228,42 @@ local_authorization_new (GKeyFile      *key_file,
           goto out;
     }
 
+  return_value_strings = g_key_file_get_string_list (key_file,
+                                                     group,
+                                                     "ReturnValue",
+                                                     NULL,
+                                                     error);
+  if (return_value_strings != NULL)
+    {
+      for (n = 0; return_value_strings[n] != NULL; n++)
+        {
+          gchar *p;
+          const gchar *key;
+          const gchar *value;
+
+          p = strchr (return_value_strings[n], '=');
+          if (p == NULL)
+            {
+              g_warning ("Item `%s' in ReturnValue is malformed. Ignoring.",
+                         return_value_strings[n]);
+              continue;
+            }
+
+          *p = '\0';
+          key = return_value_strings[n];
+          value = p + 1;
+
+          if (authorization->return_value == NULL)
+            {
+              authorization->return_value = g_hash_table_new_full (g_str_hash,
+                                                                   g_str_equal,
+                                                                   g_free,
+                                                                   g_free);
+            }
+          g_hash_table_insert (authorization->return_value, g_strdup (key), g_strdup (value));
+        }
+    }
+
   authorization->id = g_strdup_printf ("%s::%s", filename, group);
 
  out:
@@ -229,6 +272,7 @@ local_authorization_new (GKeyFile      *key_file,
   g_free (result_any_string);
   g_free (result_inactive_string);
   g_free (result_active_string);
+  g_strfreev (return_value_strings);
   return authorization;
 }
 
@@ -605,6 +649,7 @@ polkit_backend_local_authorization_store_ensure (PolkitBackendLocalAuthorization
  * @out_result_any: Return location for the result for any subjects if the look up matched.
  * @out_result_inactive: Return location for the result for subjects in local inactive sessions if the look up matched.
  * @out_result_active: Return location for the result for subjects in local active sessions if the look up matched.
+ * @out_details: %NULL or a #PolkitDetails object to append key/value pairs to on a positive match.
  *
  * Checks if an authorization entry from @store matches @identity, @action_id and @details.
  *
@@ -618,7 +663,8 @@ polkit_backend_local_authorization_store_lookup (PolkitBackendLocalAuthorization
                                                  PolkitDetails                        *details,
                                                  PolkitImplicitAuthorization          *out_result_any,
                                                  PolkitImplicitAuthorization          *out_result_inactive,
-                                                 PolkitImplicitAuthorization          *out_result_active)
+                                                 PolkitImplicitAuthorization          *out_result_active,
+                                                 PolkitDetails                        *out_details)
 {
   GList *l, *ll;
   gboolean ret;
@@ -667,6 +713,19 @@ polkit_backend_local_authorization_store_lookup (PolkitBackendLocalAuthorization
       *out_result_active = authorization->result_active;
       ret = TRUE;
 
+      if (out_details != NULL && authorization->return_value != NULL)
+        {
+          GHashTableIter iter;
+          const gchar *key;
+          const gchar *value;
+
+          g_hash_table_iter_init (&iter, authorization->return_value);
+          while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value))
+            {
+              polkit_details_insert (out_details, key, value);
+            }
+        }
+
 #if 0
       g_debug ("authorization with id `%s' matched action_id `%s' for identity `%s'",
                authorization->id,
index 2f2b452..865bf22 100644 (file)
@@ -77,7 +77,8 @@ gboolean  polkit_backend_local_authorization_store_lookup   (PolkitBackendLocalA
                                                              PolkitDetails                        *details,
                                                              PolkitImplicitAuthorization          *out_result_any,
                                                              PolkitImplicitAuthorization          *out_result_inactive,
-                                                             PolkitImplicitAuthorization          *out_result_active);
+                                                             PolkitImplicitAuthorization          *out_result_active,
+                                                             PolkitDetails                        *out_details);
 
 G_END_DECLS
 
index 074fb39..226c9f3 100644 (file)
@@ -17,7 +17,7 @@ INCLUDES =                                                    \
 
 # ----------------------------------------------------------------------------------------------------
 
-bin_PROGRAMS = pkexec pkcheck pkaction
+bin_PROGRAMS = pkexec pkcheck pkaction pklalockdown
 
 # ----------------------------------------------------------------------------------------------------
 
@@ -82,6 +82,19 @@ pkaction_LDADD =                                                     \
 
 # ----------------------------------------------------------------------------------------------------
 
+pklalockdown_SOURCES = pklalockdown.c
+
+pklalockdown_CFLAGS =                                                  \
+       $(GLIB_CFLAGS)                                                  \
+       $(NULL)
+
+pklalockdown_LDADD =                                                   \
+       $(GLIB_LDADD)                                                   \
+       $(top_builddir)/src/polkit/libpolkit-gobject-1.la               \
+       $(NULL)
+
+# ----------------------------------------------------------------------------------------------------
+
 clean-local :
        rm -f *~
 
diff --git a/src/programs/pklalockdown.c b/src/programs/pklalockdown.c
new file mode 100644 (file)
index 0000000..88ce660
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * 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 <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <errno.h>
+#include <glib/gstdio.h>
+
+#include <polkit/polkit.h>
+
+static gchar *get_lockdown_filename (const gchar *action_id);
+static gboolean lockdown_exists (const gchar *action_id);
+
+
+static void
+usage (int argc, char *argv[])
+{
+  GError *error;
+
+  error = NULL;
+  if (!g_spawn_command_line_sync ("man pklalockdown",
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  &error))
+    {
+      g_printerr ("Cannot show manual page: %s\n", error->message);
+      g_error_free (error);
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  guint n;
+  guint ret;
+  gboolean opt_show_help;
+  gboolean opt_show_version;
+  gchar *opt_lockdown;
+  gchar *opt_remove_lockdown;
+
+  ret = 1;
+
+  opt_show_help = FALSE;
+  opt_show_version = FALSE;
+  opt_lockdown = NULL;
+  opt_remove_lockdown = NULL;
+
+  /* if we are not yet uid 0, make us uid 0 through pkexec */
+  if (getuid () != 0)
+    {
+      gchar **exec_argv;
+
+      exec_argv = g_new0 (gchar *, argc + 2);
+      exec_argv[0] = PACKAGE_BIN_DIR "/pkexec";
+      memcpy (exec_argv + 1, argv, argc * sizeof (gchar *));
+
+      if (execv (PACKAGE_BIN_DIR "/pkexec", exec_argv) != 0)
+        {
+          g_printerr ("Error executing " PACKAGE_BIN_DIR "/pkexec: %s\n", g_strerror (errno));
+          goto out;
+        }
+
+      g_assert_not_reached ();
+    }
+
+  /* We are now uid 0 (by default, the user had to authenticate to get
+   * here) - be careful to check all incoming args
+   */
+  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], "--lockdown") == 0 || strcmp (argv[n], "-l") == 0)
+        {
+          n++;
+          if (n >= (guint) argc)
+            {
+              usage (argc, argv);
+              goto out;
+            }
+
+          opt_lockdown = g_strdup (argv[n]);
+        }
+      else if (strcmp (argv[n], "--remove-lockdown") == 0 || strcmp (argv[n], "-r") == 0)
+        {
+          n++;
+          if (n >= (guint) argc)
+            {
+              usage (argc, argv);
+              goto out;
+            }
+
+          opt_remove_lockdown = 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;
+    }
+  else if (opt_lockdown != NULL)
+    {
+      gchar *filename;
+      gchar *contents;
+      GError *error;
+
+      if (lockdown_exists (opt_lockdown))
+        {
+          g_printerr ("Error: action %s is already locked down\n", opt_lockdown);
+          goto out;
+        }
+
+      filename = get_lockdown_filename (opt_lockdown);
+      contents = g_strdup_printf ("# Added by pklalockdown(1)\n"
+                                  "#\n"
+                                  "[Lockdown]\n"
+                                  "Identity=unix-user:*\n"
+                                  "Action=%s\n"
+                                  "ResultAny=no\n"
+                                  "ResultInactive=no\n"
+                                  "ResultActive=auth_admin_keep\n"
+                                  "ReturnValue=polkit.localauthority.lockdown=1",
+                                  opt_lockdown);
+      error = NULL;
+      if (!g_file_set_contents (filename,
+                                contents,
+                                -1,
+                                &error))
+        {
+          g_printerr ("Error: Cannot write to file %s: %s\n", filename, error->message);
+          g_error_free (error);
+          g_free (filename);
+          g_free (contents);
+          goto out;
+        }
+      g_free (filename);
+      g_free (contents);
+      ret = 0;
+      goto out;
+    }
+  else if (opt_remove_lockdown != NULL)
+    {
+      gchar *filename;
+
+      if (!lockdown_exists (opt_remove_lockdown))
+        {
+          g_printerr ("Error: action %s is not locked down\n", opt_remove_lockdown);
+          goto out;
+        }
+
+      filename = get_lockdown_filename (opt_remove_lockdown);
+      if (g_unlink (filename) != 0)
+        {
+          g_printerr ("Error: Cannot unlink file %s: %s\n", filename, g_strerror (errno));
+          g_free (filename);
+          goto out;
+        }
+      g_free (filename);
+
+      ret = 0;
+      goto out;
+    }
+
+  usage (argc, argv);
+
+ out:
+  g_free (opt_lockdown);
+  g_free (opt_remove_lockdown);
+  return ret;
+}
+
+static gchar *
+get_lockdown_filename (const gchar *action_id)
+{
+  return g_strdup_printf (PACKAGE_LOCALSTATE_DIR
+                          "/lib/polkit-1/localauthority/90-mandatory.d/"
+                          "org.freedesktop.policykit.localauthority.lockdown.action-%s.pkla",
+                          action_id);
+}
+
+static gboolean
+lockdown_exists (const gchar *action_id)
+{
+  gchar *filename;
+  gboolean ret;
+
+  ret = FALSE;
+
+  filename = get_lockdown_filename (action_id);
+  if (g_file_test (filename, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS))
+    ret = TRUE;
+  g_free (filename);
+
+  return ret;
+}
+