Commit 8e0b9b47 authored by David Zeuthen's avatar David Zeuthen

Bug 25367 — Also read local authority configuration data from /etc

Turns out some people would rather edit local files in /etc rather
than shipping them in a package (as e.g. Fedora does with the
polkit-desktop-policy RPM).

This also drops the hard-coded list of directory names such as
10-vendor.d, 20-org.d - we now monitor the
/var/lib/polkit-1/localauthority and /etc/polkit-1/localauthority
directories for changes - whenever we see a subdirectory in any of
these directories, we create an AuthorizationStore object that looks
for .pkla files.
Signed-off-by: default avatarDavid Zeuthen <davidz@redhat.com>
parent c367f5f7
......@@ -484,6 +484,10 @@ echo "
"
echo "NOTE: The directory ${sysconfdir}/polkit-1/localauthority must be owned"
echo " by root and have mode 700"
echo
echo "NOTE: The directory ${localstatedir}/lib/polkit-1 must be owned"
echo " by root and have mode 700"
echo
......
......@@ -91,7 +91,22 @@
<title>DIRECTORY STRUCTURE</title>
<para>
The Local Authority reads files with <filename>.pkla</filename>
extension from the following directories
extension from all directories located inside the
<filename>/etc/polkit-1/localauthority</filename>
and <filename>/var/lib/polkit-1/localauthority</filename>
directories. By default, the following sub-directories are installed.
</para>
<programlisting>
/etc/polkit-1/
`-- localauthority
|-- 10-vendor.d
|-- 20-org.d
|-- 30-site.d
|-- 50-local.d
`-- 90-mandatory.d
</programlisting>
<para>
and
</para>
<programlisting>
/var/lib/polkit-1/
......@@ -102,6 +117,12 @@
|-- 50-local.d
`-- 90-mandatory.d
</programlisting>
<para>
The <filename>/etc/polkit-1/localauthority</filename> hierarchy
is inteded for local configuration and
the <filename>/var/lib/polkit-1/localauthority</filename> is
intended for 3rd party packages.
</para>
<para>
Each <filename>.pkla</filename> file contains one or more
authorization entries. If the underlying filesystem supports
......@@ -117,7 +138,7 @@
<term><emphasis>10-vendor.d</emphasis></term>
<listitem>
<para>
Reserved for the Operating System vendor.
Intended for use by the OS vendor.
</para>
</listitem>
</varlistentry>
......@@ -125,7 +146,7 @@
<term><emphasis>20-org.d</emphasis></term>
<listitem>
<para>
Reserved for the organization deploying the system.
Intended for the organization deploying the OS.
</para>
</listitem>
</varlistentry>
......@@ -133,7 +154,7 @@
<term><emphasis>30-site.d</emphasis></term>
<listitem>
<para>
Reserved for site deploying the system.
Intended for the site deploying the system.
</para>
</listitem>
</varlistentry>
......@@ -141,7 +162,7 @@
<term><emphasis>50-local.d</emphasis></term>
<listitem>
<para>
Reserved for local usage.
Intended for local usage.
</para>
</listitem>
</varlistentry>
......@@ -149,15 +170,19 @@
<term><emphasis>90-mandatory.d</emphasis></term>
<listitem>
<para>
Reserved for the organization deploying the system.
Intended for the organization deploying the OS.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
Each <filename>.pkla</filename> file is a standard <emphasis>key
file</emphasis> and contains key/value pairs in one or more
groups with each group representing an authorization entry.
and new directories can be added/removed as needed.
</para>
<para>
As to regards to the content, each <filename>.pkla</filename>
file is a standard <emphasis>key file</emphasis> and contains
key/value pairs in one or more groups with each group
representing an authorization entry.
A <filename>.pkla</filename> file MUST be named by using a
scheme to ensure that the name is unique, e.g. reverse DNS
notation or similar. For example, if the organization is
......@@ -261,13 +286,78 @@
following algorithm.
</para>
<para>
First, the user of the Subject is determined and the groups that
the user belongs are looked up. For each group identity, the
authorization entries are consulted in the lexigraphical order
(using standard lexicographical sorting (using the standard C
locale) of file names and appearance of each group in each
file). If the authorization check matches the data from the
authorization check, then the authorization result
The authorization entries from all .pkla files are ordered using
the following rules. First all the basename of all
sub-directories (e.g. <emphasis>30-site.d</emphasis>) from both
the <filename>/etc/polkit-1/localauthority</filename>
and <filename>/var/lib/polkit-1/localauthority</filename>
directories are enumerated and sorted (using the C locale). If a
name exists in both <filename>/etc</filename>
and <filename>/var</filename>, the one
in <filename>/etc</filename> takes precedence. Then
all <filename>.pkla</filename> files are read in order from this
list of sub-directories. For each <filename>.pkla</filename>
file, authorizations from each file are appended in order resulting
in an ordered list of authorization entries.
</para>
<para>
For example, given the following files
</para>
<programlisting>
/var/lib/polkit-1
└── localauthority
├── 10-vendor.d
│ └── 10-desktop-policy.pkla
├── 20-org.d
├── 30-site.d
├── 50-local.d
├── 55-org.my.company.d
│ └── 10-org.my.company.product.pkla
└── 90-mandatory.d
/etc/polkit-1
└── localauthority
├── 10-vendor.d
│ └── 01-some-changes-from-a-subvendor.pkla
├── 20-org.d
├── 30-site.d
├── 50-local.d
├── 55-org.my.company.d
│ └── 10-org.my.company.product.pkla
└── 90-mandatory.d
</programlisting>
<para>
the evaluation order of the <filename>.pkla</filename> files is:
</para>
<orderedlist>
<listitem>
<para>
<filename>10-desktop-policy.pkla</filename>
</para>
</listitem>
<listitem>
<para>
<filename>01-some-changes-from-a-subvendor.pkla</filename>
</para>
</listitem>
<listitem>
<para>
<filename>10-org.my.company.product.pkla</filename> (the <filename>/var</filename> one)
</para>
</listitem>
<listitem>
<para>
<filename>10-org.my.company.product.pkla</filename> (the <filename>/etc</filename> one)
</para>
</listitem>
</orderedlist>
<para>
When the list of authorization entries has been calculated, the
authorization check can be made. First, the user of the Subject
is determined and the groups that the user belongs are looked
up. For each group identity, the authorization entries are
consulted in order. If the 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
and <emphasis>ReturnValue</emphasis> is added to the
......
......@@ -103,4 +103,7 @@ install-exec-hook:
mkdir -p $(DESTDIR)$(localstatedir)/lib/polkit-1
mkdir -p $(DESTDIR)$(localstatedir)/lib/polkit-1/localauthority/{10-vendor.d,20-org.d,30-site.d,50-local.d,90-mandatory.d}
-chmod 700 $(DESTDIR)$(localstatedir)/lib/polkit-1
mkdir -p $(DESTDIR)$(sysconfdir)/polkit-1
mkdir -p $(DESTDIR)$(sysconfdir)/polkit-1/localauthority/{10-vendor.d,20-org.d,30-site.d,50-local.d,90-mandatory.d}
-chmod 700 $(DESTDIR)$(sysconfdir)/polkit-1/localauthority
mkdir -p $(DESTDIR)$(libdir)/polkit-1/extensions
......@@ -66,6 +66,9 @@ typedef struct
GList *authorization_stores;
GFileMonitor *sysconf_dir_monitor;
GFileMonitor *localstate_dir_monitor;
} PolkitBackendLocalAuthorityPrivate;
/* ---------------------------------------------------------------------------------------------------- */
......@@ -131,43 +134,226 @@ on_store_changed (PolkitBackendLocalAuthorizationStore *store,
g_signal_emit_by_name (authority, "changed");
}
/* ---------------------------------------------------------------------------------------------------- */
static void
polkit_backend_local_authority_init (PolkitBackendLocalAuthority *authority)
purge_all_authorization_stores (PolkitBackendLocalAuthority *authority)
{
PolkitBackendLocalAuthorityPrivate *priv;
GFile *directory;
GList *l;
priv = POLKIT_BACKEND_LOCAL_AUTHORITY_GET_PRIVATE (authority);
for (l = priv->authorization_stores; l != NULL; l = l->next)
{
PolkitBackendLocalAuthorizationStore *store = POLKIT_BACKEND_LOCAL_AUTHORIZATION_STORE (l->data);
g_signal_handlers_disconnect_by_func (store,
G_CALLBACK (on_store_changed),
authority);
g_object_unref (store);
}
g_list_free (priv->authorization_stores);
priv->authorization_stores = NULL;
g_debug ("Purged all local authorization stores");
}
/* ---------------------------------------------------------------------------------------------------- */
static void
add_one_authorization_store (PolkitBackendLocalAuthority *authority,
GFile *directory)
{
PolkitBackendLocalAuthorizationStore *store;
PolkitBackendLocalAuthorityPrivate *priv;
priv = POLKIT_BACKEND_LOCAL_AUTHORITY_GET_PRIVATE (authority);
store = polkit_backend_local_authorization_store_new (directory, ".pkla");
priv->authorization_stores = g_list_append (priv->authorization_stores, store);
g_signal_connect (store,
"changed",
G_CALLBACK (on_store_changed),
authority);
}
static gint
authorization_store_path_compare_func (GFile *file_a,
GFile *file_b)
{
const gchar *a;
const gchar *b;
a = g_object_get_data (G_OBJECT (file_a), "sort-key");
b = g_object_get_data (G_OBJECT (file_b), "sort-key");
return g_strcmp0 (a, b);
}
static void
add_all_authorization_stores (PolkitBackendLocalAuthority *authority)
{
guint n;
const gchar *store_locations[] =
GList *directories;
GList *l;
directories = NULL;
for (n = 0; n < 2; n++)
{
const gchar *toplevel_path;
GFile *toplevel_directory;
GFileEnumerator *directory_enumerator;
GFileInfo *file_info;
GError *error;
error = NULL;
if (n == 0)
toplevel_path = PACKAGE_LOCALSTATE_DIR "/lib/polkit-1/localauthority";
else
toplevel_path = PACKAGE_SYSCONF_DIR "/polkit-1/localauthority";
toplevel_directory = g_file_new_for_path (toplevel_path);
directory_enumerator = g_file_enumerate_children (toplevel_directory,
"standard::*",
G_FILE_QUERY_INFO_NONE,
NULL,
&error);
if (directory_enumerator == NULL)
{
g_warning ("Error getting enumerator for %s: %s", toplevel_path, error->message);
g_error_free (error);
g_object_unref (toplevel_directory);
continue;
}
while ((file_info = g_file_enumerator_next_file (directory_enumerator, NULL, &error)) != NULL)
{
/* only consider directories */
if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
{
const gchar *name;
GFile *directory;
gchar *sort_key;
name = g_file_info_get_name (file_info);
/* This makes entries in directories in /etc take precedence to entries in directories in /var */
sort_key = g_strdup_printf ("%s-%d", name, n);
directory = g_file_get_child (toplevel_directory, name);
g_object_set_data_full (G_OBJECT (directory), "sort-key", sort_key, g_free);
directories = g_list_prepend (directories, directory);
}
g_object_unref (file_info);
}
if (error != NULL)
{
g_warning ("Error enumerating files in %s: %s", toplevel_path, error->message);
g_error_free (error);
g_object_unref (toplevel_directory);
g_object_unref (directory_enumerator);
continue;
}
g_object_unref (directory_enumerator);
g_object_unref (toplevel_directory);
}
/* Sort directories */
directories = g_list_sort (directories, (GCompareFunc) authorization_store_path_compare_func);
/* And now add an authorization store for each one */
for (l = directories; l != NULL; l = l->next)
{
PACKAGE_LOCALSTATE_DIR "/lib/polkit-1/localauthority/10-vendor.d",
PACKAGE_LOCALSTATE_DIR "/lib/polkit-1/localauthority/20-org.d",
PACKAGE_LOCALSTATE_DIR "/lib/polkit-1/localauthority/30-site.d",
PACKAGE_LOCALSTATE_DIR "/lib/polkit-1/localauthority/50-local.d",
PACKAGE_LOCALSTATE_DIR "/lib/polkit-1/localauthority/90-mandatory.d",
NULL
};
GFile *directory = G_FILE (l->data);
gchar *name;
name = g_file_get_path (directory);
g_debug ("Added `%s' as a local authorization store", name);
g_free (name);
add_one_authorization_store (authority, directory);
}
g_list_foreach (directories, (GFunc) g_object_unref, NULL);
g_list_free (directories);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
on_toplevel_authority_store_monitor_changed (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
gpointer user_data)
{
PolkitBackendLocalAuthority *authority = POLKIT_BACKEND_LOCAL_AUTHORITY (user_data);
purge_all_authorization_stores (authority);
add_all_authorization_stores (authority);
}
static void
polkit_backend_local_authority_init (PolkitBackendLocalAuthority *authority)
{
PolkitBackendLocalAuthorityPrivate *priv;
GFile *config_directory;
guint n;
priv = POLKIT_BACKEND_LOCAL_AUTHORITY_GET_PRIVATE (authority);
directory = g_file_new_for_path (PACKAGE_SYSCONF_DIR "/polkit-1/localauthority.conf.d");
priv->config_source = polkit_backend_config_source_new (directory);
g_object_unref (directory);
config_directory = g_file_new_for_path (PACKAGE_SYSCONF_DIR "/polkit-1/localauthority.conf.d");
priv->config_source = polkit_backend_config_source_new (config_directory);
g_object_unref (config_directory);
for (n = 0; store_locations[n] != NULL; n++)
add_all_authorization_stores (authority);
/* Monitor the toplevels */
for (n = 0; n < 2; n++)
{
PolkitBackendLocalAuthorizationStore *store;
const gchar *toplevel_path;
GFile *toplevel_directory;
GFileMonitor *monitor;
GError *error;
if (n == 0)
toplevel_path = PACKAGE_LOCALSTATE_DIR "/lib/polkit-1/localauthority";
else
toplevel_path = PACKAGE_SYSCONF_DIR "/polkit-1/localauthority";
toplevel_directory = g_file_new_for_path (toplevel_path);
error = NULL;
monitor = g_file_monitor_directory (toplevel_directory,
G_FILE_MONITOR_NONE,
NULL,
&error);
if (monitor == NULL)
{
g_warning ("Error creating file monitor for %s: %s", toplevel_path, error->message);
g_error_free (error);
g_object_unref (toplevel_directory);
continue;
}
directory = g_file_new_for_path (store_locations[n]);
store = polkit_backend_local_authorization_store_new (directory, ".pkla");
priv->authorization_stores = g_list_prepend (priv->authorization_stores, store);
g_object_unref (directory);
g_debug ("Monitoring `%s' for changes", toplevel_path);
g_signal_connect (store,
g_signal_connect (monitor,
"changed",
G_CALLBACK (on_store_changed),
G_CALLBACK (on_toplevel_authority_store_monitor_changed),
authority);
if (n == 0)
priv->sysconf_dir_monitor = monitor;
else
priv->localstate_dir_monitor = monitor;
g_object_unref (toplevel_directory);
}
priv->authorization_stores = g_list_reverse (priv->authorization_stores);
}
static void
......@@ -179,12 +365,16 @@ polkit_backend_local_authority_finalize (GObject *object)
local_authority = POLKIT_BACKEND_LOCAL_AUTHORITY (object);
priv = POLKIT_BACKEND_LOCAL_AUTHORITY_GET_PRIVATE (local_authority);
purge_all_authorization_stores (local_authority);
if (priv->sysconf_dir_monitor != NULL)
g_object_unref (priv->sysconf_dir_monitor);
if (priv->localstate_dir_monitor != NULL)
g_object_unref (priv->localstate_dir_monitor);
if (priv->config_source != NULL)
g_object_unref (priv->config_source);
g_list_foreach (priv->authorization_stores, (GFunc) g_object_unref, NULL);
g_list_free (priv->authorization_stores);
G_OBJECT_CLASS (polkit_backend_local_authority_parent_class)->finalize (object);
}
......
......@@ -518,6 +518,12 @@ polkit_backend_local_authorization_store_new (GFile *directory,
static void
polkit_backend_local_authorization_store_purge (PolkitBackendLocalAuthorizationStore *store)
{
gchar *path;
path = g_file_get_path (store->priv->directory);
g_debug ("Dropping all .pkla caches for directory `%s'", path);
g_free (path);
g_list_foreach (store->priv->authorizations, (GFunc) local_authorization_free, NULL);
g_list_free (store->priv->authorizations);
store->priv->authorizations = NULL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment