From 516e171bb0489e1803894d4ec1a422002bf9972d Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 28 Feb 2020 09:16:31 +0100 Subject: document-portal: Support new DOCUMENT_ADD_FLAGS_DIRECTORY in AddFull For directoy entries the DOCUMENT_ENTRY_FLAG_DIRECTORY flag is set in the document store for the entry. Also, in the directory case we store the device/inode of the directory itself, not the parent. --- document-portal/document-enums.h | 3 +- document-portal/document-portal.c | 99 ++++++++++++++++++++++++--------------- document-portal/document-portal.h | 1 + document-portal/document-store.h | 1 + document-portal/file-transfer.c | 2 +- 5 files changed, 65 insertions(+), 41 deletions(-) (limited to 'document-portal') diff --git a/document-portal/document-enums.h b/document-portal/document-enums.h index 8ac37a4..d126ecc 100644 --- a/document-portal/document-enums.h +++ b/document-portal/document-enums.h @@ -16,8 +16,9 @@ typedef enum { DOCUMENT_ADD_FLAGS_REUSE_EXISTING = (1 << 0), DOCUMENT_ADD_FLAGS_PERSISTENT = (1 << 1), DOCUMENT_ADD_FLAGS_AS_NEEDED_BY_APP = (1 << 2), + DOCUMENT_ADD_FLAGS_DIRECTORY = (1 << 3), - DOCUMENT_ADD_FLAGS_FLAGS_ALL = ((1 << 3) - 1) + DOCUMENT_ADD_FLAGS_FLAGS_ALL = ((1 << 4) - 1) } DocumentAddFullFlags; G_END_DECLS diff --git a/document-portal/document-portal.c b/document-portal/document-portal.c index 8197270..6a56ad6 100644 --- a/document-portal/document-portal.c +++ b/document-portal/document-portal.c @@ -286,7 +286,7 @@ portal_delete (GDBusMethodInvocation *invocation, } static char * -do_create_doc (struct stat *parent_st_buf, const char *path, gboolean reuse_existing, gboolean persistent) +do_create_doc (struct stat *parent_st_buf, const char *path, gboolean reuse_existing, gboolean persistent, gboolean directory) { g_autoptr(GVariant) data = NULL; g_autoptr(PermissionDbEntry) entry = NULL; @@ -294,12 +294,14 @@ do_create_doc (struct stat *parent_st_buf, const char *path, gboolean reuse_exis char *id = NULL; guint32 flags = 0; - g_debug ("Creating document at path '%s', resuse_existing: %d, persistent: %d", path, reuse_existing, persistent); + g_debug ("Creating document at path '%s', resuse_existing: %d, persistent: %d, directory: %d", path, reuse_existing, persistent, directory); if (!reuse_existing) flags |= DOCUMENT_ENTRY_FLAG_UNIQUE; if (!persistent) flags |= DOCUMENT_ENTRY_FLAG_TRANSIENT; + if (directory) + flags |= DOCUMENT_ENTRY_FLAG_DIRECTORY; data = g_variant_ref_sink (g_variant_new ("(^ayttu)", path, @@ -348,8 +350,9 @@ do_create_doc (struct stat *parent_st_buf, const char *path, gboolean reuse_exis gboolean validate_fd (int fd, XdpAppInfo *app_info, + gboolean is_directory, struct stat *st_buf, - struct stat *real_parent_st_buf, + struct stat *real_dir_st_buf, char **path_out, gboolean *writable_out, GError **error) @@ -360,41 +363,54 @@ validate_fd (int fd, xdp_autofd int dir_fd = -1; struct stat real_st_buf; - path = xdp_app_info_get_path_for_fd (app_info, fd, S_IFREG, st_buf, writable_out); + path = xdp_app_info_get_path_for_fd (app_info, fd, 0, st_buf, writable_out); if (path == NULL) + goto errout; + + if (!is_directory && S_ISREG (st_buf->st_mode)) { - /* Don't leak any info about real file path existence, etc */ - g_set_error (error, - XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid fd passed"); - return FALSE; + /* We open the parent directory and do the stat in that, so that we have + * trustworthy parent dev/ino + filename 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); + name = g_path_get_basename (path); + } + else if (is_directory && S_ISDIR (st_buf->st_mode)) + { + /* For dirs, we keep the dev/ino of the directory itself */ + dirname = g_strdup (path); } + else + goto errout; - /* We open the parent directory and do the stat in that, so that we have - * trustworthy parent dev/ino + filename 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); - name = g_path_get_basename (path); dir_fd = open (dirname, O_CLOEXEC | O_PATH); + if (dir_fd < 0 || fstat (dir_fd, real_dir_st_buf) != 0) + goto errout; - if (dir_fd < 0 || - 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) + if (name != NULL) { - /* Don't leak any info about real file path existence, etc */ - g_set_error (error, - XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid fd passed"); - return FALSE; + if (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) + goto errout; } + else if (st_buf->st_dev != real_dir_st_buf->st_dev || + st_buf->st_ino != real_dir_st_buf->st_ino) + goto errout; + if (path_out) *path_out = g_steal_pointer (&path); return TRUE; + + errout: + /* Don't leak any info about real file path existence, etc */ + g_set_error (error, + XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, + "Invalid fd passed"); + return FALSE; } static char * @@ -727,8 +743,8 @@ document_add_full (int *fd, const char *app_id = xdp_app_info_get_id (app_info); g_autoptr(GPtrArray) ids = g_ptr_array_new_with_free_func (g_free); g_autoptr(GPtrArray) paths = g_ptr_array_new_with_free_func (g_free); - gboolean reuse_existing, persistent, as_needed_by_app, allow_write; - g_autofree struct stat *real_parent_st_bufs = NULL; + gboolean reuse_existing, persistent, as_needed_by_app, allow_write, is_dir; + g_autofree struct stat *real_dir_st_bufs = NULL; struct stat st_buf; g_autofree gboolean *writable = NULL; int i; @@ -737,24 +753,25 @@ document_add_full (int *fd, reuse_existing = (flags & DOCUMENT_ADD_FLAGS_REUSE_EXISTING) != 0; persistent = (flags & DOCUMENT_ADD_FLAGS_PERSISTENT) != 0; as_needed_by_app = (flags & DOCUMENT_ADD_FLAGS_AS_NEEDED_BY_APP) != 0; + is_dir = (flags & DOCUMENT_ADD_FLAGS_DIRECTORY) != 0; allow_write = (target_perms & DOCUMENT_PERMISSION_FLAGS_WRITE) != 0; g_ptr_array_set_size (paths, n_args + 1); g_ptr_array_set_size (ids, n_args + 1); - real_parent_st_bufs = g_new0 (struct stat, n_args); + real_dir_st_bufs = g_new0 (struct stat, n_args); writable = g_new0 (gboolean, n_args); for (i = 0; i < n_args; i++) { g_autofree char *path = NULL; - if (!validate_fd (fd[i], app_info, &st_buf, &real_parent_st_bufs[i], &path, &writable[i], error)) + if (!validate_fd (fd[i], app_info, is_dir, &st_buf, &real_dir_st_bufs[i], &path, &writable[i], error)) return NULL; if (parent_dev != NULL && parent_ino != NULL) { - if (real_parent_st_bufs[i].st_dev != parent_dev[i] || - real_parent_st_bufs[i].st_ino != parent_ino[i]) + if (real_dir_st_bufs[i].st_dev != parent_dev[i] || + real_dir_st_bufs[i].st_ino != parent_ino[i]) { g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_NOT_ALLOWED, @@ -776,6 +793,7 @@ document_add_full (int *fd, if (st_buf.st_dev == fuse_dev) { /* The passed in fd is on the fuse filesystem itself */ + /* TODO: This reuse code is not right for directory exposes (i.e. which file in there?) */ id = verify_existing_document (&st_buf, reuse_existing); if (id == NULL) { @@ -814,7 +832,7 @@ document_add_full (int *fd, if (g_ptr_array_index(ids,i) == NULL) { - id = do_create_doc (&real_parent_st_bufs[i], path, reuse_existing, persistent); + id = do_create_doc (&real_dir_st_bufs[i], path, reuse_existing, persistent, is_dir); g_ptr_array_index(ids,i) = id; if (app_id[0] != '\0' && strcmp (app_id, target_app_id) != 0) @@ -894,7 +912,9 @@ portal_add_named_full (GDBusMethodInvocation *invocation, return; } - if ((flags & ~DOCUMENT_ADD_FLAGS_FLAGS_ALL) != 0) + if ((flags & ~DOCUMENT_ADD_FLAGS_FLAGS_ALL) != 0 || + /* Don't support directory named documents */ + (flags & DOCUMENT_ADD_FLAGS_DIRECTORY) != 0) { g_dbus_method_invocation_return_error (invocation, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, @@ -965,7 +985,7 @@ portal_add_named_full (GDBusMethodInvocation *invocation, } else { - id = do_create_doc (&parent_st_buf, path, reuse_existing, persistent); + id = do_create_doc (&parent_st_buf, path, reuse_existing, persistent, FALSE); if (app_id[0] != '\0' && strcmp (app_id, target_app_id) != 0) { @@ -1066,7 +1086,7 @@ portal_add_named (GDBusMethodInvocation *invocation, XDP_AUTOLOCK (db); - id = do_create_doc (&parent_st_buf, path, reuse_existing, persistent); + id = do_create_doc (&parent_st_buf, path, reuse_existing, persistent, FALSE); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", id)); @@ -1116,7 +1136,7 @@ portal_lookup (GDBusMethodInvocation *invocation, const char *filename; g_autofree char *path = NULL; xdp_autofd int fd = -1; - struct stat st_buf, real_parent_st_buf; + struct stat st_buf, real_dir_st_buf; g_auto(GStrv) ids = NULL; g_autofree char *id = NULL; GError *error = NULL; @@ -1141,7 +1161,7 @@ portal_lookup (GDBusMethodInvocation *invocation, return TRUE; } - if (!validate_fd (fd, app_info, &st_buf, &real_parent_st_buf, &path, NULL, &error)) + if (!validate_fd (fd, app_info, FALSE, &st_buf, &real_dir_st_buf, &path, NULL, &error)) { g_dbus_method_invocation_take_error (invocation, error); return TRUE; @@ -1151,6 +1171,7 @@ portal_lookup (GDBusMethodInvocation *invocation, { /* The passed in fd is on the fuse filesystem itself */ id = xdp_fuse_lookup_id_for_inode (st_buf.st_ino); + /* TODO: Handle directory documents here */ g_debug ("path on fuse, id %s", id); } else @@ -1159,8 +1180,8 @@ portal_lookup (GDBusMethodInvocation *invocation, data = g_variant_ref_sink (g_variant_new ("(^ayttu)", path, - (guint64)real_parent_st_buf.st_dev, - (guint64)real_parent_st_buf.st_ino, + (guint64)real_dir_st_buf.st_dev, + (guint64)real_dir_st_buf.st_ino, 0)); ids = permission_db_list_ids_by_value (db, data); if (ids[0] != NULL) diff --git a/document-portal/document-portal.h b/document-portal/document-portal.h index 26159a5..079cb37 100644 --- a/document-portal/document-portal.h +++ b/document-portal/document-portal.h @@ -25,6 +25,7 @@ gboolean validate_fd (int fd, XdpAppInfo *app_info, + gboolean is_directory, struct stat *st_buf, struct stat *real_parent_st_buf, char **path_out, diff --git a/document-portal/document-store.h b/document-portal/document-store.h index 1d4a15d..5af5d7e 100644 --- a/document-portal/document-store.h +++ b/document-portal/document-store.h @@ -9,6 +9,7 @@ G_BEGIN_DECLS #define DOCUMENT_ENTRY_FLAG_UNIQUE (1 << 0) #define DOCUMENT_ENTRY_FLAG_TRANSIENT (1 << 1) +#define DOCUMENT_ENTRY_FLAG_DIRECTORY (1 << 2) const char ** xdg_unparse_permissions (DocumentPermissionFlags permissions); DocumentPermissionFlags xdp_parse_permissions (const char **permissions, diff --git a/document-portal/file-transfer.c b/document-portal/file-transfer.c index 73c73f5..eaf96aa 100644 --- a/document-portal/file-transfer.c +++ b/document-portal/file-transfer.c @@ -417,7 +417,7 @@ add_files (GDBusMethodInvocation *invocation, return; } - if (!validate_fd (fd, app_info, &st_buf, &parent_st_buf, &path, &fd_is_writable, NULL) || + if (!validate_fd (fd, app_info, FALSE, &st_buf, &parent_st_buf, &path, &fd_is_writable, NULL) || (transfer->writable && !fd_is_writable)) { g_dbus_method_invocation_return_error (invocation, -- cgit v1.2.3