fix(plugin): generate valid filestore names for certificates and CRLs

Use the common name when available and fall back to the subject for CRLs and
certificates without a common name.

Check path formatting, propagate storage errors, and clean up trust-list data
on every exit path.
This commit is contained in:
Julius Pfrommer
2026-09-03 17:43:01 +02:00
parent a476806541
commit 46a74698bb
3 changed files with 114 additions and 42 deletions
+42 -40
View File
@@ -129,48 +129,43 @@ getCertFileName(const char *path, const char *fileExt,
const UA_ByteString *certificate, char *fileNameBuf,
size_t fileNameLen) {
/* Check parameter */
if(path == NULL || fileExt == NULL || certificate == NULL || fileNameBuf == NULL)
if(path == NULL || fileExt == NULL || certificate == NULL ||
fileNameBuf == NULL || fileNameLen == 0)
return UA_STATUSCODE_BADINTERNALERROR;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
UA_Byte thumbprintData[40];
UA_String thumbprint = {sizeof(thumbprintData), thumbprintData};
UA_StatusCode retval = UA_CertificateUtils_getThumbprint(
(UA_ByteString*)(uintptr_t)certificate, &thumbprint);
if(retval != UA_STATUSCODE_GOOD)
return retval;
UA_String thumbprint = UA_STRING_NULL;
thumbprint.length = 40;
thumbprint.data = (UA_Byte*)UA_calloc(thumbprint.length, sizeof(UA_Byte));
UA_String commonName = UA_STRING_NULL;
UA_CertificateUtils_getThumbprint((UA_ByteString*)(uintptr_t)certificate, &thumbprint);
UA_CertificateUtils_getCertCommonName((UA_ByteString*)(uintptr_t)certificate, &commonName);
if((thumbprint.length + commonName.length + 2) > fileNameLen) {
UA_String_clear(&thumbprint);
UA_String_clear(&commonName);
UA_String name = UA_STRING_NULL;
retval = UA_CertificateUtils_getCertCommonName(certificate, &name);
if(retval != UA_STATUSCODE_GOOD) {
retval = UA_CertificateUtils_getSubjectName(
(UA_ByteString*)(uintptr_t)certificate, &name);
}
if(retval != UA_STATUSCODE_GOOD)
return retval;
if(name.length > UA_INT32_MAX) {
UA_String_clear(&name);
return UA_STATUSCODE_BADINTERNALERROR;
}
char *thumbprintBuffer = (char*)UA_malloc(thumbprint.length + 1);
char *commonNameBuffer = (char*)UA_malloc(commonName.length + 1);
memcpy(thumbprintBuffer, thumbprint.data, thumbprint.length);
thumbprintBuffer[thumbprint.length] = '\0';
memcpy(commonNameBuffer, commonName.data, commonName.length);
commonNameBuffer[commonName.length] = '\0';
for(char *c = commonNameBuffer; *c; c++) {
if(*c == '/' || *c == '\\')
*c = '_';
for(size_t i = 0; i < name.length; i++) {
if(name.data[i] == '/' || name.data[i] == '\\' || name.data[i] < 0x20)
name.data[i] = '_';
}
if(mp_snprintf(fileNameBuf, fileNameLen, "%s/%s[%s]%s", path,
commonNameBuffer,
thumbprintBuffer, fileExt) < 0)
int len = mp_snprintf(fileNameBuf, fileNameLen, "%s/%.*s[%.*s]%s", path,
(int)name.length, (char*)name.data,
(int)thumbprint.length,
(char*)thumbprint.data, fileExt);
if(len < 0 || (size_t)len >= fileNameLen)
retval = UA_STATUSCODE_BADINTERNALERROR;
UA_String_clear(&thumbprint);
UA_String_clear(&commonName);
UA_free(thumbprintBuffer);
UA_free(commonNameBuffer);
UA_String_clear(&name);
return retval;
}
@@ -340,7 +335,7 @@ writeCertificates(const UA_ByteString *list, size_t listSize,
char filename[UA_PATH_MAX] = {0};
retval = getCertFileName(listPath, fileExt, &list[i], filename, UA_PATH_MAX);
if(retval != UA_STATUSCODE_GOOD)
return UA_STATUSCODE_BADINTERNALERROR;
return retval;
/* Store data in file */
retval = writeByteStringToFile(filename, &list[i]);
@@ -361,7 +356,10 @@ writeTrustList(const UA_ByteString *list, size_t listSize,
return UA_STATUSCODE_BADINTERNALERROR;
char listPath[UA_PATH_MAX] = {0};
mp_snprintf(listPath, UA_PATH_MAX, "%.*s", (int)path.length, (char *)path.data);
int len = mp_snprintf(listPath, UA_PATH_MAX, "%.*s",
(int)path.length, (char*)path.data);
if(len < 0 || len >= UA_PATH_MAX)
return UA_STATUSCODE_BADINTERNALERROR;
/* remove existing files in directory */
UA_StatusCode retval = removeAllFilesFromDir(listPath, false);
if(retval != UA_STATUSCODE_GOOD)
@@ -382,37 +380,41 @@ writeTrustStore(UA_CertificateGroup *certGroup, const UA_UInt32 trustListMask) {
UA_TrustListDataType_init(&trustList);
trustList.specifiedLists = trustListMask;
context->store->getTrustList(context->store, &trustList);
UA_StatusCode retval =
context->store->getTrustList(context->store, &trustList);
if(retval != UA_STATUSCODE_GOOD)
goto cleanup;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
if(trustList.specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES) {
retval = writeTrustList(trustList.trustedCertificates,
trustList.trustedCertificatesSize, context->trustedCertFolder,
".der");
if(retval != UA_STATUSCODE_GOOD)
return retval;
goto cleanup;
}
if(trustList.specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCRLS) {
retval = writeTrustList(trustList.trustedCrls,
trustList.trustedCrlsSize, context->trustedCrlFolder,
".crl");
if(retval != UA_STATUSCODE_GOOD)
return retval;
goto cleanup;
}
if(trustList.specifiedLists & UA_TRUSTLISTMASKS_ISSUERCERTIFICATES) {
retval = writeTrustList(trustList.issuerCertificates,
trustList.issuerCertificatesSize, context->issuerCertFolder,
".der");
if(retval != UA_STATUSCODE_GOOD)
return retval;
goto cleanup;
}
if(trustList.specifiedLists & UA_TRUSTLISTMASKS_ISSUERCRLS) {
retval = writeTrustList(trustList.issuerCrls,
trustList.issuerCrlsSize, context->issuerCrlFolder,
".crl");
if(retval != UA_STATUSCODE_GOOD)
return retval;
goto cleanup;
}
cleanup:
UA_TrustListDataType_clear(&trustList);
return retval;
@@ -92,7 +92,7 @@ UA_CertificateGroup_Filestore(UA_CertificateGroup *certGroup,
const UA_String storePath,
const UA_Logger *logger,
const UA_KeyValueMap *params);
#endif /* defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) */
#endif /* supported filestore platforms */
#endif /* UA_ENABLE_ENCRYPTION */
+71 -1
View File
@@ -9,6 +9,7 @@
#include <open62541/client.h>
#include <open62541/client_config_default.h>
#include <open62541/plugin/certificategroup_default.h>
#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>
@@ -19,13 +20,18 @@
#include <stdlib.h>
#include "test_helpers.h"
#include "../common.h"
#include "certificates.h"
#include "certificate_eku.h"
#include "check.h"
#include "thread_wrapper.h"
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32)
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || \
defined(__APPLE__) || defined(__OpenBSD__)
#include "mp_printf.h"
#endif
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32)
#define TEST_PATH_MAX 256
#endif /* defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) */
@@ -588,6 +594,63 @@ START_TEST(verify_expired_certificate_status_depends_on_trust) {
}
END_TEST
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || \
defined(__APPLE__) || defined(__OpenBSD__)
START_TEST(filestore_uses_crl_issuer_in_filename) {
UA_CertificateGroup group;
memset(&group, 0, sizeof(group));
UA_NodeId groupId = UA_NS0ID(
SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
UA_String storePath = UA_STRING("pki-crl-filename-test");
UA_StatusCode retval = UA_CertificateGroup_Filestore(
&group, &groupId, storePath, UA_Log_Stdout, NULL);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_ByteString caCertificate = {ROOT_CERT_DER_LENGTH, ROOT_CERT_DER_DATA};
UA_ByteString crl = {ROOT_EMPTY_CRL_PEM_LENGTH, ROOT_EMPTY_CRL_PEM_DATA};
UA_TrustListDataType trustList;
UA_TrustListDataType_init(&trustList);
trustList.specifiedLists = UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES |
UA_TRUSTLISTMASKS_TRUSTEDCRLS;
trustList.trustedCertificates = &caCertificate;
trustList.trustedCertificatesSize = 1;
trustList.trustedCrls = &crl;
trustList.trustedCrlsSize = 1;
retval = group.setTrustList(&group, &trustList);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_String issuer = UA_STRING_NULL;
retval = UA_CertificateUtils_getSubjectName(&crl, &issuer);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
for(size_t i = 0; i < issuer.length; i++) {
if(issuer.data[i] == '/' || issuer.data[i] == '\\' || issuer.data[i] < 0x20)
issuer.data[i] = '_';
}
UA_Byte thumbprintData[40];
UA_String thumbprint = {sizeof(thumbprintData), thumbprintData};
retval = UA_CertificateUtils_getThumbprint(&crl, &thumbprint);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
char filename[2048];
int len = mp_snprintf(
filename, sizeof(filename),
"pki-crl-filename-test/ApplCerts/trusted/crl/%.*s[%.*s].crl",
(int)issuer.length, (char*)issuer.data,
(int)thumbprint.length, (char*)thumbprint.data);
ck_assert_int_ge(len, 0);
ck_assert_uint_lt((size_t)len, sizeof(filename));
UA_ByteString storedCrl = loadFile(filename);
ck_assert(UA_ByteString_equal(&storedCrl, &crl));
UA_ByteString_clear(&storedCrl);
UA_String_clear(&issuer);
group.clear(&group);
}
END_TEST
#endif
#ifdef UA_ENABLE_ENCRYPTION_MBEDTLS
START_TEST(memorystore_rejects_invalid_initial_trust_material) {
UA_Byte invalidData[] = {0x01, 0x02, 0x03};
@@ -793,6 +856,13 @@ static Suite* testSuite_encryption(void) {
suite_add_tcase(s,tc_encryption_filestore);
#endif /* UA_ENABLE_ENCRYPTION */
#endif /* defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) */
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || \
defined(__APPLE__) || defined(__OpenBSD__)
TCase *tc_filestore_filename = tcase_create("CertificateGroup filenames");
tcase_add_test(tc_filestore_filename, filestore_uses_crl_issuer_in_filename);
suite_add_tcase(s, tc_filestore_filename);
#endif
return s;
}