Commit 871e4c93 authored by David Zeuthen's avatar David Zeuthen

provide a polkit D-Bus service that is activated on demand

Right now we provide two methods

 IsProcessAuthorized
 IsSystemBusNameAuthorized

This is useful for a couple of reasons

 - some mechanisms (e.g. Avahi) runs in a chroot and their only
   life-line to the world is a system bus connection. If it were to
   use libpolkit (and Lennart says he wants it to, yay!) it would need
   to bindmount crazy stuff into the chroot.

 - languages for which libpolkit bindings not yet exist can use
   this interface

Going forward, this service can expose a private interface meaning we
can get rid of (almost) all of our setgid helpers.
parent d9d79087
## Process this file with automake to produce Makefile.in
SUBDIRS = data polkit polkit-dbus polkit-grant doc tools policy po
SUBDIRS = data polkit polkit-dbus polkit-grant polkitd doc tools policy po
# Creating ChangeLog from git log (taken from cairo/Makefile.am):
ChangeLog: $(srcdir)/ChangeLog
......
......@@ -136,6 +136,10 @@ PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.0])
AC_SUBST(DBUS_CFLAGS)
AC_SUBST(DBUS_LIBS)
PKG_CHECK_MODULES(DBUS_GLIB, [dbus-glib-1 >= 0.73])
AC_SUBST(DBUS_GLIB_CFLAGS)
AC_SUBST(DBUS_GLIB_LIBS)
AC_CHECK_FUNCS(getgrouplist)
EXPAT_LIB=""
......@@ -429,6 +433,7 @@ data/polkit-grant.pc
polkit/Makefile
polkit-dbus/Makefile
polkit-grant/Makefile
polkitd/Makefile
tools/Makefile
doc/Makefile
doc/version.xml
......
......@@ -341,6 +341,8 @@ main (int argc, char *argv[])
uid_t caller_uid;
uid_t requesting_info_for_uid;
char *endp;
struct passwd *pw;
uid_t uid_for_polkit_user;
ret = 1;
/* clear the entire environment to avoid attacks using with libraries honoring environment variables */
......@@ -379,6 +381,13 @@ main (int argc, char *argv[])
goto out;
}
pw = getpwnam (POLKIT_USER);
if (pw == NULL) {
fprintf (stderr, "polkit-read-auth-helper: cannot lookup uid for " POLKIT_USER "\n");
goto out;
}
uid_for_polkit_user = pw->pw_uid;
/*----------------------------------------------------------------------------------------------------*/
requesting_info_for_uid = strtoul (argv[1], &endp, 10);
......@@ -387,8 +396,8 @@ main (int argc, char *argv[])
goto out;
}
/* uid 0 is allowed to read anything */
if (caller_uid != 0) {
/* uid 0 and user polkituser is allowed to read anything */
if (caller_uid != 0 && caller_uid != uid_for_polkit_user) {
if (caller_uid != requesting_info_for_uid) {
/* see if calling user has the
......
## Process this file with automake to produce Makefile.in
INCLUDES = \
-I$(top_builddir) -I$(top_srcdir) \
-DPACKAGE_LIBEXEC_DIR=\""$(libexecdir)"\" \
-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
-DPACKAGE_LOCALSTATE_DIR=\""$(localstatedir)"\" \
-DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \
-DPACKAGE_LIB_DIR=\""$(libdir)"\" \
-D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT \
-DPOLKIT_COMPILATION \
$(DBUS_GLIB_CFLAGS) \
@GLIB_CFLAGS@
BUILT_SOURCES = \
polkit-daemon-glue.h
polkit-daemon-glue.h: org.freedesktop.PolicyKit.xml Makefile.am
dbus-binding-tool --prefix=polkit_daemon --mode=glib-server --output=polkit-daemon-glue.h org.freedesktop.PolicyKit.xml
libexec_PROGRAMS = polkitd
polkitd_SOURCES = \
polkit-daemon.h polkit-daemon.c \
main.c \
$(BUILT_SOURCES)
polkitd_CPPFLAGS = \
-I$(top_srcdir) \
-DG_LOG_DOMAIN=\"polkitd\" \
-DDATADIR=\""$(pkgdatadir)"\" \
-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
$(DISABLE_DEPRECATED) \
$(AM_CPPFLAGS)
polkitd_LDADD = \
$(DBUS_GLIB_LIBS) $(top_builddir)/polkit/libpolkit.la $(top_builddir)/polkit-dbus/libpolkit-dbus.la $(top_builddir)/polkit-grant/libpolkit-grant.la
servicedir = $(datadir)/dbus-1/system-services
service_in_files = org.freedesktop.PolicyKit.service.in
service_DATA = $(service_in_files:.service.in=.service)
$(service_DATA): $(service_in_files) Makefile
@sed -e "s|\@polkituser\@|$(POLKIT_USER)|" -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
dbusconfdir = $(sysconfdir)/dbus-1/system.d
dbusconf_in_files = org.freedesktop.PolicyKit.conf.in
dbusconf_DATA = $(dbusconf_in_files:.conf.in=.conf)
$(dbusconf_DATA): $(dbusconf_in_files) Makefile
@sed -e "s|\@polkituser\@|$(POLKIT_USER)|" $< > $@
CLEANFILES = $(BUILT_SOURCES)
EXTRA_DIST = org.freedesktop.PolicyKit.xml $(service_in_files) $(dbusconf_in_files)
clean-local :
rm -f *~ $(service_DATA) $(dbusconf_DATA)
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 David Zeuthen <david@fubar.dk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <glib.h>
#include <glib/gi18n-lib.h>
#include <glib-object.h>
#define DBUS_API_SUBJECT_TO_CHANGE
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "polkit-daemon.h"
#define NAME_TO_CLAIM "org.freedesktop.PolicyKit"
static gboolean
acquire_name_on_proxy (DBusGProxy *bus_proxy)
{
GError *error;
guint result;
gboolean res;
gboolean ret;
ret = FALSE;
if (bus_proxy == NULL) {
goto out;
}
error = NULL;
res = dbus_g_proxy_call (bus_proxy,
"RequestName",
&error,
G_TYPE_STRING, NAME_TO_CLAIM,
G_TYPE_UINT, 0,
G_TYPE_INVALID,
G_TYPE_UINT, &result,
G_TYPE_INVALID);
if (! res) {
if (error != NULL) {
g_warning ("Failed to acquire %s: %s", NAME_TO_CLAIM, error->message);
g_error_free (error);
} else {
g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
}
goto out;
}
if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
if (error != NULL) {
g_warning ("Failed to acquire %s: %s", NAME_TO_CLAIM, error->message);
g_error_free (error);
} else {
g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
}
goto out;
}
ret = TRUE;
out:
return ret;
}
int
main (int argc, char **argv)
{
GError *error;
GMainLoop *loop;
PolKitDaemon *daemon;
GOptionContext *context;
DBusGProxy *bus_proxy;
DBusGConnection *bus;
int ret;
struct passwd *pw = NULL;
struct group *gr = NULL;
static gboolean no_exit = FALSE;
static GOptionEntry entries [] = {
{ "no-exit", 0, 0, G_OPTION_ARG_NONE, &no_exit, "Don't exit after 30 seconds of inactivity", NULL },
{ NULL }
};
ret = 1;
pw = getpwnam (POLKIT_USER);
if (pw == NULL) {
g_warning ("polkitd: user " POLKIT_USER " does not exist");
goto out;
}
gr = getgrnam (POLKIT_GROUP);
if (gr == NULL) {
g_warning ("polkitd: group " POLKIT_GROUP " does not exist");
goto out;
}
if (initgroups (POLKIT_USER, gr->gr_gid)) {
g_warning ("polkitd: could not initialize groups");
goto out;
}
if (setgid (gr->gr_gid)) {
g_warning ("polkitd: could not set group id");
goto out;
}
if (setuid (pw->pw_uid)) {
g_warning ("polkitd: could not set user id");
goto out;
}
g_type_init ();
context = g_option_context_new ("PolicyKit daemon");
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_parse (context, &argc, &argv, NULL);
g_option_context_free (context);
error = NULL;
bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
if (bus == NULL) {
g_warning ("Couldn't connect to session bus: %s", error->message);
g_error_free (error);
goto out;
}
bus_proxy = dbus_g_proxy_new_for_name (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
if (bus_proxy == NULL) {
g_warning ("Could not construct bus_proxy object; bailing out");
goto out;
}
if (!acquire_name_on_proxy (bus_proxy) ) {
g_warning ("Could not acquire name; bailing out");
goto out;
}
g_debug ("Starting polkitd version %s", VERSION);
daemon = polkit_daemon_new (no_exit);
if (daemon == NULL) {
goto out;
}
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_object_unref (daemon);
g_main_loop_unref (loop);
ret = 0;
out:
return ret;
}
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only @polkituser@ can own the service -->
<policy user="@polkituser@">
<allow own="org.freedesktop.PolicyKit"/>
</policy>
</busconfig>
[D-BUS Service]
Name=org.freedesktop.PolicyKit
Exec=@libexecdir@/polkitd
User=root
# Hmm when using User=@polkituser@ I get this:
#
# Error org.freedesktop.DBus.Error.Spawn.ChildExited: Launch helper exited with unknown return code 1
#
# Need to investigate
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/">
<interface name="org.freedesktop.PolicyKit">
<method name="IsProcessAuthorized">
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<!-- IN: PolicyKit action identifier -->
<arg name="action_id" direction="in" type="s"/>
<!-- IN: process id of caller to check for -->
<arg name="pid" direction="in" type="u"/>
<!-- OUT: the PolKitResult in textual form -->
<arg name="result" direction="out" type="s"/>
</method>
<method name="IsSystemBusNameAuthorized">
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<!-- IN: PolicyKit action identifier -->
<arg name="action_id" direction="in" type="s"/>
<!-- IN: Unique name on the system bus of the caller to check for -->
<arg name="system_bus_name" direction="in" type="s"/>
<!-- OUT: the PolKitResult in textual form -->
<arg name="result" direction="out" type="s"/>
</method>
</interface>
</node>
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 David Zeuthen <david@fubar.dk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <signal.h>
#include <glib.h>
#include <glib/gi18n-lib.h>
#include <glib-object.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <polkit/polkit.h>
#include <polkit/polkit-utils.h>
#include <polkit-dbus/polkit-dbus.h>
#include "polkit-daemon.h"
static gboolean no_exit = FALSE;
/*--------------------------------------------------------------------------------------------------------------*/
#include "polkit-daemon-glue.h"
static gboolean
do_exit (gpointer user_data)
{
g_debug ("Exiting due to inactivity");
exit (1);
return FALSE;
}
static void
reset_killtimer (void)
{
static guint timer_id = 0;
if (no_exit)
return;
if (timer_id > 0) {
g_source_remove (timer_id);
}
g_debug ("Setting killtimer to 30 seconds...");
timer_id = g_timeout_add (30 * 1000, do_exit, NULL);
}
struct PolKitDaemonPrivate
{
DBusGConnection *system_bus_connection;
DBusGProxy *system_bus_proxy;
PolKitContext *pk_context;
PolKitTracker *pk_tracker;
};
static void polkit_daemon_class_init (PolKitDaemonClass *klass);
static void polkit_daemon_init (PolKitDaemon *seat);
static void polkit_daemon_finalize (GObject *object);
G_DEFINE_TYPE (PolKitDaemon, polkit_daemon, G_TYPE_OBJECT)
#define POLKIT_DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), POLKIT_TYPE_DAEMON, PolKitDaemonPrivate))
GQuark
polkit_daemon_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0) {
ret = g_quark_from_static_string ("polkit_daemon_error");
}
return ret;
}
#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
GType
polkit_daemon_error_get_type (void)
{
static GType etype = 0;
if (etype == 0)
{
static const GEnumValue values[] =
{
ENUM_ENTRY (POLKIT_DAEMON_ERROR_GENERAL, "GeneralError"),
ENUM_ENTRY (POLKIT_DAEMON_ERROR_NOT_AUTHORIZED, "NotAuthorized"),
{ 0, 0, 0 }
};
g_assert (POLKIT_DAEMON_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
etype = g_enum_register_static ("PolKitDaemonError", values);
}
return etype;
}
static GObject *
polkit_daemon_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
PolKitDaemon *daemon;
PolKitDaemonClass *klass;
klass = POLKIT_DAEMON_CLASS (g_type_class_peek (POLKIT_TYPE_DAEMON));
daemon = POLKIT_DAEMON (
G_OBJECT_CLASS (polkit_daemon_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
return G_OBJECT (daemon);
}
static void
polkit_daemon_class_init (PolKitDaemonClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructor = polkit_daemon_constructor;
object_class->finalize = polkit_daemon_finalize;
g_type_class_add_private (klass, sizeof (PolKitDaemonPrivate));
dbus_g_object_type_install_info (POLKIT_TYPE_DAEMON, &dbus_glib_polkit_daemon_object_info);
dbus_g_error_domain_register (POLKIT_DAEMON_ERROR, NULL, POLKIT_DAEMON_TYPE_ERROR);
}
static void
polkit_daemon_init (PolKitDaemon *daemon)
{
daemon->priv = POLKIT_DAEMON_GET_PRIVATE (daemon);
}
static void
polkit_daemon_finalize (GObject *object)
{
PolKitDaemon *daemon;
g_return_if_fail (object != NULL);
g_return_if_fail (POLKIT_IS_DAEMON (object));
daemon = POLKIT_DAEMON (object);
g_return_if_fail (daemon->priv != NULL);
g_object_unref (daemon->priv->system_bus_proxy);
G_OBJECT_CLASS (polkit_daemon_parent_class)->finalize (object);
}
static gboolean
pk_io_watch_have_data (GIOChannel *channel, GIOCondition condition, gpointer user_data)
{
int fd;
PolKitContext *pk_context = user_data;
fd = g_io_channel_unix_get_fd (channel);
polkit_context_io_func (pk_context, fd);
return TRUE;
}
static int
pk_io_add_watch (PolKitContext *pk_context, int fd)
{
guint id = 0;
GIOChannel *channel;
channel = g_io_channel_unix_new (fd);
if (channel == NULL)
goto out;
id = g_io_add_watch (channel, G_IO_IN, pk_io_watch_have_data, pk_context);
if (id == 0) {
g_io_channel_unref (channel);
goto out;
}
g_io_channel_unref (channel);
out:
return id;
}
static void
pk_io_remove_watch (PolKitContext *pk_context, int watch_id)
{
g_source_remove (watch_id);
}
static DBusHandlerResult
_filter (DBusConnection *connection, DBusMessage *message, void *user_data)
{
PolKitDaemon *daemon = POLKIT_DAEMON (user_data);
/* pass NameOwnerChanged signals from the bus and ConsoleKit to PolKitTracker */
if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged") ||
(dbus_message_get_interface (message) != NULL &&
g_str_has_prefix (dbus_message_get_interface (message), "org.freedesktop.ConsoleKit"))) {
if (polkit_tracker_dbus_func (daemon->priv->pk_tracker, message)) {
/* Something has changed! TODO: emit D-Bus signal? */
g_debug ("Something has changed!");
}
}
/* other filters might want to process this message too */
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static gboolean
register_daemon (PolKitDaemon *daemon)
{
DBusConnection *connection;
DBusError dbus_error;
GError *error = NULL;
daemon->priv->pk_context = polkit_context_new ();
polkit_context_set_io_watch_functions (daemon->priv->pk_context, pk_io_add_watch, pk_io_remove_watch);
if (!polkit_context_init (daemon->priv->pk_context, NULL)) {
g_critical ("cannot initialize libpolkit");
goto error;
}
error = NULL;
daemon->priv->system_bus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
if (daemon->priv->system_bus_connection == NULL) {
if (error != NULL) {