Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/1.4' into merge_14_master_24
Browse files Browse the repository at this point in the history
  • Loading branch information
jpfr committed Nov 29, 2024
2 parents bfaec71 + 9e59f1a commit 7956d72
Show file tree
Hide file tree
Showing 17 changed files with 1,164 additions and 431 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ Pipfile.lock
.vscode
/.vs
**/libxml2
debian/*
/CMakeSettings.json
# clangd cache
.cache/
# project local vim settings
Expand Down
42 changes: 22 additions & 20 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# overwritten with more detailed information if git is available.
set(OPEN62541_VER_MAJOR 1)
set(OPEN62541_VER_MINOR 4)
set(OPEN62541_VER_PATCH 7)
set(OPEN62541_VER_PATCH 8)
set(OPEN62541_VER_LABEL "-undefined") # like "-rc1" or "-g4538abcd" or "-g4538abcd-dirty"
set(OPEN62541_VER_COMMIT "unknown-commit")

Expand Down Expand Up @@ -496,6 +496,11 @@ if(UA_ENABLE_TPM2_SECURITY)
list(APPEND open62541_LIBRARIES ${TPM2_LIB})
endif()

if(MINGW)
# GCC stack protector support
list(APPEND open62541_LIBRARIES ws2_32 ssp)
endif()

#####################
# Compiler Settings #
#####################
Expand Down Expand Up @@ -917,7 +922,8 @@ set(plugin_sources ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_hashmap.c
${PROJECT_SOURCE_DIR}/plugins/ua_config_default.c
${PROJECT_SOURCE_DIR}/plugins/crypto/ua_certificategroup_none.c
${PROJECT_SOURCE_DIR}/plugins/crypto/ua_securitypolicy_none.c)
${PROJECT_SOURCE_DIR}/plugins/crypto/ua_securitypolicy_none.c
${PROJECT_SOURCE_DIR}/plugins/ua_config_default.c)

if(UA_ARCHITECTURE_POSIX OR UA_ARCHITECTURE_WIN32)
list(APPEND plugin_sources
Expand Down Expand Up @@ -1045,20 +1051,22 @@ endif()
set(UA_FILE_NODESETS) # List of nodeset-xml files to be considered in the generated information model
set(UA_NODESET_DIR ${PROJECT_SOURCE_DIR}/deps/ua-nodeset CACHE STRING "The path to the node-set directory (e.g. from https://github.com/OPCFoundation/UA-Nodeset)")

unset(UA_FILE_NS0_PRIVATE)
if(UA_FILE_NS0)
set(UA_FILE_NS0_PRIVATE "${UA_FILE_NS0}")
endif()

if(UA_NAMESPACE_ZERO STREQUAL "FULL")
# Use the "full" schema files also for datatypes and statuscodes
set(UA_SCHEMA_DIR ${UA_NODESET_DIR}/Schema CACHE INTERNAL "")

# Set the full Nodeset for NS0.
# Use a new variable UA_FILE_NS0_INTERNAL so we don't pollute the options.
if(UA_FILE_NS0)
set(UA_FILE_NS0_INTERNAL ${UA_FILE_NS0})
else()
set(UA_FILE_NS0_INTERNAL ${UA_SCHEMA_DIR}/Opc.Ua.NodeSet2.xml CACHE INTERNAL "")
# Set the full Nodeset for NS0
if(NOT UA_FILE_NS0_PRIVATE)
set(UA_FILE_NS0_PRIVATE ${UA_SCHEMA_DIR}/Opc.Ua.NodeSet2.xml)
endif()

# Check that the submodule was checked out or manually downloaded into the folder
if(NOT EXISTS "${UA_FILE_NS0_INTERNAL}")
if(NOT EXISTS "${UA_FILE_NS0_PRIVATE}")
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
Expand All @@ -1071,12 +1079,9 @@ else()
# Directory with the schema files for installation
set(UA_SCHEMA_DIR ${PROJECT_SOURCE_DIR}/tools/schema CACHE INTERNAL "")

# Set the reduced Nodeset for NS0.
# Use a new variable UA_FILE_NS0_INTERNAL so we don't pollute the options.
if(UA_FILE_NS0)
set(UA_FILE_NS0_INTERNAL ${UA_FILE_NS0})
else()
set(UA_FILE_NS0_INTERNAL ${UA_SCHEMA_DIR}/Opc.Ua.NodeSet2.Reduced.xml CACHE INTERNAL "")
# Set the reduced Nodeset for NS0
if(NOT UA_FILE_NS0_PRIVATE)
set(UA_FILE_NS0_PRIVATE ${UA_SCHEMA_DIR}/Opc.Ua.NodeSet2.Reduced.xml CACHE INTERNAL "")
endif()

# Set feature-specific datatypes definitions and nodesets
Expand Down Expand Up @@ -1135,7 +1140,7 @@ else()
endif()
endif()

list(INSERT UA_FILE_NODESETS 0 "${UA_FILE_NS0_INTERNAL}")
list(INSERT UA_FILE_NODESETS 0 "${UA_FILE_NS0_PRIVATE}")
set(UA_FILE_NODEIDS ${UA_SCHEMA_DIR}/NodeIds.csv)
set(UA_FILE_STATUSCODES ${UA_SCHEMA_DIR}/StatusCode.csv)
set(UA_FILE_TYPES_BSD ${UA_SCHEMA_DIR}/Opc.Ua.Types.bsd)
Expand All @@ -1161,7 +1166,6 @@ ua_generate_datatypes(INTERNAL NAME "transport" TARGET_SUFFIX "transport" NAMESP
# statuscode explanation
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/open62541/statuscodes.h
${PROJECT_BINARY_DIR}/src_generated/open62541/statuscodes.c
PRE_BUILD
COMMAND ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_statuscode_descriptions.py
${UA_FILE_STATUSCODES} ${PROJECT_BINARY_DIR}/src_generated/open62541/statuscodes
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_statuscode_descriptions.py
Expand All @@ -1181,15 +1185,13 @@ add_custom_target(open62541-generator-statuscode DEPENDS
if(UA_ENABLE_AMALGAMATION)
# single-file release
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.h
PRE_BUILD
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
${OPEN62541_VERSION} ${CMAKE_CURRENT_BINARY_DIR}/open62541.h
${exported_headers} ${NODESETLOADER_PUBLIC_HEADERS} ${plugin_headers}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
${exported_headers} ${plugin_headers})

add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.c
PRE_BUILD
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
${OPEN62541_VERSION} ${CMAKE_CURRENT_BINARY_DIR}/open62541.c
${lib_headers} ${NODESETLOADER_PRIVATE_HEADERS} ${lib_sources} ${plugin_sources}
Expand All @@ -1216,7 +1218,7 @@ if(UA_ENABLE_NODESET_INJECTOR)
message(STATUS "Nodesetinjector feature enabled")
cmake_minimum_required(VERSION 3.20)
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/open62541/nodesetinjector.h
${PROJECT_BINARY_DIR}/src_generated/open62541/nodesetinjector.c PRE_BUILD
${PROJECT_BINARY_DIR}/src_generated/open62541/nodesetinjector.c
COMMAND ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_injector/generate_nodesetinjector.py
${PROJECT_BINARY_DIR}/src_generated/open62541/nodesetinjector)
add_custom_target(open62541-generator-nodesetinjector DEPENDS
Expand Down
2 changes: 0 additions & 2 deletions doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ list(APPEND GENERATED_RST "")
# Generated type definitions
add_custom_command(OUTPUT ${DOC_SRC_DIR}/types_generated.rst
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/src_generated/open62541/types_generated.rst ${DOC_SRC_DIR}
PRE_BUILD
DEPENDS ${PROJECT_BINARY_DIR}/src_generated/open62541/types_generated.rst)
list(APPEND GENERATED_RST ${DOC_SRC_DIR}/types_generated.rst)

macro(generate_rst in out)
add_custom_command(OUTPUT ${out}
DEPENDS ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in}
PRE_BUILD
COMMAND ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in} ${out})
list(APPEND GENERATED_RST "${out}")
endmacro()
Expand Down
117 changes: 79 additions & 38 deletions plugins/crypto/mbedtls/certificategroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ static const struct {
{{0, UA_STRING_STATIC("max-rejected-listsize")}, &UA_TYPES[UA_TYPES_STRING], false}
};

struct MemoryCertStore;
typedef struct MemoryCertStore MemoryCertStore;

struct MemoryCertStore {
typedef struct {
UA_TrustListDataType trustList;
size_t rejectedCertificatesSize;
UA_ByteString *rejectedCertificates;
Expand All @@ -59,7 +56,9 @@ struct MemoryCertStore {
mbedtls_x509_crt issuerCertificates;
mbedtls_x509_crl trustedCrls;
mbedtls_x509_crl issuerCrls;
};

UA_CertificateGroup *cg;
} MemoryCertStore;

static UA_Boolean mbedtlsCheckCA(mbedtls_x509_crt *cert);

Expand Down Expand Up @@ -372,10 +371,17 @@ mbedtlsSameName(UA_String name, const mbedtls_x509_name *name2) {
return UA_String_equal(&name, &nameString);
}

static UA_Boolean
mbedtlsSameBuf(mbedtls_x509_buf *a, mbedtls_x509_buf *b) {
if(a->len != b->len)
return false;
return (memcmp(a->p, b->p, a->len) == 0);
}

/* Return the first matching issuer candidate AFTER prev.
* This can return the cert itself if self-signed. */
static mbedtls_x509_crt *
mbedtlsFindNextIssuer(MemoryCertStore *context, mbedtls_x509_crt *stack,
mbedtlsFindNextIssuer(MemoryCertStore *ctx, mbedtls_x509_crt *stack,
mbedtls_x509_crt *cert, mbedtls_x509_crt *prev) {
char inbuf[UA_MBEDTLS_MAX_DN_LENGTH];
int nameLen = mbedtls_x509_dn_gets(inbuf, UA_MBEDTLS_MAX_DN_LENGTH, &cert->issuer);
Expand All @@ -395,30 +401,57 @@ mbedtlsFindNextIssuer(MemoryCertStore *context, mbedtls_x509_crt *stack,
mbedtls_pk_can_do(&i->pk, cert->MBEDTLS_PRIVATE(sig_pk)))
return i;
}
/* Switch from the stack that came with the cert to the ctx->skIssue list */
stack = (stack != &context->issuerCertificates) ? &context->issuerCertificates: NULL;

/* Switch from the stack that came with the cert to the issuer list and
* then to the trust list. */
if(stack == &ctx->trustedCertificates)
stack = NULL;
else if(stack == &ctx->issuerCertificates)
stack = &ctx->trustedCertificates;
else
stack = &ctx->issuerCertificates;
} while(stack);
return NULL;
}

