/** * daemon_authorize_method: * @daemon: a #Daemon * @invocation: method invocation handle * * Global hook used to authorize DBus methods. We restrict them to * root at the moment (but this forces the bridge to run as root). * * Possibly a better long term fix is that the bridge actually starts * cockpitd as root, opens a private socketpair between them to speak * DBus, then drops privileges. * * Returns: %TRUE if call should be authorized, %FALSE otherwise */ gboolean daemon_authorize_method (Daemon *daemon, GDBusMethodInvocation *invocation) { GError *error = NULL; gboolean is_authorized = FALSE; if (!authorize_method (daemon, invocation, &is_authorized, NULL, &error)) { g_warning ("Error while authorizing method %s.%s: %s", g_dbus_method_invocation_get_interface_name (invocation), g_dbus_method_invocation_get_method_name (invocation), error->message); g_clear_error (&error); return FALSE; } if (!is_authorized) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Method %s.%s cannot be invoked by non-root", g_dbus_method_invocation_get_interface_name (invocation), g_dbus_method_invocation_get_method_name (invocation)); } return is_authorized; }
static gboolean sysroot_transform_transaction_to_attrs (GBinding *binding, const GValue *src_value, GValue *dst_value, gpointer user_data) { RpmostreedTransaction *transaction; GVariant *variant; const char *method_name = ""; const char *path = ""; const char *sender_name = ""; transaction = g_value_get_object (src_value); if (transaction != NULL) { GDBusMethodInvocation *invocation; invocation = rpmostreed_transaction_get_invocation (transaction); method_name = g_dbus_method_invocation_get_method_name (invocation); path = g_dbus_method_invocation_get_object_path (invocation); sender_name = g_dbus_method_invocation_get_sender (invocation); } variant = g_variant_new ("(sss)", method_name, sender_name, path); g_value_set_variant (dst_value, variant); return TRUE; }
/* * This handler is run in a separate thread, so all operations can be * synchronous. */ static gboolean on_authorize_method_check (GDBusInterfaceSkeleton *interface, GDBusMethodInvocation *invocation, EmerDaemon *daemon) { const gchar *method_name = g_dbus_method_invocation_get_method_name (invocation); const AuthorizedMethod *authorized_method = lookup_authorized_method (method_name); if (authorized_method == NULL) return TRUE; GError *error = NULL; PolkitAuthority *authority = polkit_authority_get_sync (NULL /*GCancellable*/, &error); if (authority == NULL) { g_critical ("Could not get PolicyKit authority: %s.", error->message); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); return FALSE; } const gchar *sender_name = g_dbus_method_invocation_get_sender (invocation); PolkitSubject *subject = polkit_system_bus_name_new (sender_name); PolkitAuthorizationResult *result = polkit_authority_check_authorization_sync (authority, subject, authorized_method->method_full_name, NULL /*PolkitDetails*/, POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, NULL /*GCancellable*/, &error); g_object_unref (authority); g_object_unref (subject); if (result == NULL) { g_critical ("Could not get PolicyKit authorization result: %s.", error->message); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); return FALSE; } gboolean authorized = polkit_authorization_result_get_is_authorized (result); if (!authorized) g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_AUTH_FAILED, authorized_method->error_message); g_object_unref (result); return authorized; }
gboolean auth_check_uid_role (GDBusMethodInvocation *invocation, uid_t uid, const gchar *role) { int err = 0; gs_free struct passwd *pw = NULL; gs_free struct group *wheel_gr = NULL; gs_free struct group *role_gr = NULL; gs_free gid_t *gids = NULL; if (uid == 0) return TRUE; pw = getpwuid_a (uid, &err); if (pw == NULL) goto error; wheel_gr = getgrnam_a ("wheel", NULL); role_gr = role ? getgrnam_a (role, NULL) : NULL; int n_groups; gids = getgrouplist_a (pw->pw_name, pw->pw_gid, &n_groups, &err); if (gids == NULL) goto error; for (int i = 0; i < n_groups; i++) if ((wheel_gr && gids[i] == wheel_gr->gr_gid) || (role_gr && gids[i] == role_gr->gr_gid)) return TRUE; error: if (err) g_dbus_method_invocation_return_error (invocation, COCKPIT_ERROR, COCKPIT_ERROR_FAILED, "%s", strerror(err)); else g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Method %s.%s needs role '%s'", g_dbus_method_invocation_get_interface_name (invocation), g_dbus_method_invocation_get_method_name (invocation), role ? role : "wheel"); return FALSE; }
static gboolean flatpak_authorize_method_handler (GDBusInterfaceSkeleton *interface, GDBusMethodInvocation *invocation, gpointer user_data) { const gchar *method_name = g_dbus_method_invocation_get_method_name (invocation); const gchar *sender = g_dbus_method_invocation_get_sender (invocation); GVariant *parameters = g_dbus_method_invocation_get_parameters (invocation); g_autoptr(AutoPolkitSubject) subject = polkit_system_bus_name_new (sender); g_autoptr(AutoPolkitDetails) details = polkit_details_new (); const gchar *action = NULL; gboolean authorized = FALSE; /* Ensure we don't idle exit */ schedule_idle_callback (); if (on_session_bus) { /* This is test code, make sure it never runs with privileges */ g_assert (geteuid () != 0); g_assert (getuid () != 0); g_assert (getegid () != 0); g_assert (getgid () != 0); authorized = TRUE; } else if (g_strcmp0 (method_name, "Deploy") == 0) { const char *ref, *origin; guint32 flags; gboolean is_update, is_app; g_variant_get_child (parameters, 1, "u", &flags); g_variant_get_child (parameters, 2, "&s", &ref); g_variant_get_child (parameters, 3, "&s", &origin); is_update = (flags & FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE) != 0; is_app = g_str_has_prefix (ref, "app/"); if (is_update) { if (is_app) action = "org.freedesktop.Flatpak.app-update"; else action = "org.freedesktop.Flatpak.runtime-update"; } else { if (is_app) action = "org.freedesktop.Flatpak.app-install"; else action = "org.freedesktop.Flatpak.runtime-install"; } polkit_details_insert (details, "origin", origin); polkit_details_insert (details, "ref", ref); } else if (g_strcmp0 (method_name, "DeployAppstream") == 0) { const char *arch, *origin; g_variant_get_child (parameters, 1, "&s", &origin); g_variant_get_child (parameters, 2, "&s", &arch); action = "org.freedesktop.Flatpak.appstream-update"; polkit_details_insert (details, "origin", origin); polkit_details_insert (details, "arch", arch); } else if (g_strcmp0 (method_name, "InstallBundle") == 0) { const char *path; g_variant_get_child (parameters, 0, "^&ay", &path); action = "org.freedesktop.Flatpak.install-bundle"; polkit_details_insert (details, "path", path); } else if (g_strcmp0 (method_name, "Uninstall") == 0) { const char *ref; gboolean is_app; g_variant_get_child (parameters, 1, "&s", &ref); is_app = g_str_has_prefix (ref, "app/"); if (is_app) action = "org.freedesktop.Flatpak.app-uninstall"; else action = "org.freedesktop.Flatpak.runtime-uninstall"; polkit_details_insert (details, "ref", ref); } else if (g_strcmp0 (method_name, "ConfigureRemote") == 0) { const char *remote; g_variant_get_child (parameters, 1, "&s", &remote); action = "org.freedesktop.Flatpak.configure-remote"; polkit_details_insert (details, "remote", remote); } if (action) { g_autoptr(AutoPolkitAuthorizationResult) result; g_autoptr(GError) error = NULL; result = polkit_authority_check_authorization_sync (authority, subject, action, details, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, &error); if (result == NULL) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Authorization error: %s", error->message); return FALSE; } authorized = polkit_authorization_result_get_is_authorized (result); } if (!authorized) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Flatpak system operation %s not allowed for user", method_name); } return authorized; }
static gboolean handle_file_chooser_open (XdpFileChooser *object, GDBusMethodInvocation *invocation, const gchar *arg_sender, const gchar *arg_app_id, const gchar *arg_parent_window, const gchar *arg_title, GVariant *arg_options) { const gchar *method_name; GtkFileChooserAction action; gboolean multiple; GtkWidget *dialog; GdkWindow *foreign_parent = NULL; GtkWidget *fake_parent; FileDialogHandle *handle; XdpFileChooser *chooser = XDP_FILE_CHOOSER (g_dbus_method_invocation_get_user_data (invocation)); const char *cancel_label; const char *accept_label; GVariantIter *iter; const char *current_name; const char *path; g_autoptr (GVariant) choices = NULL; method_name = g_dbus_method_invocation_get_method_name (invocation); g_print ("%s, app_id: %s, object: %p, user_data: %p\n", method_name, arg_app_id, object, g_dbus_method_invocation_get_user_data (invocation)); fake_parent = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_object_ref_sink (fake_parent); action = GTK_FILE_CHOOSER_ACTION_OPEN; multiple = FALSE; if (strcmp (method_name, "SaveFile") == 0) action = GTK_FILE_CHOOSER_ACTION_SAVE; else if (strcmp (method_name, "OpenFiles") == 0) multiple = TRUE; if (!g_variant_lookup (arg_options, "accept_label", "&s", &accept_label)) accept_label = _("_Open"); cancel_label = _("_Cancel"); dialog = gtk_file_chooser_dialog_new (arg_title, GTK_WINDOW (fake_parent), action, cancel_label, GTK_RESPONSE_CANCEL, accept_label, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), multiple); handle = dialog_handle_new (arg_app_id, arg_sender, dialog, G_DBUS_INTERFACE_SKELETON (chooser)); handle->dialog = dialog; handle->action = action; handle->multiple = multiple; choices = g_variant_lookup_value (arg_options, "choices", G_VARIANT_TYPE ("a(ssa(ss)s)")); if (choices) { int i; GtkWidget *box; box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_widget_show (box); for (i = 0; i < g_variant_n_children (choices); i++) { GVariant *value = g_variant_get_child_value (choices, i); gtk_container_add (GTK_CONTAINER (box), deserialize_choice (value, handle)); } gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), box); } if (g_variant_lookup (arg_options, "filters", "a(sa(us))", &iter)) { GVariant *variant; while (g_variant_iter_next (iter, "@(sa(us))", &variant)) { GtkFileFilter *filter; filter = gtk_file_filter_from_gvariant (variant); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); g_variant_unref (variant); } g_variant_iter_free (iter); } if (strcmp (method_name, "SaveFile") == 0) { if (g_variant_lookup (arg_options, "current_name", "&s", ¤t_name)) gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), current_name); /* TODO: is this useful ? * In a sandboxed situation, the current folder and current file * are likely in the fuse filesystem */ if (g_variant_lookup (arg_options, "current_folder", "&ay", &path)) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), path); if (g_variant_lookup (arg_options, "current_file", "&ay", &path)) gtk_file_chooser_select_filename (GTK_FILE_CHOOSER (dialog), path); } g_object_unref (fake_parent); #ifdef GDK_WINDOWING_X11 if (g_str_has_prefix (arg_parent_window, "x11:")) { int xid; if (sscanf (arg_parent_window, "x11:%x", &xid) != 1) g_warning ("invalid xid"); else foreign_parent = gdk_x11_window_foreign_new_for_display (gtk_widget_get_display (dialog), xid); } #endif else g_warning ("Unhandled parent window type %s\n", arg_parent_window); gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (handle_file_chooser_open_response), handle); if (action == GTK_FILE_CHOOSER_ACTION_OPEN) { GtkWidget *readonly; readonly = gtk_check_button_new_with_label ("Open files read-only"); gtk_widget_show (readonly); g_signal_connect (readonly, "toggled", G_CALLBACK (read_only_toggled), handle); gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), readonly); } gtk_widget_realize (dialog); if (foreign_parent) gdk_window_set_transient_for (gtk_widget_get_window (dialog), foreign_parent); gtk_widget_show (dialog); xdp_file_chooser_complete_open_file (chooser, invocation, handle->base.id); return TRUE; }