Commit ffc99a26 authored by David Zeuthen's avatar David Zeuthen

Implement lockdown for the Local Authority implementation

parent 2a932ebb
......@@ -58,4 +58,15 @@
<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>
......@@ -129,7 +129,7 @@
</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>
......
......@@ -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:
......
<?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>
......@@ -155,7 +155,7 @@
<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>
......@@ -214,12 +214,23 @@
</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>
......@@ -240,8 +251,10 @@
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
......
......@@ -110,10 +110,11 @@
<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">
......
......@@ -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
......
......@@ -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;
}
......@@ -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);
/* ---------------------------------------------------------------------------------------------------- */
......
......@@ -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;
......
......@@ -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
......
......@@ -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)
{
......
......@@ -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,
......
......@@ -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
......
......@@ -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 *~
......
/*
* 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