From f0bf1985c3cf08810924cf040f9b42fb7fbe6d37 Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Tue, 30 Jun 2026 09:52:15 +0200 Subject: [PATCH] libkmipclient: Protect from symlink-following attacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating or writing files, make sure that the file is not a sysmlink. Such files created by libkmipclient are typically stored inside the zkey repository and the owner and mode of them are changed to. allow read/write for the owner user and the 'zkeyadm' group. It would allow a symlink-following attack if the file being created are symlinks. Make sure to open such files with the 'O_NOFOLLOW' flag. Assisted-by: IBM Bob:2.0.0 Signed-off-by: Ingo Franzki Reviewed-by: Finn Callies Signed-off-by: Jan Höppner --- libkmipclient/kmip.c | 6 +++--- libkmipclient/utils.c | 31 +++++++++++++++++++++++++++++++ libkmipclient/utils.h | 2 ++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/libkmipclient/kmip.c b/libkmipclient/kmip.c index 1c3a07d4..7d847798 100644 --- a/libkmipclient/kmip.c +++ b/libkmipclient/kmip.c @@ -1489,7 +1489,7 @@ retry: } if (i == 0 && server_cert_pem != NULL) { - fp = fopen(server_cert_pem, "w"); + fp = fopen_nofollow(server_cert_pem, "w"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to open %s for write", @@ -1507,7 +1507,7 @@ retry: fp = NULL; if (server_pubkey_pem != NULL) { - fp = fopen(server_pubkey_pem, "w"); + fp = fopen_nofollow(server_pubkey_pem, "w"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to open %s " @@ -1533,7 +1533,7 @@ retry: if (i > 0 && cert_chain_pem != NULL) { if (fp == NULL) - fp = fopen(cert_chain_pem, "w"); + fp = fopen_nofollow(cert_chain_pem, "w"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to open %s for write", diff --git a/libkmipclient/utils.c b/libkmipclient/utils.c index ef70c453..0baf586c 100644 --- a/libkmipclient/utils.c +++ b/libkmipclient/utils.c @@ -12,9 +12,11 @@ #include #include +#include #include #include #include +#include #include "utils.h" #include "names.h" @@ -726,3 +728,32 @@ enum kmip_tag kmip_find_v1_attribute_name_tag(struct kmip_node *parent) return 0; } +FILE *fopen_nofollow(const char *path, const char *mode) +{ + int flags = O_NOFOLLOW; + int fd; + FILE *fp; + + /* Determine flags based on mode */ + if (mode[0] == 'r') + flags |= (mode[1] == '+') ? O_RDWR : O_RDONLY; + else if (mode[0] == 'w') + flags |= O_CREAT | O_TRUNC | + ((mode[1] == '+') ? O_RDWR : O_WRONLY); + else if (mode[0] == 'a') + flags |= O_CREAT | O_APPEND | + ((mode[1] == '+') ? O_RDWR : O_WRONLY); + else + return NULL; + + fd = open(path, flags, 0600); + if (fd < 0) + return NULL; + + fp = fdopen(fd, mode); + if (fp == NULL) { + close(fd); + return NULL; + } + return fp; +} diff --git a/libkmipclient/utils.h b/libkmipclient/utils.h index 89734feb..a8ef8c5c 100644 --- a/libkmipclient/utils.h +++ b/libkmipclient/utils.h @@ -58,4 +58,6 @@ void kmip_node_dump(struct kmip_node *node, bool debug); enum kmip_tag kmip_find_v1_attribute_name_tag(struct kmip_node *parent); +FILE *fopen_nofollow(const char *path, const char *mode); + #endif