1207 Commits

Author SHA1 Message Date
Julius Pfrommer 617cbcb2ec refactor(plugin): defer default event loop startup until use 2026-10-12 17:08:39 +02:00
Julius Pfrommer 3d390d26b1 Merge remote-tracking branch 'origin/1.5' into merge_15_master
# Conflicts:
#	CHANGES.md
#	src/server/ua_services_attribute.c
#	src/server/ua_transport_tcp.c
#	tests/server/check_accesscontrol.c
2026-10-08 14:33:33 +02:00
Andreas Ebner f265859191 fix(plugins): free the parsed IndexRange in the default history read
readRaw_service_default parsed the IndexRange of each node to read, but
never freed the dimensions of the parsed range. Every raw history read with
an IndexRange leaked them. They are now freed after the backend call.
2026-10-06 18:22:49 +02:00
Niels Beier d7dedf2750 fix(server): allow the owner to transfer a detached Subscription
Remember the identity of the Session on detach. Only a Session
with the same identity can transfer the Subscription.
2026-10-06 07:37:13 +02:00
Julius Pfrommer 6901aedc07 Merge remote-tracking branch 'origin/1.5' into merge_15_master
Port keyless certificate validation into the GDS receiver driver instead
of restoring the removed server implementation. Preserve master's timer
batching, PubSub lifecycle and client HTTP configuration handling.

Adapt Publish cancellation to response tokens and the HEL/fuzz harnesses
to the explicit TCP driver. Keep SKS policy history and custom datatype
ownership intact, including failed replacement configuration cleanup.
Retain TPM error cleanup while enforcing key-handle and nonce lengths.

Validation:
- Full mbedTLS unit-test build with full namespace, GDS, SKS and Query.
- 21 affected suites passed with ASan/UBSan, plus 9 isolated SKS ownership
  and lifecycle checks.
- OpenSSL certificate update, GDS information model and client encryption
  suites passed with sanitizers.
- OPN, MSG and binary-message fuzzers each completed 100 sanitizer runs.
- Both TPM PubSub policy sources passed compilation checks.

The full SKS client suite timed out after multicast UDP sends reported
"No route to host" on this machine; its affected ownership and lifecycle
cases passed separately. No live TPM hardware validation was performed.
2026-10-04 16:49:17 +02:00
Andreas Ebner 918f2cfd4b fix(docs): typos in comments, docs and messages
Fix misspellings found with codespell across headers, sources, examples,
tests and the documentation, including "propably" in the nodeset
compiler docs. Identifiers, companion spec names and generated code are
left untouched.
2026-09-28 16:20:44 +02:00
StalderT e23ad2f4ee feat(plugins): support OpenSSL 4
OpenSSL 4 makes ASN1_STRING opaque, returns const pointers from the X509_NAME, X509_CRL and X509_EXTENSION getters, and deprecates the X509_cmp_*time family and X509_NAME_get_text_by_NID. The plugin no longer built: the ASN1_IA5STRING access is a hard error, and -Werror turns the rest into errors too.

Read ASN1 strings through ASN1_STRING_length and ASN1_STRING_get0_data, keep the getter results const, compare the validity period with ASN1_TIME_cmp_time_t and read the common name through its name entry. The self-signed certificate gets its subject from a fresh X509_NAME instead of editing the one X509_get_subject_name returns, and the CSR duplicates the subjectAltName extension instead of pushing one it does not own. All of this exists since OpenSSL 1.1.1, which the plugin already requires.

Add a CI job that builds OpenSSL 4.0.2 from source like the LibreSSL job does. The runner ships OpenSSL 3, so the job sets OPENSSL_ROOT_DIR to make sure it cannot quietly fall back to the system library.

