static void _dbus_steadyflow_iapp_service_set_visible (SteadyflowIAppService* self, GVariant* parameters, GDBusMethodInvocation* invocation) { GError* error = NULL; GVariantIter _arguments_iter; gboolean visible = FALSE; GVariant* _tmp1_; GDBusMessage* _reply_message; GVariant* _reply; GVariantBuilder _reply_builder; g_variant_iter_init (&_arguments_iter, parameters); _tmp1_ = g_variant_iter_next_value (&_arguments_iter); visible = g_variant_get_boolean (_tmp1_); g_variant_unref (_tmp1_); steadyflow_iapp_service_set_visible (self, visible, &error); if (error) { g_dbus_method_invocation_return_gerror (invocation, error); return; } _reply_message = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation)); g_variant_builder_init (&_reply_builder, G_VARIANT_TYPE_TUPLE); _reply = g_variant_builder_end (&_reply_builder); g_dbus_message_set_body (_reply_message, _reply); g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), _reply_message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (invocation); g_object_unref (_reply_message); }
static gboolean photos_thumbnailer_handle_cancel (PhotosThumbnailer *self, GDBusMethodInvocation *invocation, guint serial) { GCancellable *cancellable; GDBusConnection *connection; GDBusMethodInvocation *invocation_ongoing; GHashTableIter iter; g_return_val_if_fail (PHOTOS_IS_THUMBNAILER (self), FALSE); g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE); photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Handling Cancel for %u", serial); g_application_hold (G_APPLICATION (self)); connection = g_dbus_method_invocation_get_connection (invocation); g_hash_table_iter_init (&iter, self->cancellables); while (g_hash_table_iter_next (&iter, (gpointer *) &invocation_ongoing, (gpointer *) &cancellable)) { GDBusConnection *connection_ongoing; GDBusMessage *message_ongoing; guint32 serial_ongoing; connection_ongoing = g_dbus_method_invocation_get_connection (invocation_ongoing); message_ongoing = g_dbus_method_invocation_get_message (invocation_ongoing); serial_ongoing = g_dbus_message_get_serial (message_ongoing); if (connection == connection_ongoing && (guint32) serial == serial_ongoing) { g_cancellable_cancel (cancellable); photos_thumbnailer_dbus_complete_cancel (self->skeleton, invocation); goto out; } } g_dbus_method_invocation_return_error_literal (invocation, PHOTOS_ERROR, 0, "Invalid serial"); out: photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Completed Cancel"); g_application_release (G_APPLICATION (self)); return TRUE; }
static void portal_add_named (GDBusMethodInvocation *invocation, GVariant *parameters, const char *app_id) { GDBusMessage *message; GUnixFDList *fd_list; g_autofree char *id = NULL; g_autofree char *proc_path = NULL; int parent_fd_id, parent_fd, fds_len, fd_flags; const int *fds; char parent_path_buffer[PATH_MAX+1]; g_autofree char *path = NULL; ssize_t symlink_size; struct stat parent_st_buf; const char *filename; gboolean reuse_existing, persistent; g_autoptr(GVariant) filename_v = NULL; g_variant_get (parameters, "(h@aybb)", &parent_fd_id, &filename_v, &reuse_existing, &persistent); filename = g_variant_get_bytestring (filename_v); /* This is only allowed from the host, or else we could leak existance of files */ if (*app_id != 0) { g_dbus_method_invocation_return_error (invocation, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_NOT_ALLOWED, "Not enough permissions"); return; } message = g_dbus_method_invocation_get_message (invocation); fd_list = g_dbus_message_get_unix_fd_list (message); parent_fd = -1; if (fd_list != NULL) { fds = g_unix_fd_list_peek_fds (fd_list, &fds_len); if (parent_fd_id < fds_len) parent_fd = fds[parent_fd_id]; } if (strchr (filename, '/') != NULL) { g_dbus_method_invocation_return_error (invocation, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_INVALID_ARGUMENT, "Invalid filename passed"); return; } proc_path = g_strdup_printf ("/proc/self/fd/%d", parent_fd); if (parent_fd == -1 || /* Must be able to get fd flags */ (fd_flags = fcntl (parent_fd, F_GETFL)) == -1 || /* Must be O_PATH */ ((fd_flags & O_PATH) != O_PATH) || /* Must not be O_NOFOLLOW (because we want the target file) */ ((fd_flags & O_NOFOLLOW) == O_PATH) || /* Must be able to fstat */ fstat (parent_fd, &parent_st_buf) < 0 || /* Must be a directory file */ (parent_st_buf.st_mode & S_IFMT) != S_IFDIR || /* Must be able to read path from /proc/self/fd */ /* This is an absolute and (at least at open time) symlink-expanded path */ (symlink_size = readlink (proc_path, parent_path_buffer, sizeof (parent_path_buffer) - 1)) < 0) { g_dbus_method_invocation_return_error (invocation, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_INVALID_ARGUMENT, "Invalid fd passed"); return; } if (parent_st_buf.st_dev == fuse_dev) { g_dbus_method_invocation_return_error (invocation, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_INVALID_ARGUMENT, "Invalid fd passed"); return; } parent_path_buffer[symlink_size] = 0; path = g_build_filename (parent_path_buffer, filename, NULL); g_debug ("portal_add_named %s", path); AUTOLOCK(db); id = do_create_doc (&parent_st_buf, path, reuse_existing, persistent); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", id)); }
static void portal_add (GDBusMethodInvocation *invocation, GVariant *parameters, const char *app_id) { GDBusMessage *message; GUnixFDList *fd_list; g_autofree char *id = NULL; g_autofree char *proc_path = NULL; int fd_id, fd, fds_len, fd_flags; glnx_fd_close int dir_fd = -1; const int *fds; char path_buffer[PATH_MAX+1]; ssize_t symlink_size; struct stat st_buf, real_st_buf, real_parent_st_buf; g_autofree char *dirname = NULL; g_autofree char *name = NULL; gboolean reuse_existing, persistent; g_variant_get (parameters, "(hbb)", &fd_id, &reuse_existing, &persistent); message = g_dbus_method_invocation_get_message (invocation); fd_list = g_dbus_message_get_unix_fd_list (message); fd = -1; if (fd_list != NULL) { fds = g_unix_fd_list_peek_fds (fd_list, &fds_len); if (fd_id < fds_len) fd = fds[fd_id]; } proc_path = g_strdup_printf ("/proc/self/fd/%d", fd); if (fd == -1 || /* Must be able to get fd flags */ (fd_flags = fcntl (fd, F_GETFL)) == -1 || /* Must be O_PATH */ ((fd_flags & O_PATH) != O_PATH) || /* Must not be O_NOFOLLOW (because we want the target file) */ ((fd_flags & O_NOFOLLOW) == O_PATH) || /* Must be able to fstat */ fstat (fd, &st_buf) < 0 || /* Must be a regular file */ (st_buf.st_mode & S_IFMT) != S_IFREG || /* Must be able to read path from /proc/self/fd */ /* This is an absolute and (at least at open time) symlink-expanded path */ (symlink_size = readlink (proc_path, path_buffer, sizeof (path_buffer) - 1)) < 0) { g_dbus_method_invocation_return_error (invocation, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_INVALID_ARGUMENT, "Invalid fd passed"); return; } path_buffer[symlink_size] = 0; /* We open the parent directory and do the stat in that, so that we have trustworthy parent dev/ino for later verification. Otherwise the caller could later replace a parent with a symlink and make us read some other file */ dirname = g_path_get_dirname (path_buffer); name = g_path_get_basename (path_buffer); dir_fd = open (dirname, O_CLOEXEC|O_PATH); if (fstat (dir_fd, &real_parent_st_buf) < 0 || fstatat (dir_fd, name, &real_st_buf, AT_SYMLINK_NOFOLLOW) < 0 || st_buf.st_dev != real_st_buf.st_dev || st_buf.st_ino != real_st_buf.st_ino) { /* Don't leak any info about real file path existance, etc */ g_dbus_method_invocation_return_error (invocation, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_INVALID_ARGUMENT, "Invalid fd passed"); return; } g_debug ("portal_add %s", path_buffer); if (st_buf.st_dev == fuse_dev) { /* The passed in fd is on the fuse filesystem itself */ g_autoptr(XdgAppDbEntry) old_entry = NULL; id = xdp_fuse_lookup_id_for_inode (st_buf.st_ino); g_debug ("path on fuse, id %s", id); if (id == NULL) { g_dbus_method_invocation_return_error (invocation, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_INVALID_ARGUMENT, "Invalid fd passed"); return; } /* Don't lock the db before doing the fuse call above, because it takes takes a lock that can block something calling back, causing a deadlock on the db lock */ AUTOLOCK(db); /* If the entry doesn't exist anymore, fail. Also fail if not resuse_existing, because otherwise the user could use this to get a copy with permissions and thus escape later permission revocations */ old_entry = xdg_app_db_lookup (db, id); if (old_entry == NULL || !reuse_existing) { g_dbus_method_invocation_return_error (invocation, XDG_APP_PORTAL_ERROR, XDG_APP_PORTAL_ERROR_INVALID_ARGUMENT, "Invalid fd passed"); return; } } else { { AUTOLOCK(db); id = do_create_doc (&real_parent_st_buf, path_buffer, reuse_existing, persistent); if (app_id[0] != '\0') { g_autoptr(XdgAppDbEntry) entry = NULL; XdpPermissionFlags perms = XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS | XDP_PERMISSION_FLAGS_READ | XDP_PERMISSION_FLAGS_WRITE; { entry = xdg_app_db_lookup (db, id); /* If its a unique one its safe for the creator to delete it at will */ if (!reuse_existing) perms |= XDP_PERMISSION_FLAGS_DELETE; do_set_permissions (entry, id, app_id, perms); } } } /* Invalidate with lock dropped to avoid deadlock */ xdp_fuse_invalidate_doc_app (id, NULL); if (app_id[0] != '\0') xdp_fuse_invalidate_doc_app (id, app_id); } g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", id)); }
static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { if (g_strcmp0 (method_name, "HelloWorld") == 0) { const gchar *greeting; g_variant_get (parameters, "(&s)", &greeting); if (g_strcmp0 (greeting, "Return Unregistered") == 0) { g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED, "As requested, here's a GError not registered (G_IO_ERROR_FAILED_HANDLED)"); } else if (g_strcmp0 (greeting, "Return Registered") == 0) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, "As requested, here's a GError that is registered (G_DBUS_ERROR_MATCH_RULE_NOT_FOUND)"); } else if (g_strcmp0 (greeting, "Return Raw") == 0) { g_dbus_method_invocation_return_dbus_error (invocation, "org.gtk.GDBus.SomeErrorName", "As requested, here's a raw D-Bus error"); } else { gchar *response; response = g_strdup_printf ("You greeted me with '%s'. Thanks!", greeting); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", response)); g_free (response); } } else if (g_strcmp0 (method_name, "EmitSignal") == 0) { GError *local_error; gdouble speed_in_mph; gchar *speed_as_string; g_variant_get (parameters, "(d)", &speed_in_mph); speed_as_string = g_strdup_printf ("%g mph!", speed_in_mph); local_error = NULL; g_dbus_connection_emit_signal (connection, NULL, object_path, interface_name, "VelocityChanged", g_variant_new ("(ds)", speed_in_mph, speed_as_string), &local_error); g_assert_no_error (local_error); g_free (speed_as_string); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "GimmeStdout") == 0) { #ifdef G_OS_UNIX if (g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING) { GDBusMessage *reply; GUnixFDList *fd_list; GError *error; fd_list = g_unix_fd_list_new (); error = NULL; g_unix_fd_list_append (fd_list, STDOUT_FILENO, &error); g_assert_no_error (error); reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation)); g_dbus_message_set_unix_fd_list (reply, fd_list); error = NULL; g_dbus_connection_send_message (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, /* out_serial */ &error); g_assert_no_error (error); g_object_unref (invocation); g_object_unref (fd_list); g_object_unref (reply); } else { g_dbus_method_invocation_return_dbus_error (invocation, "org.gtk.GDBus.Failed", "Your message bus daemon does not support file descriptor passing (need D-Bus >= 1.3.0)"); } #else g_dbus_method_invocation_return_dbus_error (invocation, "org.gtk.GDBus.NotOnUnix", "Your OS does not support file descriptor passing"); #endif } }