document-portal: factor out common code to validate FD

We're going to reuse this in another location.
tingping/wmclass
Cosimo Cecchi 2016-07-11 13:22:35 -07:00
parent 71e60945fd
commit d488914614
1 changed files with 39 additions and 34 deletions

View File

@ -329,19 +329,14 @@ do_create_doc (struct stat *parent_st_buf, const char *path, gboolean reuse_exis
}
static gboolean
validate_fd (int fd,
struct stat *st_buf,
struct stat *real_parent_st_buf,
char *path_buffer,
GError **error)
validate_fd_common (int fd,
struct stat *st_buf,
char *path_buffer,
GError **error)
{
g_autofree char *proc_path = NULL;
ssize_t symlink_size;
g_autofree char *dirname = NULL;
g_autofree char *name = NULL;
int fd_flags;
glnx_fd_close int dir_fd = -1;
struct stat real_st_buf;
proc_path = g_strdup_printf ("/proc/self/fd/%d", fd);
@ -367,13 +362,26 @@ validate_fd (int fd,
}
path_buffer[symlink_size] = 0;
return TRUE;
}
static gboolean
validate_parent_dir (const char *path,
struct stat *st_buf,
struct stat *real_parent_st_buf,
GError **error)
{
g_autofree char *dirname = NULL;
g_autofree char *name = NULL;
glnx_fd_close int dir_fd = -1;
struct stat real_st_buf;
/* 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);
dirname = g_path_get_dirname (path);
name = g_path_get_basename (path);
dir_fd = open (dirname, O_CLOEXEC | O_PATH);
if (fstat (dir_fd, real_parent_st_buf) < 0 ||
@ -391,6 +399,22 @@ validate_fd (int fd,
return TRUE;
}
static gboolean
validate_fd (int fd,
struct stat *st_buf,
struct stat *real_parent_st_buf,
char *path_buffer,
GError **error)
{
if (!validate_fd_common (fd, st_buf, path_buffer, error))
return FALSE;
if (!validate_parent_dir (path_buffer, st_buf, real_parent_st_buf, error))
return FALSE;
return TRUE;
}
static void
portal_add (GDBusMethodInvocation *invocation,
GVariant *parameters,
@ -506,14 +530,14 @@ portal_add_named (GDBusMethodInvocation *invocation,
GUnixFDList *fd_list;
g_autofree char *id = NULL;
g_autofree char *proc_path = NULL;
int parent_fd_id, parent_fd, fds_len, fd_flags;
int parent_fd_id, parent_fd, fds_len;
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(GError) error = NULL;
g_autoptr(GVariant) filename_v = NULL;
@ -548,26 +572,9 @@ portal_add_named (GDBusMethodInvocation *invocation,
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)
if (!validate_fd_common (parent_fd, &parent_st_buf, parent_path_buffer, &error))
{
g_dbus_method_invocation_return_error (invocation,
FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT,
"Invalid fd passed");
g_dbus_method_invocation_return_gerror (invocation, error);
return;
}
@ -579,8 +586,6 @@ portal_add_named (GDBusMethodInvocation *invocation,
return;
}
parent_path_buffer[symlink_size] = 0;
path = g_build_filename (parent_path_buffer, filename, NULL);
g_debug ("portal_add_named %s", path);