diff --git a/src/meson.build b/src/meson.build
index 992dcd2a9..bc5b0b61e 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -57,6 +57,7 @@ xdp_utils_includes = include_directories('.')
xdp_utils_sources = files(
'xdp-utils.c',
'xdp-app-info.c',
+ 'xdp-sealed-fd.c',
)
if have_libsystemd
diff --git a/src/xdp-sealed-fd.c b/src/xdp-sealed-fd.c
new file mode 100644
index 000000000..0aa4147d3
--- /dev/null
+++ b/src/xdp-sealed-fd.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright © 2024 GNOME Foundation Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors:
+ * Julian Sparber
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "xdp-utils.h"
+#include "xdp-sealed-fd.h"
+
+static const int required_seals = F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SHRINK;
+
+struct _XdpSealedFd {
+ GObject parent_instance;
+
+ int fd;
+};
+
+G_DEFINE_FINAL_TYPE (XdpSealedFd, xdp_sealed_fd, G_TYPE_OBJECT)
+
+static void
+xdp_sealed_fd_finalize (GObject *object)
+{
+ XdpSealedFd *sealed_fd = XDP_SEALED_FD (object);
+
+ xdp_close_fd (&sealed_fd->fd);
+
+ G_OBJECT_CLASS (xdp_sealed_fd_parent_class)->finalize (object);
+}
+
+static void
+xdp_sealed_fd_class_init (XdpSealedFdClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = xdp_sealed_fd_finalize;
+}
+
+static void
+xdp_sealed_fd_init (XdpSealedFd *sealed_fd)
+{
+ sealed_fd->fd = -1;
+}
+
+XdpSealedFd *
+xdp_sealed_fd_new_take_memfd (int memfd,
+ GError **error)
+{
+ int saved_errno = -1;
+ g_autoptr(XdpSealedFd) sealed_fd = NULL;
+ xdp_autofd int fd = g_steal_fd (&memfd);
+ int seals;
+
+ g_return_val_if_fail (fd != -1, NULL);
+
+ if ((seals = fcntl (fd, F_GET_SEALS)) == -1)
+ {
+ saved_errno = errno;
+
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (saved_errno),
+ "fcntl F_GET_SEALS: %s", g_strerror (saved_errno));
+ return NULL;
+ }
+
+ /* If the seal seal is set and some required seal is missing report EPERM error directly */
+ if ((seals & F_SEAL_SEAL) && (seals & required_seals) != required_seals)
+ saved_errno = EPERM;
+ else if (fcntl (fd, F_ADD_SEALS, required_seals) == -1)
+ saved_errno = errno;
+
+ if (saved_errno != -1)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (saved_errno),
+ "fcntl F_ADD_SEALS: %s", g_strerror (saved_errno));
+ return NULL;
+ }
+
+ sealed_fd = g_object_new (XDP_TYPE_SEALED_FD, NULL);
+ sealed_fd->fd = xdp_steal_fd (&fd);
+
+ return g_steal_pointer (&sealed_fd);
+}
+
+XdpSealedFd *
+xdp_sealed_fd_new_from_bytes (GBytes *bytes,
+ GError **error)
+{
+ gconstpointer bytes_data;
+ gsize bytes_len;
+ xdp_autofd int fd = -1;
+ int saved_errno = -1;
+ g_autoptr(XdpSealedFd) sealed_fd = NULL;
+ char *shm;
+ g_autoptr(GOutputStream) stream = NULL;
+
+ g_return_val_if_fail (bytes != NULL, NULL);
+
+ if ((fd = memfd_create ("xdp-sealed-fd", MFD_ALLOW_SEALING)) == -1)
+ {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (saved_errno),
+ "memfd_create: %s", g_strerror (saved_errno));
+ return NULL;
+ }
+
+ bytes_data = g_bytes_get_data (bytes, &bytes_len);
+
+ if (ftruncate (fd, bytes_len) == -1)
+ {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (saved_errno),
+ "ftruncate: %s", g_strerror (saved_errno));
+ return NULL;
+ }
+
+ if ((shm = (char *) mmap (NULL, bytes_len, PROT_WRITE, MAP_SHARED, fd, 0)) == (void *) -1)
+ {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (saved_errno),
+ "mmap: %s", g_strerror (saved_errno));
+ return NULL;
+ }
+
+ memcpy (shm, bytes_data, bytes_len);
+
+ if (munmap (shm, bytes_len) == -1)
+ {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (saved_errno),
+ "munmap: %s", g_strerror (saved_errno));
+ return NULL;
+ }
+
+ if (fcntl (fd, F_ADD_SEALS, required_seals) == -1)
+ {
+ saved_errno = errno;
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (saved_errno),
+ "fcntl F_ADD_SEALS: %s", g_strerror (saved_errno));
+ return NULL;
+ }
+
+ sealed_fd = g_object_new (XDP_TYPE_SEALED_FD, NULL);
+ sealed_fd->fd = xdp_steal_fd (&fd);
+
+ return g_steal_pointer (&sealed_fd);
+}
+
+XdpSealedFd *
+xdp_sealed_fd_new_from_handle (GVariant *handle,
+ GUnixFDList *fd_list,
+ GError **error)
+{
+ int fd_id;
+ xdp_autofd int fd = -1;
+
+ g_return_val_if_fail (g_variant_is_of_type (handle, G_VARIANT_TYPE_HANDLE), NULL);
+ g_return_val_if_fail (G_IS_UNIX_FD_LIST (fd_list), NULL);
+ g_return_val_if_fail (g_unix_fd_list_get_length (fd_list) > 0, NULL);
+
+ fd_id = g_variant_get_handle (handle);
+ if ((fd = g_unix_fd_list_get (fd_list, fd_id, error)) == -1)
+ return NULL;
+
+ return xdp_sealed_fd_new_take_memfd (xdp_steal_fd (&fd), error);
+}
+
+int
+xdp_sealed_fd_get_fd (XdpSealedFd *sealed_fd)
+{
+ g_return_val_if_fail (XDP_IS_SEALED_FD (sealed_fd), -1);
+
+ return sealed_fd->fd;
+}
+
+int
+xdp_sealed_fd_dup_fd (XdpSealedFd *sealed_fd)
+{
+ g_return_val_if_fail (XDP_IS_SEALED_FD (sealed_fd), -1);
+
+ return dup (sealed_fd->fd);
+}
diff --git a/src/xdp-sealed-fd.h b/src/xdp-sealed-fd.h
new file mode 100644
index 000000000..e5883d85a
--- /dev/null
+++ b/src/xdp-sealed-fd.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2024 GNOME Foundation Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ */
+
+#pragma once
+
+#include
+
+#define XDP_TYPE_SEALED_FD (xdp_sealed_fd_get_type())
+G_DECLARE_FINAL_TYPE (XdpSealedFd,
+ xdp_sealed_fd,
+ XDP, SEALED_FD,
+ GObject)
+
+XdpSealedFd * xdp_sealed_fd_new_take_memfd (int memfd,
+ GError **error);
+XdpSealedFd * xdp_sealed_fd_new_from_bytes (GBytes *bytes,
+ GError **error);
+XdpSealedFd * xdp_sealed_fd_new_from_handle (GVariant *handle,
+ GUnixFDList *fd_list,
+ GError **error);
+int xdp_sealed_fd_get_fd (XdpSealedFd *sealed_fd);
+int xdp_sealed_fd_dup_fd (XdpSealedFd *sealed_fd);
+