Refs #8493
2026-09-27 17:40:24 +02:00
Andreas Ebner 44b527efb7 fix(plugins): bound the key handle and nonce copies of the PKCS#11 PubSub policies
The TPM AES-CTR policies copied sizeof(pointer) bytes regardless of the key length,
and setKeys read 8 bytes from the 4-byte key nonce, past the end of the SKS key
buffer. Check the lengths and copy the size of the destination.
2026-09-26 19:17:01 +02:00
Andreas Ebner 45ccdb1fda fix(plugins): release the lock and context on PKCS#11 PubSub setup errors
newContext of the TPM AES-CTR policies leaked the group context on errors, and
the AES128 variant also returned with initLock128_g still held, deadlocking the
next call. Route all error paths through one cleanup.
2026-09-26 19:17:01 +02:00
Andreas Ebner c0be293ee5 fix(plugins): fail closed when a PKCS#11 PubSub encryption fails
On a C_EncryptUpdate/Final error the TPM AES-CTR policies returned GOOD with the
buffer unchanged, so the NetworkMessage was sent in plaintext. Return the error,
also for decryption, and check the allocation before the operation is initialized.
2026-09-26 19:17:01 +02:00
Andreas Ebner c021a5a772 fix(plugins): add the UA_CertificateUtils_getKeySize stub without encryption
certificategroup.h declares it in every configuration, but only the crypto backends defined
it, so a caller failed to link without encryption. Return BadNotSupported like the others.
2026-09-26 13:52:32 +02:00
Bastian Engel 3d36a7ef0e fix(crypto): don't clear the securityPolicy context
The `securityPolicy` context is still in use, so it should not be
cleared. This commit stores changes in intermediate keys and
certificates and on failure clears these instead.
2026-09-25 13:25:54 +02:00
Andreas Ebner 05fae78d75 fix(plugins): parse the async operation settings without multithreading
asyncOperationTimeout and maxAsyncOperationQueueSize exist in every configuration, but the
JSON config parsed them only with UA_MULTITHREADING >= 100. Without it UInt64Field_parseJson
had no caller and -Werror builds failed.
2026-09-25 12:40:49 +02:00
Bastian Engel a3e733cc16 feat(plugins): restrict MAC context digest initialization to initial key setup
Re-initializing the MAC context with the same digest for every message
is unnecessary work. The digest is set once at key import and later
initializations pass NULL.
2026-09-24 16:52:03 +02:00
Andreas Ebner 9cb2a89c35 feat(plugins): implement PubSub-Aes128/256-CTR with OpenSSL and LibreSSL (Part 14 §7.2.4.4.3.2)
Mirrors the mbedTLS policy (Tables 155-157, HMAC-SHA256 over the whole
NetworkMessage). A group context caches the AES and the MAC context. The
MAC is re-keyed per message as OpenSSL 3.0 cannot restart it otherwise.
2026-09-24 16:52:03 +02:00
Andreas Ebner 1c903aa45b fix(plugins): wipe the P_SHA scratch memory in the OpenSSL backend
The P_SHA256/P_SHA1 key derivation left the derived key stream, the
secret, the seed and the A(n) chain in freed heap memory. The SKS
derives every group key this way. Buffer and context are now cleansed.
2026-09-24 16:52:03 +02:00
Julius Pfrommer e01e2310af Merge remote-tracking branch 'origin/1.4' into merge_14_15 2026-09-24 12:31:21 +02:00
Niels Beier c0277aa87c fix(client): reserve SecurityPolicy array headroom to avoid reallocation
UA_ClientConfig_setDefaultEncryption reallocates config->securityPolicies.
A SecureChannel that is already open holds a raw pointer into that array
(e.g. to the "None" policy), so reallocating it -- which may move the
array -- left that pointer dangling and crashed on the next use.

