From a3a2d70603811e3703d8e831b07487dd9e0f1c3e Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Tue, 30 Jun 2026 09:51:34 +0200 Subject: [PATCH] libekmfweb: 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 libekmfweb 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 --- libekmfweb/ekmfweb.c | 8 ++++---- libekmfweb/utilities.c | 40 ++++++++++++++++++++++++++++++++++++---- libekmfweb/utilities.h | 2 ++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/libekmfweb/ekmfweb.c b/libekmfweb/ekmfweb.c index 268e6474..2ccf1db4 100644 --- a/libekmfweb/ekmfweb.c +++ b/libekmfweb/ekmfweb.c @@ -233,7 +233,7 @@ static int _ekmf_extract_pubkey(const char *cert, const char *pub_key_pem, goto out; } - fp = fopen(pub_key_pem, "w"); + fp = fopen_nofollow(pub_key_pem, "w"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "File '%s': %s", pub_key_pem, @@ -458,7 +458,7 @@ retry: out); if (server_cert_pem != NULL) { - fp = fopen(server_cert_pem, "w"); + fp = fopen_nofollow(server_cert_pem, "w"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "File '%s': %s", server_cert_pem, @@ -495,7 +495,7 @@ retry: */ if (ci->num_of_certs > 1 && ca_bundle_pem != NULL && do_verify == 0) { - fp = fopen(ca_bundle_pem, "w"); + fp = fopen_nofollow(ca_bundle_pem, "w"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "File '%s': %s", @@ -1415,7 +1415,7 @@ int ekmf_login(const struct ekmf_config *config, CURL **curl_handle, while (*tok == ' ') tok++; - fp = fopen(config->login_token, "w"); + fp = fopen_nofollow(config->login_token, "w"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "Failed to open file %s: '%s'", diff --git a/libekmfweb/utilities.c b/libekmfweb/utilities.c index 10960273..1ccf934b 100644 --- a/libekmfweb/utilities.c +++ b/libekmfweb/utilities.c @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -1931,7 +1933,7 @@ int write_key_blob(const char *filename, unsigned char *key_blob, if (filename == NULL || key_blob == NULL || key_blob_len == 0) return -EINVAL; - fp = fopen(filename, "w"); + fp = fopen_nofollow(filename, "w"); if (fp == NULL) return -errno; @@ -2052,7 +2054,7 @@ int write_x509_certificate(const char *pem_filename, X509 *cert) if (pem_filename == NULL || cert == NULL) return -EINVAL; - fp = fopen(pem_filename, "w"); + fp = fopen_nofollow(pem_filename, "w"); if (fp == NULL) return -errno; @@ -2086,7 +2088,7 @@ int write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr) if (pem_filename == NULL || req == NULL) return -EINVAL; - fp = fopen(pem_filename, "w"); + fp = fopen_nofollow(pem_filename, "w"); if (fp == NULL) return -errno; @@ -2154,7 +2156,7 @@ int write_public_key(const char *pem_filename, EVP_PKEY *pkey) if (pem_filename == NULL || pkey == NULL) return -EINVAL; - fp = fopen(pem_filename, "w"); + fp = fopen_nofollow(pem_filename, "w"); if (fp == NULL) return -errno; @@ -2606,3 +2608,33 @@ int json_object_object_add_ex(struct json_object *obj, const char *const key, return 0; } #endif + +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/libekmfweb/utilities.h b/libekmfweb/utilities.h index f460449b..19120238 100644 --- a/libekmfweb/utilities.h +++ b/libekmfweb/utilities.h @@ -124,4 +124,6 @@ int json_object_object_add_ex(struct json_object *obj, const char *const key, const unsigned int opts); #endif +FILE *fopen_nofollow(const char *path, const char *mode); + #endif