<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>AddValidateFS=</varname></term>
+
+ <listitem><para>Takes a boolean argument. If enabled will set the <varname>user.validatefs.gpt_label</varname>,
+ <varname>user.validatefs.gpt_type_uuid</varname> and <varname>user.validatefs.mount_point</varname>
+ extended attributes on the root inode of the formatted file system to the partition label, partition
+ type UUID and the intended mount point for the partition. Defaults to on if
+ <varname>Format=</varname> is used and the specified argument is neither <literal>swap</literal> nor
+ <literal>vfat</literal>.</para>
+
+ <para>These extended attributes are read by
+ <citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and may encode constraints on mounted file systems that must be fulfilled for the system to
+ successfully boot. This is particular important in
+ <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ scenarios, which puts together the mount hierarchy from untrusted data from the GPT partition
+ table. As these extended attributes are stored inside the file system, they are typically
+ authenticated as part of the file system (assuming it is contained in protected volume; i.e. LUKS or
+ dm-verity), and hence may be used to securely validate the matching partition table fields.</para>
+
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
#include "tpm2-util.h"
#include "user-util.h"
#include "utf8.h"
+#include "xattr-util.h"
/* If not configured otherwise use a minimal partition size of 10M */
#define DEFAULT_MIN_SIZE (10ULL*1024ULL*1024ULL)
char *compression;
char *compression_level;
+ int add_validatefs;
+
uint64_t gpt_flags;
int no_auto;
int read_only;
.growfs = -1,
.verity_data_block_size = UINT64_MAX,
.verity_hash_block_size = UINT64_MAX,
+ .add_validatefs = -1,
};
return p;
p->read_only = -1;
p->growfs = -1;
p->verity = VERITY_OFF;
+ p->add_validatefs = false;
partition_mountpoint_free_many(p->mountpoints, p->n_mountpoints);
p->mountpoints = NULL;
return free_and_strdup_warn(&p->format, v);
}
+static bool partition_add_validatefs(const Partition *p) {
+ assert(p);
+
+ if (p->add_validatefs >= 0)
+ return p->add_validatefs;
+
+ return p->format && !STR_IN_SET(p->format, "swap", "vfat");
+}
+
static bool partition_needs_populate(const Partition *p) {
assert(p);
assert(!p->supplement_for || !p->suppressing); /* Avoid infinite recursion */
- return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks) ||
+
+ return !strv_isempty(p->copy_files) ||
+ !strv_isempty(p->make_directories) ||
+ !strv_isempty(p->make_symlinks) ||
+ partition_add_validatefs(p) ||
(p->suppressing && partition_needs_populate(p->suppressing));
}
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
+ { "Partition", "AddValidateFS", config_parse_tristate, 0, &p->add_validatefs },
{}
};
_cleanup_free_ char *filename = NULL;
return 0;
}
+static int do_make_validatefs_xattrs(const Partition *p, const char *root) {
+ int r;
+
+ assert(p);
+ assert(root);
+
+ if (!partition_add_validatefs(p))
+ return 0;
+
+ _cleanup_close_ int fd = open(root, O_DIRECTORY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open root inode '%s': %m", root);
+
+ if (p->new_label) {
+ r = xsetxattr(fd, /* path= */ NULL, AT_EMPTY_PATH, "user.validatefs.gpt_label", p->new_label);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set 'user.validatefs.gpt_label' extended attribute: %m");
+ }
+
+ r = xsetxattr(fd, /* path= */ NULL, AT_EMPTY_PATH, "user.validatefs.gpt_type_uuid", SD_ID128_TO_UUID_STRING(p->type.uuid));
+ if (r < 0)
+ return log_error_errno(r, "Failed to set 'user.validatefs.gpt_type_uuid' extended attribute: %m");
+
+ /* Prefer the data from MountPoint= if specified, otherwise use data we derive from the partition type */
+ _cleanup_strv_free_ char **l = NULL;
+ if (p->n_mountpoints > 0) {
+ FOREACH_ARRAY(m, p->mountpoints, p->n_mountpoints)
+ if (strv_extend(&l, m->where) < 0)
+ return log_oom();
+ } else {
+ const char *m = gpt_partition_type_mountpoint_nulstr(p->type);
+ if (m) {
+ l = strv_split_nulstr(m);
+ if (!l)
+ return log_oom();
+ }
+ }
+
+ if (!strv_isempty(l)) {
+ r = xsetxattr_strv(fd, /* path= */ NULL, AT_EMPTY_PATH, "user.validatefs.mount_point", l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set 'user.validatefs.mount_point' extended attribute: %m");
+ }
+
+ return 0;
+}
+
static int partition_populate_directory(Context *context, Partition *p, char **ret) {
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
const char *vt;
if (r < 0)
return r;
+ r = do_make_validatefs_xattrs(p, root);
+ if (r < 0)
+ return r;
+
log_info("Ready to populate %s filesystem.", p->format);
*ret = TAKE_PTR(root);
if (do_make_symlinks(p, fs) < 0)
_exit(EXIT_FAILURE);
+ if (do_make_validatefs_xattrs(p, fs) < 0)
+ _exit(EXIT_FAILURE);
+
if (make_subvolumes_read_only(p, fs) < 0)
_exit(EXIT_FAILURE);
log_info("Formatting future partition %" PRIu64 ".", p->partno);
/* If we're not writing to a loop device or if we're populating a read-only filesystem, we
- * have to populate using the filesystem's mkfs's --root (or equivalent) option. To do that,
+ * have to populate using the filesystem's mkfs's --root= (or equivalent) option. To do that,
* we need to set up the final directory tree beforehand. */
if (partition_needs_populate(p) && (!t->loop || fstype_is_ro(p->format))) {