UA_ClientConfig_setDefault now reserves enough headroom for the policies
UA_ClientConfig_setDefaultEncryption adds, so the common case of calling
it once needs no reallocation at all: the array's address never changes,
so an already-open channel's pointer stays valid. This also preserves
in-place updates to a policy already in use (e.g. UA_Server_updateCertificate
mutating a policy's certificate), which a previous per-channel-copy
approach would have hidden from already-open channels.

Fixes #6943
2026-09-24 08:46:45 +02:00
Julius Pfrommer 07b632a6f7 fix(client): verify selected CreateSession endpoint
Compare the selected discovery endpoint with its authenticated counterpart
from CreateSession using only the seven fields specified by Part 4. Ignore
optional metadata and the duplicated endpoint certificate, which servers may
omit. Keep the separate certificate validation unchanged.

Reject mismatches by default, with configurable warning and acceptance modes.
Skip the comparison for directly configured endpoints. Cover omitted metadata,
endpoint reordering, mismatch handling and trusted endpoint configuration.
2026-09-23 17:07:09 +02:00
Julius Pfrommer 7b3a849f8e Merge remote-tracking branch 'origin/1.3' into 1.4
# Conflicts:
#	plugins/ua_pubsub_udp.c
#	src/pubsub/ua_pubsub_ns0.c
#	tests/server/check_accesscontrol.c
2026-09-23 10:35:30 +02:00
Julius Pfrommer 4c9d20f124 fix(logging): Handle out-of-range log categories 2026-09-23 07:24:15 +02:00
Julius Pfrommer 4f419cf62e fix(server): Validate endpoint security modes before lookup 2026-09-23 07:24:15 +02:00
Julius Pfrommer 758f798438 fix(plugin): guard subscription-only configuration helpers
Exclude range-initialization helpers when subscriptions are disabled so unused-function warnings do
not fail builds with warnings treated as errors.
2026-09-21 16:23:09 +02:00
Julius Pfrommer 9a43b33a4b Merge remote-tracking branch 'origin/1.5' into merge_15_master
# Conflicts:
#	plugins/crypto/mbedtls/create_certificate.c
#	plugins/crypto/mbedtls/securitypolicy_aes256sha256rsapss.c
#	plugins/crypto/mbedtls/securitypolicy_common.c
#	src/client/ua_client.c
#	src/pubsub/ua_pubsub_config.c
#	src/pubsub/ua_pubsub_keystorage.c
#	src/pubsub/ua_pubsub_keystorage.h
#	src/pubsub/ua_pubsub_ns0.c
#	src/pubsub/ua_pubsub_ns0_sks.c
#	src/pubsub/ua_pubsub_securitygroup.c
#	src/pubsub/ua_pubsub_writer.c
#	src/pubsub/ua_pubsub_writergroup.c
#	src/server/ua_server_ns0.c
#	src/server/ua_server_ns0_gds.c
#	src/server/ua_server_utils.c
#	src/server/ua_services_session.c
#	src/server/ua_subscription_alarms_conditions.c
#	src/ua_types_encoding_xml.c
#	tests/check_types_builtin_xml.c
#	tests/encryption/check_cert_generation.c
#	tests/pubsub/check_pubsub_configuration.c
#	tests/pubsub/check_pubsub_informationmodel_methods.c
#	tests/pubsub/check_pubsub_publish.c
#	tests/pubsub/check_pubsub_sks_client.c
#	tests/pubsub/check_pubsub_sks_keystorage.c
#	tests/pubsub/check_pubsub_sks_push.c
#	tests/pubsub/check_pubsub_sks_securitygroups.c
#	tests/server/check_server_alarmsconditions.c
2026-09-20 10:02:09 +02:00
Julius Pfrommer 30b54d61af fix(crypto): bound PEM certificate-chain search
Search the certificate input for the next PEM header with the haystack and needle in the correct order. Use a length-bounded helper so partial headers at the end cannot read past the input.
2026-09-18 18:16:02 +02:00
Julius Pfrommer 0677590700 fix(crypto): reject certificates without an application URI
The exact-comparison change in f4c2dc2aa made an absent URI compare equal to an empty ApplicationUri. Require a non-empty URI SAN before accepting the certificate.
2026-09-18 18:16:02 +02:00
Julius Pfrommer 3065049520 fix(securitypolicy): validate RSA-PSS signature length
The mbedTLS RSA-PSS verifier consumes one complete RSA modulus but its low-level API does not take an input length. Reject malformed ByteStrings before invoking it so a short application signature cannot cause an out-of-bounds read.
2026-09-18 18:16:02 +02:00
Julius Pfrommer 9f28ac4ac8 fix(securitypolicy): validate raw ECDSA signature lengths
Reject malformed raw ECDSA signatures before converting their r and s coordinates. Short signatures could otherwise cause out-of-bounds reads in the OpenSSL backend and an underflowing bounds check in the mbedTLS backend; oversized mbedTLS signatures could overflow the fixed DER buffer.
2026-09-18 18:16:02 +02:00
Julius Pfrommer f782a38cb6 fix(pubsub): validate TPM key handles and nonce
Require serialized PKCS#11 handles and the specified four-byte key nonce before copying key material into the TPM channel context. This rejects incompatible raw SKS keys and prevents out-of-bounds reads from undersized inputs.
2026-09-18 12:14:58 +02:00
Julius Pfrommer 25aa35db42 fix(pubsub): bound UDP interface-name stack use
Reject interface names that do not fit the transport's fixed input buffer and replace the request-sized stack allocation with a fixed-size array.
2026-09-18 12:14:58 +02:00
Julius Pfrommer c15f297b8d Merge remote-tracking branch 'origin/1.4' into merge_14_15
# Conflicts:
#	CHANGELOG
#	CMakeLists.txt
#	plugins/crypto/openssl/securitypolicy_openssl_common.c
#	plugins/crypto/pkcs11/securitypolicy_pubsub_aes128ctr_tpm.c
#	plugins/crypto/pkcs11/securitypolicy_pubsub_aes256ctr_tpm.c
#	src/pubsub/ua_pubsub_networkmessage.c
#	src/pubsub/ua_pubsub_ns0.c
#	src/pubsub/ua_pubsub_writer.c
#	src/pubsub/ua_pubsub_writergroup.c
#	src/server/ua_services_monitoreditem.c
#	src/server/ua_services_session.c
#	src/server/ua_session.h
#	tests/CMakeLists.txt
#	tests/check_securechannel.c
#	tests/pubsub/check_pubsub_encryption.c
#	tests/pubsub/check_pubsub_publish.c
#	tests/server/check_server_alarmsconditions.c
2026-09-17 21:58:21 +02:00
Julius Pfrommer c3742e2a5b Merge remote-tracking branch 'origin/1.3' into merge_13_14 2026-09-17 19:53:50 +02:00
Niels Beier 54eaa5b3bf fix(pubsub): fix OOB read/write and truncation in AES-CTR TPM en/decrypt
encrypt_pubsub_aes128ctr_tpm/decrypt_pubsub_aes128ctr_tpm and their
AES-256-CTR counterparts computed the block-aligned processing size
(sizeToEncrypt/sizeToDecrypt) by rounding data->length up to the next
16-byte boundary, but stored the result in a CK_BYTE. Two distinct bugs
followed from this:

- For a data->length whose rounded-up value exceeds 255, the 8-bit value
  wraps around, so far fewer bytes than intended are processed while the
  function still returns UA_STATUSCODE_GOOD.
- For any data->length that is not itself a multiple of 16, the rounded-up
  size is larger than the caller's actual buffer. The PKCS#11 update calls
  then read block-sized chunks directly from data->data past its end, and
  the final loop that copies the result back writes that same rounded-up
  size into data->data, overflowing the caller's allocation.

AES-CTR is a stream cipher and never needs block-aligned input. Fix this
by processing exactly data->length bytes throughout, using size_t/CK_ULONG
for all lengths instead of CK_BYTE: each PKCS#11 update call is chunked at
MAX_ENCRYPTION_SIZE bytes with a shorter final chunk when data->length is
not itself a multiple of it, operating on the caller's buffer directly
with no rounding, no padding, and no risk of the output exceeding the
caller's allocation.

Co-authored-by: Luigi Colluto <gigioneggiando@users.noreply.github.com>

Internal Vulnerability Advisory: open62541-SA-2026-0033
2026-09-16 07:11:01 +02:00
Niels Beier 1c1cbbdf8a fix(crypto): use constant-time, length-checked comparison for HMAC/MAC verification
The OpenSSL HMAC-SHA1/SHA256 verify helpers compared the recomputed
MAC against the peer-supplied signature with UA_ByteString_equal(),
which reduces to a plain memcmp(). memcmp() carries no constant-time
guarantee and, on a byte-at-a-time implementation (the norm on the
small-footprint libcs open62541 is routinely built against), returns
as soon as it finds a differing byte -- a timing side channel a
remote peer can use to recover a valid MAC byte-by-byte.

Switch to UA_constantTimeEqual(), already used correctly by every
other symmetric SecurityPolicy (mbedTLS Basic128Rsa15, Basic256,
Basic256Sha256, Aes128Sha256RsaOaep). Since it takes a single length
and reads that many bytes from both buffers, first validate
signature->length against the expected MAC length, mirroring the
pattern used by the project's mbedTLS RSA policy Verify functions.
The digest length is fixed by the algorithm and policy, not attacker
data, so this early-return introduces no new timing side channel.

Internal Vulnerability Advisory: open62541-SA-2026-0038
Co-authored-by: Luigi Colluto <gigioneggiando@users.noreply.github.com>
2026-09-15 20:31:48 +02:00
Niels Beier b6bd2cf88d fix(pubsub): fix OOB read/write and truncation in AES-CTR TPM en/decrypt
encrypt_sp_pubsub_aes128ctr_tpm/decrypt_sp_pubsub_aes128ctr_tpm and their
AES-256-CTR counterparts computed the block-aligned processing size
(sizeToEncrypt/sizeToDecrypt) by rounding data->length up to the next
16-byte boundary, but stored the result in a CK_BYTE. Two distinct bugs
followed from this:

- For a data->length whose rounded-up value exceeds 255, the 8-bit value
  wraps around, so far fewer bytes than intended are processed while the
  function still returns UA_STATUSCODE_GOOD.
- For any data->length that is not itself a multiple of 16, the rounded-up
  size is larger than the caller's actual buffer. The PKCS#11 update calls
  then read block-sized chunks directly from data->data past its end, and
  the final loop that copies the result back writes that same rounded-up
  size into data->data, overflowing the caller's allocation.

AES-CTR is a stream cipher and never needs block-aligned input. Fix this
by processing exactly data->length bytes throughout, using size_t/CK_ULONG
for all lengths instead of CK_BYTE: each PKCS#11 update call is chunked at
MAX_ENCRYPTION_SIZE bytes with a shorter final chunk when data->length is
not itself a multiple of it, operating on the caller's buffer directly
with no rounding, no padding, and no risk of the output exceeding the
caller's allocation.

Co-authored-by: Luigi Colluto <gigioneggiando@users.noreply.github.com>

Internal Vulnerability Advisory: open62541-SA-2026-0033
2026-09-15 20:10:23 +02:00
Julius Pfrommer e938a497dc fix(historydata): validate circular continuation point skip
Reject circular HistoryRead continuation points whose skip exceeds the available result count, and cap the backend copy to the allocated response capacity. A forged skip could otherwise underflow the copy length and cause a heap-buffer-overflow when the response size was capped.
2026-09-14 12:54:49 +02:00
Julius Pfrommer 7e419a3c4c fix(history): enforce UserAccessLevel for history access
The default HistoryDatabase checked only the Variable node AccessLevel. Apply the session-specific AccessControl getUserAccessLevel result as well so clients denied HISTORYREAD or HISTORYWRITE cannot read, insert, replace, update or delete stored values.
2026-09-14 12:54:25 +02:00
Julius Pfrommer f4588beae7 fix(history): handle result allocation failures
Return BadOutOfMemory before invoking the read backend or writing HistoryUpdate operation results when their per-result allocations fail. This prevents null pointers from reaching the backend and avoids a null write under memory pressure.
2026-09-14 12:53:43 +02:00
StalderT 1a67cf7ab0 fix(arch,plugins): remove the non-threadsafe gmtime and inet_ntoa calls
gmtime and inet_ntoa return a pointer to a static buffer shared by all threads. The mbedTLS certificate creation now derives its validity dates from UA_DateTime_now/UA_DateTime_toStruct, which also keeps the expiry date in UTC (mktime interpreted the gmtime result as local time). Zephyr uses gmtime_r and lwIP ip4addr_ntoa_r.

Fixes #7997
2026-09-13 17:54:16 +02:00
srberard 53dbd13c6d fix(server): fix the build when UA_DEBUG_DUMP_PKGS is defined
Use the by-value msg parameter and drop the unreachable plugin include.
2026-09-09 00:04:33 +02:00
Julius Pfrommer d2bc0bccb5 fix(pubsub): validate security policy contracts early 2026-09-05 20:57:32 +02:00
Julius Pfrommer 548bc703d6 fix(pubsub): harden SKS key lifecycle and policy contracts 2026-09-05 20:57:32 +02:00
Julius Pfrommer 7314745ece Merge pull request #8382 from o6-automation/merge_15_master
Merge 1.5 to master
2026-09-05 20:49:08 +02:00
Julius Pfrommer d3e968d143 feat(nodeset-loader): move loader into core utility 2026-09-05 19:31:54 +02:00
Julius Pfrommer fb2f813b30 feat(server): allow unattached methods by rule 2026-09-05 19:31:54 +02:00
Julius Pfrommer 4ee7a57015 Merge remote-tracking branch 'origin/1.5' into merge_15_master 2026-09-05 16:20:52 +02:00
Julius Pfrommer 132deca9c0 Merge remote-tracking branch 'origin/1.4' into merge_14_15 2026-09-05 09:49:43 +02:00
Julius Pfrommer 36fbc14811 Merge remote-tracking branch 'origin/1.3' into merge_13_14 2026-09-04 16:57:38 +02:00
Niels Beier 7a08139590 fix(historydata): prevent UAF in memory backend range-limited history reads
UA_DataValue_backend_copyRange() performed a shallow memcpy of the
source UA_DataValue before attempting the range copy. Because the
destination's UA_Variant fields were not reinitialized first, a
failing UA_Variant_copyRange() (for example when the client-supplied
indexRange exceeds UA_MAX_ARRAY_DIMS) left the destination value as an
uninitialized alias of the backend-owned storage instead of an
independent copy.

copyDataValues_backend_memory() also discarded the status code from
both UA_DataValue_backend_copyRange() and UA_DataValue_copy(), so the
aliased, partially-copied value was still counted as a successful
result and forwarded to the HistoryRead response. Freeing that
response later released the backend's live storage, leaving the
backend with a dangling pointer that was dereferenced on a subsequent
HistoryRead.

Reinitialize the destination variant before the range copy and clear
it again on failure, mirroring the safe pattern already used by
UA_DataValue_copyRange(). Propagate copy failures out of
copyDataValues_backend_memory() immediately instead of counting a
failed entry as provided.

Internal Vulnerability Advisory: open62541-SA-2026-0021
Co-authored-by: gff-cw <gff-cw@users.noreply.github.com>
2026-09-04 13:20:06 +02:00
Julius Pfrommer 46a74698bb 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.
2026-09-03 17:43:01 +02:00