summaryrefslogtreecommitdiffstats
path: root/src/ck-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ck-manager.c')
-rw-r--r--src/ck-manager.c667
1 files changed, 609 insertions, 58 deletions
diff --git a/src/ck-manager.c b/src/ck-manager.c
index 5c77547..b6bcb2b 100644
--- a/src/ck-manager.c
+++ b/src/ck-manager.c
@@ -38,6 +38,8 @@
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
+#include <polkit/polkit.h>
+
#include "ck-manager.h"
#include "ck-manager-glue.h"
#include "ck-seat.h"
@@ -58,6 +60,8 @@
struct CkManagerPrivate
{
+ PolKitContext *pol_ctx;
+
GHashTable *seats;
GHashTable *sessions;
GHashTable *leaders;
@@ -634,6 +638,561 @@ log_seat_device_removed_event (CkManager *manager,
g_free (device_id);
}
+static char *
+get_cookie_for_pid (CkManager *manager,
+ guint pid)
+{
+ char *cookie;
+
+ /* FIXME: need a better way to get the cookie */
+
+ cookie = ck_unix_pid_get_env (pid, "XDG_SESSION_COOKIE");
+
+ return cookie;
+}
+
+static CkSession *
+get_session_for_unix_process (CkManager *manager,
+ guint pid)
+{
+ CkSessionLeader *leader;
+ CkSession *session;
+ char *cookie;
+
+ session = NULL;
+ leader = NULL;
+
+ cookie = get_cookie_for_pid (manager, pid);
+ if (cookie == NULL) {
+ goto out;
+ }
+
+ leader = g_hash_table_lookup (manager->priv->leaders, cookie);
+ if (leader == NULL) {
+ goto out;
+ }
+
+ session = g_hash_table_lookup (manager->priv->sessions, ck_session_leader_peek_session_id (leader));
+
+ out:
+ g_free (cookie);
+
+ return session;
+}
+
+static PolKitSession *
+new_polkit_session_from_session (CkManager *manager,
+ CkSession *ck_session)
+{
+ PolKitSession *pk_session;
+ PolKitSeat *pk_seat;
+ uid_t uid;
+ gboolean is_active;
+ gboolean is_local;
+ char *sid;
+ char *ssid;
+ char *remote_host;
+
+ sid = NULL;
+ ssid = NULL;
+ remote_host = NULL;
+
+ ck_session_get_seat_id (ck_session, &sid, NULL);
+
+ g_object_get (ck_session,
+ "active", &is_active,
+ "is-local", &is_local,
+ "id", &ssid,
+ "unix-user", &uid,
+ "remote-host-name", &remote_host,
+ NULL);
+
+ pk_session = polkit_session_new ();
+ if (pk_session == NULL) {
+ goto out;
+ }
+ if (!polkit_session_set_uid (pk_session, uid)) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+ if (!polkit_session_set_ck_objref (pk_session, ssid)) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+ if (!polkit_session_set_ck_is_active (pk_session, is_active)) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+ if (!polkit_session_set_ck_is_local (pk_session, is_local)) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+ if (!is_local) {
+ if (!polkit_session_set_ck_remote_host (pk_session, remote_host)) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+
+ }
+
+
+ pk_seat = polkit_seat_new ();
+ if (pk_seat == NULL) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+ if (!polkit_seat_set_ck_objref (pk_seat, sid)) {
+ polkit_seat_unref (pk_seat);
+ pk_seat = NULL;
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+ if (!polkit_seat_validate (pk_seat)) {
+ polkit_seat_unref (pk_seat);
+ pk_seat = NULL;
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+
+ if (!polkit_session_set_seat (pk_session, pk_seat)) {
+ polkit_seat_unref (pk_seat);
+ pk_seat = NULL;
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+ polkit_seat_unref (pk_seat); /* session object now owns this object */
+ pk_seat = NULL;
+
+ if (!polkit_session_validate (pk_session)) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+
+out:
+ g_free (ssid);
+ g_free (sid);
+ g_free (remote_host);
+
+ return pk_session;
+}
+
+static PolKitCaller *
+new_polkit_caller_from_dbus_name (CkManager *manager,
+ const char *dbus_name)
+{
+ PolKitCaller *caller;
+ pid_t pid;
+ uid_t uid;
+ char *selinux_context;
+ PolKitSession *pk_session;
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter sub_iter;
+ char *str;
+ int num_elems;
+ DBusConnection *con;
+ DBusError error;
+ CkSession *ck_session;
+
+ dbus_error_init (&error);
+
+ con = dbus_g_connection_get_connection (manager->priv->connection);
+
+ g_return_val_if_fail (con != NULL, NULL);
+ g_return_val_if_fail (dbus_name != NULL, NULL);
+
+ selinux_context = NULL;
+
+ caller = NULL;
+ ck_session = NULL;
+ pk_session = NULL;
+
+ uid = dbus_bus_get_unix_user (con, dbus_name, &error);
+ if (dbus_error_is_set (&error)) {
+ g_warning ("Could not get uid for connection: %s %s",
+ error.name,
+ error.message);
+ dbus_error_free (&error);
+ goto out;
+ }
+
+ message = dbus_message_new_method_call ("org.freedesktop.DBus",
+ "/org/freedesktop/DBus/Bus",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixProcessID");
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &dbus_name);
+ reply = dbus_connection_send_with_reply_and_block (con, message, -1, &error);
+
+ if (reply == NULL || dbus_error_is_set (&error)) {
+ g_warning ("Error doing GetConnectionUnixProcessID on Bus: %s: %s",
+ error.name,
+ error.message);
+ dbus_message_unref (message);
+ if (reply != NULL) {
+ dbus_message_unref (reply);
+ }
+ dbus_error_free (&error);
+ goto out;
+ }
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &pid);
+ dbus_message_unref (message);
+ dbus_message_unref (reply);
+
+ message = dbus_message_new_method_call ("org.freedesktop.DBus",
+ "/org/freedesktop/DBus/Bus",
+ "org.freedesktop.DBus",
+ "GetConnectionSELinuxSecurityContext");
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &dbus_name);
+ reply = dbus_connection_send_with_reply_and_block (con, message, -1, &error);
+ /* SELinux might not be enabled */
+ if (dbus_error_is_set (&error) &&
+ strcmp (error.name, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown") == 0) {
+ dbus_message_unref (message);
+ if (reply != NULL) {
+ dbus_message_unref (reply);
+ }
+ dbus_error_init (&error);
+ } else if (reply == NULL || dbus_error_is_set (&error)) {
+ g_warning ("Error doing GetConnectionSELinuxSecurityContext on Bus: %s: %s", error.name, error.message);
+ dbus_message_unref (message);
+ if (reply != NULL) {
+ dbus_message_unref (reply);
+ }
+ goto out;
+ } else {
+ /* TODO: verify signature */
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_recurse (&iter, &sub_iter);
+ dbus_message_iter_get_fixed_array (&sub_iter, (void *) &str, &num_elems);
+ if (str != NULL && num_elems > 0) {
+ selinux_context = g_strndup (str, num_elems);
+ }
+ dbus_message_unref (message);
+ dbus_message_unref (reply);
+ }
+
+ ck_session = get_session_for_unix_process (manager, pid);
+ if (ck_session == NULL) {
+ /* OK, this is not a catastrophe; just means the caller is not a
+ * member of any session or that ConsoleKit is not available..
+ */
+ goto not_in_session;
+ }
+
+ pk_session = new_polkit_session_from_session (manager, ck_session);
+ if (pk_session == NULL) {
+ g_warning ("Got a session but couldn't construct polkit session object!");
+ goto out;
+ }
+ if (!polkit_session_validate (pk_session)) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ goto out;
+ }
+
+not_in_session:
+
+ caller = polkit_caller_new ();
+ if (caller == NULL) {
+ if (pk_session != NULL) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ }
+ goto out;
+ }
+
+ if (!polkit_caller_set_dbus_name (caller, dbus_name)) {
+ if (pk_session != NULL) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ }
+ polkit_caller_unref (caller);
+ caller = NULL;
+ goto out;
+ }
+ if (!polkit_caller_set_uid (caller, uid)) {
+ if (pk_session != NULL) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ }
+ polkit_caller_unref (caller);
+ caller = NULL;
+ goto out;
+ }
+ if (!polkit_caller_set_pid (caller, pid)) {
+ if (pk_session != NULL) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ }
+ polkit_caller_unref (caller);
+ caller = NULL;
+ goto out;
+ }
+ if (selinux_context != NULL) {
+ if (!polkit_caller_set_selinux_context (caller, selinux_context)) {
+ if (pk_session != NULL) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ }
+ polkit_caller_unref (caller);
+ caller = NULL;
+ goto out;
+ }
+ }
+ if (pk_session != NULL) {
+ if (!polkit_caller_set_ck_session (caller, pk_session)) {
+ if (pk_session != NULL) {
+ polkit_session_unref (pk_session);
+ pk_session = NULL;
+ }
+ polkit_caller_unref (caller);
+ caller = NULL;
+ goto out;
+ }
+ polkit_session_unref (pk_session); /* caller object now own this object */
+ pk_session = NULL;
+ }
+
+ if (!polkit_caller_validate (caller)) {
+ polkit_caller_unref (caller);
+ caller = NULL;
+ goto out;
+ }
+
+out:
+ g_free (selinux_context);
+
+ return caller;
+}
+
+static gboolean
+_check_polkit_for_action (CkManager *manager,
+ DBusGMethodInvocation *context,
+ const char *action)
+{
+ const char *sender;
+ GError *error;
+ DBusError dbus_error;
+ PolKitCaller *pk_caller;
+ PolKitAction *pk_action;
+ PolKitResult pk_result;
+
+ error = NULL;
+
+ g_debug ("constructing polkit data");
+
+ /* Check that caller is privileged */
+ sender = dbus_g_method_get_sender (context);
+ dbus_error_init (&dbus_error);
+
+ pk_caller = new_polkit_caller_from_dbus_name (manager, sender);
+ if (pk_caller == NULL) {
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Error getting information about caller: %s: %s",
+ dbus_error.name,
+ dbus_error.message);
+ dbus_error_free (&dbus_error);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ pk_action = polkit_action_new ();
+ polkit_action_set_action_id (pk_action, action);
+
+ g_debug ("checking if caller %s is authorized", sender);
+
+ /* this version crashes if error is used */
+ pk_result = polkit_context_is_caller_authorized (manager->priv->pol_ctx,
+ pk_action,
+ pk_caller,
+ TRUE,
+ NULL);
+ g_debug ("answer is: %s", (pk_result == POLKIT_RESULT_YES) ? "yes" : "no");
+
+ polkit_caller_unref (pk_caller);
+ polkit_action_unref (pk_action);
+
+ if (pk_result != POLKIT_RESULT_YES) {
+ error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_NOT_PRIVILEGED,
+ "Not privileged for action: %s",
+ action);
+ dbus_error_free (&dbus_error);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* adapted from PolicyKit */
+static gboolean
+get_caller_info (CkManager *manager,
+ const char *sender,
+ uid_t *calling_uid,
+ pid_t *calling_pid)
+{
+ gboolean res;
+ GError *error = NULL;
+
+ res = FALSE;
+
+ if (sender == NULL) {
+ goto out;
+ }
+
+ if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixUser", &error,
+ G_TYPE_STRING, sender,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, calling_uid,
+ G_TYPE_INVALID)) {
+ g_debug ("GetConnectionUnixUser() failed: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixProcessID", &error,
+ G_TYPE_STRING, sender,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, calling_pid,
+ G_TYPE_INVALID)) {
+ g_debug ("GetConnectionUnixProcessID() failed: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ res = TRUE;
+
+ g_debug ("uid = %d", *calling_uid);
+ g_debug ("pid = %d", *calling_pid);
+
+out:
+ return res;
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.freedesktop.ConsoleKit \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/freedesktop/ConsoleKit/Manager \
+ org.freedesktop.ConsoleKit.Manager.Restart
+*/
+gboolean
+ck_manager_restart (CkManager *manager,
+ DBusGMethodInvocation *context)
+{
+ gboolean ret;
+ gboolean res;
+ const char *action;
+ GError *error;
+
+ ret = FALSE;
+
+ if (g_hash_table_size (manager->priv->sessions) > 1) {
+ action = "org.freedesktop.consolekit.system.restart-multiple-users";
+ } else {
+ action = "org.freedesktop.consolekit.system.restart";
+ }
+
+ g_debug ("ConsoleKit Restart: %s", action);
+
+ res = _check_polkit_for_action (manager, context, action);
+
+ if (! res) {
+ goto out;
+ }
+
+ g_debug ("ConsoleKit preforming Restart: %s", action);
+
+ error = NULL;
+ res = g_spawn_command_line_async (LIBDIR "/ConsoleKit/scripts/ck-system-restart",
+ &error);
+ if (! res) {
+ GError *new_error;
+
+ g_warning ("Unable to restart system: %s", error->message);
+
+ new_error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Unable to restart system: %s", error->message);
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+
+ g_error_free (error);
+ } else {
+ ret = TRUE;
+ dbus_g_method_return (context);
+ }
+
+ out:
+
+ return ret;
+}
+
+gboolean
+ck_manager_stop (CkManager *manager,
+ DBusGMethodInvocation *context)
+{
+ gboolean ret;
+ gboolean res;
+ const char *action;
+ GError *error;
+
+ ret = TRUE;
+
+ if (g_hash_table_size (manager->priv->sessions) > 1) {
+ action = "org.freedesktop.consolekit.system.stop-multiple-users";
+ } else {
+ action = "org.freedesktop.consolekit.system.stop";
+ }
+
+ res = _check_polkit_for_action (manager, context, action);
+ if (! res) {
+ goto out;
+ }
+
+ g_debug ("Stopping system");
+ error = NULL;
+ res = g_spawn_command_line_async (LIBDIR "/ConsoleKit/scripts/ck-system-stop",
+ &error);
+ if (! res) {
+ GError *new_error;
+
+ g_warning ("Unable to stop system: %s", error->message);
+
+ new_error = g_error_new (CK_MANAGER_ERROR,
+ CK_MANAGER_ERROR_GENERAL,
+ "Unable to stop system: %s", error->message);
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+
+ g_error_free (error);
+ } else {
+ ret = TRUE;
+ dbus_g_method_return (context);
+ }
+
+ out:
+ return ret;
+}
+
static void
on_seat_active_session_changed (CkSeat *seat,
const char *ssid,
@@ -844,51 +1403,6 @@ find_seat_for_session (CkManager *manager,
return seat;
}
-/* adapted from PolicyKit */
-static gboolean
-get_caller_info (CkManager *manager,
- const char *sender,
- uid_t *calling_uid,
- pid_t *calling_pid)
-{
- gboolean res;
- GError *error = NULL;
-
- res = FALSE;
-
- if (sender == NULL) {
- goto out;
- }
-
- if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixUser", &error,
- G_TYPE_STRING, sender,
- G_TYPE_INVALID,
- G_TYPE_UINT, calling_uid,
- G_TYPE_INVALID)) {
- g_debug ("GetConnectionUnixUser() failed: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
- if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixProcessID", &error,
- G_TYPE_STRING, sender,
- G_TYPE_INVALID,
- G_TYPE_UINT, calling_pid,
- G_TYPE_INVALID)) {
- g_debug ("GetConnectionUnixProcessID() failed: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
- res = TRUE;
-
- g_debug ("uid = %d", *calling_uid);
- g_debug ("pid = %d", *calling_pid);
-
-out:
- return res;
-}
-
static gboolean
manager_set_system_idle_hint (CkManager *manager,
gboolean idle_hint)
@@ -1278,19 +1792,6 @@ ck_manager_get_session_for_cookie (CkManager *manager,
return TRUE;
}
-static char *
-get_cookie_for_pid (CkManager *manager,
- guint pid)
-{
- char *cookie;
-
- /* FIXME: need a better way to get the cookie */
-
- cookie = ck_unix_pid_get_env (pid, "XDG_SESSION_COOKIE");
-
- return cookie;
-}
-
/*
Example:
dbus-send --system --dest=org.freedesktop.ConsoleKit \
@@ -1682,10 +2183,60 @@ bus_name_owner_changed (DBusGProxy *bus_proxy,
}
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 gboolean
register_manager (CkManager *manager)
{
GError *error = NULL;
+ manager->priv->pol_ctx = polkit_context_new ();
+ polkit_context_set_io_watch_functions (manager->priv->pol_ctx, pk_io_add_watch, pk_io_remove_watch);
+ if (! polkit_context_init (manager->priv->pol_ctx, NULL)) {
+ g_critical ("cannot initialize libpolkit");
+ return FALSE;
+ }
+
error = NULL;
manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
if (manager->priv->connection == NULL) {