static UA_Boolean
mbedtlsCheckRevoked(MemoryCertStore *context, mbedtls_x509_crt *cert) {
static UA_StatusCode
mbedtlsCheckRevoked(MemoryCertStore *ctx, mbedtls_x509_crt *cert) {
/* Parse the Issuer Name */
char inbuf[UA_MBEDTLS_MAX_DN_LENGTH];
int nameLen = mbedtls_x509_dn_gets(inbuf, UA_MBEDTLS_MAX_DN_LENGTH, &cert->issuer);
if(nameLen < 0)
return true;
return UA_STATUSCODE_BADINTERNALERROR;
UA_String issuerName = {(size_t)nameLen, (UA_Byte*)inbuf};
for(mbedtls_x509_crl *crl = &context->trustedCrls; crl; crl = crl->next) {
if(mbedtlsSameName(issuerName, &crl->issuer) &&
mbedtls_x509_crt_is_revoked(cert, crl) != 0)
return true;

if(ctx->trustedCrls.raw.len == 0 && ctx->issuerCrls.raw.len == 0) {
UA_LOG_WARNING(ctx->cg->logging, UA_LOGCATEGORY_SECURITYPOLICY,
"Zero revocation lists have been loaded. "
"This seems intentional - omitting the check.");
return UA_STATUSCODE_GOOD;
}
for(mbedtls_x509_crl *crl = &context->issuerCrls; crl; crl = crl->next) {
if(mbedtlsSameName(issuerName, &crl->issuer) &&
mbedtls_x509_crt_is_revoked(cert, crl) != 0)
return true;

/* Loop over the crl and match the Issuer Name */
UA_StatusCode res = UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN;
for(mbedtls_x509_crl *crl = &ctx->trustedCrls; crl; crl = crl->next) {
/* Is the CRL for certificates from the cert issuer?
* Is the serial number of the certificate contained in the CRL? */
if(mbedtlsSameName(issuerName, &crl->issuer)) {
if(mbedtls_x509_crt_is_revoked(cert, crl) != 0)
return UA_STATUSCODE_BADCERTIFICATEREVOKED;
res = UA_STATUSCODE_GOOD; /* There was at least one crl that did not revoke (so far) */
}
}

/* Loop over the issuer crls separately */
for(mbedtls_x509_crl *crl = &ctx->issuerCrls; crl; crl = crl->next) {
if(mbedtlsSameName(issuerName, &crl->issuer)) {
if(mbedtls_x509_crt_is_revoked(cert, crl) != 0)
return UA_STATUSCODE_BADCERTIFICATEREVOKED;
res = UA_STATUSCODE_GOOD;
}
}
return false;

return res;
}

/* Verify that the public key of the issuer was used to sign the certificate */
Expand All @@ -445,7 +478,8 @@ mbedtlsCheckSignature(const mbedtls_x509_crt *cert, mbedtls_x509_crt *issuer) {
}

static UA_StatusCode
mbedtlsVerifyChain(MemoryCertStore *context, mbedtls_x509_crt *stack, mbedtls_x509_crt **old_issuers,
mbedtlsVerifyChain(MemoryCertStore *ctx, mbedtls_x509_crt *stack,
mbedtls_x509_crt **old_issuers,
mbedtls_x509_crt *cert, int depth) {
/* Maxiumum chain length */
if(depth == UA_MBEDTLS_MAX_CHAIN_LENGTH)
Expand All @@ -457,11 +491,6 @@ mbedtlsVerifyChain(MemoryCertStore *context, mbedtls_x509_crt *stack, mbedtls_x5
return (depth == 0) ? UA_STATUSCODE_BADCERTIFICATETIMEINVALID :
UA_STATUSCODE_BADCERTIFICATEISSUERTIMEINVALID;

/* Verification Step: Revocation Check */
if(mbedtlsCheckRevoked(context, cert))
return (depth == 0) ? UA_STATUSCODE_BADCERTIFICATEREVOKED :
UA_STATUSCODE_BADCERTIFICATEISSUERREVOKED;

/* Return the most specific error code. BADCERTIFICATECHAININCOMPLETE is
* returned only if all possible chains are incomplete. */
mbedtls_x509_crt *issuer = NULL;
Expand All @@ -470,7 +499,7 @@ mbedtlsVerifyChain(MemoryCertStore *context, mbedtls_x509_crt *stack, mbedtls_x5
/* Find the issuer. This can return the same certificate if it is
* self-signed (subject == issuer). We come back here to try a different
* "path" if a subsequent verification fails. */
issuer = mbedtlsFindNextIssuer(context, stack, cert, issuer);
issuer = mbedtlsFindNextIssuer(ctx, stack, cert, issuer);
if(!issuer)
break;

Expand All @@ -491,32 +520,43 @@ mbedtlsVerifyChain(MemoryCertStore *context, mbedtls_x509_crt *stack, mbedtls_x5
* chain. We check whether the certificate is trusted below. This is the
* only place where we return UA_STATUSCODE_BADCERTIFICATEUNTRUSTED.
* This signals that the chain is complete (but can be still
* untrusted). */
if(issuer == cert || (cert->tbs.len == issuer->tbs.len &&
memcmp(cert->tbs.p, issuer->tbs.p, cert->tbs.len) == 0)) {
* untrusted).
*
* Break here as we have reached the end of the chain. Omit the
* Revocation Check for self-signed certificates. */
if(issuer == cert || mbedtlsSameBuf(&cert->tbs, &issuer->tbs)) {
ret = UA_STATUSCODE_BADCERTIFICATEUNTRUSTED;
continue;
break;
}

/* Detect (endless) loops of issuers. The last one can be skipped by the
* check for self-signed just before. */
for(int i = 0; i < depth - 1; i++) {
/* Verification Step: Revocation Check */
ret = mbedtlsCheckRevoked(ctx, cert);
if(depth > 0) {
if(ret == UA_STATUSCODE_BADCERTIFICATEREVOKED)
ret = UA_STATUSCODE_BADCERTIFICATEISSUERREVOKED;
if(ret == UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN)
ret = UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN;
}
if(ret != UA_STATUSCODE_GOOD)
continue;

/* Detect (endless) loops of issuers */
for(int i = 0; i < depth; i++) {
if(old_issuers[i] == issuer)
return UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE;
}
old_issuers[depth] = issuer;

/* We have found the issuer certificate used for the signature. Recurse
* to the next certificate in the chain (verify the current issuer). */
ret = mbedtlsVerifyChain(context, stack, old_issuers, issuer, depth + 1);
ret = mbedtlsVerifyChain(ctx, stack, old_issuers, issuer, depth + 1);
}

/* The chain is complete, but we haven't yet identified a trusted
* certificate "on the way down". Can we trust this certificate? */
if(ret == UA_STATUSCODE_BADCERTIFICATEUNTRUSTED) {
for(mbedtls_x509_crt *t = &context->trustedCertificates; t; t = t->next) {
if(cert->tbs.len == t->tbs.len &&
memcmp(cert->tbs.p, t->tbs.p, cert->tbs.len) == 0)
for(mbedtls_x509_crt *t = &ctx->trustedCertificates; t; t = t->next) {
if(mbedtlsSameBuf(&cert->tbs, &t->tbs))
return UA_STATUSCODE_GOOD;
}
}
Expand Down Expand Up @@ -627,6 +667,7 @@ UA_CertificateGroup_Memorystore(UA_CertificateGroup *certGroup,
retval = UA_STATUSCODE_BADOUTOFMEMORY;
goto cleanup;
}
context->cg = certGroup;
certGroup->context = context;
/* Default values */
context->maxTrustListSize = 65535;
Expand Down
Loading

0 comments on commit 7956d72

Please sign in to comment.