zkey: Protect from symlink-following attacks

Files in the zkey repository can be created by any member of the
'zkeyadm' group as well as 'root'. Such files are owned by the creator
and the 'zkeyadm' group, and allow read and write for the owner user
and owner group.

When creating or writing files inside the zkey repository, make sure
that the file is not a sysmlink. That way, only files within the zkey
repository are set to be read/write for the owner user and members of
the 'zkeyadm' group. Make sure to open such files with the 'O_NOFOLLOW'
flag, and use 'lstat()' to check for files and directories.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Ingo Franzki
2026-06-30 09:46:27 +02:00
committed by Jan Höppner
parent 278f4f6fd5
commit 94292dac54
7 changed files with 104 additions and 32 deletions
+31 -11
View File
@@ -382,7 +382,7 @@ static int _save_kms_properties(const struct keystore *keystore,
struct properties *kms_props, bool verbose)
{
char *filename = NULL;
int rc;
int fd = -1, rc = 0;
util_assert(keystore != NULL, "Internal error: keystore is NULL");
util_assert(kms_props != NULL, "Internal error: kms_props is NULL");
@@ -398,19 +398,28 @@ static int _save_kms_properties(const struct keystore *keystore,
goto out;
}
if (chmod(filename, keystore->mode) != 0) {
fd = open(filename, O_RDONLY | O_NOFOLLOW);
if (fd < 0) {
rc = -errno;
warnx("Failed to open '%s': %s", filename, strerror(-rc));
return rc;
}
if (fchmod(fd, keystore->mode) != 0) {
rc = -errno;
warnx("chmod failed on file '%s': %s", filename, strerror(-rc));
return rc;
goto out;
}
if (chown(filename, geteuid(), keystore->owner) != 0) {
if (fchown(fd, geteuid(), keystore->owner) != 0) {
rc = -errno;
warnx("chown failed on file '%s': %s", filename, strerror(-rc));
return rc;
goto out;
}
out:
if (fd >= 0)
close(fd);
if (filename != NULL)
free(filename);
@@ -609,7 +618,7 @@ int bind_kms_plugin(struct keystore *keystore, const char *plugin,
char *plugin_name = NULL;
void *plugin_lib = NULL;
bool created = false;
int rc;
int rc, fd;
util_assert(keystore != NULL, "Internal error: keystore is NULL");
util_assert(plugin != NULL, "Internal error: plugin is NULL");
@@ -649,20 +658,31 @@ int bind_kms_plugin(struct keystore *keystore, const char *plugin,
}
created = true;
if (chmod(config_dir, keystore->mode) != 0) {
fd = open(config_dir, O_RDONLY | O_NOFOLLOW);
if (fd < 0) {
rc = -errno;
warnx("Failed to open '%s': %s", config_dir, strerror(-rc));
goto out;
}
if (fchmod(fd, keystore->mode) != 0) {
rc = -errno;
warnx("chmod failed on directory '%s': %s", config_dir,
strerror(-rc));
return rc;
close(fd);
goto out;
}
if (chown(config_dir, geteuid(), keystore->owner) != 0) {
if (fchown(fd, geteuid(), keystore->owner) != 0) {
rc = -errno;
warnx("chown failed on directory '%s': %s", config_dir,
strerror(-rc));
return rc;
close(fd);
goto out;
}
close(fd);
if (funcs->kms_bind != NULL) {
rc = funcs->kms_bind(config_dir);
if (rc != 0) {