libekmfweb: Protect from symlink-following attacks

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 <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:51:34 +02:00
committed by Jan Höppner
parent 226728731c
commit a3a2d70603
3 changed files with 42 additions and 8 deletions

View File

@@ -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'",

View File

@@ -9,9 +9,11 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
@@ -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;
}

View File

@@